Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 11 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ Historically when drawing text microcontrollers have been limited to bitmapped f

More recently the power and capacity of cheap, embeddable, microcontrollers has increased to the point where for a dollar and change you can be running at hundreds of megahertz with megabytes of flash storage alongside.

It is now viable to render filled, transformed, anti-aliased, and scalable characters that are comparable in quality to the text that we see on our computer screens every day.
It is now viable to render filled, transformed, anti-aliased, and scalable characters that are comparable in quality to the text that we see on our computer screens every day.

There is, however, still a sticking point. Existing font formats are complicated beasts that define shapes as collections of curves and control points, include entire state machines for pixel hinting, have character pair specific kerning tables, and digital rights management for.. well, yeah.

Expand Down Expand Up @@ -37,7 +37,7 @@ Features:

Alright Fonts includes:

- `afinate` an extraction and encoding tool to create Alright Font (.af) files
- `afinate` an extraction and encoding tool to create Alright Font (.af) files
- `python_alright_fonts` a Python library for encoding and loading Alright Fonts
- `alright-fonts.hpp` a reference C++ library implementation

Expand Down Expand Up @@ -65,13 +65,13 @@ Font data can be output either as a binary file or as source files for C(++) and
- `c` generates a C(++) code file containing a const array of font data
- `python` generates a Python code file containing an array of font data
- `--quality`: the quality of decomposed bezier curves, either `low`, `medium`, or `high` (default: `medium` - affects file size)

The list of characters to include can be specified in three ways:

- default: full printable ASCII set (95 characters)
- `--characters CHARACTERS`: a list of characters to include in the font pack
- `--corpus FILE`: a text file containing all of the characters to include

For example:

```bash
Expand Down Expand Up @@ -118,11 +118,11 @@ This scale was chosen for a number of reasons:

Let's hedge our bets.

Being an English software developer ~~it's possible~~ an absolute certainty that I don't fully understand every nuance of every language used globally - heck, I can just barely handle my own.
Being an English software developer ~~it's possible~~ an absolute certainty that I don't fully understand every nuance of every language used globally - heck, I can just barely handle my own.

It's also likely that we may want, in future, to add a feature or two:

- excluding glyph bounding boxes
- excluding glyph bounding boxes
- including glyph pair kerning data
- allowing 4-byte character codepoints
- allow a finer scale for coordinates (i.e. `-65536..65535`)
Expand Down Expand Up @@ -192,10 +192,12 @@ Here three Alright Fonts files have been generated containing the full set of pr

The differences are easier to see when viewing the images at their original size - click to open in a new tab.

### Python `render-demo`
### Python `swatch`

To run the Python examples, install the alright fonts Python package into your virtual environment with `pip install` (either `pip install .` for a static installation, or `pip install -e .` for a development version).

You can pipe the output of `afinate` directly into the `render-demo` example script to product a swatch image.
You can pipe the output of `afinate` directly into the `swatch` example script to product a swatch image.

```bash
./afinate --font fonts/Roboto-Black.ttf --quality high - | ./render-demo
./afinate --font fonts/Roboto-Black.ttf --quality high - | examples/python/swatch
```
16 changes: 8 additions & 8 deletions afinate
Original file line number Diff line number Diff line change
Expand Up @@ -91,9 +91,9 @@ for codepoint in character_codepoints:

if glyph:
print(" \\u{:04} {} : {:>2} contours / {:>3} points".format(
codepoint,
"'" + chr(codepoint) + "'",
len(glyph.contours),
codepoint,
"'" + chr(codepoint) + "'",
len(glyph.contours),
sum([len(c) for c in glyph.contours])
))
printable_count += 1
Expand Down Expand Up @@ -141,23 +141,23 @@ with open(args.out, "wb") as outfile:
for char in line:
outfile.write("0x{:02x}".format(char).encode("ascii"))
if i < len(result) - 1:
outfile.write(b", ")
i += 1
outfile.write(b", ")
i += 1
outfile.write(b"\n")
outfile.write(b"};\n")

# python array
if args.format == "python":
ooutfileut.write(b"_font = bytes([\n")
outfile.write(b"_font = bytes([\n")
i = 0
while i < len(result):
line = result[i:i + 12]
outfile.write(b" ")
for char in line:
outfile.write("0x{:02x}".format(char).encode("ascii"))
if i < len(result) - 1:
outfile.write(b", ")
i += 1
outfile.write(b", ")
i += 1
outfile.write(b"\n")
outfile.write(b"])\n")
pass
Expand Down
28 changes: 10 additions & 18 deletions examples/python/quality
Original file line number Diff line number Diff line change
@@ -1,31 +1,23 @@
#!/usr/bin/env python3

# renders a demo swatch using the font provided
#
#
# either pass the font file in with the `--font` argument or pipe the file to stdin
#
# for example:
#
# ./examples/python/render-demo --font out/roboto-black.af
# ./afinate --font fonts/Roboto-Black.tff --quality medium - | examples/python/render-demo


# insert top level into python module search path so python_alright_fonts import works
# what's the pythonic way to achieve this kind of structure of a repo with
# a module and an isolated `examples` folder?
import sys, os, argparse
path = os.path.abspath(os.path.join(os.path.dirname(__file__), '../..'))
sys.path.insert(1, path)

from python_alright_fonts import Glyph, Point, Face, load_font

font_low = load_font("fonts/Roboto-Black-Low.af")
font_medium = load_font("fonts/Roboto-Black-Medium.af")
font_high = load_font("fonts/Roboto-Black-High.af")

from PIL import Image, ImageDraw

def draw_glyph(glyph, position, scale, colour=(255, 255, 255)):
font_low = load_font("sample-fonts/Roboto/Roboto-Black-Low.af")
font_medium = load_font("sample-fonts/Roboto/Roboto-Black-Medium.af")
font_high = load_font("sample-fonts/Roboto/Roboto-Black-High.af")

def draw_glyph(glyph, position, scale, colour=(255, 255, 255)):
point_count = 0
for contour in glyph.contours:
for i in range(0, len(contour)):
Expand All @@ -47,7 +39,7 @@ def text(font, text, position, size, color=(255, 255, 255)):
spacing = 1
line_spacing = 0.85
for character in text:
if character == "\n":
if character == "\n":
caret.x = position.x
caret.y += size * line_spacing
continue
Expand All @@ -56,20 +48,20 @@ def text(font, text, position, size, color=(255, 255, 255)):

if not glyph:
continue

draw_glyph(glyph, caret, scale)
caret.x += glyph.advance * scale * spacing




image = Image.new("RGB", (1000, 1000))
image = Image.new("RGB", (1000, 1000))
canvas = ImageDraw.Draw(image, "RGBA")
canvas.rectangle((0, 0, 1000, 1000), fill=(60, 70, 80, 255))

message = """12345
67890
!\"$%
!\\"$%
^&*()_
abcde
fghij
Expand Down
38 changes: 17 additions & 21 deletions examples/python/swatch
Original file line number Diff line number Diff line change
@@ -1,21 +1,19 @@
#!/usr/bin/env python3

# renders a demo swatch using the font provided
#
#
# either pass the font file in with the `--font` argument or pipe the file to stdin
#
# for example:
#
# ./examples/python/render-demo --font out/roboto-black.af
# ./afinate --font fonts/Roboto-Black.tff --quality medium - | examples/python/render-demo
# ./examples/python/swatch --font out/roboto-black.af
# ./afinate --font fonts/Roboto-Black.tff --quality medium - | examples/python/swatch


import argparse
import sys

# insert top level into python module search path so python_alright_fonts import works
# what's the pythonic way to achieve this kind of structure of a repo with
# a module and an isolated `examples` folder?
import sys, os, argparse
path = os.path.abspath(os.path.join(os.path.dirname(__file__), '../..'))
sys.path.insert(1, path)
from PIL import Image, ImageDraw

from python_alright_fonts import Glyph, Point, Face, load_font

Expand All @@ -33,9 +31,7 @@ else:
face = load_font(data)
glyph = face.get_glyph(ord("a"))

from PIL import Image, ImageDraw

def draw(glyph, position, scale, colour=(255, 255, 255)):
def draw(glyph, position, scale, colour=(255, 255, 255)):
point_count = 0
for contour in glyph.contours:
for i in range(0, len(contour)):
Expand All @@ -51,23 +47,23 @@ def draw(glyph, position, scale, colour=(255, 255, 255)):
f = int((i / len(contour)) * 255)
#canvas.ellipse([p1[0] - 1, p1[1] - 1, p1[0] + 1, p1[1] + 1], fill=(f, 255 - f, 0, 255))

image = Image.new("RGB", (1000, 1000))
image = Image.new("RGB", (1000, 1000))
canvas = ImageDraw.Draw(image, "RGBA")
canvas.rectangle((0, 0, 1000, 1000), fill=(60, 70, 80, 255))

message = """1234567890

!\"$%^&*()_+-=[]`\{\};'#:@~,./<>?\|
!\\"$%^&*()_+-=[]`{};'#:@~,./<>?|

