Skip to content

Commit

Permalink
Merge pull request #897 from googlefonts/fix-info-compiler-codepage-r…
Browse files Browse the repository at this point in the history
…anges

fix InfoCompiler overwriting the default master's codepage range bits
  • Loading branch information
anthrotype authored Feb 14, 2025
2 parents 5a606b7 + 7e9c7b3 commit 16ed156
Show file tree
Hide file tree
Showing 3 changed files with 30 additions and 104 deletions.
14 changes: 6 additions & 8 deletions Lib/ufo2ft/outlineCompiler.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,6 @@
from ufo2ft.util import (
_copyGlyph,
_getNewGlyphFactory,
calcCodePageRanges,
colrClipBoxQuantization,
getMaxComponentDepth,
makeOfficialGlyphOrder,
Expand Down Expand Up @@ -665,11 +664,11 @@ def adjustOffset(offset, angle):

# codepage ranges
codepageRanges = getAttrWithFallback(font.info, "openTypeOS2CodePageRanges")
if codepageRanges is None:
unicodes = self.unicodeToGlyphNameMapping.keys()
codepageRanges = calcCodePageRanges(unicodes)
os2.ulCodePageRange1 = intListToNum(codepageRanges, 0, 32)
os2.ulCodePageRange2 = intListToNum(codepageRanges, 32, 32)
if codepageRanges is not None:
os2.ulCodePageRange1 = intListToNum(codepageRanges, 0, 32)
os2.ulCodePageRange2 = intListToNum(codepageRanges, 32, 32)
elif "cmap" in self.otf:
os2.recalcCodePageRanges(self.otf)

# vendor id, padded with spaces if < 4 bytes
os2.achVendID = getAttrWithFallback(font.info, "openTypeOS2VendorID").ljust(4)
Expand Down Expand Up @@ -1066,8 +1065,7 @@ def setupTable_meta(self):
isinstance(string, str) for string in value
):
raise TypeError(
f"public.openTypeMeta '{key}' value should "
"be a list of strings"
f"public.openTypeMeta '{key}' value should be a list of strings"
)
meta.data[key] = ",".join(value)
elif key in ["appl", "bild"]:
Expand Down
99 changes: 4 additions & 95 deletions Lib/ufo2ft/util.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,10 @@
from fontTools.pens.reverseContourPen import ReverseContourPen
from fontTools.pens.transformPen import TransformPen

# we keep this unused import in case some external code relied on the
# old location of this function, which now lives in fontTools
from fontTools.ttLib.tables.O_S_2f_2 import calcCodePageRanges # noqa: F401

from ufo2ft.constants import OPENTYPE_CATEGORIES_KEY, UNICODE_SCRIPT_ALIASES
from ufo2ft.errors import InvalidDesignSpaceData, InvalidFontData
from ufo2ft.fontInfoData import getAttrWithFallback
Expand Down Expand Up @@ -373,101 +377,6 @@ def unicodeScriptDirection(uv):
return unicodedata.script_horizontal_direction(sc, "LTR")


def calcCodePageRanges(unicodes):
"""Given a set of Unicode codepoints (integers), calculate the
corresponding OS/2 CodePage range bits.
This is a direct translation of FontForge implementation:
https://github.com/fontforge/fontforge/blob/7b2c074/fontforge/tottf.c#L3158
"""
codepageRanges = set()

chars = [chr(u) for u in unicodes]

hasAscii = set(range(0x20, 0x7E)).issubset(unicodes)
hasLineart = "┤" in chars

