Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 43c4d30

Browse files
committed
Split and cache TTC font to TTF fonts
1 parent 37cf8e4 commit 43c4d30

File tree

1 file changed

+101
-1
lines changed

1 file changed

+101
-1
lines changed

lib/matplotlib/font_manager.py

Lines changed: 101 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@
3535
from pathlib import Path
3636
import re
3737
import subprocess
38+
import struct
3839
import sys
3940
import threading
4041

@@ -1099,7 +1100,7 @@ def __init__(self, size=None, weight='normal'):
10991100
'Matplotlib is building the font cache; this may take a moment.'))
11001101
timer.start()
11011102
try:
1102-
for fontext in ["afm", "ttf"]:
1103+
for fontext in ["afm", "ttf", "ttc"]:
11031104
for path in [*findSystemFonts(paths, fontext=fontext),
11041105
*findSystemFonts(fontext=fontext)]:
11051106
try:
@@ -1129,6 +1130,9 @@ def addfont(self, path):
11291130
font = _afm.AFM(fh)
11301131
prop = afmFontProperty(path, font)
11311132
self.afmlist.append(prop)
1133+
elif Path(path).suffix.lower() == ".ttc":
1134+
for ttf_file in _split_ttc(path):
1135+
self.addfont(ttf_file)
11321136
else:
11331137
font = ft2font.FT2Font(path)
11341138
prop = ttfFontProperty(font)
@@ -1473,6 +1477,102 @@ def get_font(filename, hinting_factor=None):
14731477
thread_id=threading.get_ident())
14741478

14751479

1480+
def _split_ttc(ttc_path):
1481+
"""SPlit a TTC ont into TTF files"""
1482+
res = _read_ttc(ttc_path)
1483+
ttf_fonts, table_index, table_data = res
1484+
out_base = Path(
1485+
mpl.get_cachedir(),
1486+
os.path.basename(ttc_path) + "-"
1487+
)
1488+
return _dump_ttf(out_base, ttf_fonts, table_index, table_data)
1489+
1490+
1491+
def _read_ttc(ttc_path):
1492+
"""
1493+
Read a TTC font collection
1494+
1495+
Returns an internal list of TTF fonts, table index data, and table
1496+
contents.
1497+
"""
1498+
with open(ttc_path, "rb") as ttc_file:
1499+
def read(fmt):
1500+
"""Read with struct format"""
1501+
size = struct.calcsize(fmt)
1502+
data = ttc_file.read(size)
1503+
return struct.unpack(fmt, data)
1504+
1505+
read(">HHI") # ttcf tag and version, ignore
1506+
num_fonts = read(">I")[0] # Number of fonts
1507+
font_offsets = read(f">{num_fonts:d}I") # offsets of TTF font
1508+
1509+
# Set of tables referenced by any font
1510+
table_index = {} # (offset, length): tag, chksum
1511+
1512+
# List of TTF fonts
1513+
ttf_fonts = [] # (version, num_entries, triple, referenced tables)
1514+
1515+
# Read TTF headers and directory tables
1516+
for font_offset in font_offsets:
1517+
ttc_file.seek(font_offset)
1518+
1519+
version = read(">HH") # TTF format version
1520+
num_entries = read(">H")[0] # Number of entried in directory table
1521+
triple = read(">HHH") # Weird triple, often invalid
1522+
referenced_tables = []
1523+
1524+
for _ in range(num_entries):
1525+
tag, chksum, offset, length = read(">IIII")
1526+
referenced_tables.append((offset, length))
1527+
table_index[(offset, length)] = tag, chksum
1528+
1529+
ttf_fonts.append((version, num_entries, triple, referenced_tables))
1530+
1531+
# Read data for all tables
1532+
table_data = {}
1533+
for (offset, length), (tag, chksum) in table_index.items():
1534+
ttc_file.seek(offset)
1535+
table_data[(offset, length)] = ttc_file.read(length)
1536+
1537+
return ttf_fonts, table_index, table_data
1538+
1539+
1540+
def _dump_ttf(base_name, ttf_fonts, table_index, table_data):
1541+
"""Write each TTF font to a separate font"""
1542+
created_paths = []
1543+
1544+
# Dump TTF fonts into separate files
1545+
for i, font in enumerate(ttf_fonts):
1546+
version, num_entries, triple, referenced_tables = font
1547+
1548+
def write(file, fmt, values):
1549+
raw = struct.pack(fmt, *values)
1550+
file.write(raw)
1551+
1552+
out_path = f"{base_name}{i}.ttf"
1553+
created_paths.append(out_path)
1554+
with open(out_path, "wb") as ttf_file:
1555+
1556+
write(ttf_file, ">HH", version)
1557+
write(ttf_file, ">H", (num_entries, ))
1558+
write(ttf_file, ">HHH", triple)
1559+
1560+
# Length of header and directory
1561+
file_offset = 12 + len(referenced_tables) * 16
1562+
1563+
# Write directory
1564+
for (offset, length) in referenced_tables:
1565+
tag, chksum, = table_index[(offset, length)]
1566+
write(ttf_file, ">IIII", (tag, chksum, file_offset, length))
1567+
file_offset += length
1568+
1569+
# Write tables
1570+
for table_coord in referenced_tables:
1571+
data = table_data[table_coord]
1572+
ttf_file.write(data)
1573+
return created_paths
1574+
1575+
14761576
def _load_fontmanager(*, try_read_cache=True):
14771577
fm_path = Path(
14781578
mpl.get_cachedir(), f"fontlist-v{FontManager.__version__}.json")

0 commit comments

Comments
 (0)