Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -705,6 +705,9 @@ Feature Options:
--cn-scale-factor CN_SCALE_FACTOR
Scale factor for CN / JP glyphs. Format: <factor> or
<width_factor>,<height_factor> (e.g. 1.1 or 1.2,1.1)
--cn-scale-punctuation-factor CN_SCALE_PUNCTUATION_FACTOR
Scale factor for fullwidth punctuation glyphs. Format:
<factor> or <width_factor>,<height_factor> (e.g. 1.0)

Build Options:
--nf, --nerd-font Build Nerd-Font version (default)
Expand Down
3 changes: 3 additions & 0 deletions README_CN.md
Original file line number Diff line number Diff line change
Expand Up @@ -711,6 +711,9 @@ Feature Options:
--cn-scale-factor CN_SCALE_FACTOR
中文/日文字形的缩放因子。格式:<因子> 或
<宽度因子>,<高度因子> (例如 1.1 或 1.2,1.1)
--cn-scale-punctuation-factor CN_SCALE_PUNCTUATION_FACTOR
全角标点符号的缩放因子。格式:<因子> 或
<宽度因子>,<高度因子> (例如 1.0)

Build Options:
--nf, --nerd-font 构建 Nerd-Font 版本(默认)
Expand Down
3 changes: 3 additions & 0 deletions README_JA.md
Original file line number Diff line number Diff line change
Expand Up @@ -708,6 +708,9 @@ Feature Options:
--cn-scale-factor CN_SCALE_FACTOR
中国語/日本語グリフのスケール係数。形式:<係数> または
<幅の係数>,<高さの係数> (例:1.1 または 1.2,1.1)
--cn-scale-punctuation-factor CN_SCALE_PUNCTUATION_FACTOR
全角句読点グリフのスケール係数。形式:<係数> または
<幅の係数>,<高さの係数> (例:1.0)

Build Options:
--nf, --nerd-font Nerd-Fontバージョンをビルド(デフォルト)
Expand Down
25 changes: 24 additions & 1 deletion build.py
Original file line number Diff line number Diff line change
Expand Up @@ -227,6 +227,11 @@ def parse_args(args: list[str] | None = None):
type=parse_scale_factor,
help="Scale factor for CN / JP glyphs. Format: <factor> or <width_factor>,<height_factor> (e.g. 1.1 or 1.2,1.1)",
)
feature_group.add_argument(
"--cn-scale-punctuation-factor",
type=parse_scale_factor,
help="Scale factor for fullwidth punctuation glyphs. Format: <factor> or <width_factor>,<height_factor> (e.g. 1.0 or 1.0,1.0)",
)

build_group = parser.add_argument_group("Build Options")
nf_group = build_group.add_mutually_exclusive_group()
Expand Down Expand Up @@ -399,6 +404,8 @@ def __init__(self, args, version: str | None = None):
"use_static_base_font": True, # Deprecated. Always `True`
# scale factor for CN glyphs
"scale_factor": (1.0, 1.0),
# scale factor for fullwidth punctuation glyphs (None means use scale_factor)
"scale_punctuation_factor": None,
}
self.glyph_width = 600
self.glyph_width_cn_narrow = 1000
Expand Down Expand Up @@ -527,6 +534,11 @@ def _apply_cn_options(self, args):
if isinstance(self.cn["scale_factor"], (float, list)):
self.cn["scale_factor"] = parse_scale_factor(self.cn["scale_factor"])

if args.cn_scale_punctuation_factor:
self.cn["scale_punctuation_factor"] = args.cn_scale_punctuation_factor
if isinstance(self.cn["scale_punctuation_factor"], (float, list)):
self.cn["scale_punctuation_factor"] = parse_scale_factor(self.cn["scale_punctuation_factor"])

def _apply_build_options(self, args):
"""Apply general build options."""
self.archive = args.archive
Expand Down Expand Up @@ -1365,7 +1377,9 @@ def build_cn(f: str, font_config: FontConfig, build_option: BuildOption):
if font_config.cn["scale_factor"] != (1.0, 1.0)
else None
)
if target_width or scale_factor:
scale_punctuation_factor: tuple[float, float] | None = font_config.cn["scale_punctuation_factor"]

if target_width or scale_factor or scale_punctuation_factor:
match_width = 2 * font_config.glyph_width

# Change glyph width and keep monospace identifier will cause
Expand All @@ -1387,11 +1401,19 @@ def build_cn(f: str, font_config: FontConfig, build_option: BuildOption):
else:
scale_factor = (1.0, 1.0)

if scale_punctuation_factor:
print(
f"Scale CN punctuation glyph to ({scale_punctuation_factor[0]}x, {scale_punctuation_factor[1]}x)"
)
else:
scale_punctuation_factor = scale_factor

change_glyph_width_or_scale(
font=cn_font,
match_width=match_width,
target_width=target_width,
scale_factor=scale_factor,
scale_punctuation_factor=scale_punctuation_factor,
special_names=["ellipsis.full"],
)
elif font_config.get_width_name():
Expand All @@ -1400,6 +1422,7 @@ def build_cn(f: str, font_config: FontConfig, build_option: BuildOption):
match_width=2 * font_config.glyph_width,
target_width=2 * font_config.get_target_width(),
scale_factor=(1.0, 1.0),
scale_punctuation_factor=(1.0, 1.0),
special_names=["ellipsis.full"],
)

Expand Down
3 changes: 2 additions & 1 deletion config.json
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@
"narrow": false,
"use_hinted": false,
"use_static_base_font": true,
"scale_factor": 1.0
"scale_factor": 1.0,
"scale_punctuation_factor": 1.0
}
}
32 changes: 29 additions & 3 deletions source/py/transform.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
from fontTools.ttLib import TTFont
from fontTools.ttLib.tables._g_l_y_f import GlyphCoordinates, Glyph

