-
Notifications
You must be signed in to change notification settings - Fork 1
/
fontimport.py
executable file
·162 lines (137 loc) · 4.68 KB
/
fontimport.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
#!/usr/bin/env python3
import re, argparse, os, glob, sqlite3, tempfile, shutil, collections, sys
import proto.glyphs_pb2 as glyphs
parser = argparse.ArgumentParser(description='Import fonts and merge them into one collection of PBF glyphs in SQLite database')
parser.add_argument('fonts', type=str, nargs="*",
help='List of fonts in the order of preference (from the first to the last)')
parser.add_argument('--database', type=str, help='SQLite database where the fonts are inserted')
parser.add_argument('--directory', type=str, help='Output directory where font is written')
parser.add_argument('--fontname', type=str, help='Font name for the merged fonts. When not specified, the name of the first font is used')
#####################################################################
## Helper functions
def font(filename):
# from https://stackoverflow.com/a/199126
f = os.path.basename(re.sub(r"([a-z](?=[A-Z])|[A-Z](?=[A-Z][a-z]))", r"\1 ", filename.replace('-','')[:-4]))
return f
def get_glyph(glyph_id, gls):
for s in gls.stacks:
for g in s.glyphs:
if g.id == glyph_id:
return g
return None
def mkdir_p(path):
try:
os.makedirs(path)
except OSError as exc: # Python >2.5
if exc.errno == errno.EEXIST and os.path.isdir(path):
pass
else:
raise
############
### main ###
args = parser.parse_args()
if len(args.fonts) < 1:
print("No fonts specified for import")
sys.exit(-1)
if args.database is None and args.directory is None:
print("Please provide output directory or database")
sys.exit(-1)
if args.database is not None and args.directory is not None:
print("Please provide either output directory or database, not both")
sys.exit(-1)
if args.fontname is None:
args.fontname = font(args.fonts[0])
# check font list for doubles
flist = []
for f in args.fonts:
if f not in flist:
flist.append(f)
args.fonts = flist
# convert all to pbfs
fontdir = {}
ranges = set()
toclean = []
for f in args.fonts:
fontname = font(f)
print(f, fontname)
ft = tempfile.mkdtemp()
shutil.copy(f, ft)
d = tempfile.mkdtemp()
os.system("build_pbf_glyphs '%s' '%s'" % (ft, d))
shutil.rmtree(ft)
toclean.append(d)
fd = glob.glob(d + "/*")[0]
fontdir[f] = fd
for pbfname in glob.glob(fd + "/*pbf"):
ranges.add(os.path.basename(pbfname))
print("Fonts loaded")
# open the database
if args.database is not None:
conn = sqlite3.connect(args.database)
c = conn.cursor()
c.execute("CREATE TABLE IF NOT EXISTS fonts(stack TEXT NOT NULL, range TEXT NOT NULL, pbf BLOB, unique(stack,range))")
database_output = True
else:
mkdir_p(os.path.join(args.directory, args.fontname))
database_output = False
# iterate through ranges and merge fonts
ranges = list(ranges)
ranges.sort()
stats = collections.defaultdict(int)
for R in ranges:
fonts = {}
# load all fonts
for f in args.fonts:
fname = os.path.join(fontdir[f], R)
if os.path.exists(fname):
with open(fname, 'rb') as fin:
g = glyphs.glyphs()
g.ParseFromString(fin.read())
fonts[f] = g
# find available ids
ids = set()
for f in args.fonts:
for s in fonts[f].stacks:
for g in s.glyphs:
ids.add(g.id)
ids = list(ids)
ids.sort()
# merge fonts
merged = glyphs.glyphs()
stack = merged.stacks.add()
stack.name = args.fontname
stack.range = R[:-4]
# write only if there is at least one glyph in the range
if len(ids) > 0:
for glyph_id in ids:
g = None
i = 0
while g is None:
cf = args.fonts[i]
g = get_glyph(glyph_id, fonts[cf])
i += 1
ng = stack.glyphs.add()
ng.ParseFromString(g.SerializeToString())
stats[cf] += 1
# write merged font to db
if database_output:
c.execute("INSERT OR REPLACE INTO fonts(stack,range,pbf) VALUES(?,?,?)",
(args.fontname, R[:-4], memoryview(merged.SerializeToString())))
else:
fname = os.path.join(args.directory, args.fontname, R)
with open(fname, 'wb') as f:
f.write(memoryview(merged.SerializeToString()))
print("Range: %s ; Glyphs written: %d" % (R[:-4], len(ids)))
# index database and close it
if database_output:
c.execute("CREATE INDEX IF NOT EXISTS idx_fonts ON fonts(stack,range)")
conn.commit()
conn.close()
# cleanup
for d in toclean:
shutil.rmtree(d)
# stats
print("\nUsed glyphs:")
for f in args.fonts:
print(f, '\t', stats[f])
print()