|
|
|
|
@ -1879,5 +1879,64 @@ fn decode_unicode_escapes(text: &str) -> String {
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
result
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// 移除常见的 MTEXT 控制序列(如 `\fSimSun|b0|i0;`)并做基础替换
|
|
|
|
|
pub(crate) fn strip_mtext_control_sequences(input: &str) -> String {
|
|
|
|
|
let mut result = String::with_capacity(input.len());
|
|
|
|
|
let mut chars = input.chars().peekable();
|
|
|
|
|
|
|
|
|
|
while let Some(ch) = chars.next() {
|
|
|
|
|
if ch == '\\' {
|
|
|
|
|
let mut handled = false;
|
|
|
|
|
|
|
|
|
|
if let Some(&cmd) = chars.peek() {
|
|
|
|
|
match cmd {
|
|
|
|
|
'P' => {
|
|
|
|
|
chars.next(); // consume command
|
|
|
|
|
result.push('\n');
|
|
|
|
|
handled = true;
|
|
|
|
|
}
|
|
|
|
|
'f' | 'F' | 'H' | 'h' | 'C' | 'c' | 'W' | 'w' | 'A' | 'a' | 'p' | 'q' => {
|
|
|
|
|
chars.next(); // consume command
|
|
|
|
|
while let Some(next_ch) = chars.next() {
|
|
|
|
|
if next_ch == ';' {
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
handled = true;
|
|
|
|
|
}
|
|
|
|
|
'L' | 'l' | 'O' | 'o' | 'K' | 'k' => {
|
|
|
|
|
chars.next(); // toggle style command, no payload
|
|
|
|
|
handled = true;
|
|
|
|
|
}
|
|
|
|
|
'~' => {
|
|
|
|
|
chars.next();
|
|
|
|
|
result.push(' ');
|
|
|
|
|
handled = true;
|
|
|
|
|
}
|
|
|
|
|
'\\' => {
|
|
|
|
|
chars.next();
|
|
|
|
|
result.push('\\');
|
|
|
|
|
handled = true;
|
|
|
|
|
}
|
|
|
|
|
'{' | '}' => {
|
|
|
|
|
chars.next();
|
|
|
|
|
result.push(cmd);
|
|
|
|
|
handled = true;
|
|
|
|
|
}
|
|
|
|
|
_ => {}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if handled {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
result.push(ch);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
result
|
|
|
|
|
}
|