use crate::error::FormsError;
use crate::pdf;
use lopdf::{Dictionary, Object, ObjectId, Stream};
const FONT_NAME: &[u8] = b"BtctaxWm";
fn watermark_text() -> Vec<u8> {
let mut s = b"DRAFT ".to_vec();
s.push(0x97); s.extend_from_slice(b" ESTIMATE, NOT FOR FILING");
s
}
pub fn stamp_draft(pdf_bytes: &[u8]) -> Result<Vec<u8>, FormsError> {
let mut doc = pdf::load(pdf_bytes)?;
let mut font = Dictionary::new();
font.set("Type", Object::Name(b"Font".to_vec()));
font.set("Subtype", Object::Name(b"Type1".to_vec()));
font.set("BaseFont", Object::Name(b"Helvetica".to_vec()));
font.set("Encoding", Object::Name(b"WinAnsiEncoding".to_vec()));
let font_id = doc.add_object(Object::Dictionary(font));
let page_ids: Vec<ObjectId> = doc.get_pages().into_values().collect();
for pid in page_ids {
let res = resources_with_font(&doc, pid, font_id)?;
doc.get_dictionary_mut(pid)?
.set("Resources", Object::Dictionary(res));
let mut content = b"q\n0.78 0.78 0.78 rg\nBT\n/".to_vec();
content.extend_from_slice(FONT_NAME);
content.extend_from_slice(b" 30 Tf\n0.7071 0.7071 -0.7071 0.7071 90 250 Tm\n(");
content.extend_from_slice(&escape_pdf_string(&watermark_text()));
content.extend_from_slice(b") Tj\nET\nQ\n");
let stream_id = doc.add_object(Object::Stream(Stream::new(Dictionary::new(), content)));
append_content(&mut doc, pid, stream_id)?;
}
pdf::strip_nondeterminism(&mut doc);
pdf::save(&mut doc)
}
fn escape_pdf_string(s: &[u8]) -> Vec<u8> {
let mut out = Vec::with_capacity(s.len());
for &b in s {
if b == b'(' || b == b')' || b == b'\\' {
out.push(b'\\');
}
out.push(b);
}
out
}
fn resources_with_font(
doc: &lopdf::Document,
page_id: ObjectId,
font_id: ObjectId,
) -> Result<Dictionary, FormsError> {
let mut res = match doc.get_dictionary(page_id)?.get(b"Resources") {
Ok(Object::Reference(rid)) => doc.get_dictionary(*rid)?.clone(),
Ok(Object::Dictionary(d)) => d.clone(),
_ => Dictionary::new(),
};
let mut fonts = match res.get(b"Font") {
Ok(Object::Reference(rid)) => doc.get_dictionary(*rid)?.clone(),
Ok(Object::Dictionary(d)) => d.clone(),
_ => Dictionary::new(),
};
fonts.set(FONT_NAME.to_vec(), Object::Reference(font_id));
res.set("Font", Object::Dictionary(fonts));
Ok(res)
}
fn append_content(
doc: &mut lopdf::Document,
page_id: ObjectId,
stream_id: ObjectId,
) -> Result<(), FormsError> {
let existing = doc.get_dictionary(page_id)?.get(b"Contents").ok().cloned();
let new_contents = match existing {
Some(Object::Array(mut a)) => {
a.push(Object::Reference(stream_id));
Object::Array(a)
}
Some(Object::Reference(r)) => {
Object::Array(vec![Object::Reference(r), Object::Reference(stream_id)])
}
_ => Object::Array(vec![Object::Reference(stream_id)]),
};
doc.get_dictionary_mut(page_id)?
.set("Contents", new_contents);
Ok(())
}