use crate::object::{ObjRef, PdfDict, PdfObject, PdfStream};
struct XRefEntry {
offset: u64,
gen: u16,
in_use: bool,
}
pub struct PdfWriter {
buf: Vec<u8>,
xref: Vec<XRefEntry>,
next_id: u32,
}
impl PdfWriter {
pub fn new() -> Self {
let mut w = Self {
buf: Vec::with_capacity(64 * 1024),
xref: Vec::new(),
next_id: 1,
};
w.xref.push(XRefEntry { offset: 0, gen: 65535, in_use: false });
w
}
pub fn reserve(&mut self) -> ObjRef {
let id = self.next_id;
self.next_id += 1;
self.xref.push(XRefEntry { offset: 0, gen: 0, in_use: false });
ObjRef::new(id)
}
pub fn write_object(&mut self, r: ObjRef, obj: &PdfObject) {
let offset = self.buf.len() as u64;
self.xref[r.id as usize] = XRefEntry { offset, gen: r.gen, in_use: true };
self.emit(format!("{} {} obj\n", r.id, r.gen).as_bytes());
self.emit(obj.serialize().as_bytes());
self.emit(b"\nendobj\n\n");
}
pub fn write_stream(&mut self, r: ObjRef, stream: &PdfStream) {
let offset = self.buf.len() as u64;
self.xref[r.id as usize] = XRefEntry { offset, gen: r.gen, in_use: true };
self.emit(format!("{} {} obj\n", r.id, r.gen).as_bytes());
self.emit(&stream.serialize());
self.emit(b"\nendobj\n\n");
}
pub fn add_object(&mut self, obj: PdfObject) -> ObjRef {
let r = self.reserve();
self.write_object(r, &obj);
r
}
pub fn add_stream(&mut self, stream: PdfStream) -> ObjRef {
let r = self.reserve();
self.write_stream(r, &stream);
r
}
fn emit(&mut self, data: &[u8]) {
self.buf.extend_from_slice(data);
}
pub fn write_header(&mut self) {
self.emit(b"%PDF-1.7\n%\xe2\xe3\xcf\xd3\n\n");
}
pub fn finalize(mut self, root: ObjRef, info: Option<ObjRef>) -> Vec<u8> {
let xref_offset = self.buf.len() as u64;
let count = self.xref.len();
let xref_lines: Vec<String> = self.xref.iter().map(|entry| {
if entry.in_use {
format!("{:010} {:05} n \n", entry.offset, entry.gen)
} else {
format!("{:010} {:05} f \n", entry.offset, entry.gen)
}
}).collect();
self.emit(format!("xref\n0 {}\n", count).as_bytes());
for line in &xref_lines {
self.emit(line.as_bytes());
}
let mut trailer = PdfDict::new();
trailer.set("Size", PdfObject::Integer(count as i64));
trailer.set("Root", PdfObject::Reference(root));
if let Some(info_ref) = info {
trailer.set("Info", PdfObject::Reference(info_ref));
}
self.emit(b"trailer\n");
self.emit(trailer.serialize().as_bytes());
self.emit(format!("\nstartxref\n{}\n%%EOF\n", xref_offset).as_bytes());
self.buf
}
pub fn finalize_with_encrypt(
mut self,
root: ObjRef,
info: Option<ObjRef>,
encrypt: Option<ObjRef>,
) -> Vec<u8> {
let xref_offset = self.buf.len() as u64;
let count = self.xref.len();
let xref_lines: Vec<String> = self.xref.iter().map(|entry| {
if entry.in_use {
format!("{:010} {:05} n \n", entry.offset, entry.gen)
} else {
format!("{:010} {:05} f \n", entry.offset, entry.gen)
}
}).collect();
self.emit(format!("xref\n0 {}\n", count).as_bytes());
for line in &xref_lines {
self.emit(line.as_bytes());
}
let mut trailer = PdfDict::new();
trailer.set("Size", PdfObject::Integer(count as i64));
trailer.set("Root", PdfObject::Reference(root));
if let Some(info_ref) = info {
trailer.set("Info", PdfObject::Reference(info_ref));
}
if let Some(enc_ref) = encrypt {
trailer.set("Encrypt", PdfObject::Reference(enc_ref));
let doc_id: Vec<u8> = (0u8..16).collect();
let id_obj = PdfObject::HexString(doc_id.clone());
trailer.set("ID", PdfObject::Array(vec![id_obj.clone(), id_obj]));
}
self.emit(b"trailer\n");
self.emit(trailer.serialize().as_bytes());
self.emit(format!("\nstartxref\n{}\n%%EOF\n", xref_offset).as_bytes());
self.buf
}
}