Compare commits
23 commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 6e57a8b9e9 | |||
| d379f1962e | |||
| 383fc039c5 | |||
| ceed4c2a88 | |||
|
|
6ae6e07484 | ||
| e15c7d1f5c | |||
| 84bf5208bb | |||
| e1634e1a59 | |||
| 7dcd40828e | |||
| e5d0839aca | |||
| 7869ef43be | |||
| 3c31cff165 | |||
| 7658511786 | |||
| 2bef623710 | |||
| 27648e7ab7 | |||
|
|
51e9a1fa2e | ||
|
|
8c1fa9b684 | ||
|
|
c38baeb932 | ||
|
|
7e153547f5 | ||
|
|
ca48d4afde | ||
|
|
1f46cc3a68 | ||
|
|
977edcbd88 | ||
|
|
2501072843 |
12 changed files with 30803 additions and 118 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
|
@ -9,5 +9,4 @@ bundles
|
||||||
.eggs
|
.eggs
|
||||||
dist
|
dist
|
||||||
**/*.egg-info
|
**/*.egg-info
|
||||||
*.pcf
|
|
||||||
*.ttf
|
*.ttf
|
||||||
|
|
|
||||||
|
|
@ -63,6 +63,41 @@ class BDF(GlyphCache):
|
||||||
self.point_size = None
|
self.point_size = None
|
||||||
self.x_resolution = None
|
self.x_resolution = None
|
||||||
self.y_resolution = None
|
self.y_resolution = None
|
||||||
|
self._ascent = None
|
||||||
|
self._descent = None
|
||||||
|
|
||||||
|
@property
|
||||||
|
def descent(self):
|
||||||
|
"""The number of pixels below the baseline of a typical descender"""
|
||||||
|
if self._descent is None:
|
||||||
|
self.file.seek(0)
|
||||||
|
while True:
|
||||||
|
line = self.file.readline()
|
||||||
|
if not line:
|
||||||
|
break
|
||||||
|
|
||||||
|
if line.startswith(b"FONT_DESCENT "):
|
||||||
|
self._descent = int(line.split()[1])
|
||||||
|
break
|
||||||
|
|
||||||
|
return self._descent
|
||||||
|
|
||||||
|
@property
|
||||||
|
def ascent(self):
|
||||||
|
"""The number of pixels above the baseline of a typical ascender"""
|
||||||
|
if self._ascent is None:
|
||||||
|
self.file.seek(0)
|
||||||
|
while True:
|
||||||
|
line = self.file.readline()
|
||||||
|
line = str(line, "utf-8")
|
||||||
|
if not line:
|
||||||
|
break
|
||||||
|
|
||||||
|
if line.startswith("FONT_ASCENT "):
|
||||||
|
self._ascent = int(line.split()[1])
|
||||||
|
break
|
||||||
|
|
||||||
|
return self._ascent
|
||||||
|
|
||||||
def get_bounding_box(self):
|
def get_bounding_box(self):
|
||||||
"""Return the maximum glyph size as a 4-tuple of: width, height, x_offset, y_offset"""
|
"""Return the maximum glyph size as a 4-tuple of: width, height, x_offset, y_offset"""
|
||||||
|
|
@ -97,7 +132,7 @@ class BDF(GlyphCache):
|
||||||
remaining = code_points
|
remaining = code_points
|
||||||
else:
|
else:
|
||||||
remaining = set(code_points)
|
remaining = set(code_points)
|
||||||
for code_point in remaining:
|
for code_point in remaining.copy():
|
||||||
if code_point in self._glyphs and self._glyphs[code_point]:
|
if code_point in self._glyphs and self._glyphs[code_point]:
|
||||||
remaining.remove(code_point)
|
remaining.remove(code_point)
|
||||||
if not remaining:
|
if not remaining:
|
||||||
|
|
|
||||||
|
|
@ -57,11 +57,12 @@ def load_font(filename, bitmap=None):
|
||||||
|
|
||||||
return bdf.BDF(font_file, bitmap)
|
return bdf.BDF(font_file, bitmap)
|
||||||
if filename.endswith("pcf") and first_four == b"\x01fcp":
|
if filename.endswith("pcf") and first_four == b"\x01fcp":
|
||||||
import pcf
|
from . import pcf
|
||||||
|
|
||||||
return pcf.PCF(font_file)
|
return pcf.PCF(font_file, bitmap)
|
||||||
if filename.endswith("ttf") and first_four == b"\x00\x01\x00\x00":
|
if filename.endswith("ttf") and first_four == b"\x00\x01\x00\x00":
|
||||||
import ttf
|
from . import ttf
|
||||||
|
|
||||||
return ttf.TTF(font_file)
|
return ttf.TTF(font_file, bitmap)
|
||||||
return None
|
|
||||||
|
raise ValueError("Unknown magic number %r" % first_four)
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,51 @@
|
||||||
# pylint: skip-file
|
# The MIT License (MIT)
|
||||||
# Remove the above when PCF is actually supported.
|
#
|
||||||
|
# Copyright © 2020 Jeff Epler for Adafruit Industries LLC
|
||||||
|
#
|
||||||
|
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
# of this software and associated documentation files (the "Software"), to deal
|
||||||
|
# in the Software without restriction, including without limitation the rights
|
||||||
|
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
# copies of the Software, and to permit persons to whom the Software is
|
||||||
|
# furnished to do so, subject to the following conditions:
|
||||||
|
#
|
||||||
|
# The above copyright notice and this permission notice shall be included in
|
||||||
|
# all copies or substantial portions of the Software.
|
||||||
|
#
|
||||||
|
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
# THE SOFTWARE.
|
||||||
|
"""
|
||||||
|
`adafruit_bitmap_font.pcf`
|
||||||
|
====================================================
|
||||||
|
|
||||||
from .glyph_cache import GlyphCache
|
Loads PCF format fonts.
|
||||||
import displayio
|
|
||||||
|
* Author(s): Jeff Epler
|
||||||
|
|
||||||
|
Implementation Notes
|
||||||
|
--------------------
|
||||||
|
|
||||||
|
**Hardware:**
|
||||||
|
|
||||||
|
**Software and Dependencies:**
|
||||||
|
|
||||||
|
* Adafruit CircuitPython firmware for the supported boards:
|
||||||
|
https://github.com/adafruit/circuitpython/releases
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
from collections import namedtuple
|
||||||
|
import gc
|
||||||
import struct
|
import struct
|
||||||
|
|
||||||
|
from fontio import Glyph
|
||||||
|
from .glyph_cache import GlyphCache
|
||||||
|
|
||||||
_PCF_PROPERTIES = 1 << 0
|
_PCF_PROPERTIES = 1 << 0
|
||||||
_PCF_ACCELERATORS = 1 << 1
|
_PCF_ACCELERATORS = 1 << 1
|
||||||
_PCF_METRICS = 1 << 2
|
_PCF_METRICS = 1 << 2
|
||||||
|
|
@ -25,139 +66,345 @@ _PCF_BYTE_MASK = 1 << 2 # If set then Most Sig Byte First */
|
||||||
_PCF_BIT_MASK = 1 << 3 # If set then Most Sig Bit First */
|
_PCF_BIT_MASK = 1 << 3 # If set then Most Sig Bit First */
|
||||||
_PCF_SCAN_UNIT_MASK = 3 << 4
|
_PCF_SCAN_UNIT_MASK = 3 << 4
|
||||||
|
|
||||||
# https://fontforge.github.io/en-US/documentation/reference/pcf-format/
|
# https://fontforge.org/docs/techref/pcf-format.html
|
||||||
|
|
||||||
|
Table = namedtuple("Table", ("format", "size", "offset"))
|
||||||
|
Metrics = namedtuple(
|
||||||
|
"Metrics",
|
||||||
|
(
|
||||||
|
"left_side_bearing",
|
||||||
|
"right_side_bearing",
|
||||||
|
"character_width",
|
||||||
|
"character_ascent",
|
||||||
|
"character_descent",
|
||||||
|
"character_attributes",
|
||||||
|
),
|
||||||
|
)
|
||||||
|
Accelerators = namedtuple(
|
||||||
|
"Accelerators",
|
||||||
|
(
|
||||||
|
"no_overlap",
|
||||||
|
"constant_metrics",
|
||||||
|
"terminal_font",
|
||||||
|
"constant_width",
|
||||||
|
"ink_inside",
|
||||||
|
"ink_metrics",
|
||||||
|
"draw_direction",
|
||||||
|
"font_ascent",
|
||||||
|
"font_descent",
|
||||||
|
"max_overlap",
|
||||||
|
"minbounds",
|
||||||
|
"maxbounds",
|
||||||
|
"ink_minbounds",
|
||||||
|
"ink_maxbounds",
|
||||||
|
),
|
||||||
|
)
|
||||||
|
Encoding = namedtuple(
|
||||||
|
"Encoding", ("min_byte2", "max_byte2", "min_byte1", "max_byte1", "default_char")
|
||||||
|
)
|
||||||
|
Bitmap = namedtuple("Bitmap", ("glyph_count", "bitmap_sizes"))
|
||||||
|
|
||||||
|
|
||||||
class PCF(GlyphCache):
|
class PCF(GlyphCache):
|
||||||
def __init__(self, f):
|
"""Loads glyphs from a PCF file in the given bitmap_class."""
|
||||||
|
|
||||||
|
def __init__(self, f, bitmap_class):
|
||||||
super().__init__()
|
super().__init__()
|
||||||
self.file = f
|
self.file = f
|
||||||
self.name = f
|
self.name = f
|
||||||
f.seek(0)
|
f.seek(0)
|
||||||
header, table_count = self.read("<4sI")
|
self.buffer = bytearray(1)
|
||||||
|
self.bitmap_class = bitmap_class
|
||||||
|
_, table_count = self._read("<4sI")
|
||||||
self.tables = {}
|
self.tables = {}
|
||||||
for _ in range(table_count):
|
for _ in range(table_count):
|
||||||
type, format, size, offset = self.read("<IIII")
|
type_, format_, size, offset = self._read("<IIII")
|
||||||
self.tables[type] = {"format": format, "size": size, "offset": offset}
|
self.tables[type_] = Table(format_, size, offset)
|
||||||
print(type)
|
|
||||||
|
|
||||||
def read(self, format):
|
bitmap_format = self.tables[_PCF_BITMAPS].format
|
||||||
s = struct.calcsize(format)
|
if bitmap_format != 0xE:
|
||||||
return struct.unpack_from(format, self.file.read(s))
|
raise NotImplementedError("Unsupported format %s" % bitmap_format)
|
||||||
|
|
||||||
|
self._accel = self._read_accelerator_tables()
|
||||||
|
self._encoding = self._read_encoding_table()
|
||||||
|
self._bitmaps = self._read_bitmap_table()
|
||||||
|
|
||||||
|
self._ascent = self._accel.font_ascent
|
||||||
|
self._descent = self._accel.font_descent
|
||||||
|
|
||||||
|
minbounds = self._accel.ink_minbounds
|
||||||
|
maxbounds = self._accel.ink_maxbounds
|
||||||
|
width = maxbounds.right_side_bearing - minbounds.left_side_bearing
|
||||||
|
height = maxbounds.character_ascent + maxbounds.character_descent
|
||||||
|
|
||||||
|
self._bounding_box = (
|
||||||
|
width,
|
||||||
|
height,
|
||||||
|
minbounds.left_side_bearing,
|
||||||
|
-maxbounds.character_descent,
|
||||||
|
)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def ascent(self):
|
||||||
|
"""The number of pixels above the baseline of a typical ascender"""
|
||||||
|
return self._ascent
|
||||||
|
|
||||||
|
@property
|
||||||
|
def descent(self):
|
||||||
|
"""The number of pixels below the baseline of a typical descender"""
|
||||||
|
return self._descent
|
||||||
|
|
||||||
def get_bounding_box(self):
|
def get_bounding_box(self):
|
||||||
|
"""Return the maximum glyph size as a 4-tuple of: width, height, x_offset, y_offset"""
|
||||||
|
return self._bounding_box
|
||||||
|
|
||||||
|
def _read(self, format_):
|
||||||
|
size = struct.calcsize(format_)
|
||||||
|
if size != len(self.buffer):
|
||||||
|
self.buffer = bytearray(size)
|
||||||
|
self.file.readinto(self.buffer)
|
||||||
|
return struct.unpack_from(format_, self.buffer)
|
||||||
|
|
||||||
|
def _seek_table(self, table):
|
||||||
|
self.file.seek(table.offset)
|
||||||
|
(format_,) = self._read("<I")
|
||||||
|
|
||||||
|
if format_ & _PCF_BYTE_MASK == 0:
|
||||||
|
raise RuntimeError("Only big endian supported")
|
||||||
|
|
||||||
|
return format_
|
||||||
|
|
||||||
|
def _read_encoding_table(self):
|
||||||
|
encoding = self.tables[_PCF_BDF_ENCODINGS]
|
||||||
|
self._seek_table(encoding)
|
||||||
|
|
||||||
|
return Encoding(*self._read(">hhhhh"))
|
||||||
|
|
||||||
|
def _read_bitmap_table(self):
|
||||||
|
bitmaps = self.tables[_PCF_BITMAPS]
|
||||||
|
format_ = self._seek_table(bitmaps)
|
||||||
|
|
||||||
|
(glyph_count,) = self._read(">I")
|
||||||
|
self.file.seek(bitmaps.offset + 8 + 4 * glyph_count)
|
||||||
|
bitmap_sizes = self._read(">4I")
|
||||||
|
return Bitmap(glyph_count, bitmap_sizes[format_ & 3])
|
||||||
|
|
||||||
|
def _read_metrics(self, compressed_metrics):
|
||||||
|
if compressed_metrics:
|
||||||
|
(
|
||||||
|
left_side_bearing,
|
||||||
|
right_side_bearing,
|
||||||
|
character_width,
|
||||||
|
character_ascent,
|
||||||
|
character_descent,
|
||||||
|
) = self._read("5B")
|
||||||
|
left_side_bearing -= 0x80
|
||||||
|
right_side_bearing -= 0x80
|
||||||
|
character_width -= 0x80
|
||||||
|
character_ascent -= 0x80
|
||||||
|
character_descent -= 0x80
|
||||||
|
attributes = 0
|
||||||
|
else:
|
||||||
|
(
|
||||||
|
left_side_bearing,
|
||||||
|
right_side_bearing,
|
||||||
|
character_width,
|
||||||
|
character_ascent,
|
||||||
|
character_descent,
|
||||||
|
attributes,
|
||||||
|
) = self._read(">5hH")
|
||||||
|
return Metrics(
|
||||||
|
left_side_bearing,
|
||||||
|
right_side_bearing,
|
||||||
|
character_width,
|
||||||
|
character_ascent,
|
||||||
|
character_descent,
|
||||||
|
attributes,
|
||||||
|
)
|
||||||
|
|
||||||
|
def _read_accelerator_tables(self):
|
||||||
|
# pylint: disable=too-many-locals
|
||||||
|
accelerators = self.tables.get(_PCF_BDF_ACCELERATORS)
|
||||||
|
if not accelerators:
|
||||||
|
accelerators = self.tables.get(_PCF_ACCELERATORS)
|
||||||
|
if not accelerators:
|
||||||
|
raise RuntimeError("Accelerator table missing")
|
||||||
|
|
||||||
|
format_ = self._seek_table(accelerators)
|
||||||
|
|
||||||
|
has_inkbounds = format_ & _PCF_ACCEL_W_INKBOUNDS
|
||||||
|
compressed_metrics = False # format_ & _PCF_COMPRESSED_METRICS
|
||||||
|
|
||||||
|
(
|
||||||
|
no_overlap,
|
||||||
|
constant_metrics,
|
||||||
|
terminal_font,
|
||||||
|
constant_width,
|
||||||
|
ink_inside,
|
||||||
|
ink_metrics,
|
||||||
|
draw_direction,
|
||||||
|
_,
|
||||||
|
font_ascent,
|
||||||
|
font_descent,
|
||||||
|
max_overlap,
|
||||||
|
) = self._read(">BBBBBBBBIII")
|
||||||
|
minbounds = self._read_metrics(compressed_metrics)
|
||||||
|
maxbounds = self._read_metrics(compressed_metrics)
|
||||||
|
if has_inkbounds:
|
||||||
|
ink_minbounds = self._read_metrics(compressed_metrics)
|
||||||
|
ink_maxbounds = self._read_metrics(compressed_metrics)
|
||||||
|
else:
|
||||||
|
ink_minbounds = minbounds
|
||||||
|
ink_maxbounds = maxbounds
|
||||||
|
|
||||||
|
return Accelerators(
|
||||||
|
no_overlap,
|
||||||
|
constant_metrics,
|
||||||
|
terminal_font,
|
||||||
|
constant_width,
|
||||||
|
ink_inside,
|
||||||
|
ink_metrics,
|
||||||
|
draw_direction,
|
||||||
|
font_ascent,
|
||||||
|
font_descent,
|
||||||
|
max_overlap,
|
||||||
|
minbounds,
|
||||||
|
maxbounds,
|
||||||
|
ink_minbounds,
|
||||||
|
ink_maxbounds,
|
||||||
|
)
|
||||||
|
|
||||||
|
def _read_properties(self):
|
||||||
property_table_offset = self.tables[_PCF_PROPERTIES]["offset"]
|
property_table_offset = self.tables[_PCF_PROPERTIES]["offset"]
|
||||||
self.file.seek(property_table_offset)
|
self.file.seek(property_table_offset)
|
||||||
(format,) = self.read("<I")
|
(format_,) = self._read("<I")
|
||||||
|
|
||||||
if format & _PCF_BYTE_MASK == 0:
|
if format_ & _PCF_BYTE_MASK == 0:
|
||||||
raise RuntimeError("Only big endian supported")
|
raise RuntimeError("Only big endian supported")
|
||||||
(nprops,) = self.read(">I")
|
(nprops,) = self._read(">I")
|
||||||
self.file.seek(property_table_offset + 8 + 9 * nprops)
|
self.file.seek(property_table_offset + 8 + 9 * nprops)
|
||||||
|
|
||||||
pos = self.file.tell()
|
pos = self.file.tell()
|
||||||
if pos % 4 > 0:
|
if pos % 4 > 0:
|
||||||
self.file.read(4 - pos % 4)
|
self.file.read(4 - pos % 4)
|
||||||
(string_size,) = self.read(">I")
|
(string_size,) = self._read(">I")
|
||||||
|
|
||||||
strings = self.file.read(string_size)
|
strings = self.file.read(string_size)
|
||||||
string_map = {}
|
string_map = {}
|
||||||
i = 0
|
i = 0
|
||||||
for s in strings.split(b"\x00"):
|
for value in strings.split(b"\x00"):
|
||||||
string_map[i] = s
|
string_map[i] = value
|
||||||
i += len(s) + 1
|
i += len(value) + 1
|
||||||
|
|
||||||
self.file.seek(property_table_offset + 8)
|
self.file.seek(property_table_offset + 8)
|
||||||
for _ in range(nprops):
|
for _ in range(nprops):
|
||||||
name_offset, isStringProp, value = self.read(">IBI")
|
name_offset, is_string_prop, value = self._read(">IBI")
|
||||||
|
|
||||||
if isStringProp:
|
if is_string_prop:
|
||||||
print(string_map[name_offset], string_map[value])
|
yield (string_map[name_offset], string_map[value])
|
||||||
else:
|
else:
|
||||||
print(string_map[name_offset], value)
|
yield (string_map[name_offset], value)
|
||||||
return None
|
|
||||||
|
|
||||||
def load_glyphs(self, code_points):
|
def load_glyphs(self, code_points):
|
||||||
metadata = True
|
# pylint: disable=too-many-statements,too-many-branches,too-many-nested-blocks,too-many-locals
|
||||||
character = False
|
if isinstance(code_points, int):
|
||||||
code_point = None
|
code_points = (code_points,)
|
||||||
rounded_x = 1
|
elif isinstance(code_points, str):
|
||||||
bytes_per_row = 1
|
code_points = [ord(c) for c in code_points]
|
||||||
desired_character = False
|
|
||||||
current_info = None
|
|
||||||
current_y = 0
|
|
||||||
total_remaining = len(code_points)
|
|
||||||
|
|
||||||
x, _, _, _ = self.get_bounding_box()
|
code_points = sorted(
|
||||||
# create a scratch bytearray to load pixels into
|
c for c in code_points if self._glyphs.get(c, None) is None
|
||||||
scratch_row = memoryview(bytearray((((x - 1) // 32) + 1) * 4))
|
)
|
||||||
|
if not code_points:
|
||||||
|
return
|
||||||
|
|
||||||
self.file.seek(0)
|
indices_offset = self.tables[_PCF_BDF_ENCODINGS].offset + 14
|
||||||
while True:
|
bitmap_offset_offsets = self.tables[_PCF_BITMAPS].offset + 8
|
||||||
line = self.file.readline()
|
first_bitmap_offset = self.tables[_PCF_BITMAPS].offset + 4 * (
|
||||||
if not line:
|
6 + self._bitmaps.glyph_count
|
||||||
break
|
)
|
||||||
if line.startswith(b"CHARS "):
|
metrics_compressed = self.tables[_PCF_METRICS].format & _PCF_COMPRESSED_METRICS
|
||||||
metadata = False
|
first_metric_offset = (
|
||||||
elif line.startswith(b"SIZE"):
|
self.tables[_PCF_METRICS].offset + 6 if metrics_compressed else 8
|
||||||
_, self.point_size, self.x_resolution, self.y_resolution = line.split()
|
)
|
||||||
elif line.startswith(b"COMMENT"):
|
metrics_size = 5 if metrics_compressed else 12
|
||||||
pass
|
|
||||||
elif line.startswith(b"STARTCHAR"):
|
# These will each _tend to be_ forward reads in the file, at least
|
||||||
# print(lineno, line.strip())
|
# sometimes we'll benefit from oofatfs's 512 byte cache and avoid
|
||||||
# _, character_name = line.split()
|
# excess reads
|
||||||
character = True
|
indices = [None] * len(code_points)
|
||||||
elif line.startswith(b"ENDCHAR"):
|
for i, code_point in enumerate(code_points):
|
||||||
character = False
|
enc1 = (code_point >> 8) & 0xFF
|
||||||
if desired_character:
|
enc2 = code_point & 0xFF
|
||||||
self._glyphs[code_point] = current_info
|
|
||||||
if total_remaining == 0:
|
if enc1 < self._encoding.min_byte1 or enc1 > self._encoding.max_byte1:
|
||||||
return
|
continue
|
||||||
desired_character = False
|
if enc2 < self._encoding.min_byte2 or enc2 > self._encoding.max_byte2:
|
||||||
elif line.startswith(b"BBX"):
|
continue
|
||||||
if desired_character:
|
|
||||||
_, x, y, dx, dy = line.split()
|
encoding_idx = (
|
||||||
x = int(x)
|
(enc1 - self._encoding.min_byte1)
|
||||||
y = int(y)
|
* (self._encoding.max_byte2 - self._encoding.min_byte2 + 1)
|
||||||
dx = int(dx)
|
+ enc2
|
||||||
dy = int(dy)
|
- self._encoding.min_byte2
|
||||||
current_info["bounds"] = (x, y, dx, dy)
|
)
|
||||||
current_info["bitmap"] = displayio.Bitmap(x, y, 2)
|
self.file.seek(indices_offset + 2 * encoding_idx)
|
||||||
elif line.startswith(b"BITMAP"):
|
(glyph_idx,) = self._read(">H")
|
||||||
if desired_character:
|
if glyph_idx != 65535:
|
||||||
rounded_x = x // 8
|
indices[i] = glyph_idx
|
||||||
if x % 8 > 0:
|
|
||||||
rounded_x += 1
|
all_metrics = [None] * len(code_points)
|
||||||
bytes_per_row = rounded_x
|
for i, code_point in enumerate(code_points):
|
||||||
if bytes_per_row % 4 > 0:
|
index = indices[i]
|
||||||
bytes_per_row += 4 - bytes_per_row % 4
|
if index is None:
|
||||||
current_y = 0
|
continue
|
||||||
elif line.startswith(b"ENCODING"):
|
self.file.seek(first_metric_offset + metrics_size * index)
|
||||||
_, code_point = line.split()
|
all_metrics[i] = self._read_metrics(metrics_compressed)
|
||||||
code_point = int(code_point)
|
bitmap_offsets = [None] * len(code_points)
|
||||||
if code_point == code_points or code_point in code_points:
|
for i, code_point in enumerate(code_points):
|
||||||
total_remaining -= 1
|
index = indices[i]
|
||||||
if code_point not in self._glyphs:
|
if index is None:
|
||||||
desired_character = True
|
continue
|
||||||
current_info = {"bitmap": None, "bounds": None, "shift": None}
|
self.file.seek(bitmap_offset_offsets + 4 * index)
|
||||||
elif line.startswith(b"DWIDTH"):
|
(bitmap_offset,) = self._read(">I")
|
||||||
if desired_character:
|
bitmap_offsets[i] = bitmap_offset
|
||||||
_, shift_x, shift_y = line.split()
|
|
||||||
shift_x = int(shift_x)
|
# Batch creation of glyphs and bitmaps so that we need only gc.collect
|
||||||
shift_y = int(shift_y)
|
# once
|
||||||
current_info["shift"] = (shift_x, shift_y)
|
gc.collect()
|
||||||
elif line.startswith(b"SWIDTH"):
|
bitmaps = [None] * len(code_points)
|
||||||
pass
|
for i in range(len(all_metrics)): # pylint: disable=consider-using-enumerate
|
||||||
elif character:
|
metrics = all_metrics[i]
|
||||||
if desired_character:
|
if metrics is not None:
|
||||||
bits = int(line.strip(), 16)
|
width = metrics.right_side_bearing - metrics.left_side_bearing
|
||||||
for i in range(rounded_x):
|
height = metrics.character_ascent + metrics.character_descent
|
||||||
val = (bits >> ((rounded_x - i - 1) * 8)) & 0xFF
|
bitmap = bitmaps[i] = self.bitmap_class(width, height, 2)
|
||||||
scratch_row[i] = val
|
self._glyphs[code_points[i]] = Glyph(
|
||||||
current_info["bitmap"]._load_row(
|
bitmap,
|
||||||
current_y, scratch_row[:bytes_per_row]
|
0,
|
||||||
)
|
width,
|
||||||
current_y += 1
|
height,
|
||||||
elif metadata:
|
metrics.left_side_bearing,
|
||||||
# print(lineno, line.strip())
|
-metrics.character_descent,
|
||||||
pass
|
metrics.character_width,
|
||||||
|
0,
|
||||||
|
)
|
||||||
|
|
||||||
|
for i, code_point in enumerate(code_points):
|
||||||
|
metrics = all_metrics[i]
|
||||||
|
if metrics is None:
|
||||||
|
continue
|
||||||
|
self.file.seek(first_bitmap_offset + bitmap_offsets[i])
|
||||||
|
width = metrics.right_side_bearing - metrics.left_side_bearing
|
||||||
|
height = metrics.character_ascent + metrics.character_descent
|
||||||
|
|
||||||
|
bitmap = bitmaps[i]
|
||||||
|
words_per_row = (width + 31) // 32
|
||||||
|
buf = bytearray(4 * words_per_row)
|
||||||
|
start = 0
|
||||||
|
for _ in range(height):
|
||||||
|
self.file.readinto(buf)
|
||||||
|
for k in range(width):
|
||||||
|
if buf[k // 8] & (128 >> (k % 8)):
|
||||||
|
bitmap[start + k] = 1
|
||||||
|
start += width
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,7 @@ import struct
|
||||||
|
|
||||||
|
|
||||||
class TTF:
|
class TTF:
|
||||||
def __init__(self, f):
|
def __init__(self, f, bitmap):
|
||||||
f.seek(0)
|
f.seek(0)
|
||||||
self.file = f
|
self.file = f
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -8,8 +8,8 @@ bitmap with pixels matching glyphs from a given String
|
||||||
|
|
||||||
|
|
||||||
import board
|
import board
|
||||||
from adafruit_bitmap_font import bitmap_font # pylint: disable=wrong-import-position
|
|
||||||
import displayio
|
import displayio
|
||||||
|
from adafruit_bitmap_font import bitmap_font
|
||||||
|
|
||||||
font = bitmap_font.load_font("fonts/Arial-16.bdf")
|
font = bitmap_font.load_font("fonts/Arial-16.bdf")
|
||||||
|
|
||||||
|
|
|
||||||
44
examples/bitmap_font_label_magtag.py
Normal file
44
examples/bitmap_font_label_magtag.py
Normal file
|
|
@ -0,0 +1,44 @@
|
||||||
|
"""
|
||||||
|
This example uses addfruit_display_text.label to display text using a custom font
|
||||||
|
loaded by adafruit_bitmap_font.
|
||||||
|
Adapted for use on MagTag
|
||||||
|
"""
|
||||||
|
import time
|
||||||
|
import board
|
||||||
|
from adafruit_display_text import label
|
||||||
|
from adafruit_bitmap_font import bitmap_font
|
||||||
|
|
||||||
|
# use built in display (PyPortal, PyGamer, PyBadge, CLUE, etc.)
|
||||||
|
# see guide for setting up external displays (TFT / OLED breakouts, RGB matrices, etc.)
|
||||||
|
# https://learn.adafruit.com/circuitpython-display-support-using-displayio/display-and-display-bus
|
||||||
|
display = board.DISPLAY
|
||||||
|
# wait until we can refresh the display
|
||||||
|
time.sleep(display.time_to_refresh)
|
||||||
|
|
||||||
|
# Set text, font, and color
|
||||||
|
text = "HELLO WORLD\nbitmap_font example"
|
||||||
|
font = bitmap_font.load_font("fonts/Arial-16.bdf")
|
||||||
|
color = 0xFFFFFF
|
||||||
|
background_color = 0x999999
|
||||||
|
|
||||||
|
# Create the tet label
|
||||||
|
text_area = label.Label(
|
||||||
|
font,
|
||||||
|
text=text,
|
||||||
|
color=color,
|
||||||
|
background_color=background_color,
|
||||||
|
padding_top=3,
|
||||||
|
padding_bottom=3,
|
||||||
|
padding_right=4,
|
||||||
|
padding_left=4,
|
||||||
|
)
|
||||||
|
text_area.line_spacing = 1.0
|
||||||
|
# Set the location
|
||||||
|
text_area.x = 20
|
||||||
|
text_area.y = 20
|
||||||
|
|
||||||
|
# Show it and refresh
|
||||||
|
display.show(text_area)
|
||||||
|
display.refresh()
|
||||||
|
while True:
|
||||||
|
pass
|
||||||
|
|
@ -4,8 +4,8 @@ loaded by adafruit_bitmap_font
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import board
|
import board
|
||||||
from adafruit_bitmap_font import bitmap_font
|
|
||||||
from adafruit_display_text import label
|
from adafruit_display_text import label
|
||||||
|
from adafruit_bitmap_font import bitmap_font
|
||||||
|
|
||||||
display = board.DISPLAY
|
display = board.DISPLAY
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -9,10 +9,13 @@ from adafruit_bitmap_font import bitmap_font # pylint: disable=wrong-import-pos
|
||||||
|
|
||||||
sys.path.append(os.path.join(sys.path[0], "../test"))
|
sys.path.append(os.path.join(sys.path[0], "../test"))
|
||||||
font = bitmap_font.load_font(sys.argv[1])
|
font = bitmap_font.load_font(sys.argv[1])
|
||||||
|
specimen = "Adafruit CircuitPython" if len(sys.argv) == 2 else sys.argv[2]
|
||||||
|
|
||||||
_, height, _, dy = font.get_bounding_box()
|
_, height, _, dy = font.get_bounding_box()
|
||||||
|
font.load_glyphs(specimen)
|
||||||
|
|
||||||
for y in range(height):
|
for y in range(height):
|
||||||
for c in "Adafruit CircuitPython":
|
for c in specimen:
|
||||||
glyph = font.get_glyph(ord(c))
|
glyph = font.get_glyph(ord(c))
|
||||||
if not glyph:
|
if not glyph:
|
||||||
continue
|
continue
|
||||||
|
|
|
||||||
|
|
@ -26,7 +26,7 @@ COPYRIGHT "
|
||||||
_OTF_FONTFILE "arial.ttf"
|
_OTF_FONTFILE "arial.ttf"
|
||||||
_OTF_PSNAME "ArialMT"
|
_OTF_PSNAME "ArialMT"
|
||||||
ENDPROPERTIES
|
ENDPROPERTIES
|
||||||
CHARS 3361
|
CHARS 318
|
||||||
STARTCHAR 0020
|
STARTCHAR 0020
|
||||||
ENCODING 32
|
ENCODING 32
|
||||||
SWIDTH 270 0
|
SWIDTH 270 0
|
||||||
|
|
|
||||||
30356
examples/fonts/yasashi24.bdf
Normal file
30356
examples/fonts/yasashi24.bdf
Normal file
File diff suppressed because it is too large
Load diff
BIN
examples/fonts/yasashi24.pcf
Normal file
BIN
examples/fonts/yasashi24.pcf
Normal file
Binary file not shown.
Loading…
Reference in a new issue