From 2c825b034dbdaf6ba1898f1675c6f10a69c805a9 Mon Sep 17 00:00:00 2001 From: Abimael Martell Date: Fri, 13 Feb 2026 13:32:37 -0800 Subject: [PATCH] fix(spacing): Reduce spurious spaces in numbers and single-char fragments MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Two targeted threshold adjustments in the accurate-width path of should_join_items(): 1. Numeric continuity (0.3x threshold): When adjacent items form a number sequence (digits, commas, periods, percent signs), use a generous threshold. Fixes splits like "34,20 8" → "34,208" and "+13. 0 %" → "+13.0%". 2. Single-character fragments (0.25x threshold): Single-char items from per-glyph positioning are almost never standalone words. Fixes splits like "b illion", "C ultural", "togeth er". The general 0.15x threshold is preserved for multi-character items to avoid regressions in normal word spacing. Co-Authored-By: Claude Opus 4.6 --- src/extractor.rs | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/src/extractor.rs b/src/extractor.rs index 5b33bdd..9551e6e 100644 --- a/src/extractor.rs +++ b/src/extractor.rs @@ -738,6 +738,29 @@ fn should_join_items(prev_item: &TextItem, curr_item: &TextItem) -> bool { } } + // Numeric continuity: digits, commas, periods, and percent signs that + // are positioned close together are almost always a single number. + // e.g., "34,20" + "8" → "34,208", "+13." + "0" + "%" → "+13.0%" + // Use a generous threshold since word spaces in numbers are rare. + if let (Some(p), Some(c)) = (prev_last, curr_first) { + let prev_is_numeric = p.is_ascii_digit() || p == ',' || p == '.'; + let curr_is_numeric = c.is_ascii_digit() || c == '%' || c == '.'; + if prev_is_numeric && curr_is_numeric { + return gap < font_size * 0.3; + } + // Sign characters (+/-) followed by digits + if (p == '+' || p == '-') && c.is_ascii_digit() { + return gap < font_size * 0.3; + } + } + + // Single-character fragments from per-glyph positioning are almost never + // standalone words. Use a more generous threshold to rejoin split words + // like "b" + "illion", "C" + "ultural", "togeth" + "er". + if prev_chars == 1 || curr_chars == 1 { + return gap < font_size * 0.25; + } + // With accurate widths, a gap < 15% of font size means glyphs are // adjacent (same word). Anything larger is a deliberate space. return gap < font_size * 0.15;