use crate::geometry::Rect;
use lopdf::{Document, Object, ObjectId};
pub struct PageBoxes {
pub media_box: Rect,
pub trim_box: Option<Rect>,
pub bleed_box: Option<Rect>,
pub crop_box: Option<Rect>,
}
impl PageBoxes {
pub fn read(doc: &Document, page_id: ObjectId) -> crate::Result<Self> {
let page_dict = doc.get_dictionary(page_id)?;
let media_box = arr_to_rect(page_dict.get(b"MediaBox")?.as_array()?);
let trim_box = page_dict
.get(b"TrimBox")
.and_then(|obj| obj.as_array())
.map(|a| arr_to_rect(a))
.ok();
let bleed_box = page_dict
.get(b"BleedBox")
.and_then(|obj| obj.as_array())
.map(|a| arr_to_rect(a))
.ok();
let crop_box = page_dict
.get(b"CropBox")
.and_then(|obj| obj.as_array())
.map(|a| arr_to_rect(a))
.ok();
Ok(PageBoxes {
media_box,
trim_box,
bleed_box,
crop_box,
})
}
pub fn trim_or_media(&self) -> &Rect {
self.trim_box.as_ref().unwrap_or(&self.media_box)
}
pub fn bleed_rect(&self, pts: f64) -> Rect {
self.trim_or_media().expand(pts)
}
}
pub fn set_trim_boxes(doc: &mut Document, bleed_pts: f64) -> crate::Result<()> {
let page_ids: Vec<ObjectId> = doc.get_pages().values().copied().collect();
let trim_rects: Vec<(ObjectId, [f64; 4])> = page_ids.iter()
.map(|&page_id| {
let boxes = PageBoxes::read(doc, page_id)?;
let m = &boxes.media_box;
Ok((page_id, [
m.x + bleed_pts,
m.y + bleed_pts,
m.right() - bleed_pts,
m.top() - bleed_pts,
]))
})
.collect::<crate::Result<Vec<_>>>()?;
for (page_id, [x0, y0, x1, y1]) in trim_rects {
let arr = vec![
Object::Real(x0 as f32),
Object::Real(y0 as f32),
Object::Real(x1 as f32),
Object::Real(y1 as f32),
];
doc.get_dictionary_mut(page_id)?.set(b"TrimBox", Object::Array(arr));
}
Ok(())
}
fn arr_to_rect(arr: &[Object]) -> Rect {
Rect::from_corners(
object_to_f64(&arr[0]),
object_to_f64(&arr[1]),
object_to_f64(&arr[2]),
object_to_f64(&arr[3]),
)
}
pub(crate) fn object_to_f64(obj: &lopdf::Object) -> f64 {
match obj {
lopdf::Object::Integer(i) => *i as f64,
lopdf::Object::Real(r) => *r as f64,
_ => panic!("expected numeric object, got {:?}", obj),
}
}