7b4a051a1f
margin 後備(供缺 OpenType halt 之字型): - jiya.halfWidth: "halt"(預設)|"margin"。margin 以 width:0.5em+字身依墨色 偏側溢出定位,於 CSS 盒模型重現 halt 之半形 advance(真 0.5em;相鄰標點各 0.5em、合佔 1em,無負 margin 之鄰接疊加/單側重疊)。模式由 data-jz-halfwidth 子樹屬性決定(可逐區塊/逐 lang,含 halt-island 反置)。 - 偏側 inkBias 依字身墨色中心給 left/center/right/full;參考表據方正書宋 GBK 實測,consumer 可經 jiya.bias 逐 lang 逐字覆寫。 - _lang.css 字型堆疊僅作後備(全包 :where 零特異度、只命中帶 lang 屬性之元素、 不直接覆寫 jz-inner)→ 消費端 font-family 恆優先,標點隨之以消費端字型渲染。 - 新增 tools/measure-bias.py(量 glyph ink 中心、產偏側建議表)+ README 「字型適配工作流」。demo/fallback.html:方正書宋 GBK 對照 halt 失效 vs margin。 下游回饋修復: - 1a:adjacentLogicalChar 透明穿越 jz-jinze(同 jz-hws/樣式行內)→ pill 緊鄰 CJK 標點時,左緣間隙與作者空格剝除復原(jinze 先於 spacing 之交互)。 - 1b:CJK↔CJK 贅餘作者空格(含跨 inline 標籤)剝除、不留隙,與 CJK↔Latin 之 邊界正規化一致(DEL 哨符,全形空格 U+3000 不動)。 - 問題二:新增 justifyAtoms 選項;false → jz-char/jz-jinze 改 display:inline, 相容分頁器(Paged.js)對齊引擎(halt 半形與禁則 nowrap 保留)。 測試 43→49 全通過;dist 重建;README/ARCHITECTURE 同步更新契約。
98 lines
3.3 KiB
Python
98 lines
3.3 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 片段。
|
||
|
||
依賴: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:{'有(建議直接用 halt 預設模式,無需 margin 後備)' if halt else '無 → 需 margin 後備模式'}")
|
||
print(f"# unitsPerEm={upm}")
|
||
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()
|