use crate::Line;
const STRAY_SHARE: f64 = 0.05;
fn east_asian(c: char) -> bool {
matches!(c as u32,
0x3000..=0x30FF | 0x3400..=0x4DBF | 0x4E00..=0x9FFF | 0xAC00..=0xD7AF | 0xF900..=0xFAFF | 0xFF00..=0xFFEF) }
fn lookalike(c: char) -> Option<char> {
match c {
'一' => Some('-'),
'二' => Some('='),
'十' => Some('+'),
_ => None,
}
}
pub(crate) fn drop_stray_east_asian(lines: &mut Vec<Line>) {
let (mut ea, mut letters) = (0usize, 0usize);
for c in lines.iter().flat_map(|l| l.text.chars()) {
if lookalike(c).is_some() {
continue;
}
if east_asian(c) {
ea += 1;
letters += 1;
} else if c.is_alphabetic() {
letters += 1;
}
}
if ea as f64 > letters as f64 * STRAY_SHARE
|| !lines.iter().any(|l| l.text.chars().any(east_asian))
{
return;
}
for l in lines.iter_mut() {
if l.text.chars().any(east_asian) {
l.text = l
.text
.chars()
.filter_map(|c| lookalike(c).or((!east_asian(c)).then_some(c)))
.collect();
}
}
lines.retain(|l| l.text.chars().any(char::is_alphanumeric));
}
#[cfg(test)]
mod tests {
use super::*;
fn line(t: &str) -> Line {
Line {
quad: [(0.0, 0.0); 4],
text: t.into(),
score: 1.0,
formula: false,
}
}
fn texts(l: &[Line]) -> Vec<&str> {
l.iter().map(|l| l.text.as_str()).collect()
}
#[test]
fn 拉丁页去掉零星汉字() {
let mut l = vec![
line("God then sends at Head quarters that no doubt"),
line("中"),
line("2a-x 一 1 x"),
];
drop_stray_east_asian(&mut l);
assert_eq!(
texts(&l),
[
"God then sends at Head quarters that no doubt",
"2a-x - 1 x"
]
);
}
#[test]
fn 中文页与混排页不动() {
let mut zh = vec![line("扫描页上的文字"), line("第二行 PDF")];
drop_stray_east_asian(&mut zh);
assert_eq!(texts(&zh), ["扫描页上的文字", "第二行 PDF"]);
let mut mixed = vec![line("The word 中文 means Chinese")];
drop_stray_east_asian(&mut mixed);
assert_eq!(texts(&mixed), ["The word 中文 means Chinese"]);
}
#[test]
fn 数学页的形似字换回符号() {
let mut l = vec![line("dx dz =一"), line("1+ 二 7 x 2 十 y 一")];
drop_stray_east_asian(&mut l);
assert_eq!(texts(&l), ["dx dz =-", "1+ = 7 x 2 + y -"]);
}
}