Generalize the process
Now, * fonts can have different size settings [not tested] * library name automatically determined from font if not specified * just dropping a font in fonts/ is enough to generate it * variants can be specified in config file (but not yet per font)
This commit is contained in:
parent
3ec51eb8df
commit
8ccddec74f
4 changed files with 70 additions and 47 deletions
19
README.md
19
README.md
|
|
@ -8,14 +8,14 @@ used in CircuitPython by simply importing them.
|
|||
|
||||
It can be used with circup for easy installation:
|
||||
|
||||
```
|
||||
circup bundle-add jepler/circuitpython-fonts
|
||||
circup install font_mono_9_ascii
|
||||
```sh
|
||||
circup bundle-add jepler/circuitpython-fonts # You only need to do this once
|
||||
circup install font_free_mono_9_ascii
|
||||
```
|
||||
|
||||
The font can be used like so:
|
||||
```
|
||||
from font_mono_9_ascii import FONT as MONO_9
|
||||
```python
|
||||
from font_free_mono_9_ascii import FONT as MONO_9
|
||||
from adafruit_display_text.bitmap_label import Label
|
||||
|
||||
# ...
|
||||
|
|
@ -26,20 +26,17 @@ label = Label(font=MONO_9, text="Hi Mom!")
|
|||
|
||||
* Copy the font into `fonts/`
|
||||
* Add a reuse-recognized `.license` file for it
|
||||
* Add it to the `[FONTS]` section of `config.toml`
|
||||
|
||||
Presently all fonts are generated at the same range of sizes, `SIZES` in `config.toml`. This restriction could be lifted if it's for a good reason.
|
||||
|
||||
## Variants
|
||||
|
||||
Two variants of each font are generated: The ASCII variant with code points 32-126, and the full variant with all glyphs in the original font. The ASCII variant can be especially useful when flash space is at a premium.
|
||||
Two variants of each font are generated: The "latin1" variant with code points 32-255 inclusive, and the full variant with all glyphs in the original font. The "latin1" variant can be especially useful when flash space is at a premium.
|
||||
|
||||
More variants could be added for a good reason; it should be configurable in config.toml and possibly be settable per font.
|
||||
More variants could be added for a good reason; it is configurable in config.toml. It is not currently settable per font but if for a good reason it could be.
|
||||
|
||||
## Font Sizing
|
||||
|
||||
The font sizes in this bundle are in pixels.
|
||||
Or, to be more technically accurte, they are rendered at a resolution of 1 pixel = 1/72 inch = 1 point.
|
||||
If you follow the [directions on learn](https://learn.adafruit.com/custom-fonts-for-pyportal-circuitpython-display/use-otf2bdf) your font is rendered at a scale of 1 pixel = 1/100 inch = 0.72 points.
|
||||
This means the resulting font pixel sizes are 33-40% larger for the Learn guide instructions vs the fonts in this bundle.
|
||||
This means the resulting font pixel sizes for these libraries are around 38% smaller than if you follow the Learn guide instructions.
|
||||
In any case, you will likely need to manually test font sizes until you find the best one for your application and display.
|
||||
|
|
|
|||
71
build.py
Normal file → Executable file
71
build.py
Normal file → Executable file
|
|
@ -1,12 +1,17 @@
|
|||
#!/usr/bin/env python3
|
||||
# SPDX-FileCopyrightText: 2023 Jeff Epler for Adafruit Industries
|
||||
# SPDX-License-Identifier: MIT
|
||||
|
||||
import glob
|
||||
import os
|
||||
import re
|
||||
import shutil
|
||||
import textwrap
|
||||
import tomllib
|
||||
from collections import deque
|
||||
from multiprocessing import Pool
|
||||
from subprocess import check_call
|
||||
import textwrap
|
||||
from pathlib import Path
|
||||
import tomllib
|
||||
from subprocess import check_call
|
||||
|
||||
from convert import convert
|
||||
|
||||
|
|
@ -14,25 +19,25 @@ with open("config.toml", "rb") as f:
|
|||
config = tomllib.load(f)
|
||||
|
||||
|
||||
def build(src, dest, size, variant):
|
||||
def build(src, dest, size, variant_name, variant_arg):
|
||||
src = Path("fonts") / src
|
||||
font_license = src.with_suffix(src.suffix + ".license")
|
||||
uvariant = variant.replace("-", "_")
|
||||
destdir = Path(
|
||||
f"libraries/circuitpython-font-{dest.replace('_', '-')}-{size}{variant}"
|
||||
f"libraries/font-{dest.replace('_', '-')}-{size}-{variant_name}".strip("-")
|
||||
)
|
||||
print(destdir)
|
||||
|
||||
package = f"font_{dest}_{size}{uvariant}"
|
||||
package = f"font_{dest}_{size}_{variant_name}".replace("-", "_").strip("_")
|
||||
packagedir = destdir / package
|
||||
packagedir.mkdir(parents=True)
|
||||
init_py = packagedir / "__init__.py"
|
||||
init_py.write_text(
|
||||
textwrap.dedent(
|
||||
"""\
|
||||
f"""\
|
||||
# SPDX-FileCopyrightText: 2023 Jeff Epler for Adafruit Industries
|
||||
# SPDX-License-Identifier: Unlicense
|
||||
|
||||
# CircuitPython font generated from {src} @{size}{" " if variant_name else ""}{variant_name}
|
||||
from adafruit_bitmap_font import bitmap_font
|
||||
FONT = bitmap_font.load_font(__file__.rsplit("/", 1)[0] + "/font.pcf")
|
||||
"""
|
||||
|
|
@ -45,11 +50,11 @@ def build(src, dest, size, variant):
|
|||
# SPDX-FileCopyrightText: 2023 Jeff Epler for Adafruit Industries
|
||||
# SPDX-License-Identifier: Unlicense
|
||||
|
||||
CircuitPython font generated from {src} @{size}{variant}
|
||||
CircuitPython font generated from {src} @{size}{" " if variant_name else ""}{variant_name}
|
||||
"""
|
||||
)
|
||||
)
|
||||
convert(src, packagedir / "font.pcf", size, "32_126" if variant else None)
|
||||
convert(src, packagedir / "font.pcf", size, variant_arg)
|
||||
|
||||
dest_font_license = packagedir / "font.pcf.license"
|
||||
dest_font_license.write_text(font_license.read_text())
|
||||
|
|
@ -83,19 +88,43 @@ def build(src, dest, size, variant):
|
|||
)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
targets = [
|
||||
(src, dest, size, variant)
|
||||
for dest, src in config["FONTS"].items()
|
||||
for size in config["SIZES"]
|
||||
for variant in ("", "-ascii")
|
||||
def filename_to_package_name(filename):
|
||||
s = re.sub("[A-Z]+", lambda m: "_" + m.group(0).lower(), filename)
|
||||
s = re.sub("[^a-z0-9_]+", "_", s)
|
||||
s = s.removeprefix("_")
|
||||
return s
|
||||
|
||||
|
||||
def targets():
|
||||
defaults = config["defaults"]
|
||||
variants = config["variants"]
|
||||
font_files = [
|
||||
f for f in os.listdir("fonts") if f.lower().endswith((".ttf", ".otf"))
|
||||
]
|
||||
for filename in font_files:
|
||||
basename = filename.rsplit(".", 1)[0]
|
||||
font_config = dict(defaults)
|
||||
font_config.update(config.get("font", {}).get(basename, {}))
|
||||
dest = filename_to_package_name(font_config.get("name", basename))
|
||||
print(config, dest, basename)
|
||||
for size in sorted(font_config["sizes"], reverse=True):
|
||||
yield (filename, dest, size, "", None)
|
||||
for variant_name, variant_arg in variants.items():
|
||||
yield (filename, dest, size, variant_name, variant_arg)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
if os.access("libraries", os.F_OK):
|
||||
shutil.rmtree("libraries")
|
||||
|
||||
with Pool() as pool:
|
||||
# This construct causes all the individual calls to finish, discarding the results
|
||||
deque(pool.starmap(build, targets), 0)
|
||||
count = sum(1 for _ in pool.starmap(build, targets()))
|
||||
|
||||
check_call(
|
||||
"circuitpython-build-bundles --output_directory dist --filename_prefix circuitpython-fonts --library_location libraries/ --library_depth 1",
|
||||
shell=True,
|
||||
)
|
||||
if not "BUILD_ONLY" in os.environ:
|
||||
check_call(
|
||||
"circuitpython-build-bundles --output_directory dist --filename_prefix circuitpython-fonts --library_location libraries/ --library_depth 1",
|
||||
shell=True,
|
||||
)
|
||||
|
||||
print(f"Generated {count} font libraries")
|
||||
|
|
|
|||
23
config.toml
23
config.toml
|
|
@ -1,18 +1,13 @@
|
|||
# SPDX-FileCopyrightText: 2023 Jeff Epler for Adafruit Industries
|
||||
# SPDX-License-Identifier: MIT
|
||||
|
||||
SIZES = [6, 7, 8, 9, 10, 11, 12, 14, 16, 18, 20, 24, 28, 32, 36, 48, 60, 72]
|
||||
[defaults]
|
||||
sizes = [6, 8, 9, 10, 11, 12, 14, 18, 24, 30, 36, 42, 48, 54, 60, 72, 84, 96, 108, 120, 144]
|
||||
|
||||
[FONTS]
|
||||
mono = "FreeMono.ttf"
|
||||
mono_bold = "FreeMonoBold.ttf"
|
||||
mono_oblique = "FreeMonoOblique.ttf"
|
||||
mono_bold_oblique = "FreeMonoBoldOblique.ttf"
|
||||
serif = "FreeSerif.ttf"
|
||||
serif_bold = "FreeSerifBold.ttf"
|
||||
serif_oblique = "FreeSerifItalic.ttf"
|
||||
serif_bold_oblique = "FreeSerifBoldItalic.ttf"
|
||||
sans = "FreeSans.ttf"
|
||||
sans_bold = "FreeSansBold.ttf"
|
||||
sans_oblique = "FreeSansOblique.ttf"
|
||||
sans_bold_oblique = "FreeSansBoldOblique.ttf"
|
||||
# Note that subset ranges are *inclusive* of the upper bound in otf2bdf
|
||||
[variants]
|
||||
#ascii = "32_126"
|
||||
latin1 = "32_255"
|
||||
|
||||
# [FONT.FontFileNameWithoutExtension]
|
||||
# name = "desired_package_base_name"
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
# SPDX-License-Identifier: MIT
|
||||
|
||||
from pathlib import Path
|
||||
from subprocess import run, check_call, DEVNULL, PIPE
|
||||
from subprocess import DEVNULL, PIPE, check_call, run
|
||||
|
||||
import click
|
||||
|
||||
|
|
@ -16,11 +16,13 @@ def convert(src: Path, dest: Path, size: int, subset=None) -> None:
|
|||
otf_process = run(otf_command, stdin=DEVNULL, stdout=PIPE)
|
||||
bdf_content = otf_process.stdout
|
||||
if otf_process.returncode != 8:
|
||||
print(f"Note: {otf_command=}")
|
||||
print(f"Note: End of bdf_content: {bdf_content[-32:]!r}")
|
||||
raise RuntimeError(
|
||||
f"otf2bdf failed: exit status was {otf_process.returncode}, not 8"
|
||||
)
|
||||
if not bdf_content.endswith(b"ENDFONT\n"):
|
||||
print(f"Note: {otf_command=}")
|
||||
print(f"Note: End of bdf_content: {bdf_content[-32:]!r}")
|
||||
raise RuntimeError(f"otf2bdf failed: output did not end with ENDFONT")
|
||||
bdf_process = run(["bdftopcf", "-o", f"{dest}", "/dev/stdin"], input=bdf_content)
|
||||
|
|
|
|||
Loading…
Reference in a new issue