Skip to content

Commit c2a7953

Browse files
Merge pull request #58 from source-foundry/cvar-removal-vf
Remove cvar tables from variable fonts
2 parents 1c32a81 + fa72d69 commit c2a7953

File tree

9 files changed

+271
-45
lines changed

9 files changed

+271
-45
lines changed

Makefile

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,11 @@ all: install
33
black:
44
black lib/dehinter/*.py
55

6+
import-sort:
7+
isort lib/dehinter/*.py
8+
9+
format: import-sort black
10+
611
clean:
712
- rm dist/*.whl dist/*.tar.gz dist/*.zip
813

lib/dehinter/__main__.py

Lines changed: 47 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -21,28 +21,30 @@
2121
from fontTools.ttLib import TTFont # type: ignore
2222

2323
from dehinter import __version__
24-
from dehinter.font import is_truetype_font
2524
from dehinter.font import (
25+
has_cvar_table,
2626
has_cvt_table,
2727
has_fpgm_table,
28-
has_gasp_table,
2928
has_hdmx_table,
3029
has_ltsh_table,
3130
has_prep_table,
3231
has_ttfa_table,
3332
has_vdmx_table,
34-
)
35-
from dehinter.font import (
33+
is_truetype_font,
34+
is_variable_font,
35+
remove_cvar_table,
3636
remove_cvt_table,
3737
remove_fpgm_table,
38+
remove_glyf_instructions,
3839
remove_hdmx_table,
3940
remove_ltsh_table,
4041
remove_prep_table,
4142
remove_ttfa_table,
4243
remove_vdmx_table,
43-
remove_glyf_instructions,
44+
update_gasp_table,
45+
update_head_table_flags,
46+
update_maxp_table,
4447
)
45-
from dehinter.font import update_gasp_table, update_head_table_flags, update_maxp_table
4648
from dehinter.paths import filepath_exists, get_default_out_path
4749
from dehinter.system import get_filesize
4850

@@ -65,6 +67,7 @@ def run(argv: List[str]) -> None:
6567
"--version", action="version", version="dehinter v{}".format(__version__)
6668
)
6769
parser.add_argument("-o", "--out", help="out file path (dehinted font)")
70+
parser.add_argument("--keep-cvar", help="keep cvar table", action="store_true")
6871
parser.add_argument("--keep-cvt", help="keep cvt table", action="store_true")
6972
parser.add_argument("--keep-fpgm", help="keep fpgm table", action="store_true")
7073
parser.add_argument("--keep-hdmx", help="keep hdmx table", action="store_true")
@@ -129,61 +132,85 @@ def run(argv: List[str]) -> None:
129132
)
130133
sys.exit(1)
131134

135+
if is_variable_font(tt) and not args.keep_cvar:
136+
if has_cvar_table(tt):
137+
remove_cvar_table(tt)
138+
if not has_cvar_table(tt):
139+
print("[-] Removed cvar table")
140+
else: # pragma: no cover
141+
sys.stderr.write(
142+
f"[!] Error: failed to remove cvar table from font{os.linesep}"
143+
)
144+
132145
if not args.keep_cvt:
133146
if has_cvt_table(tt):
134147
remove_cvt_table(tt)
135148
if not has_cvt_table(tt):
136149
print("[-] Removed cvt table")
137150
else: # pragma: no cover
138-
sys.stderr.write("[!] Error: failed to remove cvt table from font")
151+
sys.stderr.write(
152+
f"[!] Error: failed to remove cvt table from font{os.linesep}"
153+
)
139154

140155
if not args.keep_fpgm:
141156
if has_fpgm_table(tt):
142157
remove_fpgm_table(tt)
143158
if not has_fpgm_table(tt):
144159
print("[-] Removed fpgm table")
145160
else: # pragma: no cover
146-
sys.stderr.write("[!] Error: failed to remove fpgm table from font")
161+
sys.stderr.write(
162+
f"[!] Error: failed to remove fpgm table from font{os.linesep}"
163+
)
147164

148165
if not args.keep_hdmx:
149166
if has_hdmx_table(tt):
150167
remove_hdmx_table(tt)
151168
if not has_hdmx_table(tt):
152169
print("[-] Removed hdmx table")
153170
else: # pragma: no cover
154-
sys.stderr.write("[!] Error: failed to remove hdmx table from font")
171+
sys.stderr.write(
172+
f"[!] Error: failed to remove hdmx table from font{os.linesep}"
173+
)
155174

156175
if not args.keep_ltsh:
157176
if has_ltsh_table(tt):
158177
remove_ltsh_table(tt)
159178
if not has_ltsh_table(tt):
160179
print("[-] Removed LTSH table")
161180
else: # pragma: no cover
162-
sys.stderr.write("[!] Error: failed to remove LTSH table from font")
181+
sys.stderr.write(
182+
f"[!] Error: failed to remove LTSH table from font{os.linesep}"
183+
)
163184

164185
if not args.keep_prep:
165186
if has_prep_table(tt):
166187
remove_prep_table(tt)
167188
if not has_prep_table(tt):
168189
print("[-] Removed prep table")
169190
else: # pragma: no cover
170-
sys.stderr.write("[!] Error: failed to remove prep table from font")
191+
sys.stderr.write(
192+
f"[!] Error: failed to remove prep table from font{os.linesep}"
193+
)
171194

172195
if not args.keep_ttfa:
173196
if has_ttfa_table(tt):
174197
remove_ttfa_table(tt)
175198
if not has_ttfa_table(tt):
176199
print("[-] Removed TTFA table")
177200
else: # pragma: no cover
178-
sys.stderr.write("[!] Error: failed to remove TTFA table from font")
201+
sys.stderr.write(
202+
f"[!] Error: failed to remove TTFA table from font{os.linesep}"
203+
)
179204

180205
if not args.keep_vdmx:
181206
if has_vdmx_table(tt):
182207
remove_vdmx_table(tt)
183208
if not has_vdmx_table(tt):
184209
print("[-] Removed VDMX table")
185210
else: # pragma: no cover
186-
sys.stderr.write("[!] Error: failed to remove VDMX table from font")
211+
sys.stderr.write(
212+
f"[!] Error: failed to remove VDMX table from font{os.linesep}"
213+
)
187214

188215
# (2) Remove glyf table instruction set bytecode
189216
if not args.keep_glyf:
@@ -196,10 +223,9 @@ def run(argv: List[str]) -> None:
196223

197224
# (3) Edit gasp table
198225
if not args.keep_gasp:
199-
if has_gasp_table(tt):
200-
if update_gasp_table(tt):
201-
gasp_string = pp.pformat(tt["gasp"].__dict__)
202-
print(f"[Δ] New gasp table values:{os.linesep} {gasp_string}")
226+
if update_gasp_table(tt):
227+
gasp_string = pp.pformat(tt["gasp"].__dict__)
228+
print(f"[Δ] New gasp table values:{os.linesep} {gasp_string}")
203229

204230
# (4) Edit maxp table
205231
if not args.keep_maxp:
@@ -224,9 +250,11 @@ def run(argv: List[str]) -> None:
224250

225251
try:
226252
tt.save(outpath)
227-
print("{os.linesep}[+] Saved dehinted font as '{outpath}'")
253+
print(f"{os.linesep}[+] Saved dehinted font as '{outpath}'")
228254
except Exception as e: # pragma: no cover
229-
sys.stderr.write(f"[!] Error: Unable to save dehinted font file: {str(e)}")
255+
sys.stderr.write(
256+
f"[!] Error: Unable to save dehinted font file: {str(e)}{os.linesep}"
257+
)
230258

231259
# File size comparison
232260
# --------------------

lib/dehinter/font.py

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,12 +16,19 @@
1616
import os
1717
from typing import Union
1818

19-
from dehinter.bitops import is_bit_k_set, clear_bit_k
19+
from fontTools import ttLib # type: ignore
20+
21+
from dehinter.bitops import clear_bit_k, is_bit_k_set
2022

2123

2224
# ========================================================
2325
# Utilities
2426
# ========================================================
27+
def has_cvar_table(tt) -> bool:
28+
"""Tests for the presence of a cvat table in a TrueType variable font."""
29+
return "cvar" in tt
30+
31+
2532
def has_cvt_table(tt) -> bool:
2633
"""Tests for the presence of a cvt table in a TrueType font."""
2734
return "cvt " in tt
@@ -72,9 +79,24 @@ def is_truetype_font(filepath: Union[bytes, str, "os.PathLike[str]"]) -> bool:
7279
return file_signature in (b"\x00\x01\x00\x00", b"\x74\x72\x75\x65")
7380

7481

82+
def is_variable_font(tt) -> bool:
83+
"""Tests for the presence of a fvar table to confirm that a file is
84+
a variable font."""
85+
return "fvar" in tt
86+
87+
7588
# ========================================================
7689
# OpenType table removal
7790
# ========================================================
91+
def remove_cvar_table(tt) -> None:
92+
"""Removes cvt table from a fontTools.ttLib.TTFont object"""
93+
try:
94+
del tt["cvar"]
95+
except KeyError:
96+
# do nothing if table is not present in the font
97+
pass
98+
99+
78100
def remove_cvt_table(tt) -> None:
79101
"""Removes cvt table from a fontTools.ttLib.TTFont object"""
80102
try:
@@ -163,6 +185,9 @@ def update_gasp_table(tt) -> bool:
163185
"""Modifies the following gasp table fields:
164186
1) rangeMaxPPEM changed to 65535
165187
2) rangeGaspBehavior changed to 0x000a (symmetric grayscale, no gridfit)"""
188+
if "gasp" not in tt:
189+
tt["gasp"] = ttLib.newTable("gasp")
190+
tt["gasp"].gaspRange = {}
166191
if tt["gasp"].gaspRange != {65535: 0x000A}:
167192
tt["gasp"].gaspRange = {65535: 0x000A}
168193
return True
520 KB
Binary file not shown.

tests/test_files/fonts/README.md

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,16 +2,19 @@
22

33
The fonts used in the testing of this project include the following:
44

5-
1. Roboto-Regular.ttf
6-
2. Roboto-Regular-dehinted.ttf (ttfautohint dehinted derivative of Roboto-Regular.ttf)
7-
3. NotoSans-Regular.ttf
8-
4. NotoSans-Regular-dehinted.ttf (ttfautohint dehinted derivative of NotoSans-Regular.ttf)
9-
5. Ubuntu-Regular.ttf
5+
1. OpenSans-VF.ttf
6+
2. Roboto-Regular.ttf
7+
3. Roboto-Regular-dehinted.ttf (ttfautohint dehinted derivative of Roboto-Regular.ttf)
8+
4. NotoSans-Regular.ttf
9+
5. NotoSans-Regular-dehinted.ttf (ttfautohint dehinted derivative of NotoSans-Regular.ttf)
10+
6. Ubuntu-Regular.ttf
11+
12+
Open Sans is licensed under the [SIL Open Font License 1.1](https://github.com/googlefonts/opensans/blob/main/OFL.txt)
1013

1114
Roboto is licensed under the [Apache License v2.0](https://github.com/google/roboto/blob/master/LICENSE).
1215

1316
Noto Sans is licensed under the [SIL Open Font License 1.1](https://github.com/googlefonts/noto-fonts/blob/master/LICENSE).
1417

1518
Ubuntu is licensed under the [Ubuntu Font License](https://ubuntu.com/legal/font-licence).
1619

17-
You may find the full text of these licenses in the `licenses` subdirectory.
20+
You may find the full text of these licenses in the `licenses` subdirectory.
Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
Copyright 2020 The Open Sans Project Authors (https://github.com/googlefonts/opensans)
2+
3+
-----------------------------------------------------------
4+
SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
5+
-----------------------------------------------------------
6+
7+
PREAMBLE
8+
The goals of the Open Font License (OFL) are to stimulate worldwide
9+
development of collaborative font projects, to support the font
10+
creation efforts of academic and linguistic communities, and to
11+
provide a free and open framework in which fonts may be shared and
12+
improved in partnership with others.
13+
14+
The OFL allows the licensed fonts to be used, studied, modified and
15+
redistributed freely as long as they are not sold by themselves. The
16+
fonts, including any derivative works, can be bundled, embedded,
17+
redistributed and/or sold with any software provided that any reserved
18+
names are not used by derivative works. The fonts and derivatives,
19+
however, cannot be released under any other type of license. The
20+
requirement for fonts to remain under this license does not apply to
21+
any document created using the fonts or their derivatives.
22+
23+
DEFINITIONS
24+
"Font Software" refers to the set of files released by the Copyright
25+
Holder(s) under this license and clearly marked as such. This may
26+
include source files, build scripts and documentation.
27+
28+
"Reserved Font Name" refers to any names specified as such after the
29+
copyright statement(s).
30+
31+
"Original Version" refers to the collection of Font Software
32+
components as distributed by the Copyright Holder(s).
33+
34+
"Modified Version" refers to any derivative made by adding to,
35+
deleting, or substituting -- in part or in whole -- any of the
36+
components of the Original Version, by changing formats or by porting
37+
the Font Software to a new environment.
38+
39+
"Author" refers to any designer, engineer, programmer, technical
40+
writer or other person who contributed to the Font Software.
41+
42+
PERMISSION & CONDITIONS
43+
Permission is hereby granted, free of charge, to any person obtaining
44+
a copy of the Font Software, to use, study, copy, merge, embed,
45+
modify, redistribute, and sell modified and unmodified copies of the
46+
Font Software, subject to the following conditions:
47+
48+
1) Neither the Font Software nor any of its individual components, in
49+
Original or Modified Versions, may be sold by itself.
50+
51+
2) Original or Modified Versions of the Font Software may be bundled,
52+
redistributed and/or sold with any software, provided that each copy
53+
contains the above copyright notice and this license. These can be
54+
included either as stand-alone text files, human-readable headers or
55+
in the appropriate machine-readable metadata fields within text or
56+
binary files as long as those fields can be easily viewed by the user.
57+
58+
3) No Modified Version of the Font Software may use the Reserved Font
59+
Name(s) unless explicit written permission is granted by the
60+
corresponding Copyright Holder. This restriction only applies to the
61+
primary font name as presented to the users.
62+
63+
4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
64+
Software shall not be used to promote, endorse or advertise any
65+
Modified Version, except to acknowledge the contribution(s) of the
66+
Copyright Holder(s) and the Author(s) or with their explicit written
67+
permission.
68+
69+
5) The Font Software, modified or unmodified, in part or in whole,
70+
must be distributed entirely under this license, and must not be
71+
distributed under any other license. The requirement for fonts to
72+
remain under this license does not apply to any document created using
73+
the Font Software.
74+
75+
TERMINATION
76+
This license becomes null and void if any of the above conditions are
77+
not met.
78+
79+
DISCLAIMER
80+
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
81+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
82+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
83+
OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
84+
COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
85+
INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
86+
DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
87+
FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
88+
OTHER DEALINGS IN THE FONT SOFTWARE.

0 commit comments

Comments
 (0)