From 622950ccce26d7ae6bc67915edcd2039f4d49d3e Mon Sep 17 00:00:00 2001 From: "Dylan M. Taylor" Date: Fri, 13 Nov 2026 05:56:45 -0500 Subject: [PATCH] Model IDML story marker nodes --- crates/idml/src/model/mod.rs | 4 ++-- crates/idml/src/model/story.rs | 43 ++++++++++++++++++++++++++++++++++ 2 files changed, 45 insertions(+), 2 deletions(-) diff --git a/crates/idml/src/model/mod.rs b/crates/idml/src/model/mod.rs index f79155b50..b5b685449 100644 --- a/crates/idml/src/model/mod.rs +++ b/crates/idml/src/model/mod.rs @@ -28,8 +28,8 @@ pub use spread::{ spreads, }; pub use story::{ - Ace, CONTAINERS, Cell, CellOptions, Container, ContentPart, Range, Story, StoryPreference, Table, TableChild, TableColumn, TableLayout, TableRow, - TextNode, stories, + Ace, CONTAINERS, Cell, CellOptions, Container, ContentPart, PageReference, Range, Story, StoryAnchor, StoryPreference, Table, TableChild, + TableColumn, TableLayout, TableRow, TextNode, stories, }; use crate::value::{matrix, nums}; diff --git a/crates/idml/src/model/story.rs b/crates/idml/src/model/story.rs index 136b58455..98360398a 100644 --- a/crates/idml/src/model/story.rs +++ b/crates/idml/src/model/story.rs @@ -48,6 +48,9 @@ pub enum TextNode { /// XML element, a cross-reference's source, a footnote, a note. Container(Container), Table(Table), + Anchor(StoryAnchor), + PageReference(PageReference), + TextVariable(Values), /// A page item anchored in the text. Item(PageItem), /// Anything else (`StoryPreference`, `HyperlinkTextDestination`, `TextVariableInstance`, …), @@ -55,6 +58,38 @@ pub enum TextNode { Other(El), } +#[derive(Clone, Debug, PartialEq)] +pub struct StoryAnchor { + pub name: String, + pub values: Values, +} + +impl StoryAnchor { + pub fn self_id(&self) -> Option<&str> { + self.values.self_id() + } + pub fn label(&self) -> Option<&str> { + self.values.name() + } +} + +#[derive(Clone, Debug, Default, PartialEq)] +pub struct PageReference { + pub values: Values, +} + +impl PageReference { + pub fn referenced_topic(&self) -> Option<&str> { + self.values.get("ReferencedTopic") + } + pub fn reference_type(&self) -> Option<&str> { + self.values.get("PageReferenceType") + } + pub fn limit(&self) -> Option { + self.values.num("PageReferenceLimit") + } +} + /// The elements read as [`TextNode::Container`]. pub const CONTAINERS: [&str; 7] = ["HyperlinkTextSource", "Change", "XMLElement", "CrossReferenceSource", "Footnote", "Note", "EndnoteRange"]; @@ -232,6 +267,11 @@ impl TextNode { "Content" => TextNode::Content(content_parts(el)), "Br" => TextNode::Break, "Table" => TextNode::Table(Table::from_el(el)), + "HyperlinkTextDestination" | "ParagraphDestination" => { + TextNode::Anchor(StoryAnchor { name: el.local().to_string(), values: Values::of(el) }) + } + "PageReference" => TextNode::PageReference(PageReference { values: Values::of(el) }), + "TextVariableInstance" => TextNode::TextVariable(Values::of(el)), n if CONTAINERS.contains(&n) => TextNode::Container(Container { name: n.to_string(), values: Values::of(el), children: nodes(el) }), _ => PageItem::from_el(el).map_or_else(|| TextNode::Other(tidy(el)), TextNode::Item), } @@ -256,6 +296,9 @@ impl TextNode { TextNode::Break => El::new("Br"), TextNode::Container(c) => with_children(c.values.element(&c.name), &c.children), TextNode::Table(t) => t.to_el(), + TextNode::Anchor(a) => a.values.element(&a.name), + TextNode::PageReference(r) => r.values.element("PageReference"), + TextNode::TextVariable(v) => v.element("TextVariableInstance"), TextNode::Item(i) => i.to_el(), TextNode::Other(e) => e.clone(), }