From 4e07ed605bfb160e120ead942b5c96ab1960f0de Mon Sep 17 00:00:00 2001 From: Peter Hinch Date: Sun, 31 Jan 2021 17:53:24 +0000 Subject: [PATCH] Document handling of bitmapped font files. --- FONT_TO_PY.md | 91 ++++++++++++++++++++++++++++-------------------- README.md | 37 +++++++++++--------- font_to_py.py | 16 ++++----- writer/writer.py | 2 +- 4 files changed, 84 insertions(+), 62 deletions(-) diff --git a/FONT_TO_PY.md b/FONT_TO_PY.md index 51682c3..50f5e18 100644 --- a/FONT_TO_PY.md +++ b/FONT_TO_PY.md @@ -1,15 +1,20 @@ -# font_to_py.py +# 1. font_to_py.py -Convert a font file to Python source code. Python font files provide a much -faster way to access glyphs than the principal alternative which is a random -access file on the filesystem. +This PC utility converts an industry standard font file to Python source code. -Another benefit is that they can save large amounts of 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. +Python font files offer advantages on microcontroller platforms running +MicroPython. They provide a much faster way to render glyphs than the principal +alternative which is a random access binary file on the filesystem. -## Recent revisions +The format of the Python font file is designed to save large amounts of 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. + +## 1.1 Recent revisions + +1 Feb 2021 V0.4 With thanks to @enigmaniac for the suggestion and code ideas. + 1. Now supports `bdf` and `pcf` font files for better results at small sizes. 17 Oct 2019 V0.33 With thanks to Stephen Irons (@ironss). 1. Fix bug where input rather than output filename was checked. @@ -32,7 +37,7 @@ API is unchanged. ###### [Main README](./README.md) -# Dependencies +# 2. Dependencies The utility requires Python 3.2 or greater, also `freetype` which may be installed using `pip3`. On Linux (you may need a root prompt): @@ -42,13 +47,15 @@ installed using `pip3`. On Linux (you may need a root prompt): # pip3 install freetype-py ``` -# Usage +# 3. 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. By default fonts are stored in variable -pitch form. This may be overidden by a command line argument. +required height in pixels and outputs a Python 3 source file. Alternatively it +will accept a `bdf` or `pcf` source file (which includes a height definition). +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. By default the printable ASCII character set (ordinal values 32 to 126 inclusive) is supported (i.e. not including control characters). Command line @@ -65,16 +72,18 @@ Examples of usage to produce Python fonts with a height of 23 pixels: $ font_to_py.py FreeSans.ttf 23 myfont.py $ font_to_py.py -k extended FreeSans.ttf 23 my_extended_font.py ``` -## Arguments +## 3.1 Arguments -### Mandatory positional arguments: +### 3.1.1 Mandatory positional arguments: 1. Font file path. Must be a ttf or otf file. - 2. Height in pixels. + 2. Height in pixels. In the case of `bdf` or `pcf` files a height of 0 should + be specified as the height is retrieved from the file. 3. Output file path. Filename must have a .py extension (unless writing a - binary font). + binary font). 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. -### Optional arguments: +### 3.1.2 Optional arguments: * -f or --fixed If specified, all characters will have the same width. By default fonts are assumed to be variable pitch. @@ -121,29 +130,32 @@ pitch result. A better apearance would be achieved by using a font designed as monospaced. There have been reports that producing fonts with Unicode characters outside -the ASCII set from ttf files is unreliable. If expected results are not -achieved, use an otf font. I have successfully created Cyrillic and extended +the ASCII set from `ttf` files is unreliable. If expected results are not +achieved, use an `otf` font. I have successfully created Cyrillic and extended fonts from a `ttf`, so I suspect the issue may be source fonts lacking the required glyphs. -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. +### 3.1.3 The -i or --iterate arg -### Output +This is for specialist applications; it 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. -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 -displayed on completion, along with the width of the widest character. +### 3.1.4 The height arg -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. +In the case of scalable `ttf` or `otf` source files 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 displayed on completion, along +with the width of the widest character. -## The font file +If a `bdf` or `pcf` bitmapped font is specified, the `height` arg should be 0. +A nonzero value will cause a warning message to be printed and the value will +be ignored. + +## 3.2 The font file Assume that the you have employed the utility to create a file `myfont.py`. In -your code you will issue +your code you will issue: ```python import myfont @@ -154,9 +166,14 @@ to render strings on demand. A practical example may be studied [here](./writer/writer_demo.py). The detailed layout of the Python file may be seen [here](./writer/DRIVERS.md). -### Python font files +# 4. Python font files -These start with a comment which is the command line used to create the font. +Users of the `Writer` or `CWriter` classes or of +[nano-gui](https://github.com/peterhinch/micropython-nano-gui) do not need to +study the file format. These details are provided for those wishing to access +Python font files directly. + +Files start with a comment which is the command line used to create the font. They include the following functions: 1. `height()` Height of bitmaps in pixels (all are the same height). @@ -177,7 +194,7 @@ They include the following functions: See [this link](https://stackoverflow.com/questions/27631736/meaning-of-top-ascent-baseline-descent-bottom-and-leading-in-androids-font) for an explanation of `baseline`. -### Binary font files +# 5. Binary font files 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 @@ -197,7 +214,7 @@ Only the following optional arguments are valid: * -x or --xmap. * -r or --reverse. -# Dependencies, links and licence +# 6. Dependencies, links and licence The code is released under the MIT licence. The `font_to_py.py` utility requires Python 3.2 or later. diff --git a/README.md b/README.md index defff6b..fb406aa 100644 --- a/README.md +++ b/README.md @@ -4,9 +4,11 @@ This repository defines a method of creating and deploying fonts for use with MicroPython display drivers. A PC utility renders industry standard font files as a bitmap in the form of Python sourcecode. A MicroPython module enables such files to be displayed on devices with suitable device drivers. These include -OLED displays using the SSD1306 chip and the official device driver. +OLED displays using the SSD1306 chip and the official device driver. Compatible +drivers for a variety of display technologies are available as part of the +[nano-gui repository](https://github.com/peterhinch/micropython-nano-gui). -# Introduction +# 1. Introduction MicroPython platforms generally have limited RAM, but more abundant storage in the form of flash memory. Font files tend to be relatively large. The @@ -29,7 +31,7 @@ The resultant file is usable with two varieties of display device drivers: 2. Drivers for displays where the frame buffer is implemented in the display device hardware. -# Solution +# 2. Solution This comprises three components, links to docs below: @@ -41,12 +43,13 @@ This comprises three components, links to docs below: device drivers. Provides details of the font file format and information on ensuring comptibility with the `Writer` classes. -# font_to_py.py +# 3. font_to_py.py -This command line utility is written in Python 3 and runs on a PC. It takes -as input a font file in `ttf` or `otf` form together with a height in pixels -and outputs a Python source file containing the font as a bitmap. Fixed and -variable pitch rendering are supported. The design has the following aims: +This command line utility is written in Python 3 and runs on a PC. To convert +a scalable font to Python the utility takes input a font file in `ttf` or `otf` +form together with a height in pixels and outputs a Python source file +containing the font as a bitmap. Fixed and variable pitch rendering are +supported. The design has the following aims: * Independence of specific display hardware. * The path from font file to Python code to be fully open source. @@ -58,24 +61,26 @@ The second is achieved by using Freetype and the Freetype Python bindings. Its use is documented [here](./FONT_TO_PY.md). This also details measurements of RAM usage when importing fonts stored as frozen bytecode. -# Limitations +## 3.1 Small fonts + +Converting scalable `ttf` or `otf` files programmatically works best for larger +fonts. For small fonts it is best to use hand-designed bitmapped font files. +These are now supported: `bdf` or `pcf` font files may be converted to Python +source in the same format as files originating from scalable fonts. + +## 3.2 Limitations Kerning is not supported. Fonts are one bit per pixel. Colour displays are supported by the `CWriter` class which adds colour information at the rendering stage. This assumes that all pixels of a character are coloured identically. -Converting font files programmatically works best for larger fonts. For small -fonts, like the 8*8 default used by the SSD1306 driver, it is best to use -hand-designed binary font files: these are optiised for rendering at a specific -size. - By default the `font_to_py.py` utility produces the ASCII character set from `chr(32)` to `chr(126)` inclusive. Command line options enable the character set to be modified to include arbitrary Unicode characters. Alternative sets may be specified such as for non-English languages. Efficient support is now provided for sparse character sets. -# Font file interface +# 4. Font file interface A font file is imported in the usual way e.g. `import font14`. Python font files contain the following functions. These return values defined by the @@ -103,6 +108,6 @@ The `font_to_py.py` utility allows a default glyph to be specified (typically The `min_ch` and `max_ch` functions are mainly relevant to contiguous character sets. -# Licence +# 5. Licence All code is released under the MIT licence. diff --git a/font_to_py.py b/font_to_py.py index ce30d6d..e5d267e 100755 --- a/font_to_py.py +++ b/font_to_py.py @@ -6,11 +6,12 @@ # Some code adapted from Daniel Bader's work at the following URL # http://dbader.org/blog/monochrome-font-rendering-with-freetype-and-python -# With thanks to Stephen Irons @ironss for various improvements. +# With thanks to Stephen Irons @ironss for various improvements, also to +# @enigmaniac for ideas around handling `bdf` and `pcf` files. # The MIT License (MIT) # -# Copyright (c) 2016-2019 Peter Hinch +# Copyright (c) 2016-2021 Peter Hinch # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to deal @@ -46,7 +47,7 @@ MAXCHAR = 126 # 94 chars # Lines are broken with \ for readability. -class ByteWriter(object): +class ByteWriter: bytes_per_line = 16 def __init__(self, stream, varname): @@ -92,7 +93,7 @@ def var_write(stream, name, value): # FONT HANDLING -class Bitmap(object): +class Bitmap: """ A 2D bitmap image represented as a list of byte values. Each byte indicates the state of a single pixel in the bitmap. A value of 0 indicates that the @@ -166,7 +167,7 @@ class Bitmap(object): row += 1 -class Glyph(object): +class Glyph: def __init__(self, pixels, width, height, top, left, advance_width): self.bitmap = Bitmap(width, height, pixels) @@ -495,7 +496,6 @@ def write_func(stream, name, arg): def write_font(op_path, font_path, height, monospaced, hmap, reverse, minchar, maxchar, defchar, charset, iterate, bitmapped): -# fnt = Font(font_path, height, minchar, maxchar, monospaced, defchar, charset, bitmapped) try: fnt = Font(font_path, height, minchar, maxchar, monospaced, defchar, charset, bitmapped) except freetype.ft_errors.FT_Exception: @@ -576,8 +576,8 @@ def quit(msg): print(msg) sys.exit(1) -DESC = """font_to_py.py -Utility to convert ttf or otf font files to Python source. +DESC = """font_to_py.py V0.4.0 +Utility to convert ttf, otf, bdf and pcf font files to Python source. Sample usage: font_to_py.py FreeSans.ttf 23 freesans.py diff --git a/writer/writer.py b/writer/writer.py index 5f09006..8981adb 100644 --- a/writer/writer.py +++ b/writer/writer.py @@ -6,7 +6,7 @@ # V0.35 Sept 2020 Fast rendering option for color displays # Released under the MIT License (MIT). See LICENSE. -# Copyright (c) 2019-2020 Peter Hinch +# Copyright (c) 2019-2021 Peter Hinch # A Writer supports rendering text to a Display instance in a given font. # Multiple Writer instances may be created, each rendering a font to the