8000 fix Read supplement encoding by shimondoodkin · Pull Request #3813 · fonttools/fonttools · GitHub
[go: up one dir, main page]
More Web Proxy on the site http://driver.im/
Skip to content

fix Read supplement encoding #3813

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 5 commits into from
May 1, 2025
Merged
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
82 changes: 58 additions & 24 deletions Lib/fontTools/cffLib/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -1663,25 +1663,26 @@
return "StandardEncoding"
elif value == 1:
return "ExpertEncoding"
# custom encoding at offset `value`

Check warning on line 1666 in Lib/fontTools/cffLib/__init__.py

View check run for this annotation

Codecov / codecov/patch

Lib/fontTools/cffLib/__init__.py#L1666

Added line #L1666 was not covered by tests
assert value > 1
file = parent.file
file.seek(value)
log.log(DEBUG, "loading Encoding at %s", value)
fmt = readCard8(file)
haveSupplement = bool(fmt & 0x80)
fmt = fmt & 0x7F

if fmt == 0:
encoding = parseEncoding0(parent.charset, file)
elif fmt == 1:
encoding = parseEncoding1(parent.charset, file)
else:
assert value > 1
file = parent.file
file.seek(value)
log.log(DEBUG, "loading Encoding at %s", value)
fmt = readCard8(file)
haveSupplement = fmt & 0x80
if haveSupplement:
raise NotImplementedError("Encoding supplements are not yet supported")
fmt = fmt & 0x7F
if fmt == 0:
encoding = parseEncoding0(
parent.charset, file, haveSupplement, parent.strings
)
elif fmt == 1:
encoding = parseEncoding1(
parent.charset, file, haveSupplement, parent.strings
)
return encoding
raise ValueError(f"Unknown Encoding format: {fmt}")

Check warning on line 1681 in Lib/fontTools/cffLib/__init__.py

View check run for this annotation

Codecov / codecov/patch

Lib/fontTools/cffLib/__init__.py#L1681

Added line #L1681 was not covered by tests
if haveSupplement:
parseEncodingSupplement(file, encoding, parent.strings)

return encoding

def write(self, parent, value):
if value == "StandardEncoding":
Expand Down Expand Up @@ -1719,27 +1720,60 @@
return encoding


def parseEncoding0(charset, file, haveSupplement, strings):
def readSID(file):
"""Read a String ID (SID) — 2-byte unsigned integer."""
data = file.read(2)
if len(data) != 2:
raise EOFError("Unexpected end of file while reading SID")
return struct.unpack(">H", data)[0] # big-endian uint16

Check warning on line 1728 in Lib/fontTools/cffLib/__init__.py

View check run for this annotation

Codecov / codecov/patch

Lib/fontTools/cffLib/__init__.py#L1728

Added line #L1728 was not covered by tests


def parseEncodingSupplement(file, encoding, strings):
"""
Parse the CFF Encoding supplement data:
- nSups: number of supplementary mappings
- each mapping: (code, SID) pair
and apply them to the `encoding` list in place.
"""
nSups = readCard8(file)
for _ in range(nSups):
code = readCard8(file)
sid = readSID(file)
name = strings[sid]
encoding[code] = name


def parseEncoding0(charset, file):
"""
Format 0: simple list of codes.
After reading the base table, optionally parse the supplement.
"""
nCodes = readCard8(file)
encoding = [".notdef"] * 256
for glyphID in range(1, nCodes + 1):
code = readCard8(file)
if code != 0:
encoding[code] = charset[glyphID]

return encoding


def parseEncoding1(charset, file, haveSupplement, strings):
def parseEncoding1(charset, file):
"""
Format 1: range-based encoding.
After reading the base ranges, optionally parse the supplement.
"""
nRanges = readCard8(file)
encoding = [".notdef"] * 256
glyphID = 1
for i in range(nRanges):
for _ in range(nRanges):
code = readCard8(file)
nLeft = readCard8(file)
for glyphID in range(glyphID, glyphID + nLeft + 1):
for _ in range(nLeft + 1):
encoding[code] = charset[glyphID]
code = code + 1
glyphID = glyphID + 1
code += 1
glyphID += 1

return encoding


Expand Down
23 changes: 19 additions & 4 deletions Tests/cffLib/cffLib_test.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,15 @@
from fontTools.cffLib import TopDict, PrivateDict, CharStrings
import os
import sys

libdir = os.path.join(
os.path.dirname(os.path.dirname(os.path.dirname(os.path.realpath(__file__)))), "Lib"
)
sys.path.insert(0, libdir)

from fontTools.cffLib import TopDict, PrivateDict, CharStrings, CFFFontSet
from fontTools.misc.testTools import parseXML, DataFilesHandler
from fontTools.ttLib import TTFont
import copy
import os
import sys
import unittest
from io import BytesIO

Expand Down Expand Up @@ -119,9 +125,18 @@ def test_unique_glyph_names(self):
glyphOrder = font2.getGlyphOrder()
self.assertEqual(len(glyphOrder), len(set(glyphOrder)))

def test_reading_supplement_encoding(self):
cff_path = self.getpath("TestSupplementEncoding.cff")
topDict = None
with open(cff_path, "rb") as fontfile:
cff = CFFFontSet()
cff.decompile(fontfile, None)
topDict = cff[0]
self.assertEqual(topDict.Encoding[9], "space")
self.assertEqual(topDict.Encoding[32], "space")

class CFFToCFF2Test(DataFilesHandler):

class CFFToCFF2Test(DataFilesHandler):
def test_conversion(self):
font_path = self.getpath("CFFToCFF2-1.otf")
font = TTFont(font_path)
Expand Down
Binary file added Tests/cffLib/data/TestSupplementEncoding.cff
Binary file not shown.
Loading
0