Merge pull request #1222

This commit is contained in:
aiongg committed 2026-11-03 18:36:57 +00:00
commit 7892952fe6
3 files changed
+73 -3

No files matched your search

+22
View File
@@ -6169,6 +6169,28 @@ fn mojikumi_one_em_line_end_set_sets_closing_punctuation_full_width_at_line_end(
assert!((open - one.0 - em * 0.5).abs() < 0.01 && (stop - one.1).abs() < 0.01, "one-em set: {one:?} of {open}/{stop}");
}
/// Consecutive punctuation touches (JLREQ 3.1.4): in 漢」。漢(「漢 the closing bracket and
/// the full stop, and the two opening brackets, close up by half an em each.
#[test]
fn mojikumi_closes_up_consecutive_punctuation() {
let family = "Mojikumi Consecutive";
let font = designcraft_fonts::testing::font_mapping(family, &[('漢', '‰'), ('」', '‰'), ('。', '‰'), ('(', '‰'), ('「', '‰')]).unwrap();
designcraft_fonts::FontDb::global().add_font(font);
let text = "漢」。漢(「漢";
let (mut d, sid, _) = doc_with(text, Rect::new(0.0, 0.0, 300.0, 100.0), ParaAttrs::default());
d.story_mut(sid).unwrap().format_chars(0..text.len(), |f| f.over.font_family = Some(family.into()));
let mut width = |set: &str| {
d.story_mut(sid).unwrap().paras[0].para.mojikumi = Some(set.into());
let cs = compose_story(&d, sid, &ComposeOptions::default());
let line = all_lines(&cs)[0];
let g = &line.glyphs[0];
(line.end_x - line.x0, g.face.units_per_em() * g.sx)
};
let (natural, em) = width("Nothing");
let (set, _) = width("LineEndAllOneHalfEmEnum");
assert!((natural - set - em).abs() < 0.01, "{natural} - {set} should be one em ({em})");
}
fn ruled(text: &str, rect: Rect, columns: u32, edit: impl FnOnce(&mut Document, StoryId)) -> (Document, ItemId, Vec<Rect>) {
let (mut d, sid, fid) = doc_with(text, rect, ParaAttrs::default());
{
+47
View File
@@ -126,6 +126,23 @@ impl<'a> Rules<'a> {
if right == 22 {
return Aki::fixed(if self.base == Base::LineEndHalfEm { 0.0 } else { trailing(left) });
}
// Consecutive punctuation in the Japanese sets (JLREQ 3.1.4): opening brackets together,
// and closing brackets, commas and full stops together, touch. A middle dot (・:;) after
// a closing bracket or before an opening one keeps only its own quarter em. Other
// neighbours keep the larger blank, so a closing bracket and an opening one are half an
// em apart. The Chinese sets keep the larger blank throughout.
let opening = |c| matches!(c, 1 | 26 | 27);
let closing = |c| matches!(c, 2 | 6 | 21 | 28..=31);
let closing_bracket = |c| matches!(c, 2 | 28 | 29);
let middle_dot = |c| matches!(c, 5 | 32);
if self.base != Base::Chinese {
if (opening(left) && opening(right)) || (closing(left) && closing(right)) {
return Aki::default();
}
if (closing_bracket(left) && middle_dot(right)) || (middle_dot(left) && opening(right)) {
return Aki { min: 0.0, desired: 0.25, max: 0.25, priority: 1, discrete: false };
}
}
let punctuation = trailing(left).max(leading(right));
if punctuation > 0.0 {
return Aki { min: 0.0, desired: punctuation, max: punctuation, priority: 1, discrete: false };
@@ -237,6 +254,36 @@ mod tests {
assert_ne!(class('「'), class('('));
}
#[test]
fn consecutive_punctuation_touches_except_closing_before_opening() {
let styles = Styles::default();
for set in ["LineEndAllOneHalfEmEnum", "LineEndAllOneEmEnum"] {
let rules = Rules::resolve(&styles, set).unwrap().unwrap();
let aki = |a: char, b: char| rules.pair(class(a), class(b)).desired;
for (a, b) in
[('」', '。'), ('」', '、'), ('。', '」'), ('、', '」'), ('」', '』'), (')', '」'), (',', ')'), ('「', '『'), ('(', '「')]
{
assert_eq!(aki(a, b), 0.0, "{set}: {a}{b}");
}
for (a, b) in [('」', '「'), ('。', '「'), ('、', '('), ('」', '漢'), ('漢', '「')] {
assert_eq!(aki(a, b), 0.5, "{set}: {a}{b}");
}
// A middle dot keeps its quarter em beside a bracket (JLREQ 3.1.4, Figure 69 ⑦).
for (a, b) in [('」', '・'), (')', ':'), ('』', ';'), ('・', '「'), (';', '('), (':', '〈')] {
assert_eq!(aki(a, b), 0.25, "{set}: {a}{b}");
}
// After a comma or full stop the larger blank stays.
assert_eq!(aki('、', '・'), 0.5, "{set}: 、・");
}
// The Chinese sets are unchanged: the larger blank stays between any two marks.
for set in ["SimpChineseDefault", "TradChineseDefault"] {
let rules = Rules::resolve(&styles, set).unwrap().unwrap();
for (a, b) in [('」', '。'), (',', ')'), ('「', '『'), ('」', '「'), ('」', '・'), ('・', '「')] {
assert_eq!(rules.pair(class(a), class(b)).desired, 0.5, "{set}: {a}{b}");
}
}
}
#[test]
fn one_em_line_end_set_keeps_closing_punctuation_full_at_line_end_only() {
let styles = Styles::default();
+4 -3
View File
@@ -1,6 +1,6 @@
# CJK typesetting plan
> **Last reviewed:** 2026-10-10 · **Last updated:** 2026-10-10 · **Change:** trivial (status line and revision history added; content unchanged in the 2026-10-10 review) · **Target:** Adobe InDesign 2026
> **Last reviewed:** 2026-10-10 · **Last updated:** 2026-10-18 · **Change:** minor (mojikumi applied; consecutive punctuation) · **Target:** Adobe InDesign 2026
Goal: the Chinese, Japanese and Korean typesetting that InDesign offers with its East Asian
feature set (the "Japanese/CJK" feature set, `Feature Set Locale Setting` 257), so that a
@@ -37,7 +37,7 @@ presets or tables are copied. Our default mojikumi and kinsoku tables are built
| Vertical metrics and shaping | done: upright runs shaped top to bottom (`vert`; `vkrn`/`vpal` when asked) with `vmtx`/`VORG` advances and origins, centred on the ideographic em box (`BASE`, else OS/2); turned runs shaped horizontally, without `vrt2`; Horizontal Kana (`hkna`) takes the vertical kana (`vkna`) in upright runs. Default location of variable fonts only (no `VVAR`) |
| Kinsoku | named (hard, soft, Chinese, Korean) and custom kinsoku sets, bunri-kinshi, rensuuji, hanging punctuation (regular, force); without a set, fixed no-start / no-end lists and a break anywhere between CJK characters; kinsoku type (push in / push out priorities) kept but not applied |
| Korean line breaking | at spaces (Hangul everywhere, hanja in Korean text); per-paragraph character-based breaking (`koreanCharBreaks`); DesignCraft-only, not in IDML |
| Mojikumi | tables and paragraph references kept through IDML; not applied (Preflight says so) |
| Mojikumi | applied in composition: the built-in base sets (line end half-width, line end one em, Simplified and Traditional Chinese) and the document's tables with their override rows, kept through IDML; Preflight notes tables it can't apply. In the Japanese sets consecutive punctuation closes up (JLREQ 3.1.4: 」。, 「『 touch; 」・ a quarter em, 」「 half an em apart). Still open: 、・ and ・・ take the sum of both blanks in JLREQ (Table 1 notes) |
| Aki, tsume, jidori | done ([cjk-typography.md](cjk-typography.md)) |
| Character alignment, leading model | em box top / centre / bottom, ICF top / bottom (`BASE` `icft`/`icfb`, else the em box inset by 5%), roman baseline; aki above / below, centre (centre down as centre); under aki below a line's leading is the space to the line below it; with the Japanese composers (`Composer::Japanese`, `JapaneseSingleLine`, IDML `HL Composer J` / `HL Single J`), the others set lines baseline to baseline on the Roman baseline |
| Tate-chu-yoko | manual, with offsets; auto tate-chu-yoko of short digit runs (with roman letters when asked) in vertical text, narrowed to fit the em (`type.autoTateChuYoko`, IDML `AutoTcy`, `AutoTcyIncludeRoman`); manual groups of 3+ digits overflow the em |
@@ -75,7 +75,7 @@ punctuation centred vs corner-placed).
A *mojikumi set* is a table indexed by (class before, class after) giving the space between them
as a min/optimum/max triple in em fractions, plus line-start and line-end rules. Sets are
document resources (`Styles::mojikumi_tables`, read from and written to IDML with their override
rows), referenced by name from the paragraph's `mojikumi`; what remains is applying them. We
rows), referenced by name from the paragraph's `mojikumi`, and applied in composition. We
ship a few built-in sets derived from JLREQ (no line-start indent, one-em indent, half-width
punctuation, full-width punctuation…), all original data, for the IDML built-in names.
@@ -292,6 +292,7 @@ Each follows what an InDesign user expects.
| Date | Change | Summary |
|---|---|---|
| 2026-10-18 | minor | Mojikumi is applied (since #129); consecutive punctuation closes up in the Japanese sets |
| 2026-10-10 | trivial | Reviewed in the full re-measure; status line and this table added; summarized in [typography-parity.md](typography-parity.md) and [localization-parity.md](localization-parity.md) |
| 2026-10-10 | minor | Updated (see git history) |
| 2026-10-06 | major | Created: CJK typesetting plan and status (the Japanese/CJK feature set) |