Skip to content

Commit 41be9a1

Browse files
artizclaude
andcommitted
feat(pdf): make the docling-parse line sanitizer the default
Flip the prose reconstruction to dp_lines (the docling-parse sanitizer port) by default; `DOCLING_LEGACY_LINES` restores the old gap-heuristic path. Routed through a single `use_dp_lines()` helper (pdfium_backend), used by page_cells and by assemble's cell-join + clean_text branches. PDF conformance default 3/14 → **4/14** (multi_page now byte-exact), with the rest markedly closer: 2206 200→168, redp5110 300→243, 2305v1 110→64, normal_4pages 108→82, 2203 309→263, table_mislabeled 113→102. The 3 previously-exact PDFs stay exact. DOCX/HTML/etc. are unaffected (the sanitizer is PDF-only). Updated the clean_text unit test for the preserve-spacing default. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
1 parent 11f1fe0 commit 41be9a1

2 files changed

Lines changed: 13 additions & 5 deletions

File tree

crates/fleischwolf-pdf/src/assemble.rs

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -122,7 +122,7 @@ fn clean_text(text: &str) -> String {
122122
.replace(['\u{201c}', '\u{201d}'], "\"") // “ ” → "
123123
.replace(['\u{2013}', '\u{2014}'], "-") // – — → -
124124
.replace('\u{2026}', "..."); // … → ...
125-
let out = if std::env::var("DOCLING_DP_LINES").is_ok() {
125+
let out = if crate::pdfium_backend::use_dp_lines() {
126126
// The docling-parse sanitizer already placed the correct spacing (e.g.
127127
// justified double spaces); preserve internal runs of spaces, only
128128
// normalizing line breaks/tabs and trimming the ends.
@@ -226,7 +226,7 @@ fn region_text(region: &Region, cells: &[TextCell]) -> String {
226226
// label and its value, `LABEL` | `: value` → `LABEL : value`. The legacy
227227
// reconstruction instead joins same-band cells with a space only across a real
228228
// gap, because it can split a word into abutting segments (`الت`|`ي` → `التي`).
229-
let dp = std::env::var("DOCLING_DP_LINES").is_ok();
229+
let dp = crate::pdfium_backend::use_dp_lines();
230230
let mut joined = String::new();
231231
let mut prev: Option<&&TextCell> = None;
232232
for c in &inside {
@@ -578,7 +578,8 @@ mod tests {
578578
"Graph's \"x\""
579579
);
580580
assert_eq!(clean_text("a\u{2026}"), "a...");
581-
// Whitespace collapses; a normal space-join is preserved.
582-
assert_eq!(clean_text("a b\nc"), "a b c");
581+
// The dp default (the docling-parse sanitizer) preserves internal spacing
582+
// it placed deliberately; line breaks/tabs normalize to a space, ends trim.
583+
assert_eq!(clean_text("a b\nc"), "a b c");
583584
}
584585
}

crates/fleischwolf-pdf/src/pdfium_backend.rs

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,13 @@ pub struct PdfDocument {
5555
/// Bind to the pdfium dynamic library. Honors `PDFIUM_DYNAMIC_LIB_PATH` (a
5656
/// directory or file), else the directory of the current exe, else the system
5757
/// library — mirroring how a deployment ships `libpdfium` alongside the binary.
58+
/// Whether to use the docling-parse line sanitizer ([`crate::dp_lines`]) for prose
59+
/// reconstruction — the default. Set `DOCLING_LEGACY_LINES` to fall back to the
60+
/// older gap-heuristic `lines_from_glyphs`.
61+
pub(crate) fn use_dp_lines() -> bool {
62+
std::env::var("DOCLING_LEGACY_LINES").is_err()
63+
}
64+
5865
fn bind() -> Result<Pdfium, PdfiumError> {
5966
if let Ok(path) = std::env::var("PDFIUM_DYNAMIC_LIB_PATH") {
6067
let name = Pdfium::pdfium_platform_library_name_at_path(&path);
@@ -222,7 +229,7 @@ impl<'a> FfiText<'a> {
222229
let out = if tp.is_null() {
223230
empty()
224231
} else {
225-
let dp = std::env::var("DOCLING_DP_LINES").is_ok();
232+
let dp = use_dp_lines();
226233
let g = glyphs(b, tp, dp);
227234
b.FPDFText_ClosePage(tp);
228235
// Prose line cells: the docling-parse-style sanitizer (behind a flag

0 commit comments

Comments
 (0)