Prow scuttle parrel provost Sail ho
shrouds spirits boom mizzenmast
yardarm. Pinnace holystone
mizzenmast quarter crow's nest
nipperkin grog yardarm hempen
yardarm. Pinnace holystone
mizzenmast quarter crow's nest
nipperkin grog yardarm hempen
halter furl.

Swab barque interloper chantey
doubloon starboard grog black jack
Swab barque interloper chantey
doubloon starboard grog black jack
gangway rutters.
"""

Expand All @@ -80,7 +76,7 @@ scale = font_size / 128
spacing = 1
line_spacing = 1
for character in message:
if character == "\n":
if character == "\n":
caret.x = left_margin
caret.y += font_size * line_spacing
continue
Expand All @@ -89,7 +85,7 @@ for character in message:

if not glyph:
continue

draw(glyph, caret, scale)
caret.x += glyph.advance * scale * spacing

Expand Down
38 changes: 38 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
[build-system]
build-backend = "setuptools.build_meta"
requires = ["setuptools>=61.0", "wheel>=0.37.1"]

[project]
name = "alright-fonts"
version = "0.0.1"
description = "Font format and tools for low-resource platforms"
readme = "README.md"
requires-python = ">=3.9,<4.0"
license = {file = "LICENSE"}
keywords = ["font", "microcontroller", "truetype", "opentype"]

classifiers = [
"Development Status :: 5 - Production/Stable",
"Environment :: Console",
"License :: OSI Approved :: MIT License",
"Natural Language :: English",
"Operating System :: MacOS",
"Operating System :: Microsoft :: Windows",
"Operating System :: POSIX",
"Operating System :: POSIX :: Linux",
"Programming Language :: Python",
"Programming Language :: Python :: 3",
"Programming Language :: Python :: Implementation :: CPython",
]

dependencies = [
"freetype-py", "simplification", "pillow"
]

[tool.setuptools.packages.find]
include = ["python_alright_fonts*"]
namespaces = false

[project.urls]
"Homepage" = "https://github.com/lowfatcode/alright-fonts"
"Documentation" = "https://github.com/lowfatcode/alright-fonts/README.md"