Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions crates/paperjam-core/src/bookmarks/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -170,9 +170,9 @@ fn build_outline_children(

// Recursively build children
let child_ids = build_outline_children(doc, &spec.children, item_id, page_map)?;
if !child_ids.is_empty() {
item_dict.set("First", Object::Reference(child_ids[0]));
item_dict.set("Last", Object::Reference(*child_ids.last().unwrap()));
if let (Some(first), Some(last)) = (child_ids.first(), child_ids.last()) {
item_dict.set("First", Object::Reference(*first));
item_dict.set("Last", Object::Reference(*last));
let child_count = count_all_items(&spec.children);
item_dict.set("Count", Object::Integer(child_count as i64));
}
Expand Down
12 changes: 9 additions & 3 deletions crates/paperjam-core/src/stamp/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -339,8 +339,12 @@ fn ensure_page_resources(doc: &mut lopdf::Document, page_id: ObjectId) -> Result
let res_id = *res_id;
if let Ok(res_obj) = doc.get_object(res_id) {
let cloned = res_obj.clone();
let page_obj = doc.get_object_mut(page_id).unwrap();
let page_dict = page_obj.as_dict_mut().unwrap();
let page_obj = doc
.get_object_mut(page_id)
.map_err(|e| PdfError::Annotation(format!("Cannot get page: {}", e)))?;
let page_dict = page_obj
.as_dict_mut()
.map_err(|e| PdfError::Annotation(format!("Page not a dict: {}", e)))?;
page_dict.set("Resources", cloned);
}
}
Expand All @@ -352,7 +356,9 @@ fn ensure_page_resources(doc: &mut lopdf::Document, page_id: ObjectId) -> Result
let page_obj = doc
.get_object(page_id)
.map_err(|e| PdfError::Annotation(format!("Cannot get page: {}", e)))?;
let page_dict = page_obj.as_dict().unwrap();
let page_dict = page_obj
.as_dict()
.map_err(|e| PdfError::Annotation(format!("Page not a dict: {}", e)))?;

if let Ok(parent_ref) = page_dict.get(b"Parent") {
if let Ok(parent_id) = parent_ref.as_reference() {
Expand Down
15 changes: 7 additions & 8 deletions crates/paperjam-core/src/table/grid.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,12 @@ pub fn build_from_intersections(
let mut xs: Vec<f64> = intersections.iter().map(|p| p.0).collect();
let mut ys: Vec<f64> = intersections.iter().map(|p| p.1).collect();

xs.sort_by(|a, b| a.partial_cmp(b).unwrap());
// `total_cmp` gives a total ordering even in the presence of NaN coords
// extracted from malformed PDFs, where `partial_cmp` would panic.
xs.sort_by(|a, b| a.total_cmp(b));
xs.dedup_by(|a, b| (*a - *b).abs() < options.snap_tolerance);

ys.sort_by(|a, b| a.partial_cmp(b).unwrap());
ys.sort_by(|a, b| a.total_cmp(b));
ys.dedup_by(|a, b| (*a - *b).abs() < options.snap_tolerance);

if xs.len() < 2 || ys.len() < 2 {
Expand Down Expand Up @@ -71,12 +73,9 @@ pub fn build_from_intersections(
// Reverse so first row is top of page
rows.reverse();

let bbox = (
*xs.first().unwrap(),
*ys.first().unwrap(),
*xs.last().unwrap(),
*ys.last().unwrap(),
);
// Both `xs` and `ys` have len >= 2 here (guarded above), so direct
// indexing is safe and avoids the `unwrap` pattern.
let bbox = (xs[0], ys[0], xs[xs.len() - 1], ys[ys.len() - 1]);

Ok(Some(Table {
bbox,
Expand Down
9 changes: 3 additions & 6 deletions crates/paperjam-core/src/table/lattice.rs
Original file line number Diff line number Diff line change
Expand Up @@ -165,12 +165,9 @@ fn find_intersections(
}
}

// Deduplicate
points.sort_by(|a, b| {
a.0.partial_cmp(&b.0)
.unwrap()
.then(a.1.partial_cmp(&b.1).unwrap())
});
// Deduplicate. `total_cmp` is NaN-safe; malformed PDFs can produce NaN
// coordinates that would otherwise panic `partial_cmp`.
points.sort_by(|a, b| a.0.total_cmp(&b.0).then(a.1.total_cmp(&b.1)));
points.dedup_by(|a, b| (a.0 - b.0).abs() < snap && (a.1 - b.1).abs() < snap);

points
Expand Down
2 changes: 1 addition & 1 deletion crates/paperjam-core/src/table/stream.rs
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@ fn detect_column_boundaries(lines: &[AnalyzedLine], expected_cols: usize) -> Vec
return Vec::new();
}

left_edges.sort_by(|a, b| a.partial_cmp(b).unwrap());
left_edges.sort_by(|a, b| a.total_cmp(b));

// Simple gap-based clustering
let mut boundaries = Vec::new();
Expand Down
2 changes: 1 addition & 1 deletion crates/paperjam-core/src/validation/pdf_ua.rs
Original file line number Diff line number Diff line change
Expand Up @@ -261,7 +261,7 @@ fn walk_struct_tree(
let kids = match elem_dict.get(b"K") {
Ok(Object::Array(arr)) => arr.clone(),
Ok(Object::Reference(id)) => vec![Object::Reference(*id)],
Ok(Object::Dictionary(_)) => vec![elem_dict.get(b"K").unwrap().clone()],
Ok(obj @ Object::Dictionary(_)) => vec![obj.clone()],
_ => return,
};

Expand Down
38 changes: 17 additions & 21 deletions crates/paperjam-core/src/watermark/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -52,8 +52,10 @@ fn apply_watermark_to_page(
height: f64,
options: &WatermarkOptions,
) -> Result<()> {
let font_resource_name = b"WMFont1";
let gs_resource_name = b"WMgs1";
const FONT_RESOURCE_NAME: &str = "WMFont1";
const GS_RESOURCE_NAME: &str = "WMgs1";
let font_resource_name = FONT_RESOURCE_NAME.as_bytes();
let gs_resource_name = GS_RESOURCE_NAME.as_bytes();

// 1. Create ExtGState for opacity
let gs_dict = dictionary! {
Expand Down Expand Up @@ -180,35 +182,23 @@ fn apply_watermark_to_page(
// Add font
match resources_dict.get_mut(b"Font") {
Ok(Object::Dictionary(font_resources)) => {
font_resources.set(
std::str::from_utf8(font_resource_name).unwrap(),
Object::Reference(font_id),
);
font_resources.set(FONT_RESOURCE_NAME, Object::Reference(font_id));
}
_ => {
let mut font_resources = lopdf::Dictionary::new();
font_resources.set(
std::str::from_utf8(font_resource_name).unwrap(),
Object::Reference(font_id),
);
font_resources.set(FONT_RESOURCE_NAME, Object::Reference(font_id));
resources_dict.set("Font", Object::Dictionary(font_resources));
}
}

// Add ExtGState
match resources_dict.get_mut(b"ExtGState") {
Ok(Object::Dictionary(gs_resources)) => {
gs_resources.set(
std::str::from_utf8(gs_resource_name).unwrap(),
Object::Reference(gs_id),
);
gs_resources.set(GS_RESOURCE_NAME, Object::Reference(gs_id));
}
_ => {
let mut gs_resources = lopdf::Dictionary::new();
gs_resources.set(
std::str::from_utf8(gs_resource_name).unwrap(),
Object::Reference(gs_id),
);
gs_resources.set(GS_RESOURCE_NAME, Object::Reference(gs_id));
resources_dict.set("ExtGState", Object::Dictionary(gs_resources));
}
}
Expand Down Expand Up @@ -280,8 +270,12 @@ fn ensure_page_resources(doc: &mut lopdf::Document, page_id: ObjectId) -> Result
let res_id = *res_id;
if let Ok(res_obj) = doc.get_object(res_id) {
let cloned = res_obj.clone();
let page_obj = doc.get_object_mut(page_id).unwrap();
let page_dict = page_obj.as_dict_mut().unwrap();
let page_obj = doc
.get_object_mut(page_id)
.map_err(|e| PdfError::Watermark(format!("Cannot get page: {}", e)))?;
let page_dict = page_obj
.as_dict_mut()
.map_err(|e| PdfError::Watermark(format!("Page not a dict: {}", e)))?;
page_dict.set("Resources", cloned);
}
}
Expand All @@ -293,7 +287,9 @@ fn ensure_page_resources(doc: &mut lopdf::Document, page_id: ObjectId) -> Result
let page_obj = doc
.get_object(page_id)
.map_err(|e| PdfError::Watermark(format!("Cannot get page: {}", e)))?;
let page_dict = page_obj.as_dict().unwrap();
let page_dict = page_obj
.as_dict()
.map_err(|e| PdfError::Watermark(format!("Page not a dict: {}", e)))?;

if let Ok(parent_ref) = page_dict.get(b"Parent") {
if let Ok(parent_id) = parent_ref.as_reference() {
Expand Down
Loading