from source.py.utils import FULLWIDTH_PUNCTUATION_NAMES

# Type aliases
Coordinate = Tuple[float, float]

Expand Down Expand Up @@ -231,6 +233,7 @@ def change_glyph_width_or_scale(
match_width: int,
target_width: int,
scale_factor: tuple[float, float],
scale_punctuation_factor: tuple[float, float],
special_names: list[str] = [],
):
"""
Expand All @@ -246,6 +249,8 @@ def change_glyph_width_or_scale(
target_width (int): The new width to set for matching glyphs.
scale_factor (tuple[float, float]): A tuple containing the scaling factors for
width and height (scale_w, scale_h).
scale_punctuation_factor (tuple[float, float]): A tuple containing the scaling factors for
fullwidth punctuation glyphs (scale_w, scale_h).
special_names (list[str], optional): A list of glyph names that require special
handling instead of trim whitespace only. Defaults to an empty list.

Expand All @@ -259,7 +264,13 @@ def change_glyph_width_or_scale(
glyf: Any = font["glyf"]
hmtx: Any = font["hmtx"]
factor = target_width / match_width

# Track statistics
cjk_count = 0
punctuation_count = 0

for glyph_name in font.getGlyphOrder():
# Handle special names (legacy behavior)
if glyph_name in special_names:
_change_glyph_width(
glyf=glyf,
Expand All @@ -280,13 +291,24 @@ def change_glyph_width_or_scale(
hmtx[glyph_name] = (target_width, lsb)
continue

scale_w, scale_h = scale_factor
glyph.coordinates.scale((scale_w, scale_h))
# Check if this is a punctuation glyph
is_punctuation = glyph_name in FULLWIDTH_PUNCTUATION_NAMES

# Determine which scale factor to use
if is_punctuation:
use_scale_w, use_scale_h = scale_punctuation_factor
punctuation_count += 1
else:
use_scale_w, use_scale_h = scale_factor
cjk_count += 1

# Apply scaling
glyph.coordinates.scale((use_scale_w, use_scale_h))
glyph.xMin, glyph.yMin, glyph.xMax, glyph.yMax = (
glyph.coordinates.calcIntBounds()
)

scaled_width = int(round(width * scale_w))
scaled_width = int(round(width * use_scale_w))
delta = (target_width - scaled_width) / 2

glyph.coordinates.translate((delta, 0))
Expand All @@ -296,3 +318,7 @@ def change_glyph_width_or_scale(

new_lsb = lsb + int(round(delta))
hmtx[glyph_name] = (target_width, new_lsb)

# Print summary
if cjk_count > 0 or punctuation_count > 0:
print(f" Scaled {cjk_count} CJK glyphs, {punctuation_count} punctuation glyphs")
36 changes: 36 additions & 0 deletions source/py/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,49 @@
import sys
import shutil
import subprocess
from typing import Set
from urllib.request import Request, urlopen
from zipfile import ZIP_DEFLATED, ZipFile
from fontTools.ttLib import TTFont, newTable
from fontTools.merge import Merger
from source.py.task._utils import is_ci, default_weight_map


FULLWIDTH_PUNCTUATION_NAMES: Set[str] = {
# CJK Symbols and Punctuation (U+3000-U+303F)
"uni3000", "uni3001", "uni3002", "uni3003",
"uni3005", "uni3006", "uni3007",
"uni3008", "uni3009", "uni300A", "uni300B",
"uni300C", "uni300D", "uni300E", "uni300F",
"uni3010", "uni3011",
"uni3014", "uni3015", "uni3016", "uni3017",
"uni3018", "uni3019", "uni301A", "uni301B", "uni301C",
"uni3021", "uni3022", "uni3023", "uni3024", "uni3025",
"uni3026", "uni3027", "uni3028", "uni3029",
"uni3031", "uni3032", "uni3033", "uni3034", "uni3035",
"uni3036", "uni3037", "uni3038", "uni3039", "uni303A", "uni303B",

# Fullwidth ASCII Punctuation
"uniFF01", "uniFF08", "uniFF09", "uniFF0C", "uniFF0E",
"uniFF1A", "uniFF1B", "uniFF1F",
"uniFF3B", "uniFF3D", "uniFF5B", "uniFF5C", "uniFF5D", "uniFF5E",

# .full variants (cv96, cv97, cv98)
"ellipsis.full", "emdash.full",
"quotedblleft.full", "quotedblright.full",
"quoteleft.full", "quoteright.full",

# Base glyphs that may be replaced by .full variants when cv96/cv97/cv98 are frozen
"ellipsis", "emdash",
"quotedblleft", "quotedblright",
"quoteleft", "quoteright",

# .tw variants (cv99)
"uni3001.tw", "uni3002.tw", "uniFF01.tw", "uniFF0C.tw",
"uniFF1A.tw", "uniFF1B.tw", "uniFF1F.tw",
}


def run(command: str | list[str], extra_args: list[str] | None = None, log=not is_ci()):
"""
Run a command line interface (CLI) command.
Expand Down
9 changes: 9 additions & 0 deletions source/schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -486,6 +486,15 @@
],
"description": "Scale factor for CN / JP glyphs with <factor> or [<width_factor>,<height_factor>](e.g. 1.1 or [1.2, 1.1])",
"default": 1.0
},
"scale_punctuation_factor": {
"type": [
"number",
"array",
"null"
],
"description": "Scale factor for fullwidth punctuation glyphs. When null, punctuation uses the same scale_factor as other glyphs",
"default": null
}
},
"required": [
Expand Down