1
+ # The MIT License (MIT)
2
+ #
3
+ # Copyright © 2020 Jeff Epler for Adafruit Industries LLC
4
+ #
5
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ # of this software and associated documentation files (the "Software"), to deal
7
+ # in the Software without restriction, including without limitation the rights
8
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ # copies of the Software, and to permit persons to whom the Software is
10
+ # furnished to do so, subject to the following conditions:
11
+ #
12
+ # The above copyright notice and this permission notice shall be included in
13
+ # all copies or substantial portions of the Software.
14
+ #
15
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ # THE SOFTWARE.
22
+ """
23
+ `adafruit_bitmap_font.pcf`
24
+ ====================================================
25
+
26
+ Loads PCF format fonts.
27
+
28
+ * Author(s): Jeff Epler
29
+
30
+ Implementation Notes
31
+ --------------------
32
+
33
+ **Hardware:**
34
+
35
+ **Software and Dependencies:**
36
+
37
+ * Adafruit CircuitPython firmware for the supported boards:
38
+ https://github.com/adafruit/circuitpython/releases
39
+
40
+ """
41
+
1
42
from collections import namedtuple
2
43
import gc
44
+ import struct
3
45
4
- from .glyph_cache import GlyphCache
5
46
from fontio import Glyph
6
- import displayio
7
- import struct
47
+ from .glyph_cache import GlyphCache
8
48
9
49
_PCF_PROPERTIES = 1 << 0
10
50
_PCF_ACCELERATORS = 1 << 1
66
106
67
107
68
108
class PCF (GlyphCache ):
109
+ """Loads glyphs from a PCF file in the given bitmap_class."""
110
+
69
111
def __init__ (self , f , bitmap_class ):
70
112
super ().__init__ ()
71
113
self .file = f
72
114
self .name = f
73
115
f .seek (0 )
74
116
self .buffer = bytearray (1 )
75
117
self .bitmap_class = bitmap_class
76
- header , table_count = self .read ("<4sI" )
118
+ _ , table_count = self ._read ("<4sI" )
77
119
self .tables = {}
78
120
for _ in range (table_count ):
79
- type , format , size , offset = self .read ("<IIII" )
80
- self .tables [type ] = Table (format , size , offset )
121
+ type_ , format_ , size , offset = self ._read ("<IIII" )
122
+ self .tables [type_ ] = Table (format_ , size , offset )
81
123
82
124
bitmap_format = self .tables [_PCF_BITMAPS ].format
83
125
if bitmap_format != 0xE :
84
- raise NotImplementedError (f"Unsupported format { bitmap_format :x} " )
126
+ raise NotImplementedError (f"Unsupported format_ { bitmap_format :x} " )
85
127
86
- self ._accel = self .read_accelerator_tables ()
87
- self ._encoding = self .read_encoding_table ()
88
- self ._bitmaps = self .read_bitmap_table ()
128
+ self ._accel = self ._read_accelerator_tables ()
129
+ self ._encoding = self ._read_encoding_table ()
130
+ self ._bitmaps = self ._read_bitmap_table ()
89
131
90
- self .ascent = self ._accel .font_ascent
91
- self .descent = self ._accel .font_descent
132
+ self ._ascent = self ._accel .font_ascent
133
+ self ._descent = self ._accel .font_descent
92
134
93
135
minbounds = self ._accel .ink_minbounds
94
136
maxbounds = self ._accel .ink_maxbounds
@@ -102,49 +144,60 @@ def __init__(self, f, bitmap_class):
102
144
- maxbounds .character_descent ,
103
145
)
104
146
147
+ @property
148
+ def ascent (self ):
149
+ """The number of pixels above the baseline of a typical ascender"""
150
+ return self ._ascent
151
+
152
+ @property
153
+ def descent (self ):
154
+ """The number of pixels below the baseline of a typical descender"""
155
+ return self ._descent
156
+
105
157
def get_bounding_box (self ):
158
+ """Return the maximum glyph size as a 4-tuple of: width, height, x_offset, y_offset"""
106
159
return self ._bounding_box
107
160
108
- def read (self , format ):
109
- s = struct .calcsize (format )
110
- if s != len (self .buffer ):
111
- self .buffer = bytearray (s )
161
+ def _read (self , format_ ):
162
+ size = struct .calcsize (format_ )
163
+ if size != len (self .buffer ):
164
+ self .buffer = bytearray (size )
112
165
self .file .readinto (self .buffer )
113
- return struct .unpack_from (format , self .buffer )
166
+ return struct .unpack_from (format_ , self .buffer )
114
167
115
- def seek_table (self , table ):
168
+ def _seek_table (self , table ):
116
169
self .file .seek (table .offset )
117
- (format ,) = self .read ("<I" )
170
+ (format_ ,) = self ._read ("<I" )
118
171
119
- if format & _PCF_BYTE_MASK == 0 :
172
+ if format_ & _PCF_BYTE_MASK == 0 :
120
173
raise RuntimeError ("Only big endian supported" )
121
174
122
- return format
175
+ return format_
123
176
124
- def read_encoding_table (self ):
177
+ def _read_encoding_table (self ):
125
178
encoding = self .tables [_PCF_BDF_ENCODINGS ]
126
- self .seek_table (encoding )
179
+ self ._seek_table (encoding )
127
180
128
- return Encoding (* self .read (">hhhhh" ))
181
+ return Encoding (* self ._read (">hhhhh" ))
129
182
130
- def read_bitmap_table (self ):
183
+ def _read_bitmap_table (self ):
131
184
bitmaps = self .tables [_PCF_BITMAPS ]
132
- format = self .seek_table (bitmaps )
185
+ format_ = self ._seek_table (bitmaps )
133
186
134
- (glyph_count ,) = self .read (">I" )
187
+ (glyph_count ,) = self ._read (">I" )
135
188
self .file .seek (bitmaps .offset + 8 + 4 * glyph_count )
136
- bitmap_sizes = self .read (">4I" )
137
- return Bitmap (glyph_count , bitmap_sizes [format & 3 ])
189
+ bitmap_sizes = self ._read (">4I" )
190
+ return Bitmap (glyph_count , bitmap_sizes [format_ & 3 ])
138
191
139
- def read_metrics (self , compressed_metrics ):
192
+ def _read_metrics (self , compressed_metrics ):
140
193
if compressed_metrics :
141
194
(
142
195
left_side_bearing ,
143
196
right_side_bearing ,
144
197
character_width ,
145
198
character_ascent ,
146
199
character_descent ,
147
- ) = self .read ("5B" )
200
+ ) = self ._read ("5B" )
148
201
left_side_bearing -= 0x80
149
202
right_side_bearing -= 0x80
150
203
character_width -= 0x80
@@ -159,7 +212,7 @@ def read_metrics(self, compressed_metrics):
159
212
character_ascent ,
160
213
character_descent ,
161
214
attributes ,
162
- ) = self .read (">5hH" )
215
+ ) = self ._read (">5hH" )
163
216
return Metrics (
164
217
left_side_bearing ,
165
218
right_side_bearing ,
@@ -169,17 +222,18 @@ def read_metrics(self, compressed_metrics):
169
222
attributes ,
170
223
)
171
224
172
- def read_accelerator_tables (self ):
225
+ def _read_accelerator_tables (self ):
226
+ # pylint: disable=too-many-locals
173
227
accelerators = self .tables .get (_PCF_BDF_ACCELERATORS )
174
228
if not accelerators :
175
229
accelerators = self .tables .get (_PCF_ACCELERATORS )
176
230
if not accelerators :
177
231
raise RuntimeError ("Accelerator table missing" )
178
232
179
- format = self .seek_table (accelerators )
233
+ format_ = self ._seek_table (accelerators )
180
234
181
- has_inkbounds = format & _PCF_ACCEL_W_INKBOUNDS
182
- compressed_metrics = False # format & _PCF_COMPRESSED_METRICS
235
+ has_inkbounds = format_ & _PCF_ACCEL_W_INKBOUNDS
236
+ compressed_metrics = False # format_ & _PCF_COMPRESSED_METRICS
183
237
184
238
(
185
239
no_overlap ,
@@ -193,12 +247,12 @@ def read_accelerator_tables(self):
193
247
font_ascent ,
194
248
font_descent ,
195
249
max_overlap ,
196
- ) = self .read (">BBBBBBBBIII" )
197
- minbounds = self .read_metrics (compressed_metrics )
198
- maxbounds = self .read_metrics (compressed_metrics )
250
+ ) = self ._read (">BBBBBBBBIII" )
251
+ minbounds = self ._read_metrics (compressed_metrics )
252
+ maxbounds = self ._read_metrics (compressed_metrics )
199
253
if has_inkbounds :
200
- ink_minbounds = self .read_metrics (compressed_metrics )
201
- ink_maxbounds = self .read_metrics (compressed_metrics )
254
+ ink_minbounds = self ._read_metrics (compressed_metrics )
255
+ ink_maxbounds = self ._read_metrics (compressed_metrics )
202
256
else :
203
257
ink_minbounds = minbounds
204
258
ink_maxbounds = maxbounds
@@ -220,33 +274,33 @@ def read_accelerator_tables(self):
220
274
ink_maxbounds ,
221
275
)
222
276
223
- def read_properties (self ):
277
+ def _read_properties (self ):
224
278
property_table_offset = self .tables [_PCF_PROPERTIES ]["offset" ]
225
279
self .file .seek (property_table_offset )
226
- (format ,) = self .read ("<I" )
280
+ (format_ ,) = self ._read ("<I" )
227
281
228
- if format & _PCF_BYTE_MASK == 0 :
282
+ if format_ & _PCF_BYTE_MASK == 0 :
229
283
raise RuntimeError ("Only big endian supported" )
230
- (nprops ,) = self .read (">I" )
284
+ (nprops ,) = self ._read (">I" )
231
285
self .file .seek (property_table_offset + 8 + 9 * nprops )
232
286
233
287
pos = self .file .tell ()
234
288
if pos % 4 > 0 :
235
289
self .file .read (4 - pos % 4 )
236
- (string_size ,) = self .read (">I" )
290
+ (string_size ,) = self ._read (">I" )
237
291
238
292
strings = self .file .read (string_size )
239
293
string_map = {}
240
294
i = 0
241
- for s in strings .split (b"\x00 " ):
242
- string_map [i ] = s
243
- i += len (s ) + 1
295
+ for value in strings .split (b"\x00 " ):
296
+ string_map [i ] = value
297
+ i += len (value ) + 1
244
298
245
299
self .file .seek (property_table_offset + 8 )
246
300
for _ in range (nprops ):
247
- name_offset , isStringProp , value = self .read (">IBI" )
301
+ name_offset , is_string_prop , value = self ._read (">IBI" )
248
302
249
- if isStringProp :
303
+ if is_string_prop :
250
304
yield (string_map [name_offset ], string_map [value ])
251
305
else :
252
306
yield (string_map [name_offset ], value )
@@ -295,7 +349,7 @@ def load_glyphs(self, code_points):
295
349
- self ._encoding .min_byte2
296
350
)
297
351
self .file .seek (indices_offset + 2 * encoding_idx )
298
- (glyph_idx ,) = self .read (">H" )
352
+ (glyph_idx ,) = self ._read (">H" )
299
353
if glyph_idx != 65535 :
300
354
indices [i ] = glyph_idx
301
355
@@ -305,24 +359,23 @@ def load_glyphs(self, code_points):
305
359
if index is None :
306
360
continue
307
361
self .file .seek (first_metric_offset + metrics_size * index )
308
- all_metrics [i ] = self .read_metrics (metrics_compressed )
362
+ all_metrics [i ] = self ._read_metrics (metrics_compressed )
309
363
bitmap_offsets = [None ] * len (code_points )
310
364
for i , code_point in enumerate (code_points ):
311
365
index = indices [i ]
312
366
if index is None :
313
367
continue
314
368
self .file .seek (bitmap_offset_offsets + 4 * index )
315
- (bitmap_offset ,) = self .read (">I" )
369
+ (bitmap_offset ,) = self ._read (">I" )
316
370
bitmap_offsets [i ] = bitmap_offset
317
371
318
372
# Batch creation of glyphs and bitmaps so that we need only gc.collect
319
373
# once
320
374
gc .collect ()
321
375
bitmaps = [None ] * len (code_points )
322
- for i in range (len (all_metrics )):
376
+ for i in range (len (all_metrics )): # pylint: disable=consider-using-enumerate
323
377
metrics = all_metrics [i ]
324
378
if metrics is not None :
325
- shift = metrics .character_width
326
379
width = metrics .right_side_bearing - metrics .left_side_bearing
327
380
height = metrics .character_ascent + metrics .character_descent
328
381
bitmap = bitmaps [i ] = self .bitmap_class (width , height , 2 )
@@ -342,17 +395,16 @@ def load_glyphs(self, code_points):
342
395
if metrics is None :
343
396
continue
344
397
self .file .seek (first_bitmap_offset + bitmap_offsets [i ])
345
- shift = metrics .character_width
346
398
width = metrics .right_side_bearing - metrics .left_side_bearing
347
399
height = metrics .character_ascent + metrics .character_descent
348
400
349
401
bitmap = bitmaps [i ]
350
402
words_per_row = (width + 31 ) // 32
351
403
buf = bytearray (4 * words_per_row )
352
404
start = 0
353
- for i in range (height ):
405
+ for _ in range (height ):
354
406
self .file .readinto (buf )
355
- for j in range (width ):
356
- if buf [j // 8 ] & (128 >> (j % 8 )):
357
- bitmap [start + j ] = 1
407
+ for k in range (width ):
408
+ if buf [k // 8 ] & (128 >> (k % 8 )):
409
+ bitmap [start + k ] = 1
358
410
start += width
0 commit comments