use std::{
error::Error,
fmt,
io::{self, Write},
};
pub fn ascii_85_encode<W: Write>(data: &[u8], w: &mut W) -> io::Result<usize> {
let mut ctr = 0;
let mut cut = 75;
let mut chunks_exact = data.chunks_exact(4);
for group in &mut chunks_exact {
let buf = u32::from_be_bytes([group[0], group[1], group[2], group[3]]);
if buf == 0 {
w.write_all(&[0x7A])?;
ctr += 1;
} else {
let (c_5, buf) = ((buf % 85) as u8 + 33, buf / 85);
let (c_4, buf) = ((buf % 85) as u8 + 33, buf / 85);
let (c_3, buf) = ((buf % 85) as u8 + 33, buf / 85);
let (c_2, buf) = ((buf % 85) as u8 + 33, buf / 85);
let c_1 = buf as u8 + 33;
w.write_all(&[c_1, c_2, c_3, c_4, c_5])?;
ctr += 5;
}
if ctr >= cut {
w.write_all(&[0x0A])?;
ctr += 1;
cut = ctr + 75;
}
}
match *chunks_exact.remainder() {
[b_1] => {
let buf = u32::from_be_bytes([b_1, 0, 0, 0]) / (85 * 85 * 85);
let (c_2, buf) = ((buf % 85) as u8 + 33, buf / 85);
let c_1 = buf as u8 + 33;
w.write_all(&[c_1, c_2, 0x7E, 0x3E])?;
ctr += 4;
}
[b_1, b_2] => {
let buf = u32::from_be_bytes([b_1, b_2, 0, 0]) / (85 * 85);
let (c_3, buf) = ((buf % 85) as u8 + 33, buf / 85);
let (c_2, buf) = ((buf % 85) as u8 + 33, buf / 85);
let c_1 = buf as u8 + 33;
w.write_all(&[c_1, c_2, c_3, 0x7E, 0x3E])?;
ctr += 5;
}
[b_1, b_2, b_3] => {
let buf = u32::from_be_bytes([b_1, b_2, b_3, 0]) / 85;
let (c_4, buf) = ((buf % 85) as u8 + 33, buf / 85);
let (c_3, buf) = ((buf % 85) as u8 + 33, buf / 85);
let (c_2, buf) = ((buf % 85) as u8 + 33, buf / 85);
let c_1 = buf as u8 + 33;
w.write_all(&[c_1, c_2, c_3, c_4, 0x7E, 0x3E])?;
ctr += 6;
}
_ => {
w.write_all(&[0x7E, 0x3E])?;
ctr += 2;
}
}
Ok(ctr)
}
#[derive(Debug)]
pub struct PDFDocEncodingError(char);
impl Error for PDFDocEncodingError {}
impl fmt::Display for PDFDocEncodingError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"Codepoint U+{:04x} is not valid in PDFDocEncoding",
self.0 as u32
)
}
}
fn pdf_char_encode(chr: char) -> Result<u8, PDFDocEncodingError> {
match u32::from(chr) {
0x00..=0x17 | 0x20..=0x7E | 0xA1..=0xff => Ok(chr as u8),
0x02D8 => Ok(0x18),
0x02C7 => Ok(0x19),
0x02C6 => Ok(0x1A),
0x02D9 => Ok(0x1B),
0x02DD => Ok(0x1C),
0x02DB => Ok(0x1D),
0x02DA => Ok(0x1E),
0x02DC => Ok(0x1F),
0x2022 => Ok(0x80),
0x2020 => Ok(0x81),
0x2021 => Ok(0x82),
0x2026 => Ok(0x83),
0x2014 => Ok(0x84),
0x2013 => Ok(0x85),
0x0192 => Ok(0x86),
0x2044 => Ok(0x87),
0x2039 => Ok(0x88),
0x203A => Ok(0x89),
0x2212 => Ok(0x8A),
0x2030 => Ok(0x8B),
0x201E => Ok(0x8C),
0x201C => Ok(0x8D),
0x201D => Ok(0x8E),
0x2018 => Ok(0x8F),
0x2019 => Ok(0x90),
0x201A => Ok(0x91),
0x2122 => Ok(0x92),
0xFB01 => Ok(0x93),
0xFB02 => Ok(0x94),
0x0141 => Ok(0x95),
0x0152 => Ok(0x96),
0x0160 => Ok(0x97),
0x0178 => Ok(0x98),
0x017D => Ok(0x99),
0x0131 => Ok(0x9A),
0x0142 => Ok(0x9B),
0x0153 => Ok(0x9C),
0x0161 => Ok(0x9D),
0x017e => Ok(0x9E),
0x20AC => Ok(0xA0),
_ => Err(PDFDocEncodingError(chr)),
}
}
pub fn pdf_doc_encode(input: &str) -> Result<Vec<u8>, PDFDocEncodingError> {
input.chars().map(pdf_char_encode).collect()
}