Skip to content

Commit ac5d17b

Browse files
committed
add instanceworker module
1 parent ebc0525 commit ac5d17b

File tree

1 file changed

+183
-0
lines changed

1 file changed

+183
-0
lines changed

src/slice/instanceworker.py

Lines changed: 183 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,183 @@
1+
# This file is part of Slice.
2+
#
3+
# Slice is free software: you can redistribute it and/or modify
4+
# it under the terms of the GNU General Public License as published by
5+
# the Free Software Foundation, either version 3 of the License, or
6+
# (at your option) any later version.
7+
#
8+
# Slice is distributed in the hope that it will be useful,
9+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
10+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+
# GNU General Public License for more details.
12+
#
13+
# You should have received a copy of the GNU General Public License
14+
# along with Slice. If not, see <https://www.gnu.org/licenses/>.
15+
16+
import datetime
17+
import sys
18+
import traceback
19+
20+
from fontTools.misc.textTools import num2binary
21+
from fontTools.ttLib.ttFont import TTFont
22+
from fontTools.varLib.mutator import instantiateVariableFont
23+
from PyQt5.QtCore import QObject, QRunnable, pyqtSignal, pyqtSlot
24+
25+
26+
class InstanceWorkerSignals(QObject):
27+
finished = pyqtSignal() # no return type, only signal that complete
28+
error = pyqtSignal(str) # returns the error message
29+
result = pyqtSignal(str) # returns file path for the new file write
30+
31+
32+
class InstanceWorker(QRunnable):
33+
def __init__(
34+
self,
35+
outpath=None,
36+
font_model=None,
37+
axis_model=None,
38+
name_model=None,
39+
bit_model=None,
40+
):
41+
super().__init__()
42+
self.signals = InstanceWorkerSignals()
43+
self.outpath = outpath
44+
self.font_model = font_model
45+
self.axis_model = axis_model
46+
self.name_model = name_model
47+
self.bit_model = bit_model
48+
self.ttfont = None
49+
50+
@pyqtSlot()
51+
def run(self):
52+
try:
53+
# Debugging in stdout
54+
print(f"\n\n{datetime.datetime.now()}")
55+
self.ttfont = TTFont(self.font_model.fontpath)
56+
# instantiate
57+
self.instantiate_variable_font()
58+
# edit name table records
59+
self.edit_name_table()
60+
# edit bit flags
61+
self.edit_bit_flags()
62+
# write to disk
63+
self.ttfont.save(self.outpath)
64+
except Exception as e:
65+
self.signals.error.emit(f"{e}")
66+
sys.stderr.write(f"{traceback.format_exc()}\n")
67+
else:
68+
# returns the file out file path on success
69+
self.signals.result.emit(self.outpath)
70+
self.signals.finished.emit()
71+
72+
def instantiate_variable_font(self):
73+
axis_instance_data = self.axis_model.get_instance_data()
74+
instantiateVariableFont(self.ttfont, axis_instance_data, inplace=True)
75+
print("\nAXIS INSTANCE VALUES")
76+
print(
77+
f"Instantiated variable font with axis definitions:\n{axis_instance_data}"
78+
)
79+
80+
def edit_name_table(self):
81+
# string, nameID, platformID, platEncID, langID
82+
name_record_plat_enc_lang = (3, 1, 1033)
83+
name_instance_data = self.name_model.get_instance_data()
84+
name_table = self.ttfont["name"]
85+
# set 3, 1, 1033 name records (only!)
86+
# mandatory writes
87+
name_table.setName(name_instance_data["nameID1"], 1, *name_record_plat_enc_lang)
88+
name_table.setName(name_instance_data["nameID2"], 2, *name_record_plat_enc_lang)
89+
name_table.setName(name_instance_data["nameID3"], 3, *name_record_plat_enc_lang)
90+
name_table.setName(name_instance_data["nameID4"], 4, *name_record_plat_enc_lang)
91+
name_table.setName(name_instance_data["nameID6"], 6, *name_record_plat_enc_lang)
92+
93+
# optional writes
94+
# Approach:
95+
# (1) if user text data exists, write it
96+
# (2) if user text data does not exist but record does, delete it
97+
# (3) otherwise do nothing
98+
if name_instance_data["nameID16"] != "":
99+
name_table.setName(
100+
name_instance_data["nameID16"], 16, *name_record_plat_enc_lang
101+
)
102+
elif name_table.getName(16, *name_record_plat_enc_lang):
103+
name_table.removeNames(16, *name_record_plat_enc_lang)
104+
105+
if name_instance_data["nameID17"] != "":
106+
name_table.setName(
107+
name_instance_data["nameID17"], 17, *name_record_plat_enc_lang
108+
)
109+
elif name_table.getName(17, *name_record_plat_enc_lang):
110+
name_table.removeNames(17, *name_record_plat_enc_lang)
111+
112+
if name_instance_data["nameID21"] != "":
113+
name_table.setName(
114+
name_instance_data["nameID21"], 21, *name_record_plat_enc_lang
115+
)
116+
elif name_table.getName(21, *name_record_plat_enc_lang):
117+
name_table.removeNames(21, *name_record_plat_enc_lang)
118+
119+
if name_instance_data["nameID22"] != "":
120+
name_table.setName(
121+
name_instance_data["nameID22"], 22, *name_record_plat_enc_lang
122+
)
123+
elif name_table.getName(22, *name_record_plat_enc_lang):
124+
name_table.removeNames(22, *name_record_plat_enc_lang)
125+
126+
# update name table data
127+
self.ttfont["name"] = name_table
128+
129+
# print name table report
130+
print("\nNAME TABLE EDITS")
131+
print("Name records at write time:\n")
132+
print(f"nameID1: {self.ttfont['name'].getName(1, *name_record_plat_enc_lang)}")
133+
print(f"nameID2: {self.ttfont['name'].getName(2, *name_record_plat_enc_lang)}")
134+
print(f"nameID3: {self.ttfont['name'].getName(3, *name_record_plat_enc_lang)}")
135+
print(f"nameID4: {self.ttfont['name'].getName(4, *name_record_plat_enc_lang)}")
136+
print(f"nameID6: {self.ttfont['name'].getName(6, *name_record_plat_enc_lang)}")
137+
print(
138+
f"nameID16: {self.ttfont['name'].getName(16, *name_record_plat_enc_lang)}"
139+
)
140+
print(
141+
f"nameID17: {self.ttfont['name'].getName(17, *name_record_plat_enc_lang)}"
142+
)
143+
print(
144+
f"nameID21: {self.ttfont['name'].getName(21, *name_record_plat_enc_lang)}"
145+
)
146+
print(
147+
f"nameID22: {self.ttfont['name'].getName(22, *name_record_plat_enc_lang)}"
148+
)
149+
150+
def edit_bit_flags(self):
151+
# edit the OS/2.fsSelection bit flag
152+
pre_os2_fsselection_int = self.ttfont["OS/2"].fsSelection
153+
edited_os2_fsselection_int = self.bit_model.edit_os2_fsselection_bits(
154+
pre_os2_fsselection_int
155+
)
156+
# edit OS/2.fsSelection in the TTFont attribute
157+
self.ttfont["OS/2"].fsSelection = edited_os2_fsselection_int
158+
159+
# edit head.macstyle bit flag
160+
pre_head_macstyle_int = self.ttfont["head"].macStyle
161+
edited_head_macstyle_int = self.bit_model.edit_head_macstyle_bits(
162+
pre_head_macstyle_int
163+
)
164+
self.ttfont["head"].macStyle = edited_head_macstyle_int
165+
166+
# bit flag debugging stdout report
167+
print("\nBIT FLAGS")
168+
print(
169+
f"\nOS/2.fsSelection updated with the following data:\n"
170+
f"{self.bit_model.get_os2_instance_data()}"
171+
)
172+
print(f"Pre OS/2.fsSelection: {num2binary(pre_os2_fsselection_int, bits=16)}")
173+
print(
174+
f"Post OS/2.fsSelection: {num2binary(self.ttfont['OS/2'].fsSelection, bits=16)}"
175+
)
176+
print(
177+
f"\nhead.macStyle bit flag updated with the following data:\n"
178+
f"{self.bit_model.get_head_instance_data()}"
179+
)
180+
print(f"Pre head.macStyle: {num2binary(pre_head_macstyle_int, bits=16)}")
181+
print(
182+
f"Post head.macStyle: {num2binary(self.ttfont['head'].macStyle, bits=16)}"
183+
)

0 commit comments

Comments
 (0)