diff --git a/ROADMAP.md b/ROADMAP.md index 4f349d892..27d2e8928 100644 --- a/ROADMAP.md +++ b/ROADMAP.md @@ -1,6 +1,5 @@ # DesignCraft roadmap -- CJK-MOJI9: retain the CID Mojikumi compatibility preference and diagnose actual Adobe-Japan1 font use; exact CID class mapping remains unsupported. - CJK-MOJI8: preserve shaped narrow punctuation and manual Tsume instead of restoring nonexistent full-width blank space. - CJK-MOJI7: reject malformed Mojikumi interchange values and check nested note/cell paragraph rules in preflight. - CJK-MOJI6: apply spacing at shaped-cluster boundaries and distinguish centered Traditional Chinese punctuation from Simplified Chinese side bearings. diff --git a/crates/doc/src/lib.rs b/crates/doc/src/lib.rs index ca6dd5d23..33e0c7a9a 100644 --- a/crates/doc/src/lib.rs +++ b/crates/doc/src/lib.rs @@ -352,10 +352,6 @@ pub struct DocSettings { /// Documents saved before the setting existed read as on, the way they were drawn. #[serde(default = "yes")] pub glyph_fallback: bool, - /// Preserve IDML's CID-based Mojikumi request. Unicode classification is - /// currently used; preflight reports actual Adobe-Japan1 font use. - #[serde(default)] - pub use_cid_mojikumi: bool, } impl Default for DocSettings { @@ -388,7 +384,6 @@ impl Default for DocSettings { overprint_black: true, track_changes: false, glyph_fallback: false, - use_cid_mojikumi: false, } } } diff --git a/crates/engine/src/cmd/preflight.rs b/crates/engine/src/cmd/preflight.rs index 5d0cfdb87..5a69ff1bf 100644 --- a/crates/engine/src/cmd/preflight.rs +++ b/crates/engine/src/cmd/preflight.rs @@ -110,19 +110,6 @@ fn overset_location(s: &Session, target: designcraft_doc::ItemId) -> Option<(des None } -fn rendered_faces(cs: &ComposedStory, faces: &mut Vec) { - let mut pending: Vec<_> = cs.frames.iter().collect(); - while let Some(frame) = pending.pop() { - for g in frame.lines.iter().flat_map(|line| &line.glyphs).filter(|g| g.visible) { - if !faces.contains(&g.face) { - faces.push(g.face); - } - } - pending.extend(frame.notes.iter().flat_map(|n| &n.text.frames)); - pending.extend(frame.tables.iter().flat_map(|t| t.cells.iter().flat_map(|c| &c.text.frames))); - } -} - pub fn check(s: &Session, min_ppi: f64) -> Vec { let Some(st) = s.active() else { return vec![] }; let d = &st.doc; @@ -130,15 +117,10 @@ pub fn check(s: &Session, min_ppi: f64) -> Vec { // Stories: overset and fonts. let mut missing_fonts: Vec = Vec::new(); let mut unsupported_typography = std::collections::BTreeSet::new(); - let mut cid_faces = Vec::new(); - let mut uses_mojikumi = false; let mut missing: Vec<(FaceRef, Vec, designcraft_doc::ItemId)> = Vec::new(); for story in d.stories.values() { let cs = s.cache.get(d, story.id, None); missing_glyphs(&cs, None, 0, &mut missing); - if d.settings.use_cid_mojikumi { - rendered_faces(&cs, &mut cid_faces); - } if cs.is_overset() { let last = story.frames.last().copied(); let location = last.and_then(|f| overset_location(s, f)); @@ -162,7 +144,6 @@ pub fn check(s: &Session, min_ppi: f64) -> Vec { let ranges = story.para_ranges(); for (pi, pf) in story.paras.iter().enumerate() { let (para, base) = d.styles.resolve_para(pf); - uses_mojikumi |= !matches!(para.mojikumi.as_str(), "" | "Nothing" | "None"); let Some(range) = ranges.get(pi) else { continue }; let has_rtl = story.text.get(range.clone()).is_some_and(|text| text.chars().any(designcraft_fonts::is_rtl)); if has_rtl { @@ -226,14 +207,6 @@ pub fn check(s: &Session, min_ppi: f64) -> Vec { for f in missing_fonts { out.push(Issue { severity: "error", kind: "missingFont", message: format!("Missing font: {f}"), item: None, page: None }); } - if uses_mojikumi { - for face in cid_faces.iter().filter(|face| face.uses_japan1_cids()) { - unsupported_typography.insert(format!( - "CID-based Mojikumi requested for {} {}; Unicode classes are used because CID class mapping is not implemented", - face.family, face.style - )); - } - } for message in unsupported_typography { out.push(Issue { severity: "warning", kind: "unsupportedTypography", message, item: None, page: None }); } @@ -455,20 +428,3 @@ mod mojikumi_nested_tests { assert_eq!(issues.iter().filter(|i| i.kind == "unsupportedTypography" && i.message.contains("Unrecognized")).count(), 1); } } - -#[cfg(test)] -mod cid_preference_tests { - use super::*; - #[test] - fn cid_request_is_undoable_and_does_not_warn_for_unicode_only_fonts() { - let mut s = Session::new(); - s.execute("file.new", &json!({})).unwrap(); - let r = s.execute("frame.create", &json!({"rect":[0,0,300,200], "content":"text", "text":"ABC"})).unwrap(); - s.execute("text.select", &json!({"story":r["story"],"anchor":0,"focus":3})).unwrap(); - s.execute("type.para", &json!({"attrs":{"mojikumi":"SimpChineseDefault"}})).unwrap(); - assert_eq!(s.execute("document.preferences", &json!({"useCidMojikumi":true})).unwrap()["useCidMojikumi"], true); - assert!(!check(&s, 150.0).iter().any(|i| i.message.contains("CID-based"))); - s.execute("edit.undo", &json!({})).unwrap(); - assert_eq!(s.execute("document.preferences", &json!({})).unwrap()["useCidMojikumi"], false); - } -} diff --git a/crates/engine/src/cmd/prefs.rs b/crates/engine/src/cmd/prefs.rs index 9fc0fb5de..68a5cf152 100644 --- a/crates/engine/src/cmd/prefs.rs +++ b/crates/engine/src/cmd/prefs.rs @@ -21,7 +21,6 @@ const DOC_KEYS: &[&str] = &[ "advancedType", "overprintBlack", "glyphFallback", - "useCidMojikumi", ]; pub fn specs() -> Vec { @@ -63,7 +62,7 @@ pub fn specs() -> Vec { "Document Preferences", [], None, - "{horizontalUnits?, verticalUnits?: points|picas|inches|millimeters|…, keyboardIncrement? (pt), baselineGrid?: {start, increment, relativeTo, color, viewThreshold}, grid?: {horizontal, vertical, subdivisions, color, inBack}, pasteboard?: [h, v], marginColor?, columnColor?, bleedColor?, slugColor?: [r, g, b], advancedType?: {superscriptSize, superscriptPosition, subscriptSize, subscriptPosition} (%), overprintBlack?, useCidMojikumi? (preserve CID classification request; unsupported font collections are diagnosed), glyphFallback? (draw characters the font lacks from fallback fonts; off: as the font's missing-glyph box)} → those settings", + "{horizontalUnits?, verticalUnits?: points|picas|inches|millimeters|…, keyboardIncrement? (pt), baselineGrid?: {start, increment, relativeTo, color, viewThreshold}, grid?: {horizontal, vertical, subdivisions, color, inBack}, pasteboard?: [h, v], marginColor?, columnColor?, bleedColor?, slugColor?: [r, g, b], advancedType?: {superscriptSize, superscriptPosition, subscriptSize, subscriptPosition} (%), overprintBlack?, glyphFallback? (draw characters the font lacks from fallback fonts; off: as the font's missing-glyph box)} → those settings", has_doc, |s, p| { let cur = serde_json::to_value(&s.doc()?.doc.settings).map_err(|e| bad("document.preferences", e.to_string()))?; diff --git a/crates/fonts/src/fontdb.rs b/crates/fonts/src/fontdb.rs index 8c979e15c..2bc2b00ed 100644 --- a/crates/fonts/src/fontdb.rs +++ b/crates/fonts/src/fontdb.rs @@ -181,13 +181,6 @@ impl std::fmt::Debug for FontFace { } impl FontFace { - /// Adobe-Japan1 CFF fonts are the collection for CID-based Mojikumi. - /// Other CID collections still use Unicode classes under Adobe's policy. - pub fn uses_japan1_cids(&self) -> bool { - use skrifa::raw::TableProvider; - self.skrifa().and_then(|f| f.cff().ok()).is_some_and(cff_uses_japan1_cids) - } - pub fn data(&self) -> &[u8] { match &self.bytes { FontBytes::Static(b) => b, @@ -1574,31 +1567,3 @@ mod tests_docfonts; #[cfg(test)] #[path = "tests_vertical.rs"] mod tests_vertical; - -fn cff_uses_japan1_cids(cff: skrifa::raw::tables::cff::Cff<'_>) -> bool { - use skrifa::raw::ps::cff::dict; - let Some(top) = cff.top_dicts().get(0) else { return false }; - dict::entries(top, None).any(|entry| match entry { - Ok(dict::Entry::Ros { registry, ordering, .. }) => cff.string(registry) == Some(b"Adobe") && cff.string(ordering) == Some(b"Japan1"), - _ => false, - }) -} - -#[cfg(test)] -mod mojikumi_cid_tests { - use super::*; - use skrifa::raw::{FontData, FontRead, tables::cff::Cff}; - #[test] - fn recognizes_the_collection_in_cff_metadata_without_reading_outlines() { - // Minimal synthetic CFF indices: one name, a ROS dict, two strings, - // and one return-only subroutine. SID 391/392 reference the two strings. - let mut data = vec![1, 0, 4, 4, 0, 1, 1, 1, 2, b'T', 0, 1, 1, 1, 8, 248, 27, 248, 28, 139, 12, 30, 0, 2, 1, 1, 6, 12]; - data.extend_from_slice(b"AdobeJapan1"); - data.extend_from_slice(&[0, 1, 1, 1, 2, 11]); - assert!(cff_uses_japan1_cids(Cff::read(FontData::new(&data)).unwrap())); - let end = data.len() - 6; - data[end - 6..end].copy_from_slice(b"CNS1-X"); - assert!(!cff_uses_japan1_cids(Cff::read(FontData::new(&data)).unwrap())); - assert!(!FontDb::global().face(crate::DEFAULT_FAMILY, "Regular").uses_japan1_cids()); - } -} diff --git a/crates/idml/src/export.rs b/crates/idml/src/export.rs index faa124ef8..8dd965473 100644 --- a/crates/idml/src/export.rs +++ b/crates/idml/src/export.rs @@ -799,7 +799,6 @@ impl<'a> Ex<'a> { let a = &s.advanced_type; root.push( El::new("TextPreference") - .attr("UseCidMojikumi", s.use_cid_mojikumi) .attr("SuperscriptSize", num(a.superscript_size)) .attr("SuperscriptPosition", num(a.superscript_position)) .attr("SubscriptSize", num(a.subscript_size)) diff --git a/crates/idml/src/import.rs b/crates/idml/src/import.rs index aaef23dac..932062724 100644 --- a/crates/idml/src/import.rs +++ b/crates/idml/src/import.rs @@ -273,9 +273,6 @@ impl<'r> Importer<'r> { } } "TextPreference" => { - if let Some(v) = e.boolean("UseCidMojikumi") { - self.settings.use_cid_mojikumi = v; - } let a = &mut self.settings.advanced_type; for (k, v) in [ ("SuperscriptSize", &mut a.superscript_size), diff --git a/crates/idml/src/tests.rs b/crates/idml/src/tests.rs index 7aa9fc3c1..79f242483 100644 --- a/crates/idml/src/tests.rs +++ b/crates/idml/src/tests.rs @@ -968,11 +968,3 @@ fn malformed_mojikumi_rows_are_not_silently_coerced() { assert!(err.contains("Mojikumi"), "{key}={value}: {err}"); } } - -#[test] -fn cid_mojikumi_preference_survives_interchange() { - let mut d = import_idml(&fixture_with_story(STORY)).unwrap(); - d.settings.use_cid_mojikumi = true; - let restored = import_idml(&export_idml(&d)).unwrap(); - assert!(restored.settings.use_cid_mojikumi); -} diff --git a/docs/mojikumi.md b/docs/mojikumi.md index d3baa075c..c9390d58d 100644 --- a/docs/mojikumi.md +++ b/docs/mojikumi.md @@ -26,29 +26,8 @@ Regression tests cover preset boundary differences, custom override precedence, IDML reference round trips, and unsupported names. The implementation must not be represented as a byte-for-byte reproduction of Adobe's full preset matrices. -## Composition and validation +## Audit work -Spacing is attached to complete shaped clusters. Regional punctuation side -bearings, shaped narrow forms, tracking, manual Tsume and explicit character aki -are handled before line fitting. Continuous gaps of equal priority share one -allocation pool; non-floating gaps reach discrete endpoints. Leading-only -compression is included for ragged lines. Custom rows retain their dimensions. - -Import rejects malformed integers, non-finite amounts, inverted ranges and invalid -priorities. Native document validation uses the same range policy. Unknown integer -classes remain preservable and receive diagnostics, including in nested notes and -cells. The bounded supported spacing range is -1 to 100 em. - -## Compatibility limits - -[Adobe's TextPreference API](https://developer.adobe.com/indesign/uxp/omv/t/TextPreference/) -defines CID-based classification. The IDML `UseCidMojikumi` preference now survives -round trips and is accessible through `document.preferences`. The engine currently -uses Unicode classes. Preflight reports the incompatibility when the document -requests CID mode and actually renders Adobe-Japan1 CFF fonts with Mojikumi in use; -it does not flag ordinary Unicode fonts merely because the preference is enabled. - -Exact Adobe CID-to-Mojikumi class mapping, complete proprietary preset pair matrices, -and legacy pre-CS2 vertical-scaling compatibility have not been reproduced or -certified. Public CMap resources map character codes to CIDs; they are not a -CID-to-Mojikumi-class oracle. No claim of complete Adobe output parity is made. +Further audit areas are regional punctuation metrics, shaped cluster boundaries, +equal-priority allocation, discrete line fitting, input validation, and diagnostics +for compatibility modes whose glyph classification cannot yet be reproduced.