mirror of
https://github.com/storytold/pdfcraft.git
synced 2026-10-27 03:27:21 -06:00
This commit is contained in:
@@ -30,6 +30,10 @@ pub struct OcrSettings {
|
||||
/// text read again, not a word in a picture.
|
||||
const ALREADY_ON_PAGE: f32 = 0.5;
|
||||
|
||||
/// The long side of an A4 sheet in points (a letter page's, 792, is shorter): "Downsample to"
|
||||
/// is a resolution on paper this size or smaller.
|
||||
const SHEET: f32 = 842.0;
|
||||
|
||||
/// How much of `word` the `text` boxes cover, from 0 to 1. Boxes are `[x0, y0, x1, y1]` in page
|
||||
/// view space. Overlaps between the text boxes are counted twice (glyphs rarely overlap), so the
|
||||
/// sum is capped at the whole word.
|
||||
@@ -121,11 +125,7 @@ impl OcrJob {
|
||||
out.push(skip("the page already contains text"));
|
||||
continue;
|
||||
}
|
||||
// A scan is read at its own resolution (enlarging it only blurs the letters), at most
|
||||
// the chosen one.
|
||||
let wanted = self.settings.dpi.clamp(72.0, 600.0) / 72.0;
|
||||
let scale = self.native.get(page).copied().flatten().map_or(wanted, |n| n.clamp(1.0, wanted));
|
||||
let shot = r.render(RenderRequest { page, scale, ..Default::default() });
|
||||
let shot = r.render(RenderRequest { page, scale: self.scale(page, info), ..Default::default() });
|
||||
if let Some(e) = shot.error {
|
||||
out.push(skip(&e));
|
||||
continue;
|
||||
@@ -151,6 +151,18 @@ impl OcrJob {
|
||||
progress(total, total);
|
||||
out
|
||||
}
|
||||
|
||||
/// The render scale (pixels per point) `page` is read at. A scan is read at its own
|
||||
/// resolution (enlarging it only blurs the letters), at most the chosen one.
|
||||
fn scale(&self, page: usize, info: &PageInfo) -> f32 {
|
||||
let wanted = self.settings.dpi.clamp(72.0, 600.0) / 72.0;
|
||||
let Some(native) = self.native.get(page).copied().flatten() else { return wanted };
|
||||
// A scan whose page is larger than a sheet (often a box given in pixels, one point per
|
||||
// pixel, #1848) is measured as if shrunk to one: otherwise 1 px/pt is already its full
|
||||
// image, and no "Downsample to" choice could ask for less.
|
||||
let fit = (SHEET / info.width.max(info.height).max(1e-3)).min(1.0);
|
||||
native.clamp(fit, wanted * fit)
|
||||
}
|
||||
}
|
||||
|
||||
impl Session {
|
||||
@@ -242,6 +254,7 @@ pub fn recognize_file(
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::export;
|
||||
|
||||
/// #1028: a recognised word over the page's own text is that text again; one in an empty
|
||||
/// part of the page (a picture) is new.
|
||||
@@ -260,6 +273,28 @@ mod tests {
|
||||
assert_eq!(covered([100.5, 52.0, 134.0, 61.0], &[]), 0.0);
|
||||
}
|
||||
|
||||
/// #1848: an A4 scan at 300 dpi whose page box is its pixel size (2480 x 3508 pt, one pixel
|
||||
/// per point) gets a smaller bitmap for each lower "Downsample to" choice, and a low-resolution
|
||||
/// letter page is still never shrunk below one pixel per point.
|
||||
#[test]
|
||||
fn downsample_shrinks_a_page_box_given_in_pixels() {
|
||||
let mut s = Session::new();
|
||||
let page = |w: u32, h: u32| export::encode_png(w, h, &vec![255; (w * h * 4) as usize]).unwrap();
|
||||
let bytes = s.create_from_images(&[("a4.png".into(), page(2480, 3508)), ("letter.png".into(), page(612, 792))]).unwrap();
|
||||
let id = s.open("scan.pdf", None, bytes, None).unwrap();
|
||||
let mut r = PageRenderer::new(s.get(id).unwrap().bytes.clone(), RenderConfig::default());
|
||||
for (dpi, a4) in [(72.0, [595, 842]), (150.0, [1240, 1754]), (300.0, [2480, 3508]), (600.0, [2480, 3508])] {
|
||||
let job = s.ocr_job(id, &[], OcrSettings { dpi, ..Default::default() }).unwrap();
|
||||
for (page, want) in [(0, a4), (1, [612, 792])] {
|
||||
let scale = job.scale(page, &job.infos[page]);
|
||||
let shot = r.render(RenderRequest { page, scale, ..Default::default() });
|
||||
assert!(shot.error.is_none(), "{:?}", shot.error);
|
||||
let near = |got: u32, want: u32| got.abs_diff(want) <= 1;
|
||||
assert!(near(shot.width, want[0]) && near(shot.height, want[1]), "{dpi} dpi, page {page}: {}x{}", shot.width, shot.height);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn coverage_of_overlapping_or_broken_boxes_stays_between_zero_and_one() {
|
||||
// A glyph drawn twice (fake bold) does not cover more than the whole word.
|
||||
|
||||
Reference in New Issue
Block a user