From 46a914dc4c1b431c755d992cc0cd4f83db7926b5 Mon Sep 17 00:00:00 2001
From: midasmatterhorn <248507632+midasmatterhorn@users.noreply.github.com>
Date: Thu, 15 Oct 2026 15:42:25 +0300
Subject: [PATCH] idml: one unusable CJK metric no longer refuses the whole
file
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Composite-font and mojikumi metrics were taken from the file with no
sanity check, and Document::check rejects a zero, NaN or infinite
relative_size / horizontal_scale / vertical_scale / baseline_shift — so
one attribute anywhere in the package (RelativeSize="0",
BaselineShift="NaN") failed the import with "invalid composite font
metrics" and left the file unopenable. Every neighbouring number in this
importer already filters; the metrics now do too and fall back to a
usable value.
Regression test: unusable_cjk_metrics_do_not_refuse_the_whole_file —
fails on main with Invalid("invalid composite font metrics: Rubies"),
passes with the fallback.
---
crates/idml/src/import.rs | 16 +++++++++-------
crates/idml/src/tests.rs | 26 ++++++++++++++++++++++++++
2 files changed, 35 insertions(+), 7 deletions(-)
diff --git a/crates/idml/src/import.rs b/crates/idml/src/import.rs
index 9fd519efb..108df9430 100644
--- a/crates/idml/src/import.rs
+++ b/crates/idml/src/import.rs
@@ -499,10 +499,12 @@ impl<'r> Importer<'r> {
characters: c.get("CustomCharacters").unwrap_or("").into(),
family: c.prop("AppliedFont").unwrap_or_default(),
style: c.get("FontStyle").unwrap_or("Regular").trim_start_matches("$ID/").into(),
- relative_size: c.num("RelativeSize").unwrap_or(100.0) / 100.0,
- horizontal_scale: c.num("HorizontalScale").unwrap_or(100.0) / 100.0,
- vertical_scale: c.num("VerticalScale").unwrap_or(100.0) / 100.0,
- baseline_shift: c.num("BaselineShift").unwrap_or(0.0) / 100.0,
+ // A metric `Document::check` refuses would make the whole file fail to
+ // open, so one that is unusable here falls back to full size / no shift.
+ relative_size: c.num("RelativeSize").filter(|v| v.is_finite() && *v > 0.0).unwrap_or(100.0) / 100.0,
+ horizontal_scale: c.num("HorizontalScale").filter(|v| v.is_finite() && *v > 0.0).unwrap_or(100.0) / 100.0,
+ vertical_scale: c.num("VerticalScale").filter(|v| v.is_finite() && *v > 0.0).unwrap_or(100.0) / 100.0,
+ baseline_shift: c.num("BaselineShift").filter(|v| v.is_finite()).unwrap_or(0.0) / 100.0,
scale_option: c.boolean("ScaleOption").unwrap_or(true),
})
.collect();
@@ -531,9 +533,9 @@ impl<'r> Importer<'r> {
target_class: r.num("TargetMojikumiClass").unwrap_or(0.0) as i16,
side_class: r.num("SideMojikumiClass").unwrap_or(0.0) as i16,
after: r.boolean("SideIsAfterTarget").unwrap_or(false),
- minimum: r.num("Minimum").unwrap_or(0.0),
- desired: r.num("Desired").unwrap_or(0.0),
- maximum: r.num("Maximum").unwrap_or(0.0),
+ minimum: r.num("Minimum").filter(|v| v.is_finite()).unwrap_or(0.0),
+ desired: r.num("Desired").filter(|v| v.is_finite()).unwrap_or(0.0),
+ maximum: r.num("Maximum").filter(|v| v.is_finite()).unwrap_or(0.0),
priority: r.num("CompressionPriority").unwrap_or(0.0) as i16,
does_not_float: r.boolean("AkiDoesNotFloat").unwrap_or(false),
})
diff --git a/crates/idml/src/tests.rs b/crates/idml/src/tests.rs
index 042a0c8d9..cc5067313 100644
--- a/crates/idml/src/tests.rs
+++ b/crates/idml/src/tests.rs
@@ -2443,6 +2443,32 @@ fn the_default_cjk_leading_model_applies_only_with_a_japanese_composer() {
assert_eq!(model("$ID/HL Single J"), Some(designcraft_doc::cjk::LeadingModel::AkiBelow));
}
+/// A composite-font or mojikumi metric `Document::check` refuses (zero, NaN, infinity) must
+/// not make the whole package fail to open: the entry falls back to a usable value instead.
+#[test]
+fn unusable_cjk_metrics_do_not_refuse_the_whole_file() {
+ let designmap = r#"
+
+
+
+
+
+
+
+
+
+
+ "#;
+ let spread = r#"
+
+ "#;
+ let d = import_idml(&zip_files(&[("designmap.xml", &designmap), ("Spreads/Spread_s.xml", &spread)])).unwrap();
+ let entries = &d.styles.composite_fonts[0].entries;
+ assert_eq!(entries[0].relative_size, 1.0, "an unusable relative size falls back to full size");
+ assert_eq!(entries[1].baseline_shift, 0.0, "an unusable shift falls back to none");
+ assert_eq!(d.styles.mojikumi_tables[0].overrides[0].minimum, 0.0);
+}
+
mod liquid_page_rules {
use super::*;
use designcraft_doc::LiquidRule as R;