3a4fa1c932
保留逐字 margin/jiya.bias 字体后备方案;补齐 demo 重排、量测边界说明及共享规则 1.0.1 反例向量。 验证:完整构建通过,Node 376 项、WKWebView 64 项及原生 demo 9 项通过;Firefox 仍暂缓。
104 lines
3.7 KiB
Python
104 lines
3.7 KiB
Python
#!/usr/bin/env python3
|
||
"""量測字型之標點 glyph 墨色位置,產出聚珍 margin 後備模式之偏側建議表。
|
||
|
||
用途:消費端字型缺 OpenType `halt`(如方正書宋 GBK、多數系統字型)時,margin 後備
|
||
模式需知每個標點之「墨色偏側」以於 0.5em 半形盒內正確定位字身。本工具讀字型輪廓,
|
||
量各標點 glyph 之 ink bounding box,依墨色中心建議 left/center/right/full,並印出
|
||
可貼入 `options.jiya.bias` 之 JSON 片段。
|
||
|
||
只量預設 cmap glyph,未做 shaping、未應用 `locl`;語言參數僅選 JSON 的語言分類鍵,
|
||
不切換字型的語言字形。`halt` feature 存在也不保證涵蓋所有標點。建議表須按瀏覽器
|
||
實際字形逐字微調,尤其是會依語言替換 glyph 的字型。
|
||
|
||
依賴:fonttools(`pip install fonttools`)。
|
||
用法:python3 tools/measure-bias.py /path/to/font.ttf [zh-Hans|zh-Hant|ja|other]
|
||
|
||
輸出之偏側語義(決定字身於 0.5em 盒內之左移;見 README「halt/margin 後備」):
|
||
left 墨偏左 → 不移(字身左對齊,墨在盒內、右側空白溢出)
|
||
center 墨居中 → 左移 0.25em
|
||
right 墨偏右 → 左移 0.5em(右半入盒,左側空白溢出)
|
||
full 無 glyph/空白 glyph → 不收、維持全形
|
||
門檻:ink 中心 < 0.375 → left;0.375–0.625 → center;> 0.625 → right。
|
||
判讀後仍建議於瀏覽器目視微調(字身側鬚、字面留白因字型而異)。
|
||
"""
|
||
|
||
import json
|
||
import sys
|
||
|
||
from fontTools.pens.boundsPen import BoundsPen
|
||
from fontTools.ttLib import TTFont
|
||
|
||
# 聚珍 margin 模式會收半形之標點(句末點號僅半角式收,此處一併量供參考)。
|
||
PUNCT = {
|
||
"pause_stop": ",、。.;:!?",
|
||
"open": "「『(《〈【〖〔[{“‘",
|
||
"close": "」』)》〉】〗〕]}”’",
|
||
}
|
||
|
||
|
||
def has_feature(font, tag):
|
||
for t in ("GSUB", "GPOS"):
|
||
if t in font and font[t].table.FeatureList:
|
||
for fr in font[t].table.FeatureList.FeatureRecord:
|
||
if fr.FeatureTag == tag:
|
||
return True
|
||
return False
|
||
|
||
|
||
def bias_of(font, upm, cmap, gs, ch):
|
||
cp = ord(ch)
|
||
if cp not in cmap:
|
||
return "full", None
|
||
g = gs[cmap[cp]]
|
||
pen = BoundsPen(gs)
|
||
g.draw(pen)
|
||
if pen.bounds is None:
|
||
return "full", None
|
||
x_min, _, x_max, _ = pen.bounds
|
||
center = ((x_min + x_max) / 2) / upm
|
||
if center < 0.375:
|
||
b = "left"
|
||
elif center > 0.625:
|
||
b = "right"
|
||
else:
|
||
b = "center"
|
||
return b, center
|
||
|
||
|
||
def main():
|
||
if len(sys.argv) < 2:
|
||
print(__doc__)
|
||
sys.exit(1)
|
||
path = sys.argv[1]
|
||
lang = sys.argv[2] if len(sys.argv) > 2 else "zh-Hans"
|
||
|
||
font = TTFont(path)
|
||
upm = font["head"].unitsPerEm
|
||
cmap = font.getBestCmap()
|
||
gs = font.getGlyphSet()
|
||
|
||
halt = has_feature(font, "halt")
|
||
print(f"# 字型:{path}")
|
||
print(f"# OpenType halt:{'有(feature 存在不保證涵蓋所有標點,須逐字確認)' if halt else '無 → 可用 margin 後備模式'}")
|
||
print(f"# unitsPerEm={upm}")
|
||
print("# 量測預設 cmap glyph,未做 shaping/未應用 locl;語言參數僅選 JSON 分類鍵。")
|
||
print("# 請按瀏覽器實際字形逐字微調建議表。")
|
||
print()
|
||
|
||
table = {}
|
||
for group, chars in PUNCT.items():
|
||
print(f"# {group}")
|
||
for ch in chars:
|
||
b, center = bias_of(font, upm, cmap, gs, ch)
|
||
cstr = f"{center:.2f}" if center is not None else " - "
|
||
print(f"# {ch} inkcenter={cstr} -> {b}")
|
||
table[ch] = b
|
||
print()
|
||
|
||
print("// 貼入 options.jiya.bias(依瀏覽器目視再微調):")
|
||
print(json.dumps({lang: table}, ensure_ascii=False, indent=2))
|
||
|
||
|
||
if __name__ == "__main__":
|
||
main()
|