kopia lustrzana https://github.com/peterhinch/micropython-font-to-py
Initial commit of specification
rodzic
cbb76dae92
commit
da5131a4a2
161
README.md
161
README.md
|
@ -1,2 +1,161 @@
|
|||
# micropython-font-to-py
|
||||
A Python 3 utility to convert fonts to Python source capable of being frozen as bytecode
|
||||
|
||||
This is currently a work in progress. This document specifies a forthcoming
|
||||
module.
|
||||
|
||||
# Rationale
|
||||
|
||||
MicroPython platforms generally have limited RAM, but more abundant storage in
|
||||
the form of flash memory. Font files tend to be relatively large. The
|
||||
conventional technique of rendering strings to a device involves loading the
|
||||
entire font into RAM. This is fast but ram intensive. The alternative of storing
|
||||
the font as a random access file and loading individual characters into RAM on
|
||||
demand is too slow for reasonable performance on most display devices.
|
||||
|
||||
This alternative implements a font as a Python source file, with the data being
|
||||
declared as ``bytes`` objects. Such a file may be frozen as bytecode. On import
|
||||
very little RAM is used, yet the data may be accessed fast.
|
||||
|
||||
It is intended that the resultant file be usable with a variety of display
|
||||
devices and drivers. These include:
|
||||
|
||||
1. Drivers using the official ``framebuffer`` class.
|
||||
2. Drivers using ``bytearray`` instances as frame buffers.
|
||||
3. Drivers for devices where the frame buffer is implemented in external
|
||||
hardware.
|
||||
|
||||
# Limitations
|
||||
|
||||
Only the ASCII character set from chr(32) to chr(126) is supported. Kerning is
|
||||
not supported.
|
||||
|
||||
# Usage
|
||||
|
||||
``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
|
||||
required height in pixels and outputs a Python 3 source file. The pixel layout
|
||||
is determined by command arguments. Arguments also define whether the font is to
|
||||
be stored in proportional or fixed width form.
|
||||
|
||||
Further arguments will be specified by the documentation for the specific
|
||||
device driver in use. They ensure that the byte contents and layout are correct
|
||||
for the target display hardware.
|
||||
|
||||
Example usage to produce a file ``myfont.py`` with height of 23 pixels
|
||||
``font_to_py.py FreeSans.ttf 23 -o myfont.py``
|
||||
|
||||
## Arguments
|
||||
|
||||
### Mandatory arguments:
|
||||
|
||||
1. Font file path. Must be a ttf or otf file.
|
||||
2. Height in pixels.
|
||||
3. -o or --outfile Output file path. Must have a .py extension.
|
||||
|
||||
### Optional arguments:
|
||||
|
||||
* -f or --fixedpitch If specified, all characters will have the same width. By
|
||||
default fonts are assumed to be variable pitch.
|
||||
* -h Specifies horizontal mapping (default is vertical).
|
||||
* -b Specifies big-endian bytes (default little endian).
|
||||
* -n For variable pitch fonts specifies that blank advance bits should be
|
||||
omitted from the character map.
|
||||
|
||||
Optional arguments other than the fixed pitch argument will be specified in the
|
||||
device driver documentation.
|
||||
|
||||
## The font file
|
||||
|
||||
Assume that the you have employed the utility to create a file ``myfont.py``. In
|
||||
your code you will issue
|
||||
|
||||
```python
|
||||
from myfont import myfont
|
||||
```
|
||||
|
||||
The ``myfont`` instance will then be used by the device driver to render strings
|
||||
on demand.
|
||||
|
||||
# Implementation
|
||||
|
||||
This section of the README is intended for writers of device drivers.
|
||||
|
||||
## Overview
|
||||
|
||||
The Python source file produced by ``font_to_py.py`` provides a fast means of
|
||||
accessing the byte data corresponding to an individual character. It is the
|
||||
responsibility of the driver to copy that data to the framebuffer or physical
|
||||
device. The purpose of the command line arguments specified to the user is to
|
||||
ensure that the data layout is optimised for the device so that the copy is a
|
||||
simple bytewise copy.
|
||||
|
||||
The user program imports a Python font file. This instantiates a ``PyFont``
|
||||
object with appropriate constructor arguments such as the metrics of the
|
||||
specific font. When the user program needs to display a string it passes the
|
||||
instance to the device driver. The instance exposes appropriate font metrics
|
||||
defined in pixels and a ``get_ch()`` method. The latter provides fast access to
|
||||
the bytes corresponding to an individual character together with character
|
||||
specific metrics.
|
||||
|
||||
All fixed width characters include blank bits after the character bits to define
|
||||
the width. By default variable pitch characters include blank "advance" bits to
|
||||
provide correct spacing between characters. These may optionally be omitted from
|
||||
the data with the -n argument. In this instance the driver may supply them: the
|
||||
number of bits to be supplied is stored in byte 1 of the character data.
|
||||
|
||||
## The PyFont class
|
||||
|
||||
This is defined in the file ``pyfont.py``. An outline definition of the class is
|
||||
as follows:
|
||||
|
||||
```python
|
||||
class PyFont(object):
|
||||
def __init__(self, font, index, vert, horiz):
|
||||
self.bits_horiz = horiz # Width of monospaced char or 0 if variable
|
||||
self.bits_vert = vert # Height of all chars
|
||||
self._index = index
|
||||
self._font = font
|
||||
|
||||
def get_ch(self, ch):
|
||||
from uctypes import addressof
|
||||
# Replace out of range characters with a default
|
||||
# compute offset of current character bitmap and get char metrics
|
||||
return addressof(self._font) + offset, self.bits_vert, char_width, advance)
|
||||
|
||||
def get_properties(self):
|
||||
return self.bits_vert, self.bits_horiz
|
||||
```
|
||||
|
||||
The device driver calls the ``get_ch`` method for each character. If the driver
|
||||
is to provide the advance (user told to use the -n option) the ``advance`` value
|
||||
is the number of bits to supply. Otherwise its value will be 0.
|
||||
|
||||
## Font files
|
||||
|
||||
Assume the user has run the utility to produce a file ``myfont.py`` This then
|
||||
has the following outline definition (in practice the bytes objects are large):
|
||||
|
||||
```python
|
||||
import pyfont
|
||||
_myfont = b'\x00\x00`
|
||||
_myfont_index = b'\x00\x00\x23\x00\`
|
||||
myfont = pyfont.PyFont(_myfont, _myfont_index, 24, 0)
|
||||
|
||||
```
|
||||
|
||||
# Specification Notes
|
||||
|
||||
The design aims primarily to minimise RAM usage. Minimising the size of the
|
||||
bytecode is a secondary aim. Indexed addressing will be used to reduce this in
|
||||
the case of proportional fonts, at a small cost in performance. The size of the
|
||||
Python source file is a lesser consideration, with readability being prioritised
|
||||
over size. Hence they will be "pretty printed" with the large bytes objects
|
||||
split over multiple lines for readability.
|
||||
|
||||
The bytes object for the font will store the character width in byte 0 and the
|
||||
advance in byte 1. This will be transparent to the device driver, the pointer
|
||||
returned by ``get_ch`` will be to the raw font data. This implies that the width
|
||||
will be restricted to 256 pixels: huge in the context of realistic hardware.
|
||||
|
||||
This general approach has been tested on a Pyboard connected to LCD hardware
|
||||
having an onboard frame buffer. The visual performance is good.
|
||||
|
|
Ładowanie…
Reference in New Issue