diff --git a/crates/pi-natives/src/fonts/5x8.bdf b/crates/pi-natives/src/fonts/5x8.bdf index 0fd5096d6..18257deb2 100644 --- a/crates/pi-natives/src/fonts/5x8.bdf +++ b/crates/pi-natives/src/fonts/5x8.bdf @@ -278,7 +278,7 @@ BITMAP 00 20 50 -50 +70 50 50 20 diff --git a/crates/pi-natives/src/fonts/6x12.bdf b/crates/pi-natives/src/fonts/6x12.bdf index 3fbf113d2..945262c89 100644 --- a/crates/pi-natives/src/fonts/6x12.bdf +++ b/crates/pi-natives/src/fonts/6x12.bdf @@ -364,7 +364,7 @@ BITMAP 30 48 48 -48 +78 48 48 30 diff --git a/crates/pi-natives/src/fonts/8x13.bdf b/crates/pi-natives/src/fonts/8x13.bdf index a95b7183a..656c6d0a2 100644 --- a/crates/pi-natives/src/fonts/8x13.bdf +++ b/crates/pi-natives/src/fonts/8x13.bdf @@ -380,9 +380,9 @@ BITMAP 18 24 42 -42 -42 -42 +4A +52 +62 42 24 18 diff --git a/crates/pi-natives/src/snapcompact.rs b/crates/pi-natives/src/snapcompact.rs index d05d47583..25e5e900a 100644 --- a/crates/pi-natives/src/snapcompact.rs +++ b/crates/pi-natives/src/snapcompact.rs @@ -1353,6 +1353,32 @@ mod tests { assert!(FONT_SILVER.supported.contains(&'안'), "Silver must cover Hangul syllables"); } + #[test] + fn digit_zero_is_disambiguated_from_letter_o() { + // Regression for #8713: the default snapcompact bitmap fonts drew digit + // `0` and letter `O` as bare ovals that OCR back ambiguously, corrupting + // compacted identifiers. Each `0` now carries an interior slash/bar the + // `O` lacks, so it inks strictly more of the glyph's vertical middle even + // though it is the narrower oval (its wider top/bottom arcs sit outside + // the sampled band). unscii-8 already shipped a slashed zero. + for font in [&*FONT_5X8, &*FONT_6X12, &*FONT_8X13] { + let (cw, ch) = (font.cell_w, font.cell_h); + let width = cw * 2; + let grid = Grid { cols: 2, rows: 1, repeat: 1, cell_w: cw, cell_h: ch }; + let px = render_bitmap("0O", width, ch, font, &grid, true); + let band = ch / 4..ch - ch / 4; + let mid_ink = |col0: usize| -> usize { + band + .clone() + .flat_map(|y| (col0..col0 + cw).map(move |x| (x, y))) + .filter(|&(x, y)| px[y * width + x] != 0) + .count() + }; + let (zero, oh) = (mid_ink(0), mid_ink(cw)); + assert!(zero > oh, "cell {cw}x{ch}: zero must ink its middle more than O (zero={zero}, O={oh})"); + } + } + #[test] fn bitmap_inks_sentences_and_caps_capacity() { // 40px -> 8 cols x 5 rows = 40 cells (5x8 font). diff --git a/packages/snapcompact/CHANGELOG.md b/packages/snapcompact/CHANGELOG.md index de7b4e493..02de4f0f9 100644 --- a/packages/snapcompact/CHANGELOG.md +++ b/packages/snapcompact/CHANGELOG.md @@ -2,6 +2,10 @@ ## [Unreleased] +### Fixed + +- Fixed image-based compaction confusing digit `0` with letter `O` and corrupting compacted identifiers (e.g. Slack IDs): the default frame fonts (X.org `8x13`, `6x12`, `5x8`) drew zero as a bare oval indistinguishable from `O`. Zero now carries a disambiguating interior slash (`8x13`) or bar (`6x12`/`5x8`); unscii-8 already shipped a slashed zero ([#8713](https://github.com/can1357/oh-my-pi/issues/8713)). + ## [17.2.15] - 2026-08-12 ### Fixed