mirror of
https://github.com/storytold/designcraft.git
synced 2026-10-31 13:46:52 -06:00
Parents: single-sided pages in a spread show the parent page in their place
This commit is contained in:
@@ -388,6 +388,7 @@ list) · [roadmap.md](docs/roadmap.md) (milestones, plan) · [parity-checklist.m
|
||||
|
||||
Newest first.
|
||||
|
||||
- **2026-10-30** · Without facing pages, a page of a multi-page spread shows the parent page in its place, as in InDesign: the second page of a two-page spread shows the parent's second page. Every single-sided page showed the parent's first page, so an InDesign book set up without facing pages but laid out in two-page spreads repeated the left parent page's items (a black heading bar) on its right pages and lost the right parent page's. No readiness percentage change.
|
||||
- **2026-10-30** · File > Package opens a dialog, as the reference app's: a Summary of fonts, links and images and inks, pages for each, a Printing Instructions step, and a PDF made with the options of a preset (#1514, #1515).
|
||||
- **2026-10-30** · IDML import reads TOC separators and entry styles written with the `$ID/` prefix or as `Properties` children, and the TOC separator notation (`^t`, `^y`, `^m`, `^>`) is shared by the dialog and IDML, with a special-character menu on the separator field (#1249, #1255).
|
||||
- **2026-10-29** · Inserting text with many returns no longer takes time that grows with their square (part of #1588): each return copied its paragraph's format with its own insert, moving every format after it. Splitting 20,000 returns into paragraphs took 15.8 s; placing a text file of them now takes 0.2 s, and one of a million returns 5.5 s. No readiness percentage change.
|
||||
|
||||
+10
-3
@@ -661,11 +661,18 @@ impl Document {
|
||||
}
|
||||
|
||||
/// Parent page (spread index, page index) applied to `page`: the page on its side of its
|
||||
/// parent spread.
|
||||
/// parent spread, or in its place for single-sided pages ([`Spread::parent_page_at`]).
|
||||
pub fn parent_page_of(&self, page: &Page) -> Option<(usize, usize)> {
|
||||
let pi = self.parent_index(page.parent?)?;
|
||||
// A parent spread can come without pages from a file.
|
||||
Some((pi, self.parents.get(pi)?.parent_page_for_side(page.side)?))
|
||||
let parent = self.parents.get(pi)?;
|
||||
// Only a single-sided page's position matters: look for it only then.
|
||||
let index = if page.side == PageSide::Single && parent.pages.len() >= 2 {
|
||||
self.spreads.iter().find_map(|s| s.pages.iter().position(|p| p.id == page.id)).unwrap_or(0)
|
||||
} else {
|
||||
0
|
||||
};
|
||||
Some((pi, parent.parent_page_at(page.side, index)?))
|
||||
}
|
||||
|
||||
/// The parent page applied to `page`.
|
||||
@@ -777,7 +784,7 @@ impl Document {
|
||||
c
|
||||
}
|
||||
};
|
||||
chain.into_iter().filter_map(|i| Some((i, self.parents.get(i)?.parent_page_for_side(pg.side)?))).collect()
|
||||
chain.into_iter().filter_map(|i| Some((i, self.parents.get(i)?.parent_page_at(pg.side, page)?))).collect()
|
||||
}
|
||||
|
||||
/// Whether basing parent `pi` on `base` would make a `based_on` cycle (or names no parent).
|
||||
|
||||
@@ -452,6 +452,16 @@ impl Spread {
|
||||
PageSide::Single => Some(0),
|
||||
}
|
||||
}
|
||||
/// The parent page shown behind page `index` of a document spread, on `side`: by side for
|
||||
/// facing pages ([`Self::parent_page_for_side`]); by position for single-sided pages in a
|
||||
/// spread of several, as InDesign does, so the second page of a two-page spread shows the
|
||||
/// parent's second page (the last one, for spreads longer than the parent).
|
||||
pub fn parent_page_at(&self, side: PageSide, index: usize) -> Option<usize> {
|
||||
match side {
|
||||
PageSide::Single if self.pages.len() >= 2 => Some(index.min(self.pages.len() - 1)),
|
||||
_ => self.parent_page_for_side(side),
|
||||
}
|
||||
}
|
||||
/// Index of the page under spread x (nearest page for the pasteboard).
|
||||
pub fn page_at_x(&self, x: f64) -> Option<usize> {
|
||||
if self.pages.is_empty() {
|
||||
|
||||
@@ -70,7 +70,7 @@ impl<'r> Importer<'r> {
|
||||
}
|
||||
|
||||
/// A page's margins and columns, and whether they are local (differ from its parent page's).
|
||||
fn page_margins(&self, p: &El, r: &Rect, master: bool, parent: Option<SpreadId>, side: PageSide) -> (Margins, Columns, bool) {
|
||||
fn page_margins(&self, p: &El, r: &Rect, master: bool, parent: Option<SpreadId>, (side, index): (PageSide, usize)) -> (Margins, Columns, bool) {
|
||||
// A document page's margins are local when they differ from its parent page's; a
|
||||
// page without its own takes the parent page's.
|
||||
let from_parent = if master {
|
||||
@@ -78,7 +78,7 @@ impl<'r> Importer<'r> {
|
||||
} else {
|
||||
parent
|
||||
.and_then(|id| self.parents.iter().find(|sp| sp.id == id))
|
||||
.and_then(|sp| sp.pages.get(sp.parent_page_for_side(side)?))
|
||||
.and_then(|sp| sp.pages.get(sp.parent_page_at(side, index)?))
|
||||
.map(|q| (q.margins, q.columns.clone()))
|
||||
};
|
||||
let (margins, columns) = match (p.find("MarginPreference"), &from_parent) {
|
||||
@@ -126,7 +126,7 @@ impl<'r> Importer<'r> {
|
||||
PageSide::Right
|
||||
};
|
||||
let parent = p.get("AppliedMaster").and_then(|m| self.parent_ids.get(m)).copied();
|
||||
let (margins, columns, local_margins) = self.page_margins(p, r, master, parent, side);
|
||||
let (margins, columns, local_margins) = self.page_margins(p, r, master, parent, (side, i));
|
||||
out_pages.push(Page {
|
||||
id: pid,
|
||||
width: r.width(),
|
||||
|
||||
@@ -27,6 +27,11 @@ fn story(id: &str, text: &str) -> String {
|
||||
/// right page a guide at 300 and a "Right head". Three pages: 1 alone, then 2–3. Rulers measure
|
||||
/// from each page.
|
||||
fn left_to_right_fixture() -> Vec<u8> {
|
||||
fixture(true)
|
||||
}
|
||||
|
||||
/// [`left_to_right_fixture`], with facing pages on or off.
|
||||
fn fixture(facing: bool) -> Vec<u8> {
|
||||
let margins = r#"<MarginPreference ColumnCount="1" ColumnGutter="12" Top="36" Bottom="36" Left="36" Right="120"/>"#;
|
||||
let page = |id: &str, x: f64, applied: bool, guide: Option<f64>| {
|
||||
let applied = if applied { r#" AppliedMaster="m1""# } else { "" };
|
||||
@@ -48,7 +53,7 @@ fn left_to_right_fixture() -> Vec<u8> {
|
||||
r#"<Document {NS} Self="d" StoryList="sl sr"><idPkg:Preferences src="Resources/Preferences.xml"/><Layer Self="L1" Name="Layer 1"/><idPkg:MasterSpread src="MasterSpreads/m1.xml"/><idPkg:Spread src="Spreads/sp0.xml"/><idPkg:Spread src="Spreads/sp1.xml"/><idPkg:Story src="Stories/sl.xml"/><idPkg:Story src="Stories/sr.xml"/></Document>"#
|
||||
);
|
||||
let prefs = format!(
|
||||
r#"<idPkg:Preferences {NS}><DocumentPreference PageWidth="400" PageHeight="600" FacingPages="true" PageBinding="LeftToRight"/><ViewPreference RulerOrigin="PageOrigin"/></idPkg:Preferences>"#
|
||||
r#"<idPkg:Preferences {NS}><DocumentPreference PageWidth="400" PageHeight="600" FacingPages="{facing}" PageBinding="LeftToRight"/><ViewPreference RulerOrigin="PageOrigin"/></idPkg:Preferences>"#
|
||||
);
|
||||
zip_files(&[
|
||||
("designmap.xml", &map),
|
||||
@@ -109,3 +114,19 @@ fn left_to_right_right_pages_take_mirrored_margins_and_the_right_parent_page() {
|
||||
let again = import_idml(&export_idml(&d)).unwrap();
|
||||
assert_right_pages_mirror_left_pages(&again);
|
||||
}
|
||||
|
||||
/// Without facing pages, a page of a two-page spread shows the parent page in its place, as in
|
||||
/// InDesign: page 3, the second page of its spread, shows the parent's second page, not the
|
||||
/// first page's items again.
|
||||
#[test]
|
||||
fn single_sided_pages_show_the_parent_page_in_their_place() {
|
||||
let d = import_idml(&fixture(false)).unwrap();
|
||||
assert!(d.spreads.iter().flat_map(|s| &s.pages).all(|p| p.side == PageSide::Single));
|
||||
assert_eq!(shown_heads(&d, 0), ["Left head"], "a page alone shows the parent's first page");
|
||||
assert_eq!(shown_heads(&d, 1), ["Left head"]);
|
||||
assert_eq!(shown_heads(&d, 2), ["Right head"]);
|
||||
// What the renderer draws behind each page of the second spread.
|
||||
let (ppi, _) = d.parent_page_for(1).unwrap();
|
||||
assert_eq!(d.shown_parent_pages(designcraft_doc::SpreadRef::Doc(1), 0), [(ppi, 0)]);
|
||||
assert_eq!(d.shown_parent_pages(designcraft_doc::SpreadRef::Doc(1), 1), [(ppi, 1)]);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user