From c8b197ec01bf3bbd5cad2fd119cfd7123b8ce257 Mon Sep 17 00:00:00 2001 From: minjaesong Date: Mon, 2 Mar 2026 14:12:43 +0900 Subject: [PATCH] fix: hangul filler not working on OTF --- .gitignore | 1 + OTFbuild/calligra_font_tests.odt | Bin 17283 -> 17302 bytes OTFbuild/hangul.py | 10 ++++++++++ OTFbuild/opentype_features.py | 2 +- 4 files changed, 12 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index a6b8e21..9d9ede8 100755 --- a/.gitignore +++ b/.gitignore @@ -20,3 +20,4 @@ OTFbuild/*.otf OTFbuild/*.woff OTFbuild/*.woff2 *.fea +*.xdp-* diff --git a/OTFbuild/calligra_font_tests.odt b/OTFbuild/calligra_font_tests.odt index 1e7fae617469d3c9faa948c53fedbc4dc46aab7a..0ab6a8caf3f55d01512325886e974e715dd25221 100644 GIT binary patch delta 9954 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 S!2=#oR289u*cJL0_WuAbb_t~b delta 9939 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

drbmKL{}eVkC}KEd!~HRGrGXDHQ+g(C?01 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 Dict[int, ExtractedGlyph]: result[pua] = ExtractedGlyph(pua, GlyphProps(width=w), bm) variant_count += 1 + # Ensure jungseong filler PUA variants exist (col=0, rows 15-16). + # The filler has an empty bitmap so extract_hangul_jamo_variants skips + # it, but the vjmo GSUB lookup needs a PUA target to substitute to. + empty_bm = [[0] * cell_w for _ in range(cell_h)] + for row in [15, 16]: + pua = _pua_for_jamo_variant(0, row) + if pua not in result: + result[pua] = ExtractedGlyph(pua, GlyphProps(width=0), empty_bm) + variant_count += 1 + print(f" Stored {variant_count} jamo variant glyphs in PUA (0x{HANGUL_PUA_BASE:05X}+)") print(f" Total Hangul glyphs: {len(result)}") return result diff --git a/OTFbuild/opentype_features.py b/OTFbuild/opentype_features.py index 3fe17ba..6877bb3 100644 --- a/OTFbuild/opentype_features.py +++ b/OTFbuild/opentype_features.py @@ -153,7 +153,7 @@ def _generate_hangul_gsub(glyphs, has, jamo_data): # Build codepoint lists (standard + extended jamo ranges) cho_ranges = list(range(0x1100, 0x115F)) + list(range(0xA960, 0xA97C)) - jung_ranges = list(range(0x1161, 0x11A8)) + list(range(0xD7B0, 0xD7C7)) + jung_ranges = list(range(0x1160, 0x11A8)) + list(range(0xD7B0, 0xD7C7)) jong_ranges = list(range(0x11A8, 0x1200)) + list(range(0xD7CB, 0xD7FC)) cho_cps = [cp for cp in cho_ranges if has(cp)]