use std::collections::BTreeMap;
use std::io::{Result, Write};
#[derive(Debug, Clone)]
pub struct Xref {
pub cross_reference_type: XrefType,
pub entries: BTreeMap<u32, XrefEntry>,
pub size: u32,
}
#[derive(Debug, Clone, Copy)]
pub enum XrefType {
CrossReferenceStream,
CrossReferenceTable,
}
#[derive(Debug, Clone)]
pub enum XrefEntry {
Free, UnusableFree,
Normal { offset: u32, generation: u16 },
Compressed { container: u32, index: u16 },
}
#[derive(Debug, Clone)]
pub struct XrefSection {
pub starting_id: u32,
pub entries: Vec<XrefEntry>,
}
impl Xref {
pub fn new(size: u32, xref_type: XrefType) -> Xref {
Xref {
cross_reference_type: xref_type,
entries: BTreeMap::new(),
size,
}
}
pub fn get(&self, id: u32) -> Option<&XrefEntry> {
self.entries.get(&id)
}
pub fn insert(&mut self, id: u32, entry: XrefEntry) {
self.entries.insert(id, entry);
}
pub fn merge(&mut self, xref: Xref) {
for (id, entry) in xref.entries {
self.entries.entry(id).or_insert(entry);
}
}
pub fn clear(&mut self) {
self.entries.clear()
}
pub fn max_id(&self) -> u32 {
match self.entries.keys().max() {
Some(&id) => id,
None => 0,
}
}
}
impl XrefEntry {
pub fn is_normal(&self) -> bool {
matches!(*self, XrefEntry::Normal { .. })
}
pub fn is_compressed(&self) -> bool {
matches!(*self, XrefEntry::Compressed { .. })
}
pub fn encode_for_xref_stream(&self, widths: &[usize; 3]) -> Vec<u8> {
let mut result = Vec::new();
match self {
XrefEntry::Free | XrefEntry::UnusableFree => {
encode_field(0, widths[0], &mut result);
encode_field(0, widths[1], &mut result); encode_field(0, widths[2], &mut result); }
XrefEntry::Normal { offset, generation } => {
encode_field(1, widths[0], &mut result);
encode_field(*offset as u64, widths[1], &mut result);
encode_field(*generation as u64, widths[2], &mut result);
}
XrefEntry::Compressed { container, index } => {
encode_field(2, widths[0], &mut result);
encode_field(*container as u64, widths[1], &mut result);
encode_field(*index as u64, widths[2], &mut result);
}
}
result
}
pub fn write_xref_entry(&self, file: &mut dyn Write) -> Result<()> {
match self {
XrefEntry::Normal { offset, generation } => {
writeln!(file, "{offset:>010} {generation:>05} n ")?;
}
XrefEntry::Compressed { container: _, index: _ } => {
writeln!(file, "{:>010} {:>05} f ", 0, 65535)?;
}
XrefEntry::Free => {
writeln!(file, "{:>010} {:>05} f ", 0, 0)?;
}
XrefEntry::UnusableFree => {
writeln!(file, "{:>010} {:>05} f ", 0, 65535)?;
}
}
Ok(())
}
}
impl XrefSection {
pub fn new(starting_id: u32) -> Self {
XrefSection {
starting_id,
entries: Vec::new(),
}
}
pub fn add_entry(&mut self, entry: XrefEntry) {
self.entries.push(entry);
}
pub fn add_unusable_free_entry(&mut self) {
self.add_entry(XrefEntry::UnusableFree);
}
pub fn is_empty(&self) -> bool {
self.entries.is_empty()
}
pub fn write_xref_section(&self, file: &mut dyn Write) -> Result<()> {
if !self.is_empty() {
writeln!(file, "{} {}", self.starting_id, self.entries.len())?;
for entry in &self.entries {
entry.write_xref_entry(file)?;
}
}
Ok(())
}
}
pub use crate::parser_aux::decode_xref_stream;
fn encode_field(value: u64, width: usize, output: &mut Vec<u8>) {
for i in (0..width).rev() {
output.push((value >> (i * 8)) as u8);
}
}
pub struct XrefStreamBuilder<'a> {
xref: &'a Xref,
entries: Vec<(u32, &'a XrefEntry)>,
widths: [usize; 3],
}
impl<'a> XrefStreamBuilder<'a> {
pub fn new(xref: &'a Xref) -> Self {
let entries: Vec<_> = xref.entries.iter()
.map(|(&id, entry)| (id, entry))
.collect();
Self {
xref,
entries,
widths: [1, 2, 2], }
}
pub fn entries_count(&self) -> usize {
self.entries.len()
}
pub fn calculate_optimal_widths(&self) -> [usize; 3] {
let mut max_offset = 0u64;
let mut max_gen = 0u16;
let mut max_container = 0u32;
let mut max_index = 0u16;
for (_, entry) in &self.entries {
match entry {
XrefEntry::Normal { offset, generation } => {
max_offset = max_offset.max(*offset as u64);
max_gen = max_gen.max(*generation);
}
XrefEntry::Compressed { container, index } => {
max_container = max_container.max(*container);
max_index = max_index.max(*index);
}
_ => {}
}
}
let offset_bytes = bytes_needed(max_offset);
let gen_bytes = bytes_needed(max_gen as u64);
let container_bytes = bytes_needed(max_container as u64);
let index_bytes = bytes_needed(max_index as u64);
[
1, offset_bytes.max(container_bytes),
gen_bytes.max(index_bytes),
]
}
pub fn build_stream_content(&mut self) -> crate::Result<Vec<u8>> {
self.widths = self.calculate_optimal_widths();
let mut content = Vec::new();
self.entries.sort_by_key(|(id, _)| *id);
for (_, entry) in &self.entries {
let encoded = entry.encode_for_xref_stream(&self.widths);
content.extend_from_slice(&encoded);
}
Ok(content)
}
pub fn build_index_array(&self) -> Vec<crate::Object> {
use crate::Object;
let mut index = Vec::new();
let mut sorted_entries = self.entries.clone();
sorted_entries.sort_by_key(|(id, _)| *id);
if sorted_entries.is_empty() {
return index;
}
let mut start = sorted_entries[0].0;
let mut count = 1;
for i in 1..sorted_entries.len() {
if sorted_entries[i].0 == sorted_entries[i-1].0 + 1 {
count += 1;
} else {
index.push(Object::Integer(start as i64));
index.push(Object::Integer(count as i64));
start = sorted_entries[i].0;
count = 1;
}
}
index.push(Object::Integer(start as i64));
index.push(Object::Integer(count as i64));
index
}
pub fn to_stream_object(&mut self) -> crate::Result<crate::Stream> {
use crate::{dictionary, Object, Stream};
let content = self.build_stream_content()?;
let dict = dictionary! {
"Type" => "XRef",
"Size" => self.xref.size as i64,
"W" => vec![
Object::Integer(self.widths[0] as i64),
Object::Integer(self.widths[1] as i64),
Object::Integer(self.widths[2] as i64),
],
"Index" => self.build_index_array(),
"Filter" => "FlateDecode"
};
let mut stream = Stream::new(dict, content);
stream.compress()?;
Ok(stream)
}
}
fn bytes_needed(value: u64) -> usize {
if value == 0 {
1
} else {
(64 - value.leading_zeros()).div_ceil(8) as usize
}
}