diff --git a/crates/textimport/src/tagged.rs b/crates/textimport/src/tagged.rs
index ffb106ea..b5f7815b 100644
--- a/crates/textimport/src/tagged.rs
+++ b/crates/textimport/src/tagged.rs
@@ -37,14 +37,20 @@ fn escape_value(v: &str) -> String {
out
}
-/// A tag value with backslash escapes removed.
+/// A tag value with the escapes `escape_value` writes (`\\`, `\<`, `\>`) decoded. Any other
+/// backslash, including a trailing one, stays literal, as older files and InDesign's `\:` expect.
fn unescape_value(v: &str) -> String {
let mut out = String::with_capacity(v.len());
- let mut it = v.chars();
+ let mut it = v.chars().peekable();
while let Some(c) = it.next() {
- match c {
- '\\' => out.extend(it.next()),
- c => out.push(c),
+ if c == '\\'
+ && let Some(&n) = it.peek()
+ && matches!(n, '\\' | '<' | '>')
+ {
+ out.push(n);
+ it.next();
+ } else {
+ out.push(c);
}
}
out
@@ -312,6 +318,15 @@ mod tests {
}
}
+ /// A backslash that isn't one of our escapes (older files, InDesign's `\:`) stays literal.
+ #[test]
+ fn unknown_backslashes_in_style_names_stay_literal() {
+ assert_eq!(unescape_value(r"x\y"), r"x\y");
+ assert_eq!(unescape_value(r"a\:b"), r"a\:b");
+ assert_eq!(unescape_value("end\\"), "end\\");
+ assert_eq!(unescape_value(r"\\\"), r"\");
+ }
+
#[test]
fn tagged_text_round_trips() {
let src = "\r\n\r\nCaf<0x00E9> \\<1\\>\r\nA word and big text";