2016-11-15 12:10:21 +00:00
|
|
|
# font_to_py.py
|
|
|
|
|
2018-07-04 06:38:00 +00:00
|
|
|
Convert a font file to Python source code. The principal reason for doing this
|
|
|
|
is to save RAM on resource-limited targets: the font file may be incorporated
|
|
|
|
into a firmware build such that it occupies flash memory rather than scarce
|
|
|
|
RAM. Python code built into firmware is known as frozen bytecode.
|
2016-11-15 12:10:21 +00:00
|
|
|
|
2018-08-14 16:22:21 +00:00
|
|
|
###### [Main README](./README.md)
|
2018-06-02 11:03:11 +00:00
|
|
|
|
2018-08-14 16:22:21 +00:00
|
|
|
# Dependencies
|
|
|
|
|
|
|
|
The utility requires Python 3.2 or greater, also `freetype` which may be
|
|
|
|
installed using `pip3`. On Linux at a root prompt:
|
2018-06-02 11:03:11 +00:00
|
|
|
|
|
|
|
```shell
|
|
|
|
# apt-get install python3-pip
|
|
|
|
# pip3 install freetype-py
|
|
|
|
```
|
|
|
|
|
2016-11-15 12:10:21 +00:00
|
|
|
# Usage
|
|
|
|
|
2018-07-04 06:38:00 +00:00
|
|
|
`font_to_py.py` is a command line utility written in Python 3. It is run on a
|
|
|
|
PC. It takes as input a font file with a `ttf` or `otf` extension and a
|
2016-11-15 12:10:21 +00:00
|
|
|
required height in pixels and outputs a Python 3 source file. The pixel layout
|
|
|
|
is determined by command arguments. By default fonts are stored in variable
|
|
|
|
pitch form. This may be overidden by a command line argument.
|
|
|
|
|
2017-01-12 15:48:26 +00:00
|
|
|
By default the ASCII character set (ordinal values 32 to 126 inclusive) is
|
|
|
|
supported. Command line arguments can modify this range as required, if
|
2018-08-31 10:17:58 +00:00
|
|
|
necessary to include extended ASCII characters up to 255. Alternatively non
|
|
|
|
English or non-contiguous character sets may be defined.
|
2017-01-12 15:48:26 +00:00
|
|
|
|
2016-11-15 12:10:21 +00:00
|
|
|
Further arguments ensure that the byte contents and layout are correct for the
|
|
|
|
target display hardware. Their usage should be specified in the documentation
|
|
|
|
for the device driver.
|
|
|
|
|
2018-07-04 06:38:00 +00:00
|
|
|
Example usage to produce a file `myfont.py` with height of 23 pixels:
|
|
|
|
`font_to_py.py FreeSans.ttf 23 myfont.py`
|
2016-11-15 12:10:21 +00:00
|
|
|
|
|
|
|
## Arguments
|
|
|
|
|
|
|
|
### Mandatory positional arguments:
|
|
|
|
|
|
|
|
1. Font file path. Must be a ttf or otf file.
|
|
|
|
2. Height in pixels.
|
2017-01-12 15:48:26 +00:00
|
|
|
3. Output file path. Filename must have a .py extension.
|
2016-11-15 12:10:21 +00:00
|
|
|
|
|
|
|
### Optional arguments:
|
|
|
|
|
|
|
|
* -f or --fixed If specified, all characters will have the same width. By
|
|
|
|
default fonts are assumed to be variable pitch.
|
2017-01-12 15:48:26 +00:00
|
|
|
* -x or --xmap Specifies horizontal mapping (default is vertical).
|
|
|
|
* -r or --reverse Specifies bit reversal in each font byte.
|
|
|
|
* -s or --smallest Ordinal value of smallest character to be stored. Default
|
|
|
|
32 (ASCII space).
|
|
|
|
* -l or --largest Ordinal value of largest character to be stored. Default 126.
|
|
|
|
* -e or --errchar Ordinal value of character to be rendered if an attempt is
|
|
|
|
made to display an out-of-range character. Default 63 (ASCII "?").
|
2018-09-06 10:38:52 +00:00
|
|
|
* -i or --iterate Specialist use. See below.
|
2018-08-20 06:45:39 +00:00
|
|
|
* -c or --charset Option to restrict the characters in the font to a specific
|
|
|
|
set. See below.
|
2018-08-28 10:52:33 +00:00
|
|
|
* -k or --charset_file Obtain the character set from a file. Typical use is
|
|
|
|
for alternative character sets such as Cyrillic: the file must contain the
|
2018-08-29 06:40:28 +00:00
|
|
|
character set to be included. An example file is `cyrillic`.
|
2018-08-20 06:45:39 +00:00
|
|
|
|
2018-08-31 10:17:58 +00:00
|
|
|
The -c option may be used to reduce the size of the font file by limiting the
|
|
|
|
character set. If the font file is frozen as bytecode this will not reduce RAM
|
|
|
|
usage but it will conserve flash. Example usage for a digital clock font:
|
2018-08-20 06:45:39 +00:00
|
|
|
|
|
|
|
```shell
|
2018-08-20 10:54:36 +00:00
|
|
|
$ font_to_py.py Arial.ttf 20 arial_clock.py -c 1234567890:
|
2018-08-20 06:45:39 +00:00
|
|
|
```
|
2018-08-28 10:52:33 +00:00
|
|
|
Example usage with the -k option:
|
|
|
|
```shell
|
2018-08-31 10:17:58 +00:00
|
|
|
font_to_py.py FreeSans.ttf 20 freesans_cyr_20.py -k cyrillic
|
2018-08-28 10:52:33 +00:00
|
|
|
```
|
|
|
|
|
2018-08-20 06:45:39 +00:00
|
|
|
If a character set is specified, `--smallest` and `--largest` should not be
|
2018-08-31 10:17:58 +00:00
|
|
|
specified: these values are computed from the character set.
|
2016-11-15 12:10:21 +00:00
|
|
|
|
2017-01-12 15:48:26 +00:00
|
|
|
Any requirement for arguments -xr will be specified in the device driver
|
|
|
|
documentation. Bit reversal is required by some display hardware.
|
2016-11-15 12:10:21 +00:00
|
|
|
|
2018-08-20 06:45:39 +00:00
|
|
|
There have been reports that producing extended ASCII characters (ordinal
|
|
|
|
value > 127) from ttf files is unreliable. If the expected results are not
|
2018-08-31 10:17:58 +00:00
|
|
|
achieved, use an otf font. However I have successfully created the Cyrillic
|
|
|
|
font from a `ttf`. Perhaps not all fonts are created equal...
|
2018-03-11 11:01:46 +00:00
|
|
|
|
2018-09-06 10:38:52 +00:00
|
|
|
The `-i` or `--iterate` argument. For specialist applications. Specifying this
|
|
|
|
causes a generator function `glyphs` to be included in the Python font file. A
|
|
|
|
generator instantiated with this will yield `bitmap`, `height`, and `width` for
|
|
|
|
every glyph in the font.
|
|
|
|
|
2016-11-20 17:12:32 +00:00
|
|
|
### Output
|
|
|
|
|
|
|
|
The specified height is a target. The algorithm gets as close to the target
|
|
|
|
height as possible (usually within one pixel). The actual height achieved is
|
2017-01-12 15:48:26 +00:00
|
|
|
displayed on completion, along with the width of the widest character.
|
2016-11-20 17:12:32 +00:00
|
|
|
|
|
|
|
A warning is output if the output filename does not have a .py extension as the
|
|
|
|
creation of a binary font file may not be intended.
|
|
|
|
|
2016-11-15 12:10:21 +00:00
|
|
|
## The font file
|
|
|
|
|
2018-07-04 06:38:00 +00:00
|
|
|
Assume that the you have employed the utility to create a file `myfont.py`. In
|
2016-11-15 12:10:21 +00:00
|
|
|
your code you will issue
|
|
|
|
|
|
|
|
```python
|
|
|
|
import myfont
|
|
|
|
```
|
|
|
|
|
2018-07-04 06:38:00 +00:00
|
|
|
The `myfont` module name will then be used to instantiate a `Writer` object
|
2016-11-15 13:58:52 +00:00
|
|
|
to render strings on demand. A practical example may be studied
|
2018-08-20 10:34:49 +00:00
|
|
|
[here](./writer/writer_demo.py).
|
|
|
|
The detailed layout of the Python file may be seen [here](./writer/DRIVERS.md).
|
2016-11-15 12:10:21 +00:00
|
|
|
|
2016-11-18 18:10:03 +00:00
|
|
|
### Binary font files
|
|
|
|
|
2018-07-04 06:38:00 +00:00
|
|
|
There is an option to create a binary font file, specified with a `-b` or
|
|
|
|
`--binary` command line argument. In this instance the output filename must
|
|
|
|
not have a `.py` extension. This is primarily intended for the e-paper driver
|
2017-01-12 15:48:26 +00:00
|
|
|
in applications where the file is to be stored on the display's internal flash
|
2016-11-18 18:10:03 +00:00
|
|
|
memory rather than using frozen Python modules.
|
|
|
|
|
2017-01-12 15:48:26 +00:00
|
|
|
The technique of accessing character data from a random access file is slow
|
|
|
|
and thus probably only applicable to devices such as e-paper where the update
|
|
|
|
time is slow.
|
|
|
|
|
|
|
|
Binary files currently support only the standard ASCII character set. There is
|
|
|
|
no error character: the device driver must ensure that seeks are within range.
|
|
|
|
Consequently the following arguments are invalid:
|
|
|
|
|
|
|
|
* -s or --smallest
|
|
|
|
* -l or --largest
|
|
|
|
* -e or --errchar
|
2016-11-18 18:10:03 +00:00
|
|
|
|
2016-11-15 12:10:21 +00:00
|
|
|
# Dependencies, links and licence
|
|
|
|
|
2018-08-31 10:17:58 +00:00
|
|
|
The code is released under the MIT licence. The `font_to_py.py` utility
|
|
|
|
requires Python 3.2 or later.
|
2016-11-15 12:10:21 +00:00
|
|
|
|
|
|
|
The module relies on [Freetype](https://www.freetype.org/) which is included in most Linux distributions.
|
|
|
|
It uses the [Freetype Python bindings](http://freetype-py.readthedocs.io/en/latest/index.html)
|
|
|
|
which will need to be installed.
|
|
|
|
My solution draws on the excellent example code written by Daniel Bader. This
|
2018-08-31 10:17:58 +00:00
|
|
|
may be viewed [here](https://dbader.org/blog/monochrome-font-rendering-with-freetype-and-python)
|
|
|
|
and [here](https://gist.github.com/dbader/5488053).
|
2016-11-15 12:10:21 +00:00
|
|
|
|
|
|
|
# Appendix: RAM utilisation Test Results
|
|
|
|
|
2018-08-14 16:22:21 +00:00
|
|
|
The supplied `freesans20.py` and `courier20.py` files were frozen as bytecode
|
|
|
|
on a Pyboard V1.0. The following code was pasted at the REPL:
|
2016-11-15 12:10:21 +00:00
|
|
|
|
|
|
|
```python
|
|
|
|
import gc, micropython
|
|
|
|
gc.collect()
|
|
|
|
micropython.mem_info()
|
|
|
|
|
2018-08-14 16:22:21 +00:00
|
|
|
import freesans20
|
|
|
|
|
|
|
|
gc.collect()
|
|
|
|
micropython.mem_info()
|
|
|
|
|
|
|
|
import courier20
|
2016-11-15 12:10:21 +00:00
|
|
|
|
|
|
|
gc.collect()
|
|
|
|
micropython.mem_info()
|
|
|
|
|
|
|
|
def foo():
|
2018-08-14 16:22:21 +00:00
|
|
|
addr, height, width = freesans20.get_ch('a')
|
2016-11-15 12:10:21 +00:00
|
|
|
|
|
|
|
foo()
|
|
|
|
|
|
|
|
gc.collect()
|
|
|
|
micropython.mem_info()
|
2018-08-14 16:22:21 +00:00
|
|
|
print(len(freesans20._font) + len(freesans20._index))
|
2016-11-15 12:10:21 +00:00
|
|
|
```
|
|
|
|
|
2018-08-20 12:53:20 +00:00
|
|
|
The memory used was 1712, 2032, 2384 and 2416 bytes. As increments over the
|
|
|
|
prior state this corresponds to 320, 352 and 32 bytes. The `print` statement
|
|
|
|
shows the RAM which would be consumed by the data arrays: this was 3956 bytes
|
2018-08-14 16:22:21 +00:00
|
|
|
for `freesans20`.
|
2016-11-15 12:10:21 +00:00
|
|
|
|
2018-07-04 06:38:00 +00:00
|
|
|
The `foo()` function emulates the behaviour of a device driver in rendering a
|
2018-08-14 16:22:21 +00:00
|
|
|
character to a display. The local variables constitute memory which is
|
|
|
|
reclaimed on exit from the function. Its additional RAM use was 16 bytes.
|
2016-11-15 12:10:21 +00:00
|
|
|
|
|
|
|
## Conclusion
|
|
|
|
|
2018-08-14 16:22:21 +00:00
|
|
|
With a font of height 20 pixels RAM saving was an order of magnitude. The
|
|
|
|
saving will be greater if larger fonts are used as RAM usage is independent of
|
|
|
|
the array sizes.
|