Read IDML saved cross-reference text through shared model

This commit is contained in:
Dylan M. Taylor committed 2026-11-13 06:10:40 -05:00
1 parent 1ef0047946
commit b4b3849098
3 files changed
+46 -69

No files matched your search

Generated
+18 -18
View File
@@ -1242,10 +1242,10 @@ dependencies = [
[[package]]
name = "craft-idml"
version = "0.1.0"
source = "git+https://github.com/storytold/craft-libs?rev=e725d39a7#e725d39a77c4613896e64b6da3a829bc8cb5b827"
source = "git+https://github.com/storytold/craft-libs?rev=abad7d575#abad7d575355261383c733ab65ccba3fddeca72d"
dependencies = [
"craft-xml 0.1.0 (git+https://github.com/storytold/craft-libs?rev=e725d39a7)",
"craft-zip 0.1.0 (git+https://github.com/storytold/craft-libs?rev=e725d39a7)",
"craft-xml 0.1.0 (git+https://github.com/storytold/craft-libs?rev=abad7d575)",
"craft-zip 0.1.0 (git+https://github.com/storytold/craft-libs?rev=abad7d575)",
"thiserror 2.0.21",
]
@@ -1662,6 +1662,15 @@ dependencies = [
"thiserror 2.0.21",
]
[[package]]
name = "craft-xml"
version = "0.1.0"
source = "git+https://github.com/storytold/craft-libs?rev=abad7d575#abad7d575355261383c733ab65ccba3fddeca72d"
dependencies = [
"quick-xml 0.41.0",
"thiserror 2.0.21",
]
[[package]]
name = "craft-xml"
version = "0.1.0"
@@ -1680,15 +1689,6 @@ dependencies = [
"thiserror 2.0.21",
]
[[package]]
name = "craft-xml"
version = "0.1.0"
source = "git+https://github.com/storytold/craft-libs?rev=e725d39a7#e725d39a77c4613896e64b6da3a829bc8cb5b827"
dependencies = [
"quick-xml 0.41.0",
"thiserror 2.0.21",
]
[[package]]
name = "craft-xml"
version = "0.1.0"
@@ -1740,7 +1740,7 @@ dependencies = [
[[package]]
name = "craft-zip"
version = "0.1.0"
source = "git+https://github.com/storytold/craft-libs?rev=ba24fe2435adb3c81ad6057faf36365fbba736cf#ba24fe2435adb3c81ad6057faf36365fbba736cf"
source = "git+https://github.com/storytold/craft-libs?rev=abad7d575#abad7d575355261383c733ab65ccba3fddeca72d"
dependencies = [
"flate2",
"thiserror 2.0.21",
@@ -1750,7 +1750,7 @@ dependencies = [
[[package]]
name = "craft-zip"
version = "0.1.0"
source = "git+https://github.com/storytold/craft-libs?rev=e725d39a7#e725d39a77c4613896e64b6da3a829bc8cb5b827"
source = "git+https://github.com/storytold/craft-libs?rev=ba24fe2435adb3c81ad6057faf36365fbba736cf#ba24fe2435adb3c81ad6057faf36365fbba736cf"
dependencies = [
"flate2",
"thiserror 2.0.21",
@@ -2021,7 +2021,7 @@ dependencies = [
"craft-prepress",
"craft-print",
"craft-svg 0.1.0 (git+https://github.com/storytold/craft-libs?rev=cc23ffd4d26f3074808e2187d332564817624f8f)",
"craft-xml 0.1.0 (git+https://github.com/storytold/craft-libs?rev=e725d39a7)",
"craft-xml 0.1.0 (git+https://github.com/storytold/craft-libs?rev=abad7d575)",
"craft-xml 0.1.0 (git+https://github.com/storytold/craft-libs?rev=f23b0f33f0d00b157476533a43cbbcfd901aa1f1)",
"designcraft-color",
"designcraft-compose",
@@ -2103,9 +2103,9 @@ name = "designcraft-idml"
version = "0.20.1"
dependencies = [
"craft-file-formats",
"craft-idml 0.1.0 (git+https://github.com/storytold/craft-libs?rev=e725d39a7)",
"craft-idml 0.1.0 (git+https://github.com/storytold/craft-libs?rev=abad7d575)",
"craft-svg 0.1.0 (git+https://github.com/storytold/craft-libs?rev=cc23ffd4d26f3074808e2187d332564817624f8f)",
"craft-xml 0.1.0 (git+https://github.com/storytold/craft-libs?rev=e725d39a7)",
"craft-xml 0.1.0 (git+https://github.com/storytold/craft-libs?rev=abad7d575)",
"designcraft-color",
"designcraft-compose",
"designcraft-doc",
@@ -2222,7 +2222,7 @@ version = "0.20.1"
dependencies = [
"craft-docx",
"craft-file-formats",
"craft-idml 0.1.0 (git+https://github.com/storytold/craft-libs?rev=e725d39a7)",
"craft-idml 0.1.0 (git+https://github.com/storytold/craft-libs?rev=abad7d575)",
"craft-rtf",
"craft-wordmodel",
"craft-xlsx",
+2 -2
View File
@@ -16,10 +16,10 @@ craft-pdf = { git = "https://github.com/storytold/craft-libs", rev = "4052591363
craft-designcraft-format = { git = "https://github.com/storytold/craft-libs", rev = "19692b6190465671e41967c75d0cd4c439d0c84c", 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 = "e725d39a7", version = "=0.1.0" }
craft-idml = { git = "https://github.com/storytold/craft-libs", rev = "abad7d575", 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/storytold/craft-libs", rev = "cc23ffd4d26f3074808e2187d332564817624f8f", version = "=0.1.0", features = ["tree-render", "tree-text"] }
craft-xml = { git = "https://github.com/storytold/craft-libs", rev = "e725d39a7", version = "=0.1.0" }
craft-xml = { git = "https://github.com/storytold/craft-libs", rev = "abad7d575", 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" }
+26 -49
View File
@@ -231,9 +231,32 @@ impl<'r> Importer<'r> {
self.walk_story(&container_el, b, pf, pchars, &ncf, brk);
}
craft_idml::model::ContainerKind::XMLElement => self.walk_story(&container_el, b, pf, pchars, cf, brk),
craft_idml::model::ContainerKind::CrossReferenceSource
| craft_idml::model::ContainerKind::Note
| craft_idml::model::ContainerKind::EndnoteRange => {}
craft_idml::model::ContainerKind::CrossReferenceSource => {
let page_variables: Vec<_> = self.xref_page_vars.iter().cloned().collect();
let saved = craft_idml::model::SavedCrossReference::from_el(&crate::xml::to_craft(&container_el), &page_variables);
let mut text = String::new();
let mut page = None;
for part in saved.parts {
match part {
craft_idml::model::SavedCrossReferencePart::Text(value) => text.push_str(&value),
craft_idml::model::SavedCrossReferencePart::PageNumber { result_text } => {
text.push_str(designcraft_doc::xref::PAGE_NUM_BLOCK);
if page.is_none() {
page = result_text;
}
}
}
}
b.xrefs.push(XrefSource {
me: saved.self_id.unwrap_or_default(),
format: saved.applied_format.unwrap_or_default(),
text,
page,
});
let f = container_el.find("CharacterStyleRange").map_or_else(|| cf.clone(), |r| self.range_format(r, pchars, cf));
b.push(&designcraft_doc::XREF_MARK.to_string(), &f);
}
craft_idml::model::ContainerKind::Note | craft_idml::model::ContainerKind::EndnoteRange => {}
}
}
craft_idml::model::TextNode::Anchor(anchor) => {
@@ -257,21 +280,6 @@ impl<'r> Importer<'r> {
b.push(&text, cf);
}
_ => match c.local() {
"CrossReferenceSource" => {
// The generated text is regenerated from the format.
let (mut text, mut page) = (String::new(), None);
xref_saved_text(c, &self.xref_page_vars, &mut text, &mut page, 0);
b.xrefs.push(XrefSource {
me: c.get("Self").unwrap_or("").to_string(),
format: c.get("AppliedFormat").unwrap_or("").to_string(),
text,
page,
});
// A source at paragraph level wraps a range; inside a range it holds the
// text directly and takes that range's format.
let f = c.find("CharacterStyleRange").map_or_else(|| cf.clone(), |r| self.range_format(r, pchars, cf));
b.push(&designcraft_doc::XREF_MARK.to_string(), &f);
}
// Anchored page items, including groups containing text frames.
"Rectangle" | "Oval" | "Polygon" | "GraphicLine" | "Group" | "TextFrame" => {
if let Some(item) = self.item(c, designcraft_geom::Affine::IDENTITY) {
@@ -461,37 +469,6 @@ struct XrefSource {
page: Option<String>,
}
/// Whether a `TextVariableInstance` shows a cross-reference page number: its
/// `AssociatedTextVariable` is a variable of that type (`page_vars`), or, in a file that doesn't
/// define it, has the name InDesign gives that variable.
fn is_xref_page_number(e: &El, page_vars: &HashSet<String>) -> bool {
e.get("AssociatedTextVariable").is_some_and(|v| page_vars.contains(v) || (page_vars.is_empty() && v.ends_with("TV XRefPageNumber")))
}
/// The text a `CrossReferenceSource` holds as shown, with its live page numbers (InDesign's
/// cross-reference page number variable, `page_vars`) as `<pageNum />`. Tracked insertions count;
/// tracked deletions don't. Returns the text and the page shown.
fn xref_saved_text(e: &El, page_vars: &HashSet<String>, text: &mut String, page: &mut Option<String>, depth: usize) {
if depth > 16 {
return;
}
for c in e.elements() {
match c.local() {
"Content" => text.push_str(&c.text_content()),
"TextVariableInstance" if is_xref_page_number(c, page_vars) => {
text.push_str(designcraft_doc::xref::PAGE_NUM_BLOCK);
if page.is_none() {
*page = c.get("ResultText").map(str::to_string);
}
}
"TextVariableInstance" => text.push_str(c.get("ResultText").unwrap_or("")),
"Change" if c.get("ChangeType") == Some("DeletedText") => {}
"CharacterStyleRange" | "Change" => xref_saved_text(c, page_vars, text, page, depth + 1),
_ => {}
}
}
}
impl StoryBuilder {
/// A table gets an anchor paragraph of its own.
fn push_table(&mut self, t: Table, pf: &ParaFormat, cf: &CharFormat) {