use crate::hnc8::Variant;
use crate::{Cancellation, Context, Error, ErrorKind, Hnc8Stage};
pub(crate) use arith_encoder::{MqEncoder, QmEncoder};
pub(crate) use errors::{
field_of, kind_name, page_image, pdf_class, segment, stage_of, variant_of,
};
use std::sync::atomic::{AtomicUsize, Ordering};
pub(crate) mod arith_encoder;
mod errors;
pub(crate) fn mq_encoder() -> MqEncoder {
MqEncoder::new(
&crate::jbig2::mq::STANDARD_STATES
.map(|state| (state.qe, state.next_mps, state.next_lps, state.switch_mps)),
)
}
pub(crate) fn qm_encoder() -> QmEncoder {
QmEncoder::new(
&crate::qm::STANDARD_STATES
.map(|state| (state.qe, state.next_mps, state.next_lps, state.switch_mps)),
)
}
#[derive(Debug)]
pub(crate) struct CancelAfter {
queries: AtomicUsize,
allowed: u64,
}
pub(crate) static NEVER: CancelAfter = CancelAfter::new(u64::MAX);
impl CancelAfter {
pub(crate) const fn new(allowed: u64) -> Self {
Self {
queries: AtomicUsize::new(0),
allowed,
}
}
pub(crate) fn never() -> Self {
Self::new(u64::MAX)
}
pub(crate) fn always() -> Self {
Self::new(0)
}
pub(crate) fn queries(&self) -> u64 {
self.queries.load(Ordering::Relaxed) as u64
}
}
impl Cancellation for CancelAfter {
fn is_cancelled(&self) -> bool {
self.queries.fetch_add(1, Ordering::Relaxed) as u64 >= self.allowed
}
}
pub(crate) fn bilevel_pixels(pdf: &[u8]) -> Vec<Vec<u8>> {
let mut images = Vec::new();
let marker = b"/Subtype /Image";
let mut from = 0;
while let Some(at) = pdf[from..]
.windows(marker.len())
.position(|part| part == marker)
{
let at = from + at;
let data = at
+ pdf[at..]
.windows(7)
.position(|part| part == b"stream\n")
.unwrap()
+ 7;
let dictionary = String::from_utf8_lossy(&pdf[at..data]);
if dictionary.contains("/BitsPerComponent 1\n") {
assert!(dictionary.contains("/Filter /FlateDecode"));
let (pixels, consumed) = inflate(&pdf[data..]);
images.push(pixels);
from = data + consumed;
} else {
from = data;
}
}
images
}
fn inflate(stream: &[u8]) -> (Vec<u8>, usize) {
use std::io::Read;
let mut decoder = flate2::read::ZlibDecoder::new(stream);
let mut bytes = Vec::new();
decoder.read_to_end(&mut bytes).unwrap();
(bytes, decoder.total_in() as usize)
}
pub(crate) fn inflated_stream(pdf: &[u8], marker: &[u8]) -> Vec<u8> {
let at = pdf
.windows(marker.len())
.position(|part| part == marker)
.unwrap();
let data = at
+ pdf[at..]
.windows(7)
.position(|part| part == b"stream\n")
.unwrap()
+ 7;
inflate(&pdf[data..]).0
}
pub(crate) fn pdf_text(pdf: &[u8]) -> String {
const DICTIONARY: &[u8] = b" 0 R\n/Filter /FlateDecode\n>>\nstream\n";
let mut out = Vec::new();
let mut from = 0;
while let Some(at) = pdf[from..]
.windows(DICTIONARY.len())
.position(|part| part == DICTIONARY)
{
let data = from + at + DICTIONARY.len();
out.extend_from_slice(&pdf[from..data]);
let (bytes, consumed) = inflate(&pdf[data..]);
out.extend(bytes);
from = data + consumed;
}
out.extend_from_slice(&pdf[from..]);
String::from_utf8_lossy(&out).into_owned()
}
fn trace(seed: u64, contexts: usize, length: usize) -> Vec<(usize, bool)> {
let mut state = seed;
(0..length)
.map(|_| {
state = state
.wrapping_mul(6_364_136_223_846_793_005)
.wrapping_add(1_442_695_040_888_963_407);
let context = (state >> 33) as usize % contexts;
let bit = (state >> 20).is_multiple_of(7);
(context, bit ^ context.is_multiple_of(3))
})
.collect()
}
#[test]
fn encoded_mq_traces_decode_back_with_the_standard_states() {
use crate::Limits;
use crate::jbig2::mq::{CodedSpan, ContextBank, MqDecoder, MqTable};
for (seed, contexts, length) in [
(1, 1, 0),
(2, 1, 1),
(3, 1, 500),
(4, 7, 3000),
(5, 300, 9000),
] {
let decisions = trace(seed, contexts, length);
let mut encoder = mq_encoder();
for &(context, bit) in &decisions {
encoder.encode(context, bit);
}
let bytes = encoder.finish();
let limits = Limits::default();
let mut bank = ContextBank::new(contexts, &limits).unwrap();
let table = MqTable::standard();
let mut decoder = MqDecoder::new(
(&bytes[..]).into(),
CodedSpan {
offset: 0,
length: bytes.len() as u64,
},
&table,
&mut bank,
&limits,
)
.unwrap();
for &(context, bit) in &decisions {
assert_eq!(decoder.decode_bit(context).unwrap(), bit);
}
decoder.finish(length as u64).unwrap();
}
}
#[test]
fn encoded_qm_traces_decode_back_with_the_standard_states() {
use crate::Limits;
use crate::qm::{ArithmeticDecoder, CodedSpan, ContextBank, QmTable};
for (seed, contexts, length) in [
(1, 1, 0),
(2, 1, 1),
(3, 1, 500),
(4, 7, 3000),
(5, 300, 9000),
] {
let decisions = trace(seed, contexts, length);
let mut encoder = qm_encoder();
for &(context, bit) in &decisions {
encoder.encode(context, bit);
}
assert_eq!(encoder.decisions(), length as u64);
let bytes = encoder.finish();
let limits = Limits::default();
let mut bank = ContextBank::new(contexts, &limits).unwrap();
let table = QmTable::standard();
let mut decoder = ArithmeticDecoder::new(
(&bytes[..]).into(),
CodedSpan {
offset: 0,
length: bytes.len() as u64,
},
&table,
&mut bank,
&limits,
)
.unwrap();
for &(context, bit) in &decisions {
assert_eq!(decoder.decode_symbol(context).unwrap(), bit);
}
decoder.finish(length as u64).unwrap();
}
}