From c8142a2f1f44ce28b29eb36dc2e04e27c09c24e7 Mon Sep 17 00:00:00 2001 From: "Dylan M. Taylor" Date: Fri, 13 Nov 2026 07:10:41 -0500 Subject: [PATCH] Read IDML tab stop records in shared model --- crates/idml/src/model/mod.rs | 2 +- crates/idml/src/model/resources.rs | 27 +++++++++++++++++++++++++++ 2 files changed, 28 insertions(+), 1 deletion(-) diff --git a/crates/idml/src/model/mod.rs b/crates/idml/src/model/mod.rs index 223cdec08..a2a0c2369 100644 --- a/crates/idml/src/model/mod.rs +++ b/crates/idml/src/model/mod.rs @@ -29,7 +29,7 @@ pub use resources::{ ColorDefinition, ColorGroup, ColorGroupDefinition, ColorGroupMember, ColorSpace, CompositeFontDefinition, CompositeFontEntryDefinition, EpubExportTagMap, FontDefinition, FontFamilyDefinition, FontsPart, GradientDefinition, GradientStop, GradientStopDefinition, GradientStopValue, GraphicPart, Ink, InkDefinition, StrokeDefinition, StrokeStyle, StrokeStyleKind, Style, StyleGroup, StyleGroupWriter, StyleKind, StyleNode, - Swatch, SwatchKind, TintDefinition, color_groups, inks, named, preference, stroke_styles, style_groups, swatches, + Swatch, SwatchKind, TabStopValues, TintDefinition, color_groups, inks, named, preference, stroke_styles, style_groups, swatches, }; pub use spread::{ Graphic, GraphicChild, GraphicKind, Guide, ItemChild, ItemKind, Link, MarginPreference, Page, PageChild, PageItem, Spread, SpreadChild, TextPath, diff --git a/crates/idml/src/model/resources.rs b/crates/idml/src/model/resources.rs index 2ceb652b3..235ddf3bd 100644 --- a/crates/idml/src/model/resources.rs +++ b/crates/idml/src/model/resources.rs @@ -11,6 +11,33 @@ fn others(el: &El) -> impl Iterator { el.elements().filter(|e| !e.is(Local("Properties"))) } +/// One IDML tab stop, before the caller maps alignment names into its text model. +#[derive(Clone, Debug, Default, PartialEq)] +pub struct TabStopValues { + pub position: f64, + pub alignment: String, + pub leader: String, + pub alignment_character: String, +} + +impl TabStopValues { + /// Reads `ListItem` records in a `TabList` property. Invalid or non-finite positions use 0, + /// matching IDML import fallback behavior; semantic sanitization belongs to the caller. + pub fn from_list(list: &El) -> Vec { + list.children("ListItem") + .map(|item| { + let text = |name: &str| item.child_text(name).map(|value| value.into_owned()); + Self { + position: text("Position").and_then(|value| value.trim().parse::().ok()).filter(|value| value.is_finite()).unwrap_or(0.0), + alignment: text("Alignment").unwrap_or_default(), + leader: text("Leader").unwrap_or_default(), + alignment_character: text("AlignmentCharacter").unwrap_or_default(), + } + }) + .collect() + } +} + /// What a style styles. #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub enum StyleKind {