mirror of
https://github.com/storytold/designcraft.git
synced 2026-11-14 14:22:39 +00:00
Import IDML page and spread values through model
This commit is contained in:
3 files changed
+42
-64
No files matched your search
Generated
+10
-10
@@ -1225,10 +1225,10 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "craft-idml"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/storytold/craft-libs?rev=356ddacd1aac03c285b90a0596a7320ddf5eebb5#356ddacd1aac03c285b90a0596a7320ddf5eebb5"
|
||||
source = "git+https://github.com/storytold/craft-libs?rev=584e00bf1f7e812e4fd204bc9b077c8a96e0d1ca#584e00bf1f7e812e4fd204bc9b077c8a96e0d1ca"
|
||||
dependencies = [
|
||||
"craft-xml 0.1.0 (git+https://github.com/storytold/craft-libs?rev=356ddacd1aac03c285b90a0596a7320ddf5eebb5)",
|
||||
"craft-zip 0.1.0 (git+https://github.com/storytold/craft-libs?rev=356ddacd1aac03c285b90a0596a7320ddf5eebb5)",
|
||||
"craft-xml 0.1.0 (git+https://github.com/storytold/craft-libs?rev=584e00bf1f7e812e4fd204bc9b077c8a96e0d1ca)",
|
||||
"craft-zip 0.1.0 (git+https://github.com/storytold/craft-libs?rev=584e00bf1f7e812e4fd204bc9b077c8a96e0d1ca)",
|
||||
"thiserror 2.0.21",
|
||||
]
|
||||
|
||||
@@ -1578,7 +1578,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "craft-xml"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/storytold/craft-libs?rev=356ddacd1aac03c285b90a0596a7320ddf5eebb5#356ddacd1aac03c285b90a0596a7320ddf5eebb5"
|
||||
source = "git+https://github.com/storytold/craft-libs?rev=40525913633942f84b06d4837a8d90534a55d63f#40525913633942f84b06d4837a8d90534a55d63f"
|
||||
dependencies = [
|
||||
"quick-xml 0.41.0",
|
||||
"thiserror 2.0.21",
|
||||
@@ -1587,7 +1587,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "craft-xml"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/storytold/craft-libs?rev=40525913633942f84b06d4837a8d90534a55d63f#40525913633942f84b06d4837a8d90534a55d63f"
|
||||
source = "git+https://github.com/storytold/craft-libs?rev=584e00bf1f7e812e4fd204bc9b077c8a96e0d1ca#584e00bf1f7e812e4fd204bc9b077c8a96e0d1ca"
|
||||
dependencies = [
|
||||
"quick-xml 0.41.0",
|
||||
"thiserror 2.0.21",
|
||||
@@ -1633,7 +1633,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "craft-zip"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/storytold/craft-libs?rev=356ddacd1aac03c285b90a0596a7320ddf5eebb5#356ddacd1aac03c285b90a0596a7320ddf5eebb5"
|
||||
source = "git+https://github.com/storytold/craft-libs?rev=584e00bf1f7e812e4fd204bc9b077c8a96e0d1ca#584e00bf1f7e812e4fd204bc9b077c8a96e0d1ca"
|
||||
dependencies = [
|
||||
"flate2",
|
||||
"thiserror 2.0.21",
|
||||
@@ -1914,7 +1914,7 @@ dependencies = [
|
||||
"craft-prepress",
|
||||
"craft-print",
|
||||
"craft-svg 0.1.0 (git+https://github.com/dylanmtaylor/craft-libs?rev=2e670cf9377b523112f3fe4b409399772c745895)",
|
||||
"craft-xml 0.1.0 (git+https://github.com/storytold/craft-libs?rev=356ddacd1aac03c285b90a0596a7320ddf5eebb5)",
|
||||
"craft-xml 0.1.0 (git+https://github.com/storytold/craft-libs?rev=584e00bf1f7e812e4fd204bc9b077c8a96e0d1ca)",
|
||||
"craft-xml 0.1.0 (git+https://github.com/storytold/craft-libs?rev=f23b0f33f0d00b157476533a43cbbcfd901aa1f1)",
|
||||
"designcraft-color",
|
||||
"designcraft-compose",
|
||||
@@ -1998,8 +1998,8 @@ dependencies = [
|
||||
name = "designcraft-idml"
|
||||
version = "0.20.1"
|
||||
dependencies = [
|
||||
"craft-idml 0.1.0 (git+https://github.com/storytold/craft-libs?rev=356ddacd1aac03c285b90a0596a7320ddf5eebb5)",
|
||||
"craft-xml 0.1.0 (git+https://github.com/storytold/craft-libs?rev=356ddacd1aac03c285b90a0596a7320ddf5eebb5)",
|
||||
"craft-idml 0.1.0 (git+https://github.com/storytold/craft-libs?rev=584e00bf1f7e812e4fd204bc9b077c8a96e0d1ca)",
|
||||
"craft-xml 0.1.0 (git+https://github.com/storytold/craft-libs?rev=584e00bf1f7e812e4fd204bc9b077c8a96e0d1ca)",
|
||||
"designcraft-color",
|
||||
"designcraft-compose",
|
||||
"designcraft-doc",
|
||||
@@ -2116,7 +2116,7 @@ version = "0.20.1"
|
||||
dependencies = [
|
||||
"craft-docx",
|
||||
"craft-file-formats",
|
||||
"craft-idml 0.1.0 (git+https://github.com/storytold/craft-libs?rev=356ddacd1aac03c285b90a0596a7320ddf5eebb5)",
|
||||
"craft-idml 0.1.0 (git+https://github.com/storytold/craft-libs?rev=584e00bf1f7e812e4fd204bc9b077c8a96e0d1ca)",
|
||||
"craft-rtf",
|
||||
"craft-wordmodel",
|
||||
"craft-xlsx",
|
||||
|
||||
+2
-2
@@ -16,10 +16,10 @@ craft-pdf = { git = "https://github.com/storytold/craft-libs", rev = "4052591363
|
||||
craft-document-bundle = { git = "https://github.com/storytold/craft-libs", rev = "ba24fe2435adb3c81ad6057faf36365fbba736cf", version = "=0.1.0" }
|
||||
craft-eps = { git = "https://github.com/storytold/craft-libs", rev = "ba24fe2435adb3c81ad6057faf36365fbba736cf", version = "=0.1.0" }
|
||||
craft-epub = { git = "https://github.com/storytold/craft-libs", rev = "ba24fe2435adb3c81ad6057faf36365fbba736cf", version = "=0.1.0" }
|
||||
craft-idml = { git = "https://github.com/storytold/craft-libs", rev = "356ddacd1aac03c285b90a0596a7320ddf5eebb5", version = "=0.1.0" }
|
||||
craft-idml = { git = "https://github.com/storytold/craft-libs", rev = "584e00bf1f7e812e4fd204bc9b077c8a96e0d1ca", version = "=0.1.0" }
|
||||
craft-indd = { git = "https://github.com/storytold/craft-libs", rev = "47bf5650b0d503b97bfb5e841746c4957897d11f", version = "=0.1.0" }
|
||||
craft-svg = { git = "https://github.com/dylanmtaylor/craft-libs", rev = "2e670cf9377b523112f3fe4b409399772c745895", version = "=0.1.0", features = ["tree-render", "tree-text"] }
|
||||
craft-xml = { git = "https://github.com/storytold/craft-libs", rev = "356ddacd1aac03c285b90a0596a7320ddf5eebb5", version = "=0.1.0" }
|
||||
craft-xml = { git = "https://github.com/storytold/craft-libs", rev = "584e00bf1f7e812e4fd204bc9b077c8a96e0d1ca", version = "=0.1.0" }
|
||||
craft-xml-dtd = { package = "craft-xml", git = "https://github.com/storytold/craft-libs", rev = "f23b0f33f0d00b157476533a43cbbcfd901aa1f1", version = "=0.1.0" }
|
||||
craft-rtf = { git = "https://github.com/storytold/craft-libs", rev = "ba24fe2435adb3c81ad6057faf36365fbba736cf", version = "=0.1.0" }
|
||||
craft-xlsx = { git = "https://github.com/storytold/craft-libs", rev = "ba24fe2435adb3c81ad6057faf36365fbba736cf", version = "=0.1.0" }
|
||||
|
||||
@@ -1,29 +1,8 @@
|
||||
use super::frame::{fit_columns, margins_of};
|
||||
use super::values::{nums, parse_affine};
|
||||
use super::{
|
||||
Affine, Arc, Columns, El, Guide, Importer, ItemId, Margins, Orientation, Page, PageId, PageSide, ParentInfo, Point, Rect, Spread, SpreadId, names,
|
||||
};
|
||||
|
||||
/// View › Rotate Spread from a `Spread` element's `ItemTransform`: quarter turns clockwise on
|
||||
/// screen (IDML y points down, so `0 1 -1 0` is one clockwise turn). The spread's items and
|
||||
/// pages stay in unrotated spread coordinates, so only the rotation part is read. Anything other
|
||||
/// than an exact quarter turn (identity, scale, other angles, malformed numbers) is no rotation.
|
||||
fn spread_view_rotation(s: Option<&str>) -> u8 {
|
||||
let parsed: std::result::Result<Vec<f64>, _> = s.unwrap_or("").split_whitespace().take(7).map(str::parse::<f64>).collect();
|
||||
let Some([a, b, c, d, _, _]) = parsed.ok().and_then(|v| <[f64; 6]>::try_from(v).ok()) else { return 0 };
|
||||
let is = |x: f64, y: f64| (x - y).abs() < 1e-6;
|
||||
let quarter = |m: [f64; 4]| is(a, m[0]) && is(b, m[1]) && is(c, m[2]) && is(d, m[3]);
|
||||
if quarter([0.0, 1.0, -1.0, 0.0]) {
|
||||
1
|
||||
} else if quarter([-1.0, 0.0, 0.0, -1.0]) {
|
||||
2
|
||||
} else if quarter([0.0, -1.0, 1.0, 0.0]) {
|
||||
3
|
||||
} else {
|
||||
0
|
||||
}
|
||||
}
|
||||
|
||||
impl<'r> Importer<'r> {
|
||||
// ---------- spreads and items ----------
|
||||
|
||||
@@ -55,22 +34,28 @@ impl<'r> Importer<'r> {
|
||||
}
|
||||
|
||||
/// The page rectangles of a spread element, in IDML spread coordinates.
|
||||
fn page_rects<'a>(&self, e: &'a El) -> Vec<(Rect, &'a El)> {
|
||||
let mut pages: Vec<(Rect, &'a El)> = Vec::new();
|
||||
for p in e.find_all("Page") {
|
||||
let gb = nums(p.get("GeometricBounds").unwrap_or(""));
|
||||
let (t, l, bt, r) =
|
||||
if gb.len() == 4 { (gb[0], gb[1], gb[2], gb[3]) } else { (0.0, 0.0, self.settings.page_height, self.settings.page_width) };
|
||||
let xf = parse_affine(p.get("ItemTransform"));
|
||||
fn page_rects(&self, spread: &craft_idml::model::Spread) -> Vec<(Rect, craft_idml::model::Page)> {
|
||||
let mut pages: Vec<(Rect, craft_idml::model::Page)> = Vec::new();
|
||||
for p in spread.pages() {
|
||||
let gb = p.geometric_bounds().unwrap_or([0.0, 0.0, self.settings.page_height, self.settings.page_width]);
|
||||
let [t, l, bt, r] = gb;
|
||||
let xf = Affine::new(p.item_transform());
|
||||
let a = xf * Point::new(l, t);
|
||||
let c = xf * Point::new(r, bt);
|
||||
pages.push((Rect::from_points(a, c), p));
|
||||
pages.push((Rect::from_points(a, c), p.clone()));
|
||||
}
|
||||
pages
|
||||
}
|
||||
|
||||
/// 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, index): (PageSide, usize)) -> (Margins, Columns, bool) {
|
||||
fn page_margins(
|
||||
&self,
|
||||
p: &craft_idml::model::Page,
|
||||
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 {
|
||||
@@ -81,7 +66,8 @@ impl<'r> Importer<'r> {
|
||||
.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) {
|
||||
let preference = p.margin_preference().map(|v| crate::xml::from_craft(v.element("MarginPreference")));
|
||||
let (margins, columns) = match (preference.as_ref(), &from_parent) {
|
||||
(Some(m), _) => {
|
||||
let (margins, columns) = margins_of(m);
|
||||
(margins, fit_columns(columns, &margins, r.width()))
|
||||
@@ -97,9 +83,10 @@ impl<'r> Importer<'r> {
|
||||
}
|
||||
|
||||
pub(super) fn spread(&mut self, e: &El, id: SpreadId, master: bool) -> Spread {
|
||||
let model_spread = craft_idml::model::Spread::from_el(&crate::xml::to_craft(e));
|
||||
let facing = self.settings.facing_pages;
|
||||
// Page rectangles in IDML spread coordinates.
|
||||
let pages = self.page_rects(e);
|
||||
let pages = self.page_rects(&model_spread);
|
||||
let min_x = pages.iter().map(|(r, _)| r.x0).fold(f64::INFINITY, f64::min);
|
||||
let min_y = pages.iter().map(|(r, _)| r.y0).fold(f64::INFINITY, f64::min);
|
||||
let (min_x, min_y) = if min_x.is_finite() { (min_x, min_y) } else { (0.0, 0.0) };
|
||||
@@ -107,13 +94,13 @@ impl<'r> Importer<'r> {
|
||||
// The spine: between a left and a right page (else the spread's left edge).
|
||||
let spine = pages.iter().map(|(r, _)| r.x0 - min_x).filter(|x| *x > 1e-6).fold(f64::INFINITY, f64::min);
|
||||
let spine = if spine.is_finite() && facing { spine } else { 0.0 };
|
||||
let show = e.get("ShowMasterItems") != Some("false");
|
||||
let show = model_spread.show_master_items();
|
||||
let first_abs: usize = self.spreads.iter().map(|s| s.pages.len()).sum();
|
||||
let view_rotation = if master { 0 } else { spread_view_rotation(e.get("ItemTransform")) };
|
||||
let view_rotation = model_spread.view_rotation();
|
||||
let mut out_pages = Vec::new();
|
||||
for (i, (r, p)) in pages.iter().enumerate() {
|
||||
let pid = PageId(self.alloc());
|
||||
if let Some(s) = p.get("Self")
|
||||
if let Some(s) = p.self_id()
|
||||
&& !master
|
||||
{
|
||||
self.page_index.insert(s.to_string(), first_abs + i);
|
||||
@@ -125,7 +112,7 @@ impl<'r> Importer<'r> {
|
||||
} else {
|
||||
PageSide::Right
|
||||
};
|
||||
let parent = p.get("AppliedMaster").and_then(|m| self.parent_ids.get(m)).copied();
|
||||
let parent = p.applied_master().and_then(|m| self.parent_ids.get(m)).copied();
|
||||
let (margins, columns, local_margins) = self.page_margins(p, r, master, parent, (side, i));
|
||||
out_pages.push(Page {
|
||||
id: pid,
|
||||
@@ -138,20 +125,15 @@ impl<'r> Importer<'r> {
|
||||
parent,
|
||||
side,
|
||||
overridden: vec![],
|
||||
guides: self.guides_of(
|
||||
&craft_idml::model::Page::from_el(&crate::xml::to_craft(p)),
|
||||
Rect::new(r.x0 - min_x, r.y0 - min_y, r.x1 - min_x, r.y1 - min_y),
|
||||
spine,
|
||||
),
|
||||
guides: self.guides_of(p, Rect::new(r.x0 - min_x, r.y0 - min_y, r.x1 - min_x, r.y1 - min_y), spine),
|
||||
show_parent_items: show,
|
||||
liquid: names::liquid_rule_in(p.get("LayoutRule").unwrap_or("Off")),
|
||||
liquid: names::liquid_rule_in(p.layout_rule()),
|
||||
transition: None,
|
||||
view_rotation: if i == 0 { view_rotation } else { 0 },
|
||||
show_parent_overlay: false,
|
||||
});
|
||||
}
|
||||
let overrides: Vec<Vec<String>> =
|
||||
pages.iter().map(|(_, p)| p.get("OverrideList").unwrap_or("").split_whitespace().map(str::to_string).collect()).collect();
|
||||
let overrides: Vec<Vec<String>> = pages.iter().map(|(_, p)| p.override_list().into_iter().map(str::to_string).collect()).collect();
|
||||
let mut items = Vec::new();
|
||||
for c in e.elements() {
|
||||
if let Some(it) = self.item(c, shift) {
|
||||
@@ -162,15 +144,11 @@ impl<'r> Importer<'r> {
|
||||
pg.overridden = ov.iter().filter_map(|s| self.item_ids.get(s)).copied().filter(|i| self.is_parent_item(*i)).collect();
|
||||
}
|
||||
let parent = if master {
|
||||
let prefix =
|
||||
e.get("NamePrefix").map(str::to_string).unwrap_or_else(|| e.get("Name").unwrap_or("A").split('-').next().unwrap_or("A").to_string());
|
||||
let name = e.get("BaseName").map(str::to_string).unwrap_or_else(|| {
|
||||
let n = e.get("Name").unwrap_or("Parent");
|
||||
n.split_once('-').map(|(_, b)| b.to_string()).unwrap_or_else(|| n.to_string())
|
||||
});
|
||||
let prefix = model_spread.parent_prefix();
|
||||
let name = model_spread.base_name();
|
||||
// A parent spread has one base: its first page's. Pages that disagree lose theirs.
|
||||
let applied: Vec<Option<SpreadId>> =
|
||||
pages.iter().map(|(_, p)| p.get("AppliedMaster").and_then(|m| self.parent_ids.get(m)).copied()).collect();
|
||||
pages.iter().map(|(_, p)| p.applied_master().and_then(|m| self.parent_ids.get(m)).copied()).collect();
|
||||
let label = format!("{prefix}-{name}");
|
||||
if applied.windows(2).any(|w| w[0] != w[1]) {
|
||||
self.warnings.push(format!("parent {label}: its pages are based on different parents; only the first page's is kept"));
|
||||
@@ -189,7 +167,7 @@ impl<'r> Importer<'r> {
|
||||
p.parent = None;
|
||||
}
|
||||
}
|
||||
Spread { id, pages: out_pages, items, parent, allow_shuffle: e.get("AllowPageShuffle") != Some("false") }
|
||||
Spread { id, pages: out_pages, items, parent, allow_shuffle: model_spread.allow_shuffle() }
|
||||
}
|
||||
|
||||
pub(super) fn is_parent_item(&self, id: ItemId) -> bool {
|
||||
|
||||
Reference in new issue
Block a user