mirror of
https://github.com/storytold/designcraft.git
synced 2026-10-29 12:46:53 -06:00
Merge mojikumi-base: CJK: custom mojikumi tables without a base set apply their rules
# Conflicts: # crates/engine/src/tests.rs
This commit is contained in:
@@ -23,6 +23,7 @@ impl Aki {
|
||||
pub struct Rules<'a> {
|
||||
chinese: bool,
|
||||
rows: &'a [MojikumiAki],
|
||||
assumed_base: bool,
|
||||
}
|
||||
impl<'a> Rules<'a> {
|
||||
pub fn resolve(styles: &'a Styles, name: &str) -> Result<Option<Self>, String> {
|
||||
@@ -31,7 +32,14 @@ impl<'a> Rules<'a> {
|
||||
}
|
||||
let key = name.strip_prefix("MojikumiTable/").unwrap_or(name);
|
||||
let table = styles.mojikumi_tables.iter().find(|t| t.name == key);
|
||||
let base = table.map_or(key, |t| if t.based_on.is_empty() { t.name.as_str() } else { t.based_on.as_str() });
|
||||
// A custom table whose base set is missing (IDML leaves `BasedOnMojikumiSet`
|
||||
// out when it cannot name the base) keeps its rows on the half-em default set.
|
||||
let assumed_base = table.is_some_and(|t| t.based_on.is_empty());
|
||||
let base = match table {
|
||||
Some(t) if t.based_on.is_empty() => "LineEndAllOneHalfEmEnum",
|
||||
Some(t) => t.based_on.as_str(),
|
||||
None => key,
|
||||
};
|
||||
let chinese = match base.trim_start_matches("$ID/") {
|
||||
"SimpChineseDefault" | "TradChineseDefault" => true,
|
||||
"LineEndAllOneHalfEmEnum" | "kMojikumiDefaultName1" => false,
|
||||
@@ -50,7 +58,12 @@ impl<'a> Rules<'a> {
|
||||
return Err("invalid spacing range or priority".into());
|
||||
}
|
||||
}
|
||||
Ok(Some(Self { chinese, rows }))
|
||||
Ok(Some(Self { chinese, rows, assumed_base }))
|
||||
}
|
||||
|
||||
/// True when the table names no base set and the default one was used.
|
||||
pub fn assumed_base(&self) -> bool {
|
||||
self.assumed_base
|
||||
}
|
||||
|
||||
fn override_pair(&self, left: i16, right: i16) -> Option<&MojikumiAki> {
|
||||
|
||||
@@ -158,8 +158,15 @@ pub fn check(s: &Session, min_ppi: f64) -> Vec<Issue> {
|
||||
.insert("Paragraph Kashida width preset is preserved; automatic elongation uses the engine's bounded allocation".into());
|
||||
}
|
||||
}
|
||||
if let Err(reason) = designcraft_doc::mojikumi::Rules::resolve(&d.styles, ¶.mojikumi) {
|
||||
unsupported_typography.insert(format!("Mojikumi `{}` is not applied: {reason}", para.mojikumi));
|
||||
match designcraft_doc::mojikumi::Rules::resolve(&d.styles, ¶.mojikumi) {
|
||||
Err(reason) => {
|
||||
unsupported_typography.insert(format!("Mojikumi `{}` is not applied: {reason}", para.mojikumi));
|
||||
}
|
||||
Ok(Some(rules)) if rules.assumed_base() => {
|
||||
unsupported_typography
|
||||
.insert(format!("Mojikumi `{}` names no base set; its rules apply on the default half-em set", para.mojikumi));
|
||||
}
|
||||
Ok(_) => {}
|
||||
}
|
||||
if !matches!(
|
||||
para.kinsoku_type.as_str(),
|
||||
|
||||
@@ -880,3 +880,21 @@ fn direct_selection_drag_and_arrow_keys_move_placed_content() {
|
||||
assert_eq!(frame2, frame0);
|
||||
assert_eq!(g2 - g0, designcraft_geom::Vec2::new(1.0, 10.0));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn preflight_notes_an_assumed_mojikumi_base_set_without_calling_the_table_unapplied() {
|
||||
let mut s = session();
|
||||
s.execute("frame.create", &json!({"rect": [36, 36, 300, 300], "content": "text", "text": "中文 A1"})).unwrap();
|
||||
let story = s.doc().unwrap().doc.stories.keys().next().unwrap().0;
|
||||
let d = std::sync::Arc::make_mut(&mut s.doc_mut().unwrap().doc);
|
||||
std::sync::Arc::make_mut(&mut d.styles)
|
||||
.mojikumi_tables
|
||||
.push(designcraft_doc::cjk::MojikumiTable { name: "Baseless".into(), ..Default::default() });
|
||||
s.execute("text.select", &json!({"story": story, "anchor": 0, "focus": 1})).unwrap();
|
||||
s.execute("type.para", &json!({"attrs": {"mojikumi": "MojikumiTable/Baseless"}})).unwrap();
|
||||
let result = s.execute("preflight.run", &json!({})).unwrap();
|
||||
let messages: Vec<_> =
|
||||
result["issues"].as_array().unwrap().iter().filter(|i| i["kind"] == "unsupportedTypography").filter_map(|i| i["message"].as_str()).collect();
|
||||
assert!(messages.iter().any(|m| m.contains("Baseless") && m.contains("no base set")), "{messages:?}");
|
||||
assert!(!messages.iter().any(|m| m.contains("not applied")), "{messages:?}");
|
||||
}
|
||||
|
||||
@@ -1437,6 +1437,36 @@ fn cjk_mojikumi_overrides_and_priority_round_trip() {
|
||||
assert!(back.stories.values().flat_map(|s| &s.paras).any(|p| p.para.mojikumi.as_deref() == Some("MojikumiTable/Spacing")));
|
||||
}
|
||||
|
||||
/// A custom table with no `BasedOnMojikumiSet` keeps its own rows on the
|
||||
/// default half-em set, and the attribute stays absent on export.
|
||||
#[test]
|
||||
fn cjk_mojikumi_table_without_base_set_applies_its_rows() {
|
||||
let map = DESIGNMAP.replace("</Document>", r#"<MojikumiTable Self="uMojikumi2" Name="Baseless"><Properties><OverrideMojikumiAkiList>
|
||||
<OverrideMojikumiAkiType TargetMojikumiClass="12" SideMojikumiClass="18" SideIsAfterTarget="true" Minimum="0.25" Desired="0.75" Maximum="1" CompressionPriority="2" AkiDoesNotFloat="false"/>
|
||||
</OverrideMojikumiAkiList></Properties></MojikumiTable></Document>"#);
|
||||
let story = STORY.replace("<ParagraphStyleRange ", "<ParagraphStyleRange Mojikumi=\"uMojikumi2\" ");
|
||||
let bytes = zip_files(&[
|
||||
("designmap.xml", &map),
|
||||
("Resources/Graphic.xml", GRAPHIC),
|
||||
("Resources/Styles.xml", STYLES),
|
||||
("Resources/Preferences.xml", PREFS),
|
||||
("MasterSpreads/MasterSpread_m1.xml", MASTER),
|
||||
("Spreads/Spread_sp1.xml", SPREAD),
|
||||
("Stories/Story_s1.xml", &story),
|
||||
]);
|
||||
let d = import_idml(&bytes).unwrap();
|
||||
let name = d.stories.values().flat_map(|s| &s.paras).find_map(|p| p.para.mojikumi.clone()).unwrap();
|
||||
assert_eq!(name, "MojikumiTable/Baseless");
|
||||
let rules = designcraft_doc::mojikumi::Rules::resolve(&d.styles, &name).unwrap().unwrap();
|
||||
assert!(rules.assumed_base());
|
||||
let pair = rules.pair(12, 18);
|
||||
assert_eq!((pair.min, pair.desired, pair.max, pair.priority), (0.25, 0.75, 1.0, 2));
|
||||
// Pairs the table does not list use the half-em set: no blank at line start.
|
||||
assert_eq!(rules.pair(22, designcraft_doc::mojikumi::class('(')).desired, 0.0);
|
||||
let back = import_idml(&export_idml(&d)).unwrap();
|
||||
assert_eq!(back.styles.mojikumi_tables, d.styles.mojikumi_tables);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn arabic_controls_import_independent_xml_and_round_trip() {
|
||||
let story = r#"<idPkg:Story xmlns:idPkg="http://ns.adobe.com/AdobeInDesign/idml/1.0/packaging"><Story Self="s1">
|
||||
|
||||
Reference in New Issue
Block a user