for char in chars:
if char == "Þ" and hasAscii:
codepageRanges.add(0) # Latin 1
elif char == "Ľ" and hasAscii:
codepageRanges.add(1) # Latin 2: Eastern Europe
if hasLineart:
codepageRanges.add(58) # Latin 2
elif char == "Б":
codepageRanges.add(2) # Cyrillic
if "Ѕ" in chars and hasLineart:
codepageRanges.add(57) # IBM Cyrillic
if "╜" in chars and hasLineart:
codepageRanges.add(49) # MS-DOS Russian
elif char == "Ά":
codepageRanges.add(3) # Greek
if hasLineart and "½" in chars:
codepageRanges.add(48) # IBM Greek
if hasLineart and "√" in chars:
codepageRanges.add(60) # Greek, former 437 G
elif char == "İ" and hasAscii:
codepageRanges.add(4) # Turkish
if hasLineart:
codepageRanges.add(56) # IBM turkish
elif char == "א":
codepageRanges.add(5) # Hebrew
if hasLineart and "√" in chars:
codepageRanges.add(53) # Hebrew
elif char == "ر":
codepageRanges.add(6) # Arabic
if "√" in chars:
codepageRanges.add(51) # Arabic
if hasLineart:
codepageRanges.add(61) # Arabic; ASMO 708
elif char == "ŗ" and hasAscii:
codepageRanges.add(7) # Windows Baltic
if hasLineart:
codepageRanges.add(59) # MS-DOS Baltic
elif char == "₫" and hasAscii:
codepageRanges.add(8) # Vietnamese
elif char == "ๅ":
codepageRanges.add(16) # Thai
elif char == "エ":
codepageRanges.add(17) # JIS/Japan
elif char == "ㄅ":
codepageRanges.add(18) # Chinese: Simplified chars
elif char == "ㄱ":
codepageRanges.add(19) # Korean wansung
elif char == "央":
codepageRanges.add(20) # Chinese: Traditional chars
elif char == "곴":
codepageRanges.add(21) # Korean Johab
elif char == "♥" and hasAscii:
codepageRanges.add(30) # OEM Character Set
# TODO: Symbol bit has a special meaning (check the spec), we need
# to confirm if this is wanted by default.
# elif chr(0xF000) <= char <= chr(0xF0FF):
# codepageRanges.add(31) # Symbol Character Set
elif char == "þ" and hasAscii and hasLineart:
codepageRanges.add(54) # MS-DOS Icelandic
elif char == "╚" and hasAscii:
codepageRanges.add(62) # WE/Latin 1
codepageRanges.add(63) # US
elif hasAscii and hasLineart and "√" in chars:
if char == "Å":
codepageRanges.add(50) # MS-DOS Nordic
elif char == "é":
codepageRanges.add(52) # MS-DOS Canadian French
elif char == "õ":
codepageRanges.add(55) # MS-DOS Portuguese

if hasAscii and "‰" in chars and "∑" in chars:
codepageRanges.add(29) # Macintosh Character Set (US Roman)

# when no codepage ranges can be enabled, fall back to enabling bit 0
# (Latin 1) so that the font works in MS Word:
# https://github.com/googlei18n/fontmake/issues/468
if not codepageRanges:
codepageRanges.add(0)

return codepageRanges


class _LazyFontName:
def __init__(self, font):
self.font = font
Expand Down
21 changes: 20 additions & 1 deletion tests/infoCompiler_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,11 +50,30 @@ def test_name(self, testttf, testufo):
assert ttf["name"].getDebugName(6) == "TestFontOverride-Italic"

def test_OS2(self, testttf, testufo):
info = {"openTypeOS2TypoAscender": 100, "openTypeOS2TypoDescender": -200}
info = {
"openTypeOS2TypoAscender": 100,
"openTypeOS2TypoDescender": -200,
"openTypeOS2CodePageRanges": [0, 1, 2, 3, 32, 32 + 1, 32 + 2, 32 + 3],
}
compiler = InfoCompiler(testttf, testufo, info)
ttf = compiler.compile()
assert ttf["OS/2"].sTypoAscender == 100
assert ttf["OS/2"].sTypoDescender == -200
assert ttf["OS/2"].ulCodePageRange1 == 0b1111
assert ttf["OS/2"].ulCodePageRange2 == 0b1111

def test_OS2_dont_overwrite_codePageRanges(self, testttf, testufo):
# if the variable-font's 'public.fontInfo' lib key does not override the
# openTypeOS2CodePageRanges, we should keep the original values as defined
# or computed for the default master TTF.
ulCodePageRange1 = testttf["OS/2"].ulCodePageRange1
ulCodePageRange2 = testttf["OS/2"].ulCodePageRange2
testufo.info.openTypeOS2CodePageRanges = None
info = {}
compiler = InfoCompiler(testttf, testufo, info)
ttf = compiler.compile()
assert ttf["OS/2"].ulCodePageRange1 == ulCodePageRange1
assert ttf["OS/2"].ulCodePageRange2 == ulCodePageRange2

def test_post(self, testttf, testufo):
info = {"italicAngle": 30.6}
Expand Down

0 comments on commit 16ed156

Please sign in to comment.