mirror of
https://github.com/storytold/designcraft.git
synced 2026-11-11 16:30:16 -06:00
Keep a literal ':' in style names through an IDML round trip
Found while testing crates/idml/src/names.rs:148-155 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
f3b7c3ee34
commit
fa81009be2
@@ -144,16 +144,37 @@ pub const TABLE_BUILTINS: &[(&str, &str)] = &[("[Basic Table]", "$ID/[Basic Tabl
|
||||
pub const OBJECT_BUILTINS: &[(&str, &str)] =
|
||||
&[("[None]", "$ID/[None]"), ("[Basic Graphics Frame]", "$ID/[Normal Graphics Frame]"), ("[Basic Text Frame]", "$ID/[Normal Text Frame]")];
|
||||
|
||||
/// Our style name → IDML `Name` (groups: `A/B` → `A:B`).
|
||||
/// Our style name → IDML `Name` (groups: `A/B` → `A:B`; a literal `:` or `\` is escaped as `\:`
|
||||
/// or `\\`, so it is not read back as a group separator).
|
||||
pub fn style_name_out(builtins: &[(&str, &str)], name: &str) -> String {
|
||||
builtins.iter().find(|(o, _)| *o == name).map(|(_, i)| i.to_string()).unwrap_or_else(|| name.replace('/', ":"))
|
||||
builtins
|
||||
.iter()
|
||||
.find(|(o, _)| *o == name)
|
||||
.map(|(_, i)| i.to_string())
|
||||
.unwrap_or_else(|| name.replace('\\', "\\\\").replace(':', "\\:").replace('/', ":"))
|
||||
}
|
||||
/// IDML `Name` → our style name.
|
||||
pub fn style_name_in(builtins: &[(&str, &str)], name: &str) -> String {
|
||||
if let Some((o, _)) = builtins.iter().find(|(_, i)| *i == name) {
|
||||
return o.to_string();
|
||||
}
|
||||
name.strip_prefix("$ID/").unwrap_or(name).replace(':', "/")
|
||||
let name = name.strip_prefix("$ID/").unwrap_or(name);
|
||||
let mut out = String::with_capacity(name.len());
|
||||
let mut chars = name.chars();
|
||||
while let Some(c) = chars.next() {
|
||||
match c {
|
||||
'\\' => match chars.clone().next() {
|
||||
Some(next @ ('\\' | ':')) => {
|
||||
out.push(next);
|
||||
chars.next();
|
||||
}
|
||||
_ => out.push('\\'),
|
||||
},
|
||||
':' => out.push('/'),
|
||||
_ => out.push(c),
|
||||
}
|
||||
}
|
||||
out
|
||||
}
|
||||
/// `ParagraphStyle/…` Self id for one of our names.
|
||||
pub fn style_self(kind: &str, builtins: &[(&str, &str)], name: &str) -> String {
|
||||
|
||||
@@ -4368,3 +4368,42 @@ fn cell_first_baseline_comes_from_cell_styles_table_regions_and_cells_and_round_
|
||||
let s2 = back.styles.cell.iter().find(|c| c.name == "Cap").unwrap();
|
||||
assert_eq!(s2.first_baseline, Some(FirstBaseline::CapHeight));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn style_names_with_a_literal_colon_survive_a_round_trip() {
|
||||
use designcraft_doc::styles::{CellStyle, CharacterStyle, ObjectStyle, ParagraphStyle, TableStyle};
|
||||
let names = ["Title: Big", "Group/Child: X", "Back\\slash: \\:"];
|
||||
let mut d = small_doc();
|
||||
for n in names {
|
||||
let st = d.styles_mut();
|
||||
st.paragraph.push(ParagraphStyle {
|
||||
name: n.into(),
|
||||
based_on: None,
|
||||
next_style: None,
|
||||
para: Default::default(),
|
||||
chars: Default::default(),
|
||||
shortcut: String::new(),
|
||||
});
|
||||
st.character.push(CharacterStyle { name: n.into(), based_on: None, chars: Default::default(), shortcut: String::new() });
|
||||
st.cell.push(CellStyle { name: n.into(), ..Default::default() });
|
||||
st.table.push(TableStyle { name: n.into(), ..Default::default() });
|
||||
st.object.push(ObjectStyle { name: n.into(), ..Default::default() });
|
||||
}
|
||||
let back = import_idml(&export_idml(&d)).unwrap();
|
||||
for n in names {
|
||||
assert!(
|
||||
back.styles.paragraph.iter().any(|s| s.name == n),
|
||||
"paragraph {n:?}: {:?}",
|
||||
back.styles.paragraph.iter().map(|s| &s.name).collect::<Vec<_>>()
|
||||
);
|
||||
assert!(back.styles.character.iter().any(|s| s.name == n), "character {n:?}");
|
||||
assert!(back.styles.cell.iter().any(|s| s.name == n), "cell {n:?}");
|
||||
assert!(back.styles.table.iter().any(|s| s.name == n), "table {n:?}");
|
||||
assert!(back.styles.object.iter().any(|s| s.name == n), "object {n:?}");
|
||||
}
|
||||
// A group is still written as an IDML `:` group path.
|
||||
assert_eq!(names::style_name_out(names::PARA_BUILTINS, "Group/Child"), "Group:Child");
|
||||
for n in names {
|
||||
assert_eq!(names::style_name_in(names::PARA_BUILTINS, &names::style_name_out(names::PARA_BUILTINS, n)), n);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user