From e2550b6ef6205ac63e8f5317337a56b39ad2f0cb Mon Sep 17 00:00:00 2001 From: minjaesong Date: Sun, 1 Mar 2026 16:14:50 +0900 Subject: [PATCH] diacritics anchoring reads isLowheight now --- OTFbuild/calligra_font_tests.odt | Bin 15737 -> 15711 bytes OTFbuild/opentype_features.py | 31 ++++++++++++++++++++++--------- 2 files changed, 22 insertions(+), 9 deletions(-) diff --git a/OTFbuild/calligra_font_tests.odt b/OTFbuild/calligra_font_tests.odt index 1964d3983c3c7b03c9c3996a17401611c0a67d32..96af8d68df8eebed60937946f517615fa91372b3 100644 GIT binary patch delta 8279 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 B+R6X` delta 8330 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 h_FTfhX8-Tg`d{|3|K [(cp, g), ...] + # Group marks by (writeOnTop, alignment, isDiacriticalMark). + # Diacritical marks (U+0300-036F) need separate classes because their + # base anchors are adjusted for lowheight bases (e.g. lowercase 'e'). + # Type-0 (above): shift down 4px; Type-2 (overlay): shift down 2px. + mark_groups = {} # (mark_type, align, is_dia) -> [(cp, g), ...] for cp, g in marks.items(): - key = (g.props.write_on_top, g.props.align_where) + is_dia = (0x0300 <= cp <= 0x036F) + key = (g.props.write_on_top, g.props.align_where, is_dia) mark_groups.setdefault(key, []).append((cp, g)) # Emit markClass definitions - for (mark_type, align), mark_list in sorted(mark_groups.items()): + for (mark_type, align, is_dia), mark_list in sorted(mark_groups.items()): suffix = _align_suffix.get(align, 'x') - class_name = f"@mark_t{mark_type}_{suffix}" + class_name = f"@mark_t{mark_type}_{suffix}" + ("_dia" if is_dia else "") for cp, g in mark_list: if align == SC.ALIGN_CENTRE: # Match Kotlin: anchorPoint - HALF_VAR_INIT centres the @@ -1385,12 +1389,12 @@ def _generate_mark(glyphs, has): f"markClass {glyph_name(cp)} {class_name};" ) - # Generate one lookup per (mark_type, align) group. + # Generate one lookup per (mark_type, align, is_dia) group. lookup_names = [] - for (mark_type, align), mark_list in sorted(mark_groups.items()): + for (mark_type, align, is_dia), mark_list in sorted(mark_groups.items()): suffix = _align_suffix.get(align, 'x') - class_name = f"@mark_t{mark_type}_{suffix}" - lookup_name = f"mark_t{mark_type}_{suffix}" + class_name = f"@mark_t{mark_type}_{suffix}" + ("_dia" if is_dia else "") + lookup_name = f"mark_t{mark_type}_{suffix}" + ("_dia" if is_dia else "") lines.append(f"lookup {lookup_name} {{") for cp, g in sorted(all_bases.items()): @@ -1442,6 +1446,15 @@ def _generate_mark(glyphs, has): ax = (anchor.x if anchor.x_used else W) * SC.SCALE + # Lowheight adjustment for combining diacritical marks: + # shift base anchor Y down so diacritics sit closer to + # the shorter base glyph. + if is_dia and g.props.is_low_height: + if mark_type == 2: # overlay + ay -= SC.H_OVERLAY_LOWERCASE_SHIFTDOWN * SC.SCALE + else: # above (type 0) + ay -= SC.H_STACKUP_LOWERCASE_SHIFTDOWN * SC.SCALE + lines.append( f" pos base {glyph_name(cp)}" f" mark {class_name};"