mirror of
https://github.com/storytold/designcraft.git
synced 2026-10-29 04:36:47 -06:00
Merge idml-deleted-text (update): deleted text inside nested paragraphs stays deleted; a deleted break doesn't break (#581)
Conflict: crates/idml/src/tests.rs: both sides' tests (#473's SameParaStyleSpacing tests, then the branch's deleted-text tests)
This commit is contained in:
@@ -890,12 +890,16 @@ fn compose_with_db(doc: &Document, story: &Story, frames: &[FrameSpec], opts: &C
|
||||
let pf = &story.paras[pi];
|
||||
let (mut pp, base_chars) = doc.styles.resolve_para(pf);
|
||||
// A break character that ends the previous paragraph sends this one on, like a start option.
|
||||
let after_break = pi
|
||||
.checked_sub(1)
|
||||
.and_then(|p| para_ranges.get(p))
|
||||
.and_then(|r| story.text.get(r.clone()))
|
||||
.and_then(|t| t.chars().next_back())
|
||||
.and_then(break_start);
|
||||
// Deleted paragraphs in between don't count, nor does a deleted or hidden break.
|
||||
let after_break = (0..pi).rev().find(|p| !deleted.get(*p).copied().unwrap_or(false)).and_then(|p| {
|
||||
let r = para_ranges.get(p)?;
|
||||
let (i, c) = story.text.get(r.clone())?.char_indices().next_back()?;
|
||||
let start = break_start(c)?;
|
||||
let (_, chars) = doc.styles.resolve_para(story.paras.get(p)?);
|
||||
let props = doc.styles.resolve_char(&chars, story.char_format_at(r.start + i + c.len_utf8()));
|
||||
let gone = props.change == designcraft_doc::ChangeMark::Deleted || doc.conditions_hide(&props.conditions);
|
||||
(!gone).then_some(start)
|
||||
});
|
||||
if let Some(t) = story.para_table(pi) {
|
||||
if cur.last_baseline.is_some() {
|
||||
cur.start(after_break.unwrap_or(pp.start_paragraph), &cols, frames, doc);
|
||||
|
||||
@@ -2040,7 +2040,9 @@ impl<'r> Importer<'r> {
|
||||
"ParagraphStyleRange" => {
|
||||
let style = c.get("AppliedParagraphStyle").map(|r| self.para_style_ref(r)).unwrap_or_else(|| pf.style.clone());
|
||||
let para = self.para_attrs(c);
|
||||
let chars = self.char_attrs(c);
|
||||
let mut chars = self.char_attrs(c);
|
||||
// A paragraph range inside a tracked change is part of that change.
|
||||
chars.change = chars.change.or(cf.over.change);
|
||||
let npf = ParaFormat { style, para, chars: CharAttrs::default(), table: None };
|
||||
if b.fresh
|
||||
&& let Some(last) = b.paras.last_mut()
|
||||
|
||||
@@ -3290,3 +3290,86 @@ fn same_para_style_spacing_from_a_based_on_style_element_closes_the_gap() {
|
||||
assert!((baseline(1) - baseline(0) - 13.0).abs() < 1e-6, "{} {}", baseline(0), baseline(1));
|
||||
assert!((baseline(3) - baseline(2) - 13.0 - 4.5 - 18.0).abs() < 1e-6, "{} {}", baseline(2), baseline(3));
|
||||
}
|
||||
|
||||
/// Composed lines (baseline, left, right) of the only story.
|
||||
fn story_lines(d: &Document) -> Vec<(f64, f64, f64)> {
|
||||
let sid = *d.stories.keys().next().unwrap();
|
||||
let cs = designcraft_compose::compose_story(d, sid, &Default::default());
|
||||
cs.frames.iter().flat_map(|f| f.lines.iter().map(|l| (l.baseline, l.x0, l.end_x))).collect()
|
||||
}
|
||||
|
||||
const PSR: &str = r#"<ParagraphStyleRange AppliedParagraphStyle="ParagraphStyle/$ID/NormalParagraphStyle">"#;
|
||||
|
||||
/// Lays out like the same story without the deleted paragraph.
|
||||
fn assert_deleted_para_takes_no_room(deleted: &str, deleted_text: &str) {
|
||||
let tracked = tracked_story(&format!(
|
||||
r#"{PSR}{CSR}<Content>First live paragraph.</Content><Br/></CharacterStyleRange></ParagraphStyleRange>
|
||||
{deleted}
|
||||
{PSR}{CSR}<Content>Second live paragraph.</Content></CharacterStyleRange></ParagraphStyleRange>"#
|
||||
));
|
||||
let plain = tracked_story(&format!(
|
||||
r#"{PSR}{CSR}<Content>First live paragraph.</Content><Br/></CharacterStyleRange></ParagraphStyleRange>
|
||||
{PSR}{CSR}<Content>Second live paragraph.</Content></CharacterStyleRange></ParagraphStyleRange>"#
|
||||
));
|
||||
let d = import_idml_with(&fixture_with_story(&tracked), &|_| None).unwrap();
|
||||
let story = d.stories.values().next().unwrap();
|
||||
let marks: Vec<_> = story.runs().map(|(r, f)| (story.text[r].to_string(), f.over.change)).collect();
|
||||
let want = vec![
|
||||
("First live paragraph.\n".to_string(), None),
|
||||
(deleted_text.to_string(), Some(designcraft_doc::ChangeMark::Deleted)),
|
||||
("Second live paragraph.".to_string(), None),
|
||||
];
|
||||
assert_eq!(marks, want);
|
||||
let expected = story_lines(&import_idml_with(&fixture_with_story(&plain), &|_| None).unwrap());
|
||||
assert_eq!(expected.len(), 2);
|
||||
assert_eq!(story_lines(&d), expected);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn tracked_deleted_paragraph_range_stays_deleted() {
|
||||
assert_deleted_para_takes_no_room(
|
||||
&format!(
|
||||
r#"<Change Self="c1" ChangeType="DeletedText">{PSR}{CSR}<Content>Deleted text</Content><Br/></CharacterStyleRange></ParagraphStyleRange></Change>"#
|
||||
),
|
||||
"Deleted text\n",
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn tracked_deleted_frame_break_does_not_break() {
|
||||
let fb = "\u{E003}";
|
||||
let nf = r#"<CharacterStyleRange AppliedCharacterStyle="CharacterStyle/$ID/[No character style]" ParagraphBreakType="NextFrame">"#;
|
||||
// The change around the paragraph range.
|
||||
assert_deleted_para_takes_no_room(
|
||||
&format!(
|
||||
r#"<Change Self="c1" ChangeType="DeletedText">{PSR}{CSR}<Content>Gone</Content></CharacterStyleRange>{nf}<Br/></CharacterStyleRange></ParagraphStyleRange></Change>"#
|
||||
),
|
||||
&format!("Gone{fb}\n"),
|
||||
);
|
||||
// The change inside the character range.
|
||||
assert_deleted_para_takes_no_room(
|
||||
&format!(
|
||||
r#"{PSR}{nf}<Change Self="c1" ChangeType="DeletedText"><Content>Gone</Content><Br/></Change></CharacterStyleRange></ParagraphStyleRange>"#
|
||||
),
|
||||
&format!("Gone{fb}\n"),
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn live_frame_break_before_deleted_paragraph_still_breaks() {
|
||||
let nf = r#"<CharacterStyleRange AppliedCharacterStyle="CharacterStyle/$ID/[No character style]" ParagraphBreakType="NextFrame">"#;
|
||||
let first =
|
||||
format!(r#"{PSR}{CSR}<Content>First live paragraph.</Content></CharacterStyleRange>{nf}<Br/></CharacterStyleRange></ParagraphStyleRange>"#);
|
||||
let second = format!(r#"{PSR}{CSR}<Content>Second live paragraph.</Content></CharacterStyleRange></ParagraphStyleRange>"#);
|
||||
let deleted = format!(
|
||||
r#"<Change Self="c1" ChangeType="DeletedText">{PSR}{CSR}<Content>Gone</Content><Br/></CharacterStyleRange></ParagraphStyleRange></Change>"#
|
||||
);
|
||||
let import = |body: String| import_idml_with(&fixture_with_story(&tracked_story(&body)), &|_| None).unwrap();
|
||||
let reference = import(format!("{first}\n{second}"));
|
||||
let sid = *reference.stories.keys().next().unwrap();
|
||||
let per_frame: Vec<usize> =
|
||||
designcraft_compose::compose_story(&reference, sid, &Default::default()).frames.iter().map(|f| f.lines.len()).collect();
|
||||
assert!(per_frame.first() == Some(&1) && per_frame.get(1).is_some_and(|n| *n > 0), "the break sends the second paragraph on");
|
||||
let expected = story_lines(&reference);
|
||||
assert_eq!(story_lines(&import(format!("{first}\n{deleted}\n{second}"))), expected);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user