-
Notifications
You must be signed in to change notification settings - Fork 0
/
bdf-to-passport.py
executable file
·217 lines (168 loc) · 7.26 KB
/
bdf-to-passport.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
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
#!/usr/bin/env python3
import sys
from bdflib import reader
codepoint_ranges = [(32, 127), (177, 182), (215, 216)]
total_range = (32, 216)
def read_bdf_font(filename):
with open(filename, "rb") as handle:
font = reader.read_bdf(handle)
return font
def write_header(output_file):
output_file.write('''# Passport wallet font definitions
# Autogenerated by bdf-to-passport.py: DO NOT EDIT
from ucollections import namedtuple
GlyphInfo = namedtuple('GlyphInfo', 'x y w h advance bits')
# Lookup GlyphInfo for a single codepoint or return None
def lookup(font, cp):
for r,d in font.codepoints:
if cp not in r:
continue
ptr = d[cp-r.start]
if not ptr:
return None
x, y, w, h, advance, dlen = font.bboxes[font.bitmaps[ptr]]
bits = font.bitmaps[ptr+1:ptr+1+dlen]
return GlyphInfo(x, y, w, h, advance, bits)
return None
class FontBase:
pass
''')
def print_font_properties(font):
for prop in font.property_names():
print("{} = {}".format(prop, font[prop]))
print("Num codepoints = {}".format(len(font.codepoints())))
for codepoint in font.codepoints():
print(str(codepoint) + ", ", end="")
# Print codepoint 65 as hex bytes
letter_A = font[65]
print(letter_A)
print("[", end="")
for b in letter_A.data:
print("{0:#{1}x},".format(b, 2), end="")
print("]")
print("bbox: x={} y={} w={} h={} advance={}".format(letter_A.bbX, letter_A.bbY, letter_A.bbW, letter_A.bbH, letter_A.advance))
print("dir(letter_A)=" + str(dir(letter_A)))
print("\nDONE")
def split_str_into_groups_of_n(s, n):
return [s[i:i+n] for i in range(0, len(s), n)]
# list of integers - oddly, these values have been shifted by the bbox X value
def format_row_data(row_data):
return "xxx"
# row_data is a list of ASCII-encoded numbers
print("row_data={}".format(row_data))
result = "{:x}".format(row_data)
if len(result) % 2 == 1:
result = "0{}".format(result)
print(str(result))
# Split every 2 characters
arr = split_str_into_groups_of_n(result, 2)
arr = [ascii_to_hex(i) for i in arr]
result = ""
for s in arr:
result += "\\x{}".format(s)
return result
def export_font(font, font_name, outfile):
# print_font_properties(font)
# available keys = [b'FACE_NAME', b'POINT_SIZE', b'RESOLUTION_X', b'RESOLUTION_Y', b'FONTNAME_REGISTRY', b'FOUNDRY', b'FAMILY_NAME',
# b'WEIGHT_NAME', b'SLANT', b'SETWIDTH_NAME', b'ADD_STYLE_NAME', b'SPACING', b'AVERAGE_WIDTH', b'CHARSET_REGISTRY',
# b'CHARSET_ENCODING', b'FONT_ASCENT', b'FONT_DESCENT', b'DEFAULT_CHAR', b'COPYRIGHT', b'X_HEIGHT', b'CAP_HEIGHT']
outfile.write("class {}(FontBase):\n".format(font_name))
outfile.write(" height = {}\n".format(int(font[b"POINT_SIZE"])))
try:
outfile.write(" advance = {}\n".format(int(font[b"AVERAGE_WIDTH"] // 10)))
except:
advance = font[ord('M')].advance
outfile.write(" advance = {}\n".format(advance))
outfile.write(" ascent = {}\n".format(int(font[b"FONT_ASCENT"])))
outfile.write(" descent = {}\n".format(int(font[b"FONT_DESCENT"])))
# Standard leading is 25%
outfile.write(" leading = {}\n".format(int(( (font[b"FONT_ASCENT"] + font[b"FONT_DESCENT"]) * 1.25 + 0.5)))) # Round up for leading
outfile.write(" code_range = range({}, {})\n\n".format(total_range[0], total_range[1]))
# Process the codepoints one by one
bbox_dict = {} # (x, y, w, h, data_len): offset
next_bbox_offset = 1
bitmaps = ""
codepoints = ""
bitmap_offset = 1
included_codepoints = font.codepoints()
for r in codepoint_ranges:
codepoint_bitmap_offsets = [] # One offset for each codepoint
for codepoint in range(r[0], r[1]):
info = None
try:
info = font[codepoint]
except:
info = font[ord(' ')]
data = info.get_data() # Returns data as a list of arrays of ints, each of which represents a hex digit
# data = info.data # Return an array of integers, which can be arbitrary size
num_rows = len(data)
bytes_per_row = len(data[0]) // 2 if data != None and len(data) > 0 else 0
data_len = num_rows * bytes_per_row
x = info.bbX
y = info.bbY
w = info.bbW
h = info.bbH
advance = info.advance
bbox = (x, y, w, h, advance, data_len)
bbox_offset = bbox_dict.get(bbox)
if bbox_offset == None:
# We haven't see this bbox shape before, so add it to the dict
bbox_dict.update({bbox:next_bbox_offset})
bbox_offset = next_bbox_offset
next_bbox_offset += 1
# Add bitmap offset to the codepoints range array
codepoint_bitmap_offsets.append(bitmap_offset)
# First byte in each codepoint entry is the offset to the bbox entry
bitmaps += "0x{:02x}, # {} offset = {}\n ".format(bbox_offset, info.name.decode('utf-8'), bitmap_offset)
bitmap_offset += 1 + data_len # Add 1 because of the single-byte offset before the bitmap data that points to the bbox info
# Then the bitmap bytes
for b in data:
s = "{}".format(b.decode('utf-8'))
parts = split_str_into_groups_of_n(s, 2)
for b in parts:
bitmaps += "0x{},".format(b)
bitmaps += "\n\n "
# Add entries to the out_codepoints
codepoints += " (range({},{}), [".format(r[0], r[1])
num_codepoints_written = 0
for offset in codepoint_bitmap_offsets:
codepoints += "{},".format(offset)
num_codepoints_written += 1
if num_codepoints_written == 16:
codepoints += "\n "
num_codepoints_written = 0
codepoints += "]),\n"
# Output bounding boxes - map over the dict and sort in order of the mapped-to value
outfile.write(" bboxes = [None,\n")
bboxes = list(bbox_dict.items())
bboxes.sort(key = lambda x: x[1])
bboxes = [str(b[0]) for b in bboxes]
for bbox in bboxes:
outfile.write(" " + bbox + ",\n")
outfile.write(" ]\n\n")
# Output code_points
outfile.write(" codepoints = [\n")
for cp in codepoints:
outfile.write(cp)
outfile.write(" ]\n\n")
# Output bitmaps
outfile.write(' bitmaps = bytes([\n 0xAA, # Dummy first entry\n\n')
outfile.write(' ' + bitmaps + "])\n\n")
def main():
num_fonts = len(sys.argv) - 1
if num_fonts < 1:
print("Usage: bdf-to-passport <FontName>=<FontFilename> ...")
print(" You must provide at least one font filename")
sys.exit()
# Hardcoded output file
outfile = open("passport_fonts.py", "w")
write_header(outfile)
for i in range(1, num_fonts + 1):
parts = sys.argv[i].split('=')
font_name = parts[0]
font_filename = parts[1]
print("Processing {} from file {}...".format(font_name, font_filename))
font = read_bdf_font(font_filename)
export_font(font, font_name, outfile)
if __name__ == '__main__':
main()