From 0e9a95b2cef031347ac8d47f0556e8a6a1a5471a Mon Sep 17 00:00:00 2001 From: "Dylan M. Taylor" Date: Fri, 13 Nov 2026 07:12:22 -0500 Subject: [PATCH] Parse IDML mojikumi rows in shared model --- crates/idml/src/model/mod.rs | 4 +-- crates/idml/src/model/resources.rs | 48 ++++++++++++++++++++++++++++++ 2 files changed, 50 insertions(+), 2 deletions(-) diff --git a/crates/idml/src/model/mod.rs b/crates/idml/src/model/mod.rs index a2a0c2369..0572f75d1 100644 --- a/crates/idml/src/model/mod.rs +++ b/crates/idml/src/model/mod.rs @@ -28,8 +28,8 @@ pub use preferences::TextFramePreferences; 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, TabStopValues, TintDefinition, color_groups, inks, named, preference, stroke_styles, style_groups, swatches, + GraphicPart, Ink, InkDefinition, MojikumiRow, StrokeDefinition, StrokeStyle, StrokeStyleKind, Style, StyleGroup, StyleGroupWriter, StyleKind, + StyleNode, 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 235ddf3bd..d2e339c07 100644 --- a/crates/idml/src/model/resources.rs +++ b/crates/idml/src/model/resources.rs @@ -11,6 +11,54 @@ fn others(el: &El) -> impl Iterator { el.elements().filter(|e| !e.is(Local("Properties"))) } +/// Numeric and boolean values in an IDML `MojikumiTable` row. +#[derive(Clone, Debug, PartialEq)] +pub struct MojikumiRow { + pub target_class: i16, + pub side_class: i16, + pub after: bool, + pub minimum: f64, + pub desired: f64, + pub maximum: f64, + pub priority: i16, + pub does_not_float: bool, +} + +impl MojikumiRow { + /// Parses the IDML mojikumi row attributes with bounded integer and finite-number checks. + pub fn from_el(el: &El) -> Result { + let values = Values::of(el); + let number = |key: &str| -> Result { + let Some(raw) = values.get(key) else { return Ok(0.0) }; + raw.trim().parse::().ok().filter(|value| value.is_finite()).ok_or_else(|| format!("Mojikumi row has invalid {key}")) + }; + let integer = |key: &str| -> Result { + let value = number(key)?; + if value.fract() != 0.0 || value < f64::from(i16::MIN) || value > f64::from(i16::MAX) { + return Err(format!("Mojikumi {key} must be an in-range integer")); + } + Ok(value as i16) + }; + let boolean = |key: &str| -> Result { + match values.get(key).map(str::trim) { + None | Some("false") => Ok(false), + Some("true") => Ok(true), + _ => Err(format!("Mojikumi row has invalid {key}")), + } + }; + Ok(Self { + target_class: integer("TargetMojikumiClass")?, + side_class: integer("SideMojikumiClass")?, + after: boolean("SideIsAfterTarget")?, + minimum: number("Minimum")?, + desired: number("Desired")?, + maximum: number("Maximum")?, + priority: integer("CompressionPriority")?, + does_not_float: boolean("AkiDoesNotFloat")?, + }) + } +} + /// One IDML tab stop, before the caller maps alignment names into its text model. #[derive(Clone, Debug, Default, PartialEq)] pub struct TabStopValues {