mirror of
https://github.com/storytold/designcraft.git
synced 2026-10-14 11:17:14 -04:00
fix(compose): odd/even page starts keep their parity at the top of a frame
Start options were skipped for a paragraph at the top of a frame. That is right for Next Page, Next Frame and Next Column (there is nothing to break away from), but a Next Odd Page paragraph that lands at the top of an even page stayed there, as did Next Even Page at the top of an odd page, and the same went for odd and even page break characters. A part or chapter opener whose previous section ended exactly at the bottom of an odd page therefore opened on a left-hand page. Cursor::start_at_top now moves such a paragraph to the next page of the asked parity, for paragraphs, tables and break characters alike. Other start options at the top of a frame are unchanged. Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
1 parent
e862e0a613
commit
0aaaa9600d
2 files changed
+51
-6
No files matched your search
@@ -576,8 +576,11 @@ fn compose_with_db(doc: &Document, story: &Story, frames: &[FrameSpec], opts: &C
|
||||
.and_then(|t| t.chars().next_back())
|
||||
.and_then(break_start);
|
||||
if let Some(t) = story.para_table(pi) {
|
||||
let start = after_break.unwrap_or(pp.start_paragraph);
|
||||
if cur.last_baseline.is_some() {
|
||||
cur.start(after_break.unwrap_or(pp.start_paragraph), &cols, frames, doc);
|
||||
cur.start(start, &cols, frames, doc);
|
||||
} else {
|
||||
cur.start_at_top(start, frames, doc);
|
||||
}
|
||||
let at_top = cur.last_baseline.is_none();
|
||||
let start_at = (cur.fi, cur.col);
|
||||
@@ -796,17 +799,22 @@ fn compose_with_db(doc: &Document, story: &Story, frames: &[FrameSpec], opts: &C
|
||||
StartParagraph::NextColumn => next_column(cur),
|
||||
other => cur.start(other, &cols, frames, doc),
|
||||
};
|
||||
// Paragraph start options.
|
||||
if let Some(start) = after_break
|
||||
&& cur.last_baseline.is_some()
|
||||
{
|
||||
begin_at(&mut cur, start);
|
||||
// Paragraph start options. At the top of a frame there is nothing to break away from, but
|
||||
// an odd or even page start still has to land on a page of that parity.
|
||||
if let Some(start) = after_break {
|
||||
if cur.last_baseline.is_some() {
|
||||
begin_at(&mut cur, start);
|
||||
} else {
|
||||
cur.start_at_top(start, frames, doc);
|
||||
}
|
||||
}
|
||||
if force_col[pi] {
|
||||
next_column(&mut cur);
|
||||
}
|
||||
if cur.last_baseline.is_some() {
|
||||
begin_at(&mut cur, pp.start_paragraph);
|
||||
} else {
|
||||
cur.start_at_top(pp.start_paragraph, frames, doc);
|
||||
}
|
||||
if spanning && cur.fi < frames.len() {
|
||||
// Close the band: the paragraph goes below the deepest of its columns, which are
|
||||
@@ -1983,6 +1991,20 @@ impl Cursor {
|
||||
StartParagraph::NextEvenPage => self.next_page(frames, doc, Some(false)),
|
||||
}
|
||||
}
|
||||
/// A start option for a paragraph at the top of a frame: there is nothing to break away from,
|
||||
/// but an odd or even page start still has to land on a page of that parity, so a paragraph
|
||||
/// at the top of an even page that asks for the next odd page moves on (and vice versa).
|
||||
fn start_at_top(&mut self, start: StartParagraph, frames: &[FrameSpec], doc: &Document) {
|
||||
let want_odd = match start {
|
||||
StartParagraph::NextOddPage => true,
|
||||
StartParagraph::NextEvenPage => false,
|
||||
_ => return,
|
||||
};
|
||||
let Some(page) = frames.get(self.fi).and_then(|f| f.page) else { return };
|
||||
if (doc.page_number(page) % 2 == 1) != want_odd {
|
||||
self.next_page(frames, doc, Some(want_odd));
|
||||
}
|
||||
}
|
||||
/// Move to the next frame on a later page, one with an odd page number (`odd` true) or an
|
||||
/// even one (false) when asked. Frames without a page (parent pages) qualify; with no frame
|
||||
/// left, the rest of the story is overset.
|
||||
|
||||
@@ -595,6 +595,29 @@ fn break_characters_end_their_paragraph_and_keep_page_parity() {
|
||||
assert_eq!(cs.overset_at, Some(text.find("four").unwrap()));
|
||||
}
|
||||
|
||||
/// A paragraph that starts at the top of a frame isn't pushed on by Next Page, but Next Odd Page
|
||||
/// (or Next Even Page) still moves it when that frame is on a page of the other parity.
|
||||
#[test]
|
||||
fn odd_and_even_page_starts_keep_parity_at_the_top_of_a_frame() {
|
||||
use designcraft_doc::StartParagraph;
|
||||
use designcraft_doc::story::PAGE_BREAK;
|
||||
let pages_of = |start: StartParagraph| {
|
||||
let mut d = Document::new(&NewDocument { pages: 4, facing_pages: false, primary_text_frame: true, ..Default::default() });
|
||||
let sid = d.settings.primary_story.unwrap();
|
||||
// "two" begins at the top of page 2, after the page break that ends "one".
|
||||
d.story_mut(sid).unwrap().insert(0, &format!("one{PAGE_BREAK}\ntwo\nthree"));
|
||||
d.story_mut(sid).unwrap().paras[1].para.start_paragraph = Some(start);
|
||||
let cs = compose_story(&d, sid, &ComposeOptions::default());
|
||||
// Frame i is on page i + 1.
|
||||
cs.frames.iter().enumerate().flat_map(|(fi, f)| f.lines.iter().map(move |l| (l.para, fi + 1))).collect::<Vec<_>>()
|
||||
};
|
||||
// Already at the top of an even page: Next Page and Next Even Page leave "two" where it is.
|
||||
assert_eq!(pages_of(StartParagraph::NextPage), vec![(0, 1), (1, 2), (2, 2)]);
|
||||
assert_eq!(pages_of(StartParagraph::NextEvenPage), vec![(0, 1), (1, 2), (2, 2)]);
|
||||
// Next Odd Page takes it to page 3, leaving page 2 empty.
|
||||
assert_eq!(pages_of(StartParagraph::NextOddPage), vec![(0, 1), (1, 3), (2, 3)]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn line_before_a_break_character_is_a_last_line() {
|
||||
// As in InDesign: column, frame and page breaks end the paragraph's last line (set with the
|
||||
|
||||
Reference in new issue
Block a user