mirror of
https://github.com/storytold/designcraft.git
synced 2026-11-02 22:50:27 -06:00
Integration: IDML import warns when it repairs a tab list
#343's TabStop::sanitized_list repairs a file's tab list (dropped, clamped, shortened or duplicate stops, more than MAX_TAB_STOPS); fix/idml-missing-spread's import_idml_report carries the import warnings. The import counts the lists it had to repair and reports them in one warning; sorting and a char stop's default '.' don't count.
This commit is contained in:
@@ -112,6 +112,15 @@ pub fn import_idml_report(bytes: &[u8], read_link: &dyn Fn(&str) -> Option<Vec<u
|
||||
im.modified = xmp_date(&m, "xmp:ModifyDate");
|
||||
}
|
||||
im.run(&root, &top)?;
|
||||
if im.repaired_tab_lists > 0 {
|
||||
warnings.push(format!(
|
||||
"tab lists repaired to fit the limits (at most {} stops, finite positions up to {} in, leaders of at most {} characters, one stop per position): {}",
|
||||
designcraft_doc::MAX_TAB_STOPS,
|
||||
designcraft_doc::MAX_TAB_POSITION / 72.0,
|
||||
designcraft_doc::MAX_TAB_LEADER,
|
||||
im.repaired_tab_lists,
|
||||
));
|
||||
}
|
||||
let document = im.finish(&root).map_err(|e| match e {
|
||||
// Without its spread parts there is nothing to open: say which parts were missing.
|
||||
IdmlError::Invalid(msg) if !warnings.is_empty() => IdmlError::Invalid(format!("{msg} ({})", warnings.join("; "))),
|
||||
@@ -121,6 +130,19 @@ pub fn import_idml_report(bytes: &[u8], read_link: &dyn Fn(&str) -> Option<Vec<u
|
||||
Ok(Imported { document, warnings })
|
||||
}
|
||||
|
||||
/// Whether sanitizing a file's tab list dropped, moved or shortened a stop. Sorting, and the `.`
|
||||
/// a char stop with no character aligns on, change nothing.
|
||||
fn tab_list_repaired(read: &[TabStop], sanitized: &[TabStop]) -> bool {
|
||||
let kept = |t: &TabStop| {
|
||||
sanitized.iter().any(|o| {
|
||||
o.position == t.position
|
||||
&& o.leader == t.leader
|
||||
&& (o.align_on == t.align_on || (t.align_on.is_empty() && o.align == designcraft_doc::TabAlign::Char))
|
||||
})
|
||||
};
|
||||
read.len() != sanitized.len() || !read.iter().all(kept)
|
||||
}
|
||||
|
||||
/// A package part name in the form the lookup compares: InDesign and other IDML writers differ
|
||||
/// in percent-encoding, case, path separators and a leading `./` or `/`.
|
||||
fn normalize_part_name(name: &str) -> String {
|
||||
@@ -233,6 +255,8 @@ struct Importer<'r> {
|
||||
/// Creation and modification times (Unix seconds) from the package metadata.
|
||||
created: Option<i64>,
|
||||
modified: Option<i64>,
|
||||
/// Tab lists [`TabStop::sanitized_list`] had to repair (an import warning).
|
||||
repaired_tab_lists: usize,
|
||||
}
|
||||
|
||||
fn lab_to_color(l: f32, a: f32, b: f32) -> Color {
|
||||
@@ -374,6 +398,7 @@ impl<'r> Importer<'r> {
|
||||
text_var_ids: HashMap::new(),
|
||||
created: None,
|
||||
modified: None,
|
||||
repaired_tab_lists: 0,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1593,7 +1618,11 @@ impl<'r> Importer<'r> {
|
||||
align_on: get("AlignmentCharacter").filter(|c| c != ".").unwrap_or_default(),
|
||||
});
|
||||
}
|
||||
a.tabs = Some(TabStop::sanitized_list(tabs));
|
||||
let sanitized = TabStop::sanitized_list(tabs.clone());
|
||||
if tab_list_repaired(&tabs, &sanitized) {
|
||||
self.repaired_tab_lists = self.repaired_tab_lists.saturating_add(1);
|
||||
}
|
||||
a.tabs = Some(sanitized);
|
||||
}
|
||||
a.list_type = e.prop("BulletsAndNumberingListType").map(|v| match v.trim() {
|
||||
"BulletList" => ListType::Bullets,
|
||||
|
||||
@@ -2626,10 +2626,24 @@ fn hostile_tab_lists_import_within_the_limits() {
|
||||
<CharacterStyleRange AppliedCharacterStyle="CharacterStyle/$ID/[No character style]"><Content>Many tabs</Content></CharacterStyleRange>
|
||||
</ParagraphStyleRange></Story></idPkg:Story>"#
|
||||
);
|
||||
let doc = import_idml(&fixture_with_story(&story)).unwrap();
|
||||
let report = import_idml_report(&fixture_with_story(&story), &|_| None).unwrap();
|
||||
assert!(report.warnings.iter().any(|w| w.starts_with("tab lists repaired to fit the limits") && w.ends_with("): 1")), "{:?}", report.warnings);
|
||||
let doc = report.document;
|
||||
let tabs = doc.stories.values().find(|s| s.text.contains("Many tabs")).unwrap().paras[0].para.tabs.clone().unwrap();
|
||||
assert_eq!(tabs.len(), designcraft_doc::MAX_TAB_STOPS);
|
||||
assert!(tabs.iter().all(|t| t.position.is_finite() && t.leader.chars().count() <= designcraft_doc::MAX_TAB_LEADER));
|
||||
assert!(tabs.windows(2).all(|w| w[0].position < w[1].position), "sorted");
|
||||
assert_eq!((tabs[0].position, tabs[99].position), (0.0, 297.0), "the first stops by position");
|
||||
// A list within the limits, unsorted and with a char stop on `.`, imports without a warning.
|
||||
let fine = format!(
|
||||
r#"<idPkg:Story xmlns:idPkg="http://ns.adobe.com/AdobeInDesign/idml/1.0/packaging"><Story Self="s1">
|
||||
<ParagraphStyleRange AppliedParagraphStyle="ParagraphStyle/$ID/[No paragraph style]">
|
||||
<Properties><TabList type="list">{}{}<ListItem type="record"><Alignment type="enumeration">CharacterAlign</Alignment><AlignmentCharacter type="string">.</AlignmentCharacter><Leader type="string"></Leader><Position type="unit">40</Position></ListItem></TabList></Properties>
|
||||
<CharacterStyleRange AppliedCharacterStyle="CharacterStyle/$ID/[No character style]"><Content>Few tabs</Content></CharacterStyleRange>
|
||||
</ParagraphStyleRange></Story></idPkg:Story>"#,
|
||||
item("90", "."),
|
||||
item("12", "")
|
||||
);
|
||||
let report = import_idml_report(&fixture_with_story(&fine), &|_| None).unwrap();
|
||||
assert!(report.warnings.is_empty(), "{:?}", report.warnings);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user