mirror of
https://github.com/storytold/vectorcraft.git
synced 2026-11-10 17:02:28 +00:00
Decode DXF surrogate pairs
Found while testing crates/cad/src/import/text.rs:80 Co-Authored-By: GPT-5.6-Luna <noreply@openai.com> Co-Authored-By: GPT-6-Luna <noreply@openai.com>
This commit is contained in:
committed by
bflatastic
co-authored by
GPT-5.6-Luna
parent
743f8c526b
commit
9b93c609b9
@@ -66,6 +66,31 @@ fn until_semicolon(chars: &mut std::iter::Peekable<std::str::Chars<'_>>) -> Stri
|
||||
/// and caret escapes: `^ ` is a caret, `^I` a tab; other control characters are dropped. In
|
||||
/// multiline text (`mtext`) `\\` is an escaped backslash and is kept as is, so that a `U+` after
|
||||
/// it stays literal, and the `^` in a stacked fraction `\Snum^den;` is left for the caller.
|
||||
fn unicode_escape(after: &str) -> Option<(char, usize)> {
|
||||
let hex = after.strip_prefix("U+").or_else(|| after.strip_prefix("u+"))?.get(..4)?;
|
||||
if !hex.bytes().all(|b| b.is_ascii_hexdigit()) {
|
||||
return None;
|
||||
}
|
||||
let value = u32::from_str_radix(hex, 16).ok()?;
|
||||
if (0xD800..=0xDBFF).contains(&value) {
|
||||
let next = after.get(6..)?;
|
||||
let low = next.strip_prefix("\\U+").or_else(|| next.strip_prefix("\\u+"))?.get(..4)?;
|
||||
if !low.bytes().all(|b| b.is_ascii_hexdigit()) {
|
||||
return None;
|
||||
}
|
||||
let low = u32::from_str_radix(low, 16).ok()?;
|
||||
if !(0xDC00..=0xDFFF).contains(&low) {
|
||||
return None;
|
||||
}
|
||||
let scalar = 0x1_0000 + ((value - 0xD800) << 10) + (low - 0xDC00);
|
||||
return char::from_u32(scalar).map(|ch| (ch, 13));
|
||||
}
|
||||
if (0xDC00..=0xDFFF).contains(&value) {
|
||||
return None;
|
||||
}
|
||||
char::from_u32(value).map(|ch| (ch, 6))
|
||||
}
|
||||
|
||||
fn escapes(s: &str, mtext: bool) -> String {
|
||||
let mut out = String::with_capacity(s.len());
|
||||
let mut rest = s;
|
||||
@@ -77,12 +102,10 @@ fn escapes(s: &str, mtext: bool) -> String {
|
||||
continue;
|
||||
}
|
||||
if c == '\\'
|
||||
&& let Some(hex) = after.strip_prefix("U+").or_else(|| after.strip_prefix("u+")).and_then(|h| h.get(..4))
|
||||
&& hex.bytes().all(|b| b.is_ascii_hexdigit())
|
||||
&& let Some(ch) = u32::from_str_radix(hex, 16).ok().and_then(char::from_u32)
|
||||
&& let Some((ch, consumed)) = unicode_escape(after)
|
||||
{
|
||||
out.push(ch);
|
||||
rest = after.get(6..).unwrap_or("");
|
||||
rest = after.get(consumed..).unwrap_or("");
|
||||
continue;
|
||||
}
|
||||
if mtext
|
||||
@@ -161,6 +184,14 @@ mod tests {
|
||||
assert_eq!(plain("\\U+00C9"), "É");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn unicode_escape_decodes_surrogate_pairs() {
|
||||
assert_eq!(plain("\\U+D83D\\U+DE00"), "😀");
|
||||
assert_eq!(plain("\\U+D83D"), "\\U+D83D");
|
||||
assert_eq!(plain("\\U+DE00"), "\\U+DE00");
|
||||
assert_eq!(plain("\\U+D83D\\U+0041"), "\\U+D83DA");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn control_codes_and_escapes() {
|
||||
assert_eq!(plain("45%%d %%p0.1 %%c20 100%%% %%uunder%%u"), "45° ±0.1 ⌀20 100% under");
|
||||
|
||||
Reference in New Issue
Block a user