├── .gitignore ├── FONT_TO_PY.md ├── LICENSE ├── README.md ├── c_to_python_font.py ├── charsets ├── Chinese_Japanese ├── Chinese_Japanese.md ├── Hebrew.txt ├── RTL_languages.md ├── Thailand ├── chess ├── chess.md ├── cyrillic └── extended ├── font_test.py ├── font_to_py.py ├── icon_fonts ├── README.md ├── back.c ├── default.c ├── filenames.txt ├── fwd.c ├── icon_font.jpg ├── pause.c ├── play.c └── stop.c └── writer ├── DRIVERS.md ├── WRITER.md ├── courier20.py ├── font10.py ├── font6.py ├── freesans20.py ├── images ├── IMG_2861.JPG ├── IMG_2866.JPG ├── fields.JPG ├── fonts.png ├── mixed.JPG └── rjust.JPG ├── old_versions ├── writer_fw_compatible.py └── writer_minimal.py ├── ssd1306_setup.py ├── writer.py ├── writer_demo.py ├── writer_gui.py └── writer_tests.py /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | env/ 12 | build/ 13 | develop-eggs/ 14 | dist/ 15 | downloads/ 16 | eggs/ 17 | .eggs/ 18 | lib/ 19 | lib64/ 20 | parts/ 21 | sdist/ 22 | var/ 23 | *.egg-info/ 24 | .installed.cfg 25 | *.egg 26 | 27 | # PyInstaller 28 | # Usually these files are written by a python script from a template 29 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 30 | *.manifest 31 | *.spec 32 | 33 | # Installer logs 34 | pip-log.txt 35 | pip-delete-this-directory.txt 36 | 37 | # Unit test / coverage reports 38 | htmlcov/ 39 | .tox/ 40 | .coverage 41 | .coverage.* 42 | .cache 43 | nosetests.xml 44 | coverage.xml 45 | *,cover 46 | .hypothesis/ 47 | 48 | # Translations 49 | *.mo 50 | *.pot 51 | 52 | # Django stuff: 53 | *.log 54 | local_settings.py 55 | 56 | # Flask stuff: 57 | instance/ 58 | .webassets-cache 59 | 60 | # Scrapy stuff: 61 | .scrapy 62 | 63 | # Sphinx documentation 64 | docs/_build/ 65 | 66 | # PyBuilder 67 | target/ 68 | 69 | # IPython Notebook 70 | .ipynb_checkpoints 71 | 72 | # pyenv 73 | .python-version 74 | 75 | # celery beat schedule file 76 | celerybeat-schedule 77 | 78 | # dotenv 79 | .env 80 | 81 | # virtualenv 82 | venv/ 83 | ENV/ 84 | 85 | # Spyder project settings 86 | .spyderproject 87 | 88 | # Rope project settings 89 | .ropeproject 90 | -------------------------------------------------------------------------------- /FONT_TO_PY.md: -------------------------------------------------------------------------------- 1 | # font_to_py.py Creation of Python font files 2 | 3 | A PC utility for converting industry standard font files to Python source code. 4 | 5 | # 0. Contents 6 | 7 | 1. [Introdction](./FONT_TO_PY.md#1-introduction) Creating Python fonts. 8 | 1.1 [Revision history](./FONT_TO_PY.md#11-revision-history) 9 | 2. [Installation](./FONT_TO_PY.md#2-installation) 10 | 3. [Usage](./FONT_TO_PY.md#3-usage) 11 | 3.1 [Arguments](./FONT_TO_PY.md#31-arguments) 12 |      3.1.1 [Mandatory positional arguments](./FONT_TO_PY.md#311-mandatory-positional-arguments) 13 |      3.1.2 [Optional arguments](./FONT_TO_PY.md#312-optional-arguments) 14 |      3.1.3 [The height arg](./FONT_TO_PY.md#313-the-height-arg) 15 | 3.2 [The font file](./FONT_TO_PY.md#32-the-font-file) How to use the font file. 16 | 4. [Python font files](./FONT_TO_PY.md#4-python-font-files) Python font file format. 17 | 5. [Binary font files](./FONT_TO_PY.md#5-binary-font-files) Binary font file format. 18 | 6. [Dependencies links and licence](./FONT_TO_PY.md#6-dependencies-links-and-licence) Acknowledgement of sources. 19 | 20 | [Appendix 1 RAM utilisation Test Results](./FONT_TO_PY.md#appendix-1-ram-utilisation-test-results) 21 | [Appendix 2 Recent improvements](./FONT_TO_PY.md#appendix-2-recent-improvements) 22 | [Appendix 3 Testing](./FONT_TO_PY.md#appendix-3-testing) A desktop utility to check fonts. 23 | [Appendix 4 Custom character sets](./FONT_TO_PY.md#appendix-4-custom-character-sets) Creating e.g. fonts having non-English character sets. 24 | [Appendix 5 Iteration](./FONT_TO_PY.md#appendix-5-iteration) Enabling a font to support iteration. 25 | 26 | # 1. Introduction 27 | 28 | This PC utility converts an industry standard font file to Python source code. 29 | 30 | Python font files offer advantages on microcontroller platforms running 31 | MicroPython. They provide a much faster way to render glyphs than the principal 32 | alternative which is a random access binary file on the filesystem. 33 | 34 | The format of the Python font file is designed to save large amounts of RAM on 35 | resource-limited targets: the font file may be incorporated into a firmware 36 | build such that it occupies flash memory rather than scarce RAM. Python code 37 | built into firmware is known as frozen bytecode. An alternative is 38 | [romfs](https://docs.micropython.org/en/latest/reference/mpremote.html#mpremote-command-romfs). 39 | This achieves the same RAM efficiency as frozen bytecode without the need to 40 | recompile. 41 | 42 | ## 1.1 Revision history 43 | 44 | 28 May 2025 V0.42.2 Publish to PyPi, update docs. 45 | 22 Mar 2024 V0.42 Default mapping is now horizontal. 46 | 30 Jan 2023 V0.41 With thanks to @ferrolive (Igor Oliveira) who supplied the 47 | charset file. 48 | 1. Charset file enables Chinese, Japanese and Korean glyphs to be specified. 49 | 2. Now allows much larger output files: sparse index is now 24 bit. 50 | 51 | 1 Feb 2021 V0.4 With thanks to @enigmaniac for the suggestion and code ideas. 52 | 1. Now supports `bdf` and `pcf` font files for better results at small sizes. 53 | 54 | 17 Oct 2019 V0.33 With thanks to Stephen Irons (@ironss). 55 | 1. Fix bug where input rather than output filename was checked. 56 | 2. Add `baseline()` to ouput file returning the maximum ascent. 57 | 3. Correct left position of rendered glyph. 58 | 59 | 21 Sept 2019 V0.22 60 | 61 | 1. Reduced output file size for sparse fonts. These result from large gaps 62 | between ordinal values of Unicode characters not in the standard ASCII set. 63 | 2. Output file has comment showing creation command line. 64 | 3. Repo includes the file `extended`. Using `-k extended` creates fonts 65 | comprising the printable ASCII set plus `°μπωϕθαβγδλΩ£`. Such a font has 96 66 | chars having ordinal values from 32-981. 67 | 4. Improvements to `font_test.py`. 68 | 5. Code emitted for sparse fonts now uses non-recursive search algorithm. 69 | 70 | Python files produced are interchangeable with those from prior versions: the 71 | API is unchanged. 72 | 73 | ###### [Main README](./README.md) 74 | ###### [Contents](./FONT_TO_PY.md#0-contents) 75 | 76 | # 2. Installation 77 | 78 | The utility requires Python 3.6 or greater. It is installed using pip: 79 | 80 | ```shell 81 | $ pip install font_to_py 82 | ``` 83 | 84 | # 3. Usage 85 | 86 | `font_to_py.py` is a command line utility written in Python 3. It is run on a 87 | PC. It takes as input a font file with a `ttf` or `otf` extension and a 88 | required height in pixels and outputs a Python 3 source file. Alternatively it 89 | will accept a `bdf` or `pcf` source file (which includes a height definition). 90 | The pixel layout is determined by command arguments. By default fonts are 91 | stored in variable pitch form. This may be overidden by a command line 92 | argument. 93 | 94 | By default the printable ASCII character set (ordinal values 32 to 126 95 | inclusive) is supported (i.e. not including control characters). Command line 96 | arguments can modify this range as required to specify arbitrary sets of 97 | Unicode characters. Non-English and non-contiguous character sets may be 98 | defined. 99 | 100 | Further arguments ensure that the byte contents and layout are correct for the 101 | target display hardware. Their usage should be specified in the documentation 102 | for the device driver. 103 | 104 | Examples of usage to produce Python fonts with a height of 23 pixels: 105 | ```shell 106 | $ font_to_py.py FreeSans.ttf 23 myfont.py 107 | $ font_to_py.py -k extended FreeSans.ttf 23 my_extended_font.py 108 | ``` 109 | ## 3.1 Arguments 110 | 111 | ### 3.1.1 Mandatory positional arguments 112 | 113 | 1. Font file path. Must be a ttf or otf file. 114 | 2. Height in pixels. In the case of `bdf` or `pcf` files a height of 0 should 115 | be specified as the height is retrieved from the file. 116 | 3. Output file path. Filename must have a .py extension (unless writing a 117 | binary font). A warning is output if the output filename does not have a .py 118 | extension as the creation of a binary font file may not be intended. 119 | 120 | ### 3.1.2 Optional arguments 121 | 122 | * -f or --fixed If specified, all characters will have the same width. By 123 | default fonts are assumed to be variable pitch. 124 | * -x or --xmap Specifies that the output file should be horizontally mapped. 125 | This is the default if no mapping is specified. Most display hardware requires 126 | horizontal mapping. 127 | * -y or --ymap Vertical mapping for specialist display hardware. Not compatible 128 | with `Writer` classes. 129 | * -r or --reverse Specifies bit reversal in each font byte. 130 | * -s or --smallest Ordinal value of smallest character to be stored. Default 131 | 32 (ASCII space). 132 | * -l or --largest Ordinal value of largest character to be stored. Default 126. 133 | * -e or --errchar Ordinal value of character to be rendered if an attempt is 134 | made to display an out-of-range character. Default 63 (ord("?")). 135 | * -i or --iterate Specialist use. See [Appendix 5](./FONT_TO_PY.md#appendix-5-iteration). 136 | * -b or --binary Create a binary font file. See [Binary font files](./FONT_TO_PY.md#5-binary-font-files). 137 | * -c or --charset Option to restrict the characters in the font to a specific 138 | set. See below. 139 | * -k or --charset_file Obtain the character set from a file. Typical use is 140 | for alternative character sets such as Cyrillic. Please see 141 | [Appendix 4](./FONT_TO_PY.md#appendix-4-custom-character-sets) for details of 142 | creation of custom character sets. 143 | 144 | The -c option may be used to reduce the size of the font file by limiting the 145 | character set. If the font file is frozen as bytecode this will not reduce RAM 146 | usage but it will conserve flash. Example usage for a digital clock font: 147 | 148 | ```shell 149 | $ font_to_py.py Arial.ttf 20 arial_clock.py -c 1234567890: 150 | ``` 151 | Example usage with the -k option: 152 | ```shell 153 | font_to_py.py FreeSans.ttf 20 freesans_cyr_20.py -k cyrillic 154 | font_to_py.py -x -k extended FreeSans.ttf 17 font10.py 155 | ``` 156 | 157 | If a character set is specified via `-c` or `-k`, then `--smallest` and 158 | `--largest` should not be specified: these values are computed from the 159 | character set. 160 | 161 | Any requirement for arguments -xr will be specified in the device driver 162 | documentation. Bit reversal is required by some display hardware. 163 | 164 | Using the -f argument with a variable pitch source font will produce a fixed 165 | pitch result. A better appearance would be achieved by using a font designed as 166 | monospaced. 167 | 168 | There have been reports that producing fonts with Unicode characters outside 169 | the ASCII set from `ttf` files is unreliable. If expected results are not 170 | achieved, use an `otf` font. I have successfully created Cyrillic and extended 171 | fonts from a `ttf`, so I suspect the issue may be source fonts lacking the 172 | required glyphs. 173 | 174 | ### 3.1.3 The height arg 175 | 176 | In the case of scalable `ttf` or `otf` source files the specified height is a 177 | target. The algorithm gets as close to the target height as possible (usually 178 | within one pixel). The actual height achieved is displayed on completion, along 179 | with the width of the widest character. 180 | 181 | If a `bdf` or `pcf` bitmapped font is specified, the `height` arg should be 0. 182 | A nonzero value will cause a warning message to be printed and the value will 183 | be ignored. 184 | 185 | ## 3.2 The font file 186 | 187 | Assume that the you have employed the utility to create a file `myfont.py`. In 188 | your code you will issue: 189 | 190 | ```python 191 | import myfont 192 | ``` 193 | 194 | The `myfont` module name will then be used to instantiate a `Writer` object 195 | to render strings on demand. A practical example may be studied 196 | [here](./writer/writer_demo.py). 197 | The detailed layout of the Python file may be seen [here](./writer/DRIVERS.md). 198 | 199 | ###### [Contents](./FONT_TO_PY.md#0-contents) 200 | 201 | # 4. Python font files 202 | 203 | Users of the `Writer` or `CWriter` classes or of 204 | [nano-gui](https://github.com/peterhinch/micropython-nano-gui) do not need to 205 | study the file format. These details are provided for those wishing to access 206 | Python font files directly. 207 | 208 | Files start with a comment which is the command line used to create the font. 209 | 210 | They include the following functions: 211 | 1. `height()` Height of bitmaps in pixels (all are the same height). 212 | 2. `max_width()` Width of widest glyph in pixels. 213 | 3. `baseline()` Offset from top of the bitmap to the baseline. This is a 214 | notional "ruler line" where glyphs are drawn. Enables rendering different 215 | fonts on a common baseline. It is a positive number of pixels. 216 | 4. `hmap()` `True` if bitmaps are horizonataly mapped. 217 | 5. `reverse()` `True` if bit reversal is used. 218 | 6. `monospaced()` `True` if bitmaps were created with fixed pitch. 219 | 7. `min_ch()` Returns smallest ordinal value in font. 220 | 8. `max_ch()` Largest ordinal value in font. 221 | 9. `get_ch()` Arg: a Unicode character. Returns three items: 222 | A memoryview into the bitmap for that character. 223 | Bitmap height in pixels. Equal to `height()` above. 224 | Bitmap width in pixels. 225 | 226 | See [this link](https://stackoverflow.com/questions/27631736/meaning-of-top-ascent-baseline-descent-bottom-and-leading-in-androids-font) 227 | for an explanation of `baseline`. 228 | 229 | # 5. Binary font files 230 | 231 | There is an option to create a binary font file, specified with a `-b` or 232 | `--binary` command line argument. In this instance the output filename must 233 | not have a `.py` extension. This format was developed for an e-paper driver 234 | where the file was stored on the display's internal flash memory; it is not 235 | clear if there is a current use case. 236 | 237 | Binary files currently support only the standard ASCII character set. There is 238 | no error character: the device driver must ensure that seeks are within range. 239 | Conversion of bdf and pcf font files is currently unsupported. 240 | 241 | Only the following optional arguments are valid: 242 | 243 | * -f or --fixed. 244 | * -x or --xmap. 245 | * -y or --ymap 246 | * -r or --reverse. 247 | 248 | The format of binary font files is described [here](./writer/DRIVERS.md). 249 | 250 | An alternative implementation of binary fonts may be found in 251 | [this repo](https://github.com/antirez/microfont). It provides for rotated 252 | rendering to a `FrameBuffer`. 253 | 254 | ###### [Contents](./FONT_TO_PY.md#0-contents) 255 | 256 | # 6. Dependencies links and licence 257 | 258 | The code is released under the MIT licence. The `font_to_py.py` utility 259 | requires Python 3.2 or later. 260 | 261 | The module relies on [Freetype](https://www.freetype.org/) which is included in most Linux distributions. 262 | It uses the [Freetype Python bindings](http://freetype-py.readthedocs.io/en/latest/index.html) 263 | which will need to be installed. 264 | My solution draws on the excellent example code written by Daniel Bader. This 265 | may be viewed [here](https://dbader.org/blog/monochrome-font-rendering-with-freetype-and-python) 266 | and [here](https://gist.github.com/dbader/5488053). 267 | 268 | # Appendix 1: RAM utilisation Test Results 269 | 270 | The supplied `freesans20.py` and `courier20.py` files were frozen as bytecode 271 | on a Pyboard V1.0. The following code was pasted at the REPL: 272 | 273 | ```python 274 | import gc, micropython 275 | gc.collect() 276 | micropython.mem_info() 277 | 278 | import freesans20 279 | 280 | gc.collect() 281 | micropython.mem_info() 282 | 283 | import courier20 284 | 285 | gc.collect() 286 | micropython.mem_info() 287 | 288 | def foo(): 289 | addr, height, width = freesans20.get_ch('a') 290 | 291 | foo() 292 | 293 | gc.collect() 294 | micropython.mem_info() 295 | print(len(freesans20._font) + len(freesans20._index)) 296 | ``` 297 | 298 | The memory used was 1712, 2032, 2384 and 2416 bytes. As increments over the 299 | prior state this corresponds to 320, 352 and 32 bytes. The `print` statement 300 | shows the RAM which would be consumed by the data arrays if they were not 301 | frozen: this was 3956 bytes for `freesans20`. 302 | 303 | The `foo()` function emulates the behaviour of a device driver in rendering a 304 | character to a display. The local variables constitute memory which is 305 | reclaimed on exit from the function. Its additional RAM use was 16 bytes. 306 | 307 | Similar figures were found in testing on a Pyboard D. Also using `romfs` instead 308 | of freezing bytecode. 309 | 310 | ## Conclusion 311 | 312 | With a font of height 20 pixels RAM saving was an order of magnitude. The 313 | saving will be greater if larger fonts are used as RAM usage is independent of 314 | the array sizes. 315 | 316 | ###### [Contents](./FONT_TO_PY.md#0-contents) 317 | 318 | # Appendix 2: Recent improvements 319 | 320 | The representation of non-contiguous character sets such as the `extended` set 321 | presents a challenge because the ordinal values of the Unicode characters can 322 | be expected to span a range much greater than the number of characters in the 323 | set. Using an index of the type used for the ASCII set would be inefficient as 324 | most of the elements would be null (pointing to the default character). 325 | 326 | The code now behaves as follows. If the character set contains no more than 95 327 | characters (including the default) the emitted Python file is as before. This 328 | keeps the code small and efficient for the common (default) case. 329 | 330 | Larger character sets are assumed to be sparse and the emitted code uses an 331 | index optimised for sparse values and a binary search algorithm. 332 | 333 | # Appendix 3: Testing 334 | 335 | The file `font_test.py` enables a Python font file to be described and rendered 336 | at the command prompt. It provides a useful way of checking unknown font files. 337 | Compatibility with files created by old versions of `font_to_py` is not 338 | guaranteed. 339 | 340 | It runs under Python 3.2 or above. Given a font `myfont.py` the following will 341 | render the supplied string (assuming that `font_test.py` has executable 342 | privilege): 343 | 344 | ```bash 345 | ./font_test myfont Hello 346 | ``` 347 | Omitting arguments (or supplying `--help`) will provide usage information. 348 | 349 | If no string is provided a default will be printed. This is designed to test 350 | fonts created with `-k extended`. Other fonts will show `?` characters for 351 | missing glyphs. 352 | 353 | Sample output: 354 | ```bash 355 | $ ./font_test.py freesans1 Hello 356 | Vertical map 357 | Normal bit order 358 | Proportional spacing 359 | Dimensions height*max_width 23 * 23 360 | Start char " " (ord 32) end char "~" (ord 126) 361 | ................................................... 362 | ##.........##................##...##............... 363 | ##.........##................##...##............... 364 | ##.........##................##...##............... 365 | ##.........##................##...##............... 366 | ##.........##................##...##............... 367 | ##.........##.......#####....##...##......#####.... 368 | ##.........##......#######...##...##.....#######... 369 | #############.....###....##..##...##....###...###.. 370 | #############....###......##.##...##...###.....###. 371 | ##.........##....##.......##.##...##...##.......##. 372 | ##.........##....###########.##...##...##.......##. 373 | ##.........##....###########.##...##...##.......##. 374 | ##.........##....##..........##...##...##.......##. 375 | ##.........##....##.......##.##...##...###.....###. 376 | ##.........##.....##.....##..##...##....###...###.. 377 | ##.........##......########..##...##.....#######... 378 | ##.........##.......#####....##...##......#####.... 379 | ................................................... 380 | ................................................... 381 | ................................................... 382 | ................................................... 383 | ................................................... 384 | ``` 385 | ###### [Contents](./FONT_TO_PY.md#0-contents) 386 | 387 | # Appendix 4 Custom character sets 388 | 389 | A common requirement is to create Python fonts containing a specific set of 390 | glyphs - typically for non-English languages, or simply to include additional 391 | symbols such as `°`. This is achieved by creating a "charset" file containing the 392 | entire set of required glyphs. Python fonts are then created using the `-k` 393 | option, specifying the charset file as follows: 394 | ```shell 395 | font_to_py.py FreeSans.ttf 20 freesans_cyr_20.py -k cyrillic 396 | font_to_py.py -x -k extended FreeSans.ttf 17 font10.py 397 | ``` 398 | Example charsets may be found in the `charsets` directory, a non-English example 399 | being `cyrillic`. The `extended` file adds unicode characters `°μπωϕθαβγδλΩ` to 400 | those in the original ASCII set of printable characters. This might be edited to 401 | produce a custom version. 402 | 403 | At risk of stating the obvious, for this process to succeed the source font file 404 | must include all specified glyphs. 405 | 406 | Submissions of charset files, particularly for non-English languages, are 407 | welcome. 408 | 409 | # Appendix 5 Iteration 410 | 411 | The `-i` or `--iterate` arg is for specialist applications; it causes a 412 | generator function `glyphs` to be included in the Python font file. A generator 413 | instantiated with this will yield `bitmap`, `height`, and `width` for every 414 | glyph in the font. 415 | 416 | ###### [Contents](./FONT_TO_PY.md#0-contents) 417 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2016 Peter Hinch 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # MicroPython font handling 2 | 3 | This repository defines a method of creating and deploying fonts for use with 4 | MicroPython display drivers. A PC utility renders industry standard font files 5 | as a bitmap in the form of Python sourcecode. A MicroPython module enables such 6 | files to be displayed on devices with suitable device drivers. These include 7 | OLED displays using the SSD1306 chip and the official device driver. Compatible 8 | drivers for a variety of display technologies are available as part of the 9 | [nano-gui repository](https://github.com/peterhinch/micropython-nano-gui). 10 | 11 | # 1. Introduction 12 | 13 | MicroPython platforms generally have limited RAM, but more abundant storage in 14 | the form of flash memory. Font files tend to be relatively large. The 15 | conventional technique of rendering strings to a device involves loading the 16 | entire font into RAM. This is fast but RAM intensive. The alternative of storing 17 | the font as a random access file and loading individual glyphs into RAM on 18 | demand is too slow for reasonable performance on most display devices. 19 | 20 | This alternative implements a font as a Python source file, with the data being 21 | declared as `bytes` objects. Such a file may be frozen as bytecode: this 22 | involves building the firmware from source with the Python file in a specific 23 | directory. On import very little RAM is used, yet the data may be accessed 24 | fast. Note that the use of frozen bytecode is entirely optional: font files may 25 | be imported in the normal way if RAM usage is not an issue. 26 | 27 | The resultant file is usable with two varieties of display device drivers: 28 | 29 | 1. Drivers where the display class is subclassed from the official 30 | `framebuffer` class. 31 | 2. Drivers for displays where the frame buffer is implemented in the display 32 | device hardware. 33 | 34 | # 2. Solution 35 | 36 | This comprises four components, links to docs below: 37 | 38 | 1. [font_to_py.py](./FONT_TO_PY.md) This utility runs on a PC and converts an 39 | industry standard font file to Python source. See below. 40 | 2. [Writer and CWriter classes](./writer/WRITER.md) These facilitate rendering 41 | text to a monochrome or colour display having a suitable device driver. 42 | 3. [Creating icon fonts](./icon_fonts/README.md) Ways to incorporate icons in 43 | a Python font file. 44 | 4. [Device driver notes](./writer/DRIVERS.md). Notes for authors of display 45 | device drivers. Provides details of the font file format and information on 46 | ensuring compatibility with the `Writer` classes. 47 | 48 | # 3. font_to_py.py 49 | 50 | This command line utility is written in Python 3 and runs on a PC. To convert 51 | a scalable font to Python the utility takes input a font file in `ttf` or `otf` 52 | form together with a height in pixels and outputs a Python source file 53 | containing the font as a bitmap. Fixed and variable pitch rendering are 54 | supported. The design has the following aims: 55 | 56 | * Independence of specific display hardware. 57 | * The path from font file to Python code to be fully open source. 58 | 59 | The first is achieved by supplying hardware specific arguments to the utility. 60 | These define horizontal or vertical mapping and the bit order for font data. 61 | 62 | The second is achieved by using Freetype and the Freetype Python bindings. Its 63 | use is documented [here](./FONT_TO_PY.md). This also details measurements of 64 | RAM usage when importing fonts stored as frozen bytecode. 65 | 66 | ## 3.1 Small fonts 67 | 68 | Converting scalable `ttf` or `otf` files programmatically works best for larger 69 | fonts. For small fonts it is best to use hand-designed bitmapped font files. 70 | These are now supported: `bdf` or `pcf` font files may be converted to Python 71 | source in the same format as files originating from scalable fonts. See also 72 | [microPyEZfonts](https://github.com/easytarget/microPyEZfonts) which includes 73 | compatible small fonts (in `examples/fonts`). 74 | 75 | ## 3.2 Limitations 76 | 77 | Kerning is not supported. Fonts are one bit per pixel. Colour displays are 78 | supported by the `CWriter` class which adds colour information at the rendering 79 | stage. This assumes that all pixels of a character are coloured identically. 80 | 81 | By default the `font_to_py.py` utility produces the ASCII character set from 82 | `chr(32)` to `chr(126)` inclusive. Command line options enable the character 83 | set to be modified to include arbitrary Unicode characters. Alternative sets 84 | may be specified such as for non-English languages. Efficient support is now 85 | provided for sparse character sets. 86 | 87 | # 4. Font file interface 88 | 89 | A font file is imported in the usual way e.g. `import font14`. Python font 90 | files contain the following functions. These return values defined by the 91 | arguments which were provided to `font_to_py.py`: 92 | 93 | `height` Returns height in pixels. 94 | `max_width` Returns maximum width of a glyph in pixels. 95 | `baseline` Offset from top of glyph to the baseline. 96 | `hmap` Returns `True` if font is horizontally mapped. 97 | `reverse` Returns `True` if bit reversal was specified. 98 | `monospaced` Returns `True` if monospaced rendering was specified. 99 | `min_ch` Returns the ordinal value of the lowest character in the file. 100 | `max_ch` Returns the ordinal value of the highest character in the file. 101 | 102 | Glyphs are returned with the `get_ch` function. Its argument is a Unicode 103 | character and it returns the following values: 104 | 105 | * A `memoryview` object containing the glyph bytes. 106 | * The height in pixels. 107 | * The character width in pixels. 108 | 109 | The `font_to_py.py` utility allows a default glyph to be specified (typically 110 | `?`). If called with an undefined character, this glyph will be returned. 111 | 112 | The `min_ch` and `max_ch` functions are mainly relevant to contiguous character 113 | sets. 114 | 115 | # 5. Licence 116 | 117 | All code is released under the MIT licence. 118 | -------------------------------------------------------------------------------- /c_to_python_font.py: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env python3 2 | # -*- coding: utf-8 -*- 3 | 4 | # Released under the MIT License (MIT). See LICENSE. 5 | # Copyright (c) 2022 Peter Hinch 6 | 7 | import sys 8 | from font_to_py import ByteWriter, var_write, write_func, STR02, STR02H 9 | 10 | STR01 = """# Code generated by c_to_python_font.py. 11 | # Char set: {} 12 | # Cmd: {} 13 | version = '0.10' 14 | 15 | """ 16 | 17 | 18 | class Glyph: 19 | dstart = 0 # Start index of next glyph 20 | def __init__(self): 21 | self.width = 0 22 | self.height = 0 23 | 24 | def populate(self, fn, data, idx): 25 | try: 26 | with open(fn, "r") as f: 27 | s = f.readline() 28 | elements = s.split(" ") 29 | if elements[1].endswith("width"): 30 | self.width = int(elements[2]) 31 | data.extend((self.width).to_bytes(2, 'little')) 32 | else: 33 | return False 34 | s = f.readline() 35 | elements = s.split(" ") 36 | if elements[1].endswith("height"): 37 | self.height = int(elements[2]) 38 | else: 39 | return False 40 | s = f.readline() 41 | if not s.startswith("static"): 42 | return False 43 | while s := f.readline(): 44 | if (lb := s.find("}")) != -1: 45 | s = s[:lb] # Strip trailing }; 46 | p = s.strip().split(',') 47 | if not p[-1]: 48 | p = p[: -1] 49 | z = [int(x, 16) for x in p] 50 | data.extend(bytearray(z)) 51 | # index points to start of current data block 52 | idx.extend((Glyph.dstart).to_bytes(2, 'little')) 53 | Glyph.dstart += ((self.width - 1)//8 + 1) * self.height + 2 54 | except OSError: 55 | return False 56 | return True 57 | 58 | 59 | class Font: 60 | 61 | def __init__(self): 62 | self.glyphs = [] 63 | self.height = 0 64 | self.max_width = 0 65 | self.data = bytearray() 66 | self.index = bytearray() 67 | 68 | def __getitem__(self, glyph_index): 69 | return self.glyphs[glyph_index] 70 | 71 | def populate(self, filename="filenames.txt"): 72 | try: 73 | with open(filename, "r") as f: 74 | while fn := f.readline().strip(): # Get current C file 75 | if (lc := fn.find("#")) != -1: 76 | if (fn := fn[:lc].strip()) == "": 77 | continue 78 | g = Glyph() 79 | if g.populate(fn, self.data, self.index): 80 | if ht := self.height: 81 | if ht != g.height: 82 | print(f"Fatal: file {fn} height is {g.height} while font height is {ht}") 83 | return False 84 | else: 85 | self.height = g.height 86 | self.glyphs.append(g) 87 | self.max_width = max(self.max_width, g.width) 88 | else: 89 | print('Failed to read', fn) 90 | return False 91 | except OSError: 92 | print("Failed to read", filename) 93 | return False 94 | return True 95 | 96 | def output(self, stream): 97 | minchar = ord("A") 98 | nglyphs = len(self.glyphs) - 1 # Number less default glyph 99 | maxchar = minchar + nglyphs - 1 100 | st = "" 101 | for z in range(nglyphs): 102 | st = "".join((st, chr(minchar + z))) 103 | cl = ' '.join(sys.argv) 104 | stream.write(STR01.format(st, cl)) 105 | write_func(stream, 'height', self.height) 106 | write_func(stream, 'baseline', self.height) 107 | write_func(stream, 'max_width', self.max_width) 108 | write_func(stream, 'hmap', True) 109 | write_func(stream, 'reverse', True) # ???? 110 | write_func(stream, 'monospaced', False) 111 | write_func(stream, 'min_ch', minchar) 112 | write_func(stream, 'max_ch', maxchar) 113 | bw_font = ByteWriter(stream, '_font') 114 | bw_font.odata(self.data) 115 | bw_font.eot() 116 | bw_index = ByteWriter(stream, '_index') 117 | bw_index.odata(self.index) 118 | bw_index.eot() 119 | stream.write(STR02.format(minchar, maxchar)) 120 | stream.write(STR02H.format(self.height)) 121 | 122 | 123 | def make_font(infile="filenames.txt", outfile="icofont.py"): 124 | try: 125 | with open(outfile, "w") as f: 126 | font = Font() 127 | if font.populate(infile): 128 | font.output(f) 129 | else: 130 | return # Failed 131 | except OSError: 132 | print(f"Failed to open {outfile} for writing.") 133 | print(f"{outfile} successfully written.") 134 | 135 | def version_check(): 136 | n = sys.implementation.name 137 | v = sys.implementation.version 138 | if n == "cpython": 139 | if v[0] == 3: 140 | return v[1] >= 8 141 | return v[0] > 3 142 | return False # Requires CPython 143 | 144 | usage = """Convert a set of C bitmaps to a Python font file. Usage: 145 | c_to_python_font.py [infile [outfile]] 146 | infile contains a list of C bitmap files, one per line. 147 | outfile is the name of the output Python font. 148 | Default args: 149 | infile: filenames.txt 150 | outfile: icofont.py 151 | 152 | e.g. 153 | $ ./c_to_python_font.py my_file_list.txt my_font.py 154 | """ 155 | if __name__ == "__main__": 156 | a = sys.argv 157 | if len(a) >= 2 and a[1] in ("--help", "-h", "help"): 158 | print(usage) 159 | elif not version_check(): 160 | print("This script requires Python 3.8 or above.") 161 | else: 162 | infile = a[1] if len(a) >= 2 else "filenames.txt" 163 | outfile = a[2] if len(a) >= 3 else "icofont.py" 164 | make_font(infile, outfile) 165 | -------------------------------------------------------------------------------- /charsets/Chinese_Japanese: -------------------------------------------------------------------------------- 1 | !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~ぁあぃいぅうぇえぉおかがきぎくぐけげこごさざしじすずせぜそぞただちぢっつづてでとどなにぬねのはばぱひびぴふぶぷへべぺほぼぽまみむめもゃやゅゆょよらりるれろゎわゐゑをんゔァアィイゥウェエォオカガキギクグケゲコゴサザシジスズセゼソゾタダチヂッツヅテデトドナニヌネノハバパヒビピフブプヘベペホボポマミムメモャヤュユョヨラリルレロヮワヰヱヲンヴヵヶヷヸヹヺ一丁七万丈三上下丌不与丐丑专且丕世丘丙业丛东丝丞丢两严丧丨个丫丬中丰串临丶丸丹为主丽举丿乃久乇么义之乌乍乎乏乐乒乓乔乖乘乙乜九乞也习乡书乩买乱乳乾了予争事二亍于亏云互亓五井亘亚些亟亠亡亢交亥亦产亨亩享京亭亮亲亳亵人亻亿什仁仂仃仄仅仆仇仉今介仍从仑仓仔仕他仗付仙仝仞仟仡代令以仨仪仫们仰仲仳仵件价任份仿企伉伊伍伎伏伐休众优伙会伛伞伟传伢伤伥伦伧伪伫伯估伲伴伶伸伺似伽佃但位低住佐佑体何佗佘余佚佛作佝佞佟你佣佤佥佧佩佬佯佰佳佴佶佻佼佾使侃侄侈侉例侍侏侑侔侗供依侠侣侥侦侧侨侩侪侬侮侯侵便促俄俅俊俎俏俐俑俗俘俚俜保俞俟信俣俦俨俩俪俭修俯俱俳俸俺俾倌倍倏倒倔倘候倚倜借倡倥倦倨倩倪倬倭倮债值倾偃假偈偌偎偏偕做停健偬偶偷偻偾偿傀傅傈傍傣傥傧储傩催傲傺傻像僖僚僦僧僬僭僮僳僵僻儆儇儋儒儡儿兀允元兄充兆先光克免兑兔兕兖党兜兢入全八公六兮兰共关兴兵其具典兹养兼兽冀冁冂内冈冉册再冒冕冖冗写军农冠冢冤冥冫冬冯冰冱冲决况冶冷冻冼冽净凄准凇凉凋凌减凑凛凝几凡凤凫凭凯凰凳凵凶凸凹出击凼函凿刀刁刂刃分切刈刊刍刎刑划刖列刘则刚创初删判刨利别刭刮到刳制刷券刹刺刻刽刿剀剁剂剃削剌前剐剑剔剖剜剞剡剥剧剩剪副割剽剿劁劂劈劐劓力劝办功加务劢劣动助努劫劬劭励劲劳劾势勃勇勉勋勐勒勖勘募勤勰勹勺勾勿匀包匆匈匍匏匐匕化北匙匚匝匠匡匣匦匪匮匹区医匾匿十千卅升午卉半华协卑卒卓单卖南博卜卞卟占卡卢卣卤卦卧卩卫卮卯印危即却卵卷卸卺卿厂厄厅历厉压厌厍厕厘厚厝原厢厣厥厦厨厩厮厶去县叁参又叉及友双反发叔取受变叙叛叟叠口古句另叨叩只叫召叭叮可台叱史右叵叶号司叹叻叼叽吁吃各吆合吉吊同名后吏吐向吒吓吕吖吗君吝吞吟吠吡吣否吧吨吩含听吭吮启吱吲吴吵吸吹吻吼吾呀呃呆呈告呋呐呒呓呔呕呖呗员呙呛呜呢呤呦周呱呲味呵呶呷呸呻呼命咀咂咄咆咋和咎咏咐咒咔咕咖咙咚咛咝咣咤咦咧咨咩咪咫咬咭咯咱咳咴咸咻咽咿哀品哂哄哆哇哈哉哌响哎哏哐哑哒哓哔哕哗哙哚哜哝哞哟哥哦哧哨哩哪哭哮哲哳哺哼哽哿唁唆唇唉唏唐唑唔唛唠唢唣唤唧唪唬售唯唰唱唳唷唼唾唿啁啃啄商啉啊啐啕啖啜啡啤啥啦啧啪啬啭啮啰啵啶啷啸啻啼啾喀喁喂喃善喇喈喉喊喋喏喑喔喘喙喜喝喟喧喰喱喳喵喷喹喻喽喾嗄嗅嗉嗌嗍嗑嗒嗓嗔嗖嗜嗝嗟嗡嗣嗤嗥嗦嗨嗪嗫嗬嗯嗲嗳嗵嗷嗽嗾嘀嘁嘈嘉嘌嘎嘏嘘嘛嘞嘟嘣嘤嘧嘬嘭嘱嘲嘴嘶嘹嘻嘿噌噍噎噔噗噘噙噜噢噤器噩噪噫噬噱噶噻噼嚅嚆嚎嚏嚓嚣嚯嚷嚼囊囔囗囚四囝回囟因囡团囤囫园困囱围囵囹固国图囿圃圄圆圈圉圊圜土圣在圩圪圬圭圮圯地圳圹场圻圾址坂均坊坌坍坎坏坐坑块坚坛坜坝坞坟坠坡坤坦坨坩坪坫坭坯坳坶坷坻坼垂垃垄垅垆型垌垒垓垛垠垡垢垣垤垦垧垩垫垭垮垲垴垸埂埃埋城埏埒埔埕埘埙埚埝域埠埤埭埯埴埸培基埽堀堂堆堇堋堍堑堕堙堞堠堡堤堪堰堵塄塌塍塑塔塘塞塥填塬塾墀墁境墅墉墒墓墙墚增墟墨墩墼壁壅壑壕壤士壬壮声壳壶壹夂处备复夏夔夕外夙多夜够夤夥大天太夫夭央夯失头夷夸夹夺夼奁奂奄奇奈奉奋奎奏契奔奕奖套奘奚奠奢奥女奴奶奸她好妁如妃妄妆妇妈妊妍妒妓妖妗妙妞妣妤妥妨妩妪妫妮妯妲妹妻妾姆姊始姐姑姒姓委姗姘姚姜姝姣姥姨姬姹姻姿威娃娄娅娆娇娈娉娌娑娓娘娜娟娠娣娥娩娱娲娴娶娼婀婆婉婊婕婚婢婧婪婴婵婶婷婺婿媒媚媛媪媲媳媵媸媾嫁嫂嫉嫌嫒嫔嫖嫘嫜嫠嫡嫣嫦嫩嫫嫱嬉嬖嬗嬲嬴嬷孀子孑孓孔孕字存孙孚孛孜孝孟孢季孤孥学孩孪孬孰孱孳孵孺孽宀宁它宄宅宇守安宋完宏宓宕宗官宙定宛宜宝实宠审客宣室宥宦宪宫宰害宴宵家宸容宽宾宿寂寄寅密寇富寐寒寓寝寞察寡寤寥寨寮寰寸对寺寻导寿封射将尉尊小少尔尕尖尘尚尜尝尢尤尥尧尬就尴尸尹尺尻尼尽尾尿局屁层居屈屉届屋屎屏屐屑展屙属屠屡屣履屦屮屯山屹屺屿岁岂岈岌岍岐岑岔岖岗岘岙岚岛岜岢岣岩岫岬岭岱岳岵岷岸岽岿峁峄峋峒峙峡峤峥峦峨峪峭峰峻崂崃崆崇崎崔崖崛崞崤崦崧崩崭崮崴崽崾嵇嵊嵋嵌嵘嵛嵝嵩嵫嵬嵯嵴嶂嶙嶝嶷巅巍巛川州巡巢工左巧巨巩巫差巯己已巳巴巷巽巾币市布帅帆师希帏帐帑帔帕帖帘帙帚帛帜帝带帧席帮帱帷常帻帼帽幂幄幅幌幔幕幛幞幡幢干平年并幸幺幻幼幽广庀庄庆庇床庋序庐庑库应底庖店庙庚府庞废庠庥度座庭庳庵庶康庸庹庾廉廊廑廒廓廖廛廨廪廴延廷建廾廿开弁异弃弄弈弊弋式弑弓引弗弘弛弟张弥弦弧弩弪弭弯弱弹强弼彀彐归当录彖彗彘彝彡形彤彦彩彪彬彭彰影彳彷役彻彼往征徂径待徇很徉徊律後徐徒徕得徘徙徜御徨循徭微徵德徼徽心忄必忆忉忌忍忏忐忑忒忖志忘忙忝忠忡忤忧忪快忭忮忱念忸忻忽忾忿怀态怂怃怄怅怆怊怍怎怏怒怔怕怖怙怛怜思怠怡急怦性怨怩怪怫怯怵总怼怿恁恂恃恋恍恐恒恕恙恚恝恢恣恤恧恨恩恪恫恬恭息恰恳恶恸恹恺恻恼恽恿悃悄悉悌悍悒悔悖悚悛悝悟悠患悦您悫悬悭悯悱悲悴悸悻悼情惆惊惋惑惕惘惚惜惝惟惠惦惧惨惩惫惬惭惮惯惰想惴惶惹惺愀愁愆愈愉愍愎意愕愚感愠愣愤愦愧愫愿慈慊慌慎慑慕慝慢慧慨慰慵慷憋憎憔憝憧憨憩憬憷憾懂懈懊懋懑懒懔懦懵懿戆戈戊戋戌戍戎戏成我戒戕或戗战戚戛戟戡戢戤戥截戬戮戳戴户戽戾房所扁扃扇扈扉手扌才扎扑扒打扔托扛扣扦执扩扪扫扬扭扮扯扰扳扶批扼找承技抄抉把抑抒抓投抖抗折抚抛抟抠抡抢护报抨披抬抱抵抹抻押抽抿拂拄担拆拇拈拉拊拌拍拎拐拒拓拔拖拗拘拙拚招拜拟拢拣拥拦拧拨择括拭拮拯拱拳拴拶拷拼拽拾拿持挂指挈按挎挑挖挚挛挝挞挟挠挡挢挣挤挥挨挪挫振挲挹挺挽捂捃捅捆捉捋捌捍捎捏捐捕捞损捡换捣捧捩捭据捱捶捷捺捻掀掂掇授掉掊掌掎掏掐排掖掘掠探掣接控推掩措掬掭掮掰掳掴掷掸掺掼掾揄揆揉揍揎描提插揖揞揠握揣揩揪揭揲援揶揸揽揿搀搁搂搅搋搌搏搐搓搔搛搜搞搠搡搦搪搬搭搴携搽搿摁摄摅摆摇摈摊摒摔摘摞摧摩摭摸摹摺撂撄撅撇撑撒撕撖撙撞撤撩撬播撮撰撵撷撸撺撼擀擂擅操擎擐擒擗擘擞擢擤擦攀攉攒攘攥攫攮支攴攵收攸改攻放政故效敉敌敏救敕敖教敛敝敞敢散敦敫敬数敲整敷文斋斌斐斑斓斗料斛斜斟斡斤斥斧斩斫断斯新方於施旁旃旄旅旆旋旌旎族旒旖旗无既日旦旧旨早旬旭旮旯旰旱时旷旺昀昂昃昆昊昌明昏易昔昕昙昝星映春昧昨昭是昱昴昵昶昼显晁晃晋晌晏晒晓晔晕晖晗晚晟晡晤晦晨普景晰晴晶晷智晾暂暄暇暌暑暖暗暝暧暨暮暴暹暾曙曛曜曝曦曩曰曲曳更曷曹曼曾替最月有朊朋服朐朔朕朗望朝期朦木未末本札术朱朴朵机朽杀杂权杆杈杉杌李杏材村杓杖杜杞束杠条来杨杩杪杭杯杰杲杳杵杷杼松板极构枇枉枋析枕林枘枚果枝枞枢枣枥枧枨枪枫枭枯枰枳枵架枷枸柁柃柄柏某柑柒染柔柘柙柚柜柝柞柠柢查柩柬柯柰柱柳柴柽柿栀栅标栈栉栊栋栌栎栏树栓栖栗栝校栩株栲栳样核根格栽栾桀桁桂桃桄桅框案桉桊桌桎桐桑桓桔桕桠桡桢档桤桥桦桧桨桩桫桴桶桷梁梃梅梆梏梓梗梢梦梧梨梭梯械梳梵检棂棉棋棍棒棕棘棚棠棣森棰棱棵棹棺棼椁椅椋植椎椐椒椟椠椤椭椰椴椹椽椿楂楔楗楚楝楞楠楣楦楫楮楱楷楸楹楼榀概榄榆榇榈榉榍榔榕榘榛榜榧榨榫榭榱榴榷榻槁槊槌槎槐槔槛槟槠槭槲槽槿樊樗樘樟模樨横樯樱樵樽樾橄橇橐橘橙橛橡橥橱橹橼檀檄檎檐檑檗檠檩檫檬欠次欢欣欤欧欲欷欹欺款歃歆歇歉歌歙止正此步武歧歪歹死歼殁殂殃殄殆殇殉殊残殍殒殓殖殚殛殡殪殳殴段殷殿毁毂毅毋母每毒毓比毕毖毗毙毛毡毪毫毯毳毵毹毽氅氆氇氍氏氐民氓气氕氖氘氙氚氛氟氡氢氤氦氧氨氩氪氮氯氰氲水氵永氽汀汁求汆汇汉汊汐汔汕汗汛汜汝汞江池污汤汨汩汪汰汲汴汶汹汽汾沁沂沃沅沆沈沉沌沏沐沓沔沙沛沟没沣沤沥沦沧沩沪沫沭沮沱沲河沸油治沼沽沾沿泄泅泉泊泌泐泓泔法泖泗泛泞泠泡波泣泥注泪泫泮泯泰泱泳泵泶泷泸泺泻泼泽泾洁洄洇洋洌洎洒洗洙洚洛洞津洧洪洫洮洱洲洳洵洹活洼洽派流浃浅浆浇浈浊测浍济浏浑浒浓浔浙浚浜浞浠浣浦浩浪浮浯浴海浸浼涂涅消涉涌涎涑涓涔涕涛涝涞涟涠涡涣涤润涧涨涩涪涫涮涯液涵涸涿淀淄淅淆淇淋淌淑淖淘淙淝淞淠淡淤淦淫淬淮深淳混淹添淼清渊渌渍渎渐渑渔渖渗渚渝渠渡渣渤渥温渫渭港渲渴游渺湃湄湍湎湓湔湖湘湛湟湫湮湾湿溃溅溆溉溏源溘溜溟溢溥溧溪溯溱溲溴溶溷溺溻溽滁滂滇滋滏滑滓滔滕滗滚滞滟滠满滢滤滥滦滨滩滴滹漂漆漉漏漓演漕漠漤漩漪漫漭漯漱漳漶漾潆潇潋潍潘潜潞潢潦潭潮潲潴潸潺潼澄澈澉澌澍澎澜澡澧澳澶澹激濂濉濑濒濞濠濡濮濯瀑瀚瀛瀣瀵瀹灌灏灞火灬灭灯灰灵灶灸灼灾灿炀炅炉炊炎炒炔炕炖炙炜炝炫炬炭炮炯炱炳炷炸点炻炼炽烀烁烂烃烈烊烘烙烛烟烤烦烧烨烩烫烬热烯烷烹烽焉焊焐焓焕焖焘焙焚焦焯焰焱然煅煊煌煎煜煞煤煦照煨煮煲煳煸煺煽熄熊熏熔熘熙熟熠熨熬熳熵熹燃燎燔燕燠燥燧燮燹爆爝爨爪爬爰爱爵父爷爸爹爻爽爿片版牌牍牒牖牙牛牝牟牡牢牦牧物牮牯牲牵特牺牾牿犀犁犄犊犋犍犏犒犟犬犭犯犰犴状犷犸犹狁狂狃狄狈狍狎狐狒狗狙狞狠狡狨狩独狭狮狯狰狱狲狳狴狷狸狺狻狼猁猃猊猎猓猕猖猗猛猜猝猞猡猢猥猩猪猫猬献猱猴猷猸猹猾猿獍獐獒獗獠獬獭獯獾玄率玉王玎玑玖玛玟玢玩玫玮环现玲玳玷玺玻珀珂珈珉珊珍珏珐珑珙珞珠珥珧珩班珲球琅理琉琊琏琐琚琛琢琥琦琨琪琬琮琰琳琴琵琶琼瑁瑕瑗瑙瑚瑛瑜瑞瑟瑭瑰瑶瑷瑾璀璁璃璇璋璎璐璜璞璧璨璩璺瓒瓜瓞瓠瓢瓣瓤瓦瓮瓯瓴瓶瓷瓿甄甍甏甑甓甘甙甚甜生甥用甩甫甬甭甯田由甲申电男甸町画甾畀畅畈畋界畎畏畔留畚畛畜略畦番畲畴畸畹畿疃疆疋疏疑疒疔疖疗疙疚疝疟疠疡疣疤疥疫疬疮疯疰疱疲疳疴疵疸疹疼疽疾痂痃痄病症痈痉痊痍痒痔痕痖痘痛痞痢痣痤痦痧痨痪痫痰痱痴痹痼痿瘀瘁瘃瘅瘊瘌瘐瘕瘗瘘瘙瘛瘟瘠瘢瘤瘥瘦瘩瘪瘫瘭瘰瘳瘴瘵瘸瘼瘾瘿癀癃癌癍癔癖癜癞癣癫癯癸登白百皂的皆皇皈皋皎皑皓皖皙皤皮皱皲皴皿盂盅盆盈益盍盎盏盐监盒盔盖盗盘盛盟盥目盯盱盲直相盹盼盾省眄眇眈眉看眍眙眚真眠眢眦眨眩眭眯眵眶眷眸眺眼着睁睃睇睐睑睚睛睡睢督睥睦睨睫睬睹睽睾睿瞀瞄瞅瞌瞍瞎瞑瞒瞟瞠瞢瞥瞧瞩瞪瞬瞭瞰瞳瞵瞻瞽瞿矍矗矛矜矢矣知矧矩矫矬短矮石矶矸矽矾矿砀码砂砉砌砍砑砒研砖砗砘砚砜砝砟砣砥砦砧砩砬砭砰破砷砸砹砺砻砼砾础硅硇硌硎硐硒硕硖硗硝硪硫硬硭确硷硼碇碉碌碍碎碑碓碗碘碚碛碜碟碡碣碥碧碰碱碲碳碴碹碾磁磅磉磊磋磐磔磕磙磨磬磲磴磷磺礁礅礓礞礤礴示礻礼社祀祁祆祈祉祓祖祗祚祛祜祝神祟祠祢祥祧票祭祯祷祸祺禀禁禄禅禊福禚禧禳禹禺离禽禾秀私秃秆秉秋种科秒秕秘租秣秤秦秧秩秫秭积称秸移秽稀稂稃稆程稍税稔稗稚稞稠稣稳稷稹稻稼稽稿穆穑穗穰穴究穷穸穹空穿窀突窃窄窆窈窍窑窒窕窖窗窘窜窝窟窠窥窦窨窬窭窳窿立竖站竞竟章竣童竦竭端竹竺竽竿笃笄笆笈笊笋笏笑笔笕笙笛笞笠笤笥符笨笪笫第笮笱笳笸笺笼笾筅筇等筋筌筏筐筑筒答策筘筚筛筝筠筢筮筱筲筵筷筹筻签简箅箍箐箔箕算箜箝管箢箦箧箨箩箪箫箬箭箱箴箸篁篆篇篌篑篓篙篚篝篡篥篦篪篮篱篷篼篾簇簋簌簏簖簟簦簧簪簸簿籀籁籍米籴类籼籽粉粑粒粕粗粘粜粝粞粟粢粤粥粪粮粱粲粳粹粼粽精糁糅糇糈糊糌糍糕糖糗糙糜糟糠糨糯糸系紊素索紧紫累絮絷綦綮縻繁繇纂纛纟纠纡红纣纤纥约级纨纩纪纫纬纭纯纰纱纲纳纵纶纷纸纹纺纽纾线绀绁绂练组绅细织终绉绊绋绌绍绎经绐绑绒结绔绕绗绘给绚绛络绝绞统绠绡绢绣绥绦继绨绩绪绫续绮绯绰绱绲绳维绵绶绷绸绺绻综绽绾绿缀缁缂缃缄缅缆缇缈缉缋缌缍缎缏缑缒缓缔缕编缗缘缙缚缛缜缝缟缠缡缢缣缤缥缦缧缨缩缪缫缬缭缮缯缰缱缲缳缴缵缶缸缺罂罄罅罐网罔罕罗罘罚罟罡罢罨罩罪置罱署罴罹罾羁羊羌美羔羚羝羞羟羡群羧羯羰羲羸羹羼羽羿翁翅翊翌翎翔翕翘翟翠翡翥翦翩翮翰翱翳翻翼耀老考耄者耆耋而耍耐耒耔耕耖耗耘耙耜耠耢耥耦耧耨耩耪耱耳耵耶耷耸耻耽耿聂聃聆聊聋职聍聒联聘聚聩聪聱聿肀肃肄肆肇肉肋肌肓肖肘肚肛肜肝肟肠股肢肤肥肩肪肫肭肮肯肱育肴肷肺肼肽肾肿胀胁胂胃胄胆背胍胎胖胗胙胚胛胜胝胞胡胤胥胧胨胩胪胫胬胭胯胰胱胲胳胴胶胸胺胼能脂脆脉脊脍脎脏脐脑脒脓脔脖脘脚脞脬脯脱脲脶脸脾腆腈腊腋腌腐腑腓腔腕腙腚腠腥腧腩腭腮腰腱腴腹腺腻腼腽腾腿膀膂膈膊膏膑膘膛膜膝膣膦膨膪膳膺膻臀臁臂臃臆臊臌臣臧自臬臭至致臻臼臾舀舁舂舄舅舆舌舍舐舒舔舛舜舞舟舡舢舣舨航舫般舭舯舰舱舳舴舵舶舷舸船舻舾艄艇艉艋艏艘艚艟艨艮良艰色艳艴艹艺艽艾艿节芄芈芊芋芍芎芏芑芒芗芘芙芜芝芟芡芤芥芦芨芩芪芫芬芭芮芯芰花芳芴芷芸芹芽芾苁苄苇苈苊苋苌苍苎苏苑苒苓苔苕苗苘苛苜苞苟苠苡苣苤若苦苫苯英苴苷苹苻茁茂范茄茅茆茇茈茉茌茎茏茑茔茕茗茚茛茜茧茨茫茬茭茯茱茳茴茵茶茸茹茺茼荀荃荆荇草荏荐荑荒荔荚荛荜荞荟荠荡荣荤荥荦荧荨荩荪荫荬荭荮药荷荸荻荼荽莅莆莉莎莒莓莘莛莜莞莠莨莩莪莫莰莱莲莳莴莶获莸莹莺莼莽菀菁菅菇菊菌菏菔菖菘菜菝菟菠菡菥菩菪菰菱菲菸菹菽萁萃萄萆萋萌萍萎萏萑萘萜萝萤营萦萧萨萱萸萼落葆葑著葙葚葛葜葡董葩葫葬葭葱葳葵葶葸葺蒂蒇蒈蒉蒋蒌蒎蒗蒙蒜蒡蒯蒲蒴蒸蒹蒺蒽蒿蓁蓄蓉蓊蓍蓐蓑蓓蓖蓝蓟蓠蓣蓥蓦蓬蓰蓼蓿蔌蔑蔓蔗蔚蔟蔡蔫蔬蔷蔸蔹蔺蔻蔼蔽蕃蕈蕉蕊蕖蕙蕞蕤蕨蕲蕴蕹蕺蕻蕾薄薅薇薏薛薜薤薨薪薮薯薰薷薹藁藉藏藐藓藕藜藤藩藻藿蘅蘑蘖蘧蘩蘸蘼虍虎虏虐虑虔虚虞虢虫虬虮虱虹虺虻虼虽虾虿蚀蚁蚂蚊蚋蚌蚍蚓蚕蚜蚝蚣蚤蚧蚨蚩蚪蚬蚯蚰蚱蚴蚵蚶蚺蛀蛄蛆蛇蛉蛊蛋蛎蛏蛐蛑蛔蛘蛙蛛蛞蛟蛤蛩蛭蛮蛰蛱蛲蛳蛴蛸蛹蛾蜀蜂蜃蜇蜈蜉蜊蜍蜒蜓蜕蜗蜘蜚蜜蜞蜡蜢蜣蜥蜩蜮蜱蜴蜷蜻蜾蜿蝇蝈蝉蝌蝎蝓蝗蝙蝠蝣蝤蝥蝮蝰蝴蝶蝻蝼蝽蝾螂螃螅螈螋融螓螗螟螨螫螬螭螯螳螵螺螽蟀蟆蟊蟋蟑蟒蟓蟛蟠蟥蟪蟮蟹蟾蠃蠊蠓蠕蠖蠛蠡蠢蠲蠹蠼血衄衅行衍衔街衙衡衢衣衤补表衩衫衬衮衰衲衷衽衾衿袁袂袄袅袈袋袍袒袖袜袢袤被袭袱袷袼裁裂装裆裉裎裒裔裕裘裙裟裢裣裤裥裨裰裱裳裴裸裹裼裾褂褊褐褒褓褙褚褛褡褥褪褫褰褴褶襁襄襞襟襦襻西要覃覆见观规觅视觇览觉觊觋觌觎觏觐觑角觖觚觜觞解觥触觫觯觳言訇訾詈詹誉誊誓謇謦警譬讠计订讣认讥讦讧讨让讪讫训议讯记讲讳讴讵讶讷许讹论讼讽设访诀证诂诃评诅识诈诉诊诋诌词诎诏译诒诓诔试诖诗诘诙诚诛诜话诞诟诠诡询诣诤该详诧诨诩诫诬语诮误诰诱诲诳说诵诶请诸诹诺读诼诽课诿谀谁谂调谄谅谆谇谈谊谋谌谍谎谏谐谑谒谓谔谕谖谗谘谙谚谛谜谝谟谠谡谢谣谤谥谦谧谨谩谪谫谬谭谮谯谰谱谲谳谴谵谶谷豁豆豇豉豌豕豚象豢豪豫豳豸豹豺貂貅貉貊貌貔貘贝贞负贡财责贤败账货质贩贪贫贬购贮贯贰贱贲贳贴贵贶贷贸费贺贻贼贽贾贿赀赁赂赃资赅赆赇赈赉赊赋赌赍赎赏赐赓赔赕赖赘赙赚赛赜赝赞赠赡赢赣赤赦赧赫赭走赳赴赵赶起趁趄超越趋趑趔趟趣趱足趴趵趸趺趼趾趿跃跄跆跋跌跎跏跑跖跗跚跛距跞跟跣跤跨跪跫跬路跳践跷跸跹跺跻跽踅踉踊踌踏踔踝踞踟踢踣踩踪踬踮踯踱踵踹踺踽蹀蹁蹂蹄蹇蹈蹉蹊蹋蹑蹒蹙蹦蹩蹬蹭蹯蹰蹲蹴蹶蹼蹿躁躅躇躏躐躔躜躞身躬躯躲躺軎车轧轨轩轫转轭轮软轰轱轲轳轴轵轶轷轸轹轺轻轼载轾轿辁辂较辄辅辆辇辈辉辊辋辍辎辏辐辑输辔辕辖辗辘辙辚辛辜辞辟辣辨辩辫辰辱辶边辽达迁迂迄迅过迈迎运近迓返迕还这进远违连迟迢迤迥迦迨迩迪迫迭迮述迳迷迸迹追退送适逃逄逅逆选逊逋逍透逐逑递途逖逗通逛逝逞速造逡逢逦逭逮逯逵逶逸逻逼逾遁遂遄遇遍遏遐遑遒道遗遘遛遢遣遥遨遭遮遴遵遽避邀邂邃邈邋邑邓邕邗邙邛邝邡邢那邦邪邬邮邯邰邱邳邴邵邶邸邹邺邻邾郁郄郅郇郊郎郏郐郑郓郗郛郜郝郡郢郦郧部郫郭郯郴郸都郾鄂鄄鄙鄞鄢鄣鄯鄱鄹酃酆酉酊酋酌配酎酏酐酒酗酚酝酞酡酢酣酤酥酩酪酬酮酯酰酱酲酴酵酶酷酸酹酽酾酿醅醇醉醋醌醍醐醑醒醚醛醢醣醪醭醮醯醴醵醺采釉释里重野量金釜鉴銎銮鋈錾鍪鎏鏊鏖鐾鑫钅钆钇针钉钊钋钌钍钎钏钐钒钓钔钕钗钙钚钛钜钝钞钟钠钡钢钣钤钥钦钧钨钩钪钫钬钭钮钯钰钱钲钳钴钵钶钷钸钹钺钻钼钽钾钿铀铁铂铃铄铅铆铈铉铊铋铌铍铎铐铑铒铕铖铗铘铙铛铜铝铞铟铠铡铢铣铤铥铧铨铩铪铫铬铭铮铯铰铱铲铳铴铵银铷铸铹铺铼铽链铿销锁锂锃锄锅锆锇锈锉锊锋锌锍锎锏锐锑锒锓锔锕锖锗锘错锚锛锝锞锟锡锢锣锤锥锦锨锩锪锫锬锭键锯锰锱锲锴锵锶锷锸锹锺锻锼锾锿镀镁镂镄镅镆镇镉镊镌镍镎镏镐镑镒镓镔镖镗镘镙镛镜镝镞镟镡镢镣镤镥镦镧镨镩镪镫镬镭镯镰镱镲镳镶长门闩闪闫闭问闯闰闱闲闳间闵闶闷闸闹闺闻闼闽闾阀阁阂阃阄阅阆阈阉阊阋阌阍阎阏阐阑阒阔阕阖阗阙阚阜阝队阡阢阪阮阱防阳阴阵阶阻阼阽阿陀陂附际陆陇陈陉陋陌降限陔陕陛陟陡院除陧陨险陪陬陲陴陵陶陷隅隆隈隋隍随隐隔隗隘隙障隧隰隳隶隹隼隽难雀雁雄雅集雇雉雌雍雎雏雒雕雠雨雩雪雯雳零雷雹雾需霁霄霆震霈霉霍霎霏霓霖霜霞霪霭霰露霸霹霾青靓靖静靛非靠靡面靥革靳靴靶靼鞅鞋鞍鞑鞒鞔鞘鞠鞣鞫鞭鞯鞲鞴韦韧韩韪韫韬韭音韵韶页顶顷顸项顺须顼顽顾顿颀颁颂颃预颅领颇颈颉颊颌颍颏颐频颓颔颖颗题颚颛颜额颞颟颠颡颢颤颥颦颧风飑飒飓飕飘飙飚飞食飧飨餍餐餮饔饕饣饥饧饨饩饪饫饬饭饮饯饰饱饲饴饵饶饷饺饼饽饿馀馁馄馅馆馇馈馊馋馍馏馐馑馒馓馔馕首馗馘香馥馨马驭驮驯驰驱驳驴驵驶驷驸驹驺驻驼驽驾驿骀骁骂骄骅骆骇骈骊骋验骏骐骑骒骓骖骗骘骚骛骜骝骞骟骠骡骢骣骤骥骧骨骰骱骶骷骸骺骼髀髁髂髅髋髌髑髓高髟髡髦髫髭髯髹髻鬃鬈鬏鬓鬟鬣鬯鬲鬻鬼魁魂魃魄魅魇魈魉魍魏魑魔鱼鱿鲁鲂鲅鲆鲇鲈鲋鲍鲎鲐鲑鲒鲔鲕鲚鲛鲜鲞鲟鲠鲡鲢鲣鲤鲥鲦鲧鲨鲩鲫鲭鲮鲰鲱鲲鲳鲴鲵鲶鲷鲸鲺鲻鲼鲽鳃鳄鳅鳆鳇鳊鳋鳌鳍鳎鳏鳐鳓鳔鳕鳖鳗鳘鳙鳜鳝鳞鳟鳢鸟鸠鸡鸢鸣鸥鸦鸨鸩鸪鸫鸬鸭鸯鸱鸲鸳鸵鸶鸷鸸鸹鸺鸽鸾鸿鹁鹂鹃鹄鹅鹆鹇鹈鹉鹊鹋鹌鹎鹏鹑鹕鹗鹘鹚鹛鹜鹞鹣鹤鹦鹧鹨鹩鹪鹫鹬鹭鹰鹱鹳鹾鹿麂麇麈麋麒麓麝麟麦麴麸麻麽麾黄黉黍黎黏黑黔默黛黜黝黟黠黢黥黧黩黪黯黹黻黼黾鼋鼍鼎鼐鼓鼗鼙鼠鼢鼬鼯鼷鼹鼻鼽鼾齄齐齑齿龀龃龄龅龆龇龈龉龊龋龌龙龚龛龟龠 -------------------------------------------------------------------------------- /charsets/Chinese_Japanese.md: -------------------------------------------------------------------------------- 1 | # Chinese Japanese and Korean characters 2 | 3 | The file `chinese_japanese` contains the following characters. It may be used 4 | (in full, or edited) to perpare Python font files for these languages with the 5 | `-k` or `--charset_file` option. The source TTF or OTF file must contain the 6 | relevant glyphs. 7 | 8 | # charset range 9 | 10 | |language|range| 11 | |---|---| 12 | |ASCII|Total 96 chars| 13 | |Chinese|Contain 6763 Chars in [GB/T 2312-1980](https://openstd.samr.gov.cn/bzgk/gb/newGbInfo?hcno=5664A728BD9D523DE3B99BC37AC7A2CC) and `啰`、`喰`、`瞭`、`𬌗`| 14 | |Japanese|Total 174 hiragana and katakana characters| 15 | -------------------------------------------------------------------------------- /charsets/Hebrew.txt: -------------------------------------------------------------------------------- 1 | abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!"#°{ }$%&'()*+,-:./אבגדהוזחטיכךלמםנןסעפףצץקרשת 2 | -------------------------------------------------------------------------------- /charsets/RTL_languages.md: -------------------------------------------------------------------------------- 1 | Some languages are rendered from right to left (RTL) and others are 2 | boustrophedonic, rendering alternate lines in opposite directions. While the 3 | character sets can be handled in the usual way, the `Writer` and `CWriter` 4 | classes support only LTR rendering. Given that the GUI's are based on these 5 | classes, that limitation applies to their widgets. 6 | 7 | A workround is to perform reversal in code. The CPython approach is 8 | ```py 9 | reversed_string = my_string[::-1] 10 | ``` 11 | but this is unavailable in MicroPython. Possible solutions are: 12 | ```py 13 | def reverse(s): 14 | l = list(s) 15 | l.reverse() 16 | return "".join(l) 17 | ``` 18 | or 19 | ```py 20 | def reverse(s): 21 | if not (l := len(s) // 2): 22 | return s 23 | return ''.join((reverse(s[l:]), reverse(s[:l]))) 24 | ``` 25 | which aims to minimise the number of string creations. 26 | 27 | Note that the `Textbox` widget relies on word wrap and scrolling: these features 28 | will only work with LTR text. 29 | 30 | ### [Main README](https://github.com/peterhinch/micropython-font-to-py/tree/master) 31 | 32 | ### [WRITER.md](https://github.com/peterhinch/micropython-font-to-py/blob/master/writer/WRITER.md) 33 | -------------------------------------------------------------------------------- /charsets/Thailand: -------------------------------------------------------------------------------- 1 | abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!"#$%&'()*+,-./ก ข ฃ ค ฅ ฆ ง จ ฉ ช ซ ฌ ญ ฎ ฏ ฐ ฑ ฒ ณ ด ต ถ ท ธ น บ ป ผ ฝ พ ฟ ภ ม ย ร ฤ ล ฦ ว ศ ษ ส ห ฬ อ ฮ ฯ ะ ั า ำ ิ ี ึ ื ุ ู ฺ ฻ ฼ ฽ ฾ ฿ เ แ โ ใ ไ ๅ ๆ ็ ่ ้ ๊ ๋ ์ ํ ๎ ๏ ๐ ๑ ๒ ๓ ๔ ๕ ๖ ๗ ๘ ๙ ๚ ๛ 2 | -------------------------------------------------------------------------------- /charsets/chess: -------------------------------------------------------------------------------- 1 | RNBQKBNRPrnbqkbnrp♔♕♖♗♘♙♚♛♜♝♞♟ -------------------------------------------------------------------------------- /charsets/chess.md: -------------------------------------------------------------------------------- 1 | The file "chess" supports \u2654 to \u265F inclusive. See [Wikipedia](https://en.wikipedia.org/wiki/Chess_symbols_in_Unicode). 2 | The font FreeSerif.ttf supports these glyphs - see [GNU FreeFont](https://en.wikipedia.org/wiki/GNU_FreeFont). 3 | 4 | The file also has RNBQKBNRPrnbqkbnrp used as ASCII chess symbols. 5 | 6 | -------------------------------------------------------------------------------- /charsets/cyrillic: -------------------------------------------------------------------------------- 1 | АБВГДЕЁЖЗИЙКЛМНОПРСТУФХЦЧШЩЪЫЬЭЮЯабвгдеёжзийклмнопрстуфхцчшщъыьэюя1234567890.?!:-\ 2 | -------------------------------------------------------------------------------- /charsets/extended: -------------------------------------------------------------------------------- 1 | ¬!"#£$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~°Ωαβγδθλμπωϕ£ 2 | -------------------------------------------------------------------------------- /font_test.py: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env python3 2 | # -*- coding: utf-8 -*- 3 | 4 | # The MIT License (MIT) 5 | # 6 | # Copyright (c) 2016 Peter Hinch 7 | # 8 | # Permission is hereby granted, free of charge, to any person obtaining a copy 9 | # of this software and associated documentation files (the "Software"), to deal 10 | # in the Software without restriction, including without limitation the rights 11 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | # copies of the Software, and to permit persons to whom the Software is 13 | # furnished to do so, subject to the following conditions: 14 | # 15 | # The above copyright notice and this permission notice shall be included in 16 | # all copies or substantial portions of the Software. 17 | # 18 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 24 | # THE SOFTWARE. 25 | 26 | # Test programs for the utility font_to_py and for font files created by it. 27 | # The test of most general use is test_font which enables a string to be 28 | # output to the REPL using a font file created by this utility. 29 | 30 | import sys 31 | import os 32 | from importlib import import_module 33 | from font_to_py import Font, write_font 34 | 35 | # Utility functions 36 | 37 | def validate_hmap(data, height, width): 38 | bpr = (width - 1)//8 + 1 39 | msg = 'Horizontal map, invalid data length got {} expected {}' 40 | assert len(data) == bpr * height, msg.format(len(data), bpr * height) 41 | 42 | 43 | def validate_vmap(data, height, width): 44 | bpc = (height - 1)//8 + 1 45 | msg = 'Vertical map, invalid data length got {} expected {}' 46 | assert len(data) == bpc * width, msg.format(len(data), bpc * width) 47 | 48 | 49 | # Routines to render to REPL 50 | def render_row_hmap(data, row, height, width, reverse): 51 | validate_hmap(data, height, width) 52 | bytes_per_row = (width - 1)//8 + 1 53 | for col in range(width): 54 | byte = data[row * bytes_per_row + col // 8] 55 | if reverse: 56 | bit = (byte & (1 << (col % 8))) > 0 57 | else: 58 | bit = (byte & (1 << (7 - (col % 8)))) > 0 59 | char = '#' if bit else '.' 60 | print(char, end='') 61 | 62 | 63 | def render_row_vmap(data, row, height, width, reverse): 64 | validate_vmap(data, height, width) 65 | bytes_per_col = (height - 1)//8 + 1 66 | for col in range(width): 67 | byte = data[col * bytes_per_col + row//8] 68 | if reverse: 69 | bit = (byte & (1 << (7 - (row % 8)))) > 0 70 | else: 71 | bit = (byte & (1 << (row % 8))) > 0 72 | char = '#' if bit else '.' 73 | print(char, end='') 74 | 75 | 76 | # Render a string to REPL using a specified Python font file 77 | # usage font_test.test_font('freeserif', 'abc') 78 | # Default tests outliers with fonts created with -k extended 79 | def test_font(fontfile, string='abg'+chr(126)+chr(127)+chr(176)+chr(177)+chr(937)+chr(981)): 80 | if fontfile in sys.modules: 81 | del sys.modules[fontfile] # force reload 82 | myfont = import_module(fontfile) 83 | print(('Horizontal' if myfont.hmap() else 'Vertical') + ' map') 84 | print(('Reverse' if myfont.reverse() else 'Normal') + ' bit order') 85 | print(('Fixed' if myfont.monospaced() else 'Proportional') + ' spacing') 86 | print('Dimensions height*max_width {} * {}'.format(myfont.height(), myfont.max_width())) 87 | s, e = myfont.min_ch(), myfont.max_ch() 88 | print('Start char "{}" (ord {}) end char "{}" (ord {})'.format(chr(s), s, chr(e), e)) 89 | 90 | height = myfont.height() 91 | for row in range(height): 92 | for char in string: 93 | data, _, width = myfont.get_ch(char) 94 | if myfont.hmap(): 95 | render_row_hmap(data, row, height, width, myfont.reverse()) 96 | else: 97 | render_row_vmap(data, row, height, width, myfont.reverse()) 98 | print() 99 | 100 | usage = '''Usage: 101 | ./font_test fontfile string 102 | fontfile is a Python font file name with the .py extension omitted. 103 | string is an optional string to render. 104 | If string is omitted a challenging test string will be rendered. This 105 | is for fonts created with -k extended. Other fonts will show "?" for 106 | nonexistent glyphs. 107 | Requires Python 3.2 or newer. 108 | ''' 109 | 110 | if __name__ == "__main__": 111 | if len(sys.argv) < 2 or sys.argv[1] == '--help': 112 | print(usage) 113 | elif len(sys.argv) == 2: 114 | test_font(sys.argv[1]) 115 | else: 116 | test_font(sys.argv[1], sys.argv[2]) 117 | -------------------------------------------------------------------------------- /icon_fonts/README.md: -------------------------------------------------------------------------------- 1 | # Icon Fonts 2 | 3 | It is possible to display icons by incorporating their images in a font file. 4 | There are `.ttf` and `.otf` files available which can be converted to Python 5 | using `font_to_py.py`. I have not had much success with these. I also wanted 6 | to create my own icons. I experimented with using a font editor to modify an 7 | existing font. I found the font editor unintuitive and hard to use. However it 8 | does have the advantage of producing scalable fonts which can mix normal glyphs 9 | with icons. 10 | 11 | The solution offered here uses the Linux `bitmap` editor plus a utility to 12 | convert a set of its output files to a Python font file. The image below shows 13 | typical usage. 14 | 15 | ![Image](./icon_font.jpg) 16 | 17 | # The bitmap editor 18 | 19 | This is documented in the man pages. It is easy and intuitive to use. To 20 | generate (say) 19x19 icons, issue 21 | ```bash 22 | $ bitmap -size 19x19 23 | ``` 24 | Save each bitmap under a different name: I use a `.c` extension as they are C 25 | source files. 26 | 27 | You need to create an additional icon to provide the output under error 28 | conditions, i.e. if an attempt is made to display a glyph not in the font. All 29 | icons in a font file must be the same height. 30 | 31 | # The file list 32 | 33 | Create a text file listing the bitmap filenames, one filename per line. The 34 | icon to be used as the default (error) image should be first. Subsequent icons 35 | will be assigned to characters "A", "B", "C" sequentially. 36 | 37 | The file list can include comments identified with `#`. 38 | 39 | # Creating the Python font 40 | 41 | This uses `c_to_python_font.py` which runs on a PC and requires Python 3.8 or 42 | later. The file `font_to_py.py` should be in the same directory. 43 | 44 | Assuming a file list `my_file_list.txt`, the following will create 45 | `my_font.py`. 46 | 47 | ```bash 48 | $ ./c_to_python_font.py my_file_list.txt my_font.py 49 | ``` 50 | 51 | # Using the font 52 | 53 | The following will print `icon[2]` where `icon[0]` is the default and `icon[1]` 54 | is associated with "A". 55 | ```python 56 | # Instantiate the ssd display object 57 | import my_font 58 | import CWriter 59 | wri = CWriter(ssd, my_font) 60 | wri.printstring("B") 61 | ``` 62 | -------------------------------------------------------------------------------- /icon_fonts/back.c: -------------------------------------------------------------------------------- 1 | #define back_width 19 2 | #define back_height 19 3 | static unsigned char back_bits[] = { 4 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x04, 5 | 0x80, 0x03, 0x06, 0xc0, 0x83, 0x07, 0xf0, 0xc3, 0x07, 0xf8, 0xe3, 0x07, 6 | 0xfe, 0xfb, 0x07, 0xff, 0xff, 0x07, 0xfe, 0xfb, 0x07, 0xf8, 0xf3, 0x07, 7 | 0xf0, 0xc3, 0x07, 0xc0, 0x83, 0x07, 0x80, 0x03, 0x07, 0x00, 0x02, 0x04, 8 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; 9 | -------------------------------------------------------------------------------- /icon_fonts/default.c: -------------------------------------------------------------------------------- 1 | #define default_width 19 2 | #define default_height 19 3 | static unsigned char default_bits[] = { 4 | 0x01, 0x00, 0x04, 0x02, 0x00, 0x02, 0x04, 0x00, 0x01, 0x08, 0x80, 0x00, 5 | 0x10, 0x40, 0x00, 0x20, 0x20, 0x00, 0x40, 0x10, 0x00, 0x80, 0x08, 0x00, 6 | 0x00, 0x05, 0x00, 0x00, 0x02, 0x00, 0x00, 0x05, 0x00, 0x80, 0x08, 0x00, 7 | 0x40, 0x10, 0x00, 0x20, 0x20, 0x00, 0x10, 0x40, 0x00, 0x08, 0x80, 0x00, 8 | 0x04, 0x00, 0x01, 0x02, 0x00, 0x02, 0x01, 0x00, 0x04}; 9 | -------------------------------------------------------------------------------- /icon_fonts/filenames.txt: -------------------------------------------------------------------------------- 1 | # Filenames for media player icons 2 | default.c 3 | play.c # Test 4 | stop.c 5 | pause.c 6 | back.c 7 | fwd.c 8 | -------------------------------------------------------------------------------- /icon_fonts/fwd.c: -------------------------------------------------------------------------------- 1 | #define fwd_width 19 2 | #define fwd_height 19 3 | static unsigned char fwd_bits[] = { 4 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x00, 5 | 0x03, 0x0e, 0x00, 0x0f, 0x1e, 0x00, 0x1f, 0x7e, 0x00, 0x3f, 0xfe, 0x00, 6 | 0xff, 0xfe, 0x03, 0xff, 0xff, 0x07, 0xff, 0xff, 0x07, 0xff, 0xfe, 0x03, 7 | 0x7f, 0xfe, 0x00, 0x1f, 0x3e, 0x00, 0x0f, 0x0e, 0x00, 0x07, 0x02, 0x00, 8 | 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; 9 | -------------------------------------------------------------------------------- /icon_fonts/icon_font.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/peterhinch/micropython-font-to-py/c24761448e6ef1c40716b9b2b629e6fa37b2c9d2/icon_fonts/icon_font.jpg -------------------------------------------------------------------------------- /icon_fonts/pause.c: -------------------------------------------------------------------------------- 1 | #define pause_width 19 2 | #define pause_height 19 3 | static unsigned char pause_bits[] = { 4 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, 0xf8, 0x00, 0xf8, 0xf8, 0x00, 5 | 0xf8, 0xf8, 0x00, 0xf8, 0xf8, 0x00, 0xf8, 0xf8, 0x00, 0xf8, 0xf8, 0x00, 6 | 0xf8, 0xf8, 0x00, 0xf8, 0xf8, 0x00, 0xf8, 0xf8, 0x00, 0xf8, 0xf8, 0x00, 7 | 0xf8, 0xf8, 0x00, 0xf8, 0xf8, 0x00, 0xf8, 0xf8, 0x00, 0xf8, 0xf8, 0x00, 8 | 0xf8, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; 9 | -------------------------------------------------------------------------------- /icon_fonts/play.c: -------------------------------------------------------------------------------- 1 | #define play_width 19 2 | #define play_height 19 3 | static unsigned char play_bits[] = { 4 | 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x1e, 0x00, 0x00, 0x7e, 0x00, 0x00, 5 | 0xfe, 0x01, 0x00, 0xfe, 0x07, 0x00, 0xfe, 0x1f, 0x00, 0xfe, 0x7f, 0x00, 6 | 0xfe, 0xff, 0x01, 0xfe, 0xff, 0x07, 0xfe, 0xff, 0x01, 0xfe, 0x7f, 0x00, 7 | 0xfe, 0x1f, 0x00, 0xfe, 0x07, 0x00, 0xfe, 0x01, 0x00, 0x7e, 0x00, 0x00, 8 | 0x1e, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00}; 9 | -------------------------------------------------------------------------------- /icon_fonts/stop.c: -------------------------------------------------------------------------------- 1 | #define stop_width 19 2 | #define stop_height 19 3 | static unsigned char stop_bits[] = { 4 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfc, 0xff, 0x00, 0xfc, 0xff, 0x00, 5 | 0xfc, 0xff, 0x00, 0xfc, 0xff, 0x00, 0xfc, 0xff, 0x00, 0xfc, 0xff, 0x00, 6 | 0xfc, 0xff, 0x00, 0xfc, 0xff, 0x00, 0xfc, 0xff, 0x00, 0xfc, 0xff, 0x00, 7 | 0xfc, 0xff, 0x00, 0xfc, 0xff, 0x00, 0xfc, 0xff, 0x00, 0xfc, 0xff, 0x00, 8 | 0xfc, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; 9 | -------------------------------------------------------------------------------- /writer/DRIVERS.md: -------------------------------------------------------------------------------- 1 | # Device Driver Implementation 2 | 3 | Display devices comprise two varieties, depending on whether the hardware 4 | includes a frame buffer or whether a frame buffer must be located on the 5 | controlling system. 6 | 7 | If the device has no frame buffer then the device driver should be designed 8 | to subclass `framebuf.FrameBuffer` with a suitably sized buffer on the host. If 9 | the device has its own frame buffer there are two options for the driver. One 10 | is to perform all display operations using the device's own firmware 11 | primitives. This is efficient and avoids the need for a buffer on the host, 12 | however it does involve some code complexity. 13 | 14 | The second option is to subclass `framebuf.FrameBuffer`, provide a buffer on 15 | the host, and copy its contents to the device's buffer when required. This can 16 | result in a very simple device driver at cost of RAM use and update speed. It 17 | also ensures compatibility with additional libraries to simplify display tasks. 18 | 19 | If a device subclasses `framebuf.FrameBuffer` the following libraries enhance 20 | its capability. The [Writer](./WRITER.md) class enables it to use multiple 21 | fonts with additional functionality such as word wrap, string metrics and tab 22 | handling. The [nano-gui](https://github.com/peterhinch/micropython-nano-gui.git) 23 | provides rudimentary GUI display capability with minimal RAM use. The 24 | [micro-gui](https://github.com/peterhinch/micropython-micro-gui) library is 25 | more fully featured, providing a means of user input at some cost in size and 26 | complexity. 27 | 28 | If a driver relies on a buffer located on the display device, the means of 29 | controlling the text insertion point, performing partial buffer updates and 30 | executing graphics primitives will be device dependent. If the functionality of 31 | the `writer` or `nanogui` libraries are required it will need to be 32 | implemented at device driver level. Authors of such code will need to know the 33 | format of the Python font file which is provided in this doc. 34 | 35 | ###### [Main README](../README.md) 36 | 37 | # Drivers subclassed from framebuf 38 | 39 | Where the buffer is held on the MicroPython host the driver should be 40 | subclassed from the official `framebuf.FrameBuffer` class. An example of such a 41 | driver is the [official SSD1306 driver](https://github.com/micropython/micropython-lib/blob/master/micropython/drivers/display/ssd1306/ssd1306.py). 42 | In addition the driver class should have bound variables `width` and `height` 43 | containing the size of the display in pixels, plus a `show` method which copies 44 | the buffer to the physical device. 45 | 46 | The device driver defines a buffer of the correct size to hold a full frame of 47 | data and instantiates the `framebuf.FrameBuffer` superclass to reference it. 48 | Monochrome displays should define the frame buffer format to match the physical 49 | characteristics of the display. In the case of colour displays RAM may be saved 50 | by using `framebuf.GS8` 8-bit colour. The `show` method can map this to the 51 | device's colour space if 8-bit mode is not supported. 52 | 53 | This design enables the supplied `Writer` and `CWriter` classes to be used for 54 | rendering arbitrary fonts to the display. The author of the device driver need 55 | not be concerned with the format of Python font files. 56 | 57 | The `Writer` and `CWriter` classes require horizontally mapped fonts. This is 58 | regardless of the mapping used in the device driver's `FrameBuffer`: the 59 | `Writer.printstring` method deals transparently with any mismatch. 60 | 61 | ## Example drivers 62 | 63 | See [Supported displays document](https://github.com/peterhinch/micropython-nano-gui/blob/master/DISPLAYS.md) 64 | for a full list. These drivers are subclassed from `framebuf.FrameBuffer` and 65 | have been tested with `writer`, `nanogui` and `microgui`. The following are a 66 | few samples: 67 | 68 | * The [SSD1306 OLED driver](https://github.com/micropython/micropython-lib/blob/master/micropython/drivers/display/ssd1306/ssd1306.py) 69 | * The [Nokia 5110](https://github.com/mcauser/micropython-pcd8544/blob/master/pcd8544_fb.py) 70 | * The [SSD1331 colour OLED](https://github.com/peterhinch/micropython-nano-gui/blob/master/drivers/ssd1331/ssd1331.py) 71 | * The [HX1230 96x68 LCD](https://github.com/mcauser/micropython-hx1230/blob/master/hx1230_fb.py) 72 | 73 | The latter example illustrates a very simple driver. 74 | 75 | # Drivers using the display buffer 76 | 77 | Authors of such drivers will need to have an understanding of the font file 78 | format. 79 | 80 | ## Specifying the font layout 81 | 82 | Each font file has a `get_ch()` function accepting a character as its 83 | argument. It returns a memoryview instance providing access to a bytearray 84 | corresponding to the individual glyph. The layout of this data is determined by 85 | the command line arguments presented to the `font_to_py.py` utility. It is 86 | the responsibility of the driver to copy that data to the physical device. 87 | 88 | The purpose of the `font_to_py.py` command line arguments specified to the 89 | user is to ensure that the data layout is optimised for the device so that this 90 | copy operation is a fast bytewise copy or SPI/I2C transfer. The driver 91 | documentation should therefore specify these arguments to ensure the layout is 92 | optimal. Mapping may be horizontal or vertical, and the bit order of individual 93 | bytes may be defined. These are detailed below. 94 | 95 | In the case of devices with their own frame buffer the `Writer` class will need 96 | to be re-written or adapted to match the hardware's method of tracking such 97 | things as the text insertion point. Consideration should be given to employing 98 | the same interface as the `Writer` class to simplify the porting of user code 99 | between displays with differing hardware. 100 | 101 | ## Python Font files 102 | 103 | Assume the user has run the utility to produce a file `myfont.py` This then 104 | has the following outline definition (in practice the bytes objects are large): 105 | 106 | ```python 107 | # Code generated by font-to-py.py. 108 | # Font: FreeSans.ttf 109 | # Cmd: ./font_to_py.py -x FreeSans.ttf 17 font10.py 110 | version = '0.28' 111 | 112 | def height(): 113 | return 17 114 | 115 | def max_width(): 116 | return 17 117 | 118 | def hmap(): 119 | return True 120 | 121 | def reverse(): 122 | return False 123 | 124 | def monospaced(): 125 | return False 126 | 127 | def min_ch(): 128 | return 32 129 | 130 | def max_ch(): 131 | return 126 132 | 133 | _font =\ 134 | b'\x09\x00\x3c\x00\xc7\x00\xc3\x00\x03\x00\x03\x00\x06\x00\x0c\x00'\ 135 | b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'\ 136 | 137 | _index =\ 138 | b'\x00\x00\x24\x00\x37\x00\x4a\x00\x5d\x00\x81\x00\xa5\x00\xc9\x00'\ 139 | b'\xed\x00\x00\x01\x13\x01\x26\x01\x39\x01\x5d\x01\x70\x01\x83\x01'\ 140 | b'\x60\x0b' 141 | 142 | _mvfont = memoryview(_font) 143 | 144 | def get_ch(ch): 145 | ordch = ord(ch) 146 | if ordch >= 32 and ordch <= 126: 147 | idx_offs = 2 * (ordch - 32 + 1) 148 | else: 149 | idx_offs = 0 150 | offset = int.from_bytes(_index[idx_offs : idx_offs + 2], 'little') 151 | width = int.from_bytes(_font[offset:offset + 2], 'little') 152 | 153 | next_offs = offset + 2 + ((width - 1)//8 + 1) * 17 154 | return _mvfont[offset + 2:next_offs], 17, width 155 | ``` 156 | 157 | `height` and `width` are specified in bits (pixels). See Appendix 1 for extra 158 | code in fonts created with the `--iterate` arg. 159 | 160 | In the case of monospaced fonts the `max_width` function returns the width of 161 | every character. For variable pitch fonts it returns the width of the widest 162 | character. Device drivers can use this to rapidly determine whether a string 163 | will fit the available space. If it will fit on the assumption that all chars 164 | are maximum width, it can be rendered rapidly without doing a character by 165 | character check. 166 | 167 | `get_ch()` returns a memoryview of an individual glyph with its dimensions 168 | and contains all the bytes required to render the character including trailing 169 | space. 170 | 171 | The `_font` bytearray holds the glyphs corresponding to every character in the 172 | font. Entry 0 is the default glyph, used if an attempt is made to render a 173 | nonexistent character. 174 | 175 | The index holds two integers (each occupying 2 bytes) per character. The index 176 | has an entry for every character in the specified range, whether or not that 177 | character exists. 178 | 179 | Index entries are offsets into the `_font` bytearray representing the start and 180 | end of the glyph. If the font comprises a set of characters which is not 181 | contiguous, missing characters have an index entry which points to the first 182 | glyph in the `_font` bytearray. This ensures that the default glyph is 183 | rendered. 184 | 185 | ## Fixed width fonts 186 | 187 | If a Python font file is created with the `-f` argument, all characters will 188 | be saved with the width of the widest. In general it is not necessary to 189 | specify this option. The driver can perform fixed pich rendering by rendering 190 | the character as variable pitch, then blanking and advancing the pixel column 191 | by the value returned by `font.max_width()`. 192 | 193 | ## Binary font files 194 | 195 | This format is unlikely to find application beyond the e-paper driver. It was 196 | designed for micropower applications where the Pyboard has no SD card. Fonts 197 | are stored as random access files on power-switched Flash storage or SD card. 198 | This method is probably too slow for anything other than e-paper displays. 199 | 200 | The format is as follows. Files are binary with a four byte header and 126 201 | fixed length records. The header consists of two file identifiers enabling the 202 | file format to be checked, followed by bytes specifying the width and height. 203 | The length of each record is (width + 1) bytes. 204 | 205 | The file indentifiers depend on the -x and -r arguments specified to `font_to_py.py` 206 | and are as follows: 207 | 208 | hmap reverse byte 209 | -x -r 0 1 210 | 0 0 0x3f 0xe7 211 | 1 0 0x40 0xe7 212 | 0 1 0x41 0xe7 213 | 1 1 0x42 0xe7 214 | 215 | Each record starts with a width byte specifying the x dimension of the glyph if 216 | rendered proportionally spaced, followed by the glyph data. This data includes 217 | trailing space ensuring that all records have the size specified in the header. 218 | 219 | ## Mapping (Python and Binary fonts) 220 | 221 | A character occupies a space where (0, 0) represents the coordinates of the top 222 | left hand corner of the bitmap. It comprises a set of pixels where increasing x 223 | values represent locations to the right of the origin and increasing y values 224 | represent downward positions. Mapping defines the relationship between this 225 | abstract two dimensional array of bits and the physical linear sequence of bytes. 226 | 227 | Vertical mapping means that the LSB of first byte is pixel (0,0), MSB of first 228 | byte is (0, 7). The second byte (assuming the height is greater than 8 pixels) 229 | is (0, 8) to (0, 15). Once the column is complete the next byte represents 230 | (1, 0) to (1, 7). 231 | 232 | Horizontal mapping means that the MSB of byte 0 is pixel (0,0) with LSB at 233 | (7,0), with the second byte covering (8, 0) to (15, 0) if the width is greater 234 | than 8. 235 | 236 | Bit reversal provides for the case where the bit order of each byte is reversed 237 | i.e. a byte comprising bits [b7b6b5b4b3b2b1b0] becomes [b0b1b2b3b4b5b6b7]. 238 | 239 | # Specification and Project Notes 240 | 241 | The design aims primarily to minimise RAM usage. Minimising the size of the 242 | bytecode is a secondary aim. Indexed addressing is used to reduce this in 243 | the case of proportional fonts, and also to facilitate non-contiguous fonts, at 244 | a small cost in performance. The size of the Python source file is a lesser 245 | consideration, with readability being prioritised over size. Hence they are 246 | "pretty formatted" with the large bytes objects split over multiple lines for 247 | readability. 248 | 249 | Fonts created with the `font_to_py` utility have been extensively tested with 250 | each of the mapping options. They are used with drivers for SSD1306 OLEDs, 251 | SSD1963 LCD displays, the official LCD160CR and the Digital Artists 2.7 inch 252 | e-paper display. 253 | 254 | # Writing display drivers 255 | 256 | A guide to writing suitable drivers may be found 257 | [here](https://github.com/peterhinch/micropython-nano-gui/blob/master/DRIVERS.md#7-writing-device-drivers). 258 | These can be very simple as the `FrameBuffer` base class provides much of the 259 | functionality. 260 | 261 | # Appendix 1. The -i --iterate argument 262 | 263 | This specialist arg causes extra code to be included in the font file, to 264 | provide for iterating over all the glyphs in the file. The following sample of 265 | the extra code assumes a font comprising '0123456789:' 266 | 267 | ```python 268 | def glyphs(): 269 | for c in """0123456789:""": 270 | yield c, get_ch(c) 271 | ``` 272 | 273 | Typical usage under CPython 3 (for a font `cyrillic.py`) is 274 | 275 | ```python 276 | import cyrillic 277 | res = [] 278 | for glyph in cyrillic.glyphs(): 279 | res.append(list(glyph)) # Each element is [char, glyph, height, width] 280 | ``` 281 | 282 | -------------------------------------------------------------------------------- /writer/WRITER.md: -------------------------------------------------------------------------------- 1 | # Writer and Cwriter classes 2 | 3 | These classes facilitate rendering Python font files to displays where the 4 | display driver is subclassed from the `framebuf` class. Basic support is for 5 | scrolling text display using multiple fonts. There is a growing list of 6 | displays with compatible drivers, see 7 | 8 | ### [Supported displays document](https://github.com/peterhinch/micropython-nano-gui/blob/master/DISPLAYS.md) 9 | 10 | Three cross-platform GUI libraries build on this to provide a variety of widgets. 11 | These are: 12 | * [nano-gui](https://github.com/peterhinch/micropython-nano-gui) An extremely 13 | lightweight display-only GUI. 14 | * [micro-gui](https://github.com/peterhinch/micropython-micro-gui) A GUI 15 | providing input via either pushbuttons or pushbuttons plus a rotary encoder. 16 | * [micropython-touch](https://github.com/peterhinch/micropython-touch) Input 17 | is provided by touch. 18 | 19 | For applications needing only to render text to a display, and optionally to 20 | draw graphics using `FrameBuffer` primitives, the `writer` module may be used 21 | alone. 22 | 23 | Example code and images are for 128*64 SSD1306 OLED displays. 24 | 25 | ![Image](images/IMG_2866.JPG) 26 | Scrolling text, multiple fonts. 27 | 28 | ![Image](images/IMG_2861.JPG) 29 | A field containing variable length text with a border. 30 | 31 | ![Image](images/rjust.JPG) 32 | Right justified text. 33 | 34 | ![Image](images/mixed.JPG) 35 | Mixed text and graphics. 36 | 37 | ![Image](images/fields.JPG) 38 | Labels and Fields (from nanogui.py). 39 | 40 | ![Image](images/fonts.png) 41 | The `CWriter` class (from nanogui): `Label` objects in two fonts. 42 | 43 | # Contents 44 | 45 | 1. [Introduction](./WRITER.md#1-introduction) 46 | 1.1 [Release notes](./WRITER.md#11-release-notes) 47 | 1.2 [Hardware](./WRITER.md#12-hardware) 48 | 1.3 [Files](./WRITER.md#13-files) 49 | 1.4 [Fonts](./WRITER.md#14-fonts) 50 | 2. [Writer and CWriter classes](./WRITER.md#2-writer-and-cwriter-classes) 51 | 2.1 [The Writer class](./WRITER.md#21-the-writer-class) For monochrome displays. 52 |      2.1.1 [Static Method](./WRITER.md#211-static-method) 53 |      2.1.2.[Constructor](./WRITER.md#212-constructor) 54 |      2.1.3 [Methods](./WRITER.md#213-methods) 55 | 2.2 [The CWriter class](./WRITER.md#22-the-cwriter-class) For colour displays. 56 |      2.2.1 [Static Method](./WRITER.md#221-static-method) 57 |      2.2.2 [Constructor](./WRITER.md#222-constructor) 58 |      2.2.3 [Methods](./WRITER.md#223-methods) 59 | 2.3 [Example color code](./WRITER.md#23-example-color-code) For most display drivers. 60 | 2.4 [Use with 4 bit drivers](./WRITER.md#24-use-with-4-bit-drivers) Color definition uses a different technique. 61 | 3. [Icons](./WRITER.md#3-icons) How to render simple icons. 62 | 63 | ###### [Main README](../README.md) 64 | 65 | # 1. Introduction 66 | 67 | The module provides a `Writer` class for rendering bitmapped monochrome fonts 68 | created by `font_to_py.py`. The `CWriter` class extends this to support color 69 | rendering. Rendering is to a `FrameBuffer` instance, e.g. to a display whose 70 | driver is subclassed from a `FrameBuffer`. 71 | 72 | The module has the following features: 73 | * Generality: `Writer` can work with any `framebuf` derived driver. 74 | * Multiple display operation. 75 | * Text display of fixed and variable pitch fonts with wrapping and vertical 76 | scrolling. 77 | * Wrap/clip options: clip, character wrap or word wrap. 78 | * Tab support. This is rudimentary and "micro". 79 | * String metrics to enable right or centre justification. 80 | * Inverse (background color on foreground color) display. 81 | 82 | The `CWriter` class requires a compatible display driver. These are listed 83 | [in this document](https://github.com/peterhinch/micropython-nano-gui/blob/master/DISPLAYS.md). 84 | There is no support for RTL languages but a workround is discussed 85 | [here](../charsets/RTL_languages.md). 86 | 87 | ## 1.1 Release Notes 88 | 89 | V0.5.1 Dec 2022__ 90 | Add support for 4 bit color display drivers. 91 | V0.5.0 Sep 2021 92 | Requires firmware V1.17 or later. 93 | 94 | V0.4.3 Aug 2021 95 | Supports fast rendering of glyphs to color displays (PR7682). 96 | 97 | V0.4.0 Jan 2021 98 | Improved handling of the `col_clip` and `wrap` options. Improved accuracy 99 | avoids needless word wrapping. The clip option now displays as much of the last 100 | visible glyph as possible: formerly a glyph which would not fit in its entirety 101 | was discarded. 102 | 103 | ## 1.2 Hardware 104 | 105 | Tests and demos assume a 128*64 SSD1306 OLED display connected via I2C or SPI. 106 | Wiring is specified in `ssd1306_setup.py`. Edit this to use a different bus or 107 | for a non-Pyboard target. [Section 2.3](./WRITER.md#23-example-color-code) 108 | shows how to drive color displays using the `CWriter` class. 109 | 110 | ## 1.3 Files 111 | 112 | 1. `writer.py` Supports `Writer` and `CWriter` classes. 113 | 2. `ssd1306_setup.py` Hardware initialisation for SSD1306. Requires the 114 | official [SSD1306 driver](https://github.com/micropython/micropython-lib/tree/master/micropython/drivers/display/ssd1306). 115 | 3. `writer_demo.py` Demo using a 128*64 SSD1306 OLED display. Import to see 116 | usage information. 117 | 4. `writer_tests.py` Test/demo scripts. Import to see usage information. 118 | 119 | Sample fonts: 120 | 1. `freesans20.py` Variable pitch font file. 121 | 2. `courier20.py` Fixed pitch font file. 122 | 3. `font10.py` Smaller variable pitch fonts. 123 | 4. `font6.py` 124 | 125 | ## 1.4 Fonts 126 | 127 | Python font files should be created using `font-to-py.py` using horizontal 128 | mapping; this is the default. The `-r` option is not required. If RAM is critical 129 | fonts may be frozen as bytecode reducing the RAM impact of each font to about 130 | 340 bytes. This is highly recommended. 131 | 132 | ###### [Contents](./WRITER.md#contents) 133 | 134 | # 2. Writer and CWriter classes 135 | 136 | The `Writer` class provides fast rendering to monochrome displays using bit 137 | blitting. The `CWriter` class is a subclass of `Writer` to support color 138 | displays which offers comparable performance. 139 | 140 | Multiple screens are supported. On any screen multiple `Writer` or `CWriter` 141 | instances may be used, each using a different font. A class variable holds the 142 | state of each screen to ensure that the insertion point is managed across 143 | multiple instances/fonts. 144 | 145 | ###### [Contents](./WRITER.md#contents) 146 | 147 | ## 2.1 The Writer class 148 | 149 | This class facilitates rendering characters from Python font files to a device, 150 | assuming the device has a driver subclassed from `framebuf`. It supports three 151 | ways of handling text which would overflow the display: clipping, character 152 | wrapping and simple word wrapping. 153 | 154 | It handles newline and tab characters, black-on-white inversion, and field 155 | blanking to enable variable length contents to be updated at a fixed location. 156 | 157 | Typical use with an SSD1306 display and the official driver is as follows: 158 | 159 | ```python 160 | from ssd1306_setup import WIDTH, HEIGHT, setup 161 | from writer import Writer 162 | import freesans20 # Font to use 163 | 164 | use_spi=False # Tested with a 128*64 I2C connected SSD1306 display 165 | ssd = setup(use_spi) # Instantiate display: must inherit from framebuf 166 | # Demo drawing geometric shapes 167 | rhs = WIDTH -1 168 | ssd.line(rhs - 20, 0, rhs, 20, 1) # Demo underlying framebuf methods 169 | square_side = 10 170 | ssd.fill_rect(rhs - square_side, 0, square_side, square_side, 1) 171 | # Instantiate a writer for a specific font 172 | wri = Writer(ssd, freesans20) # verbose = False to suppress console output 173 | Writer.set_textpos(ssd, 0, 0) # In case a previous test has altered this 174 | wri.printstring('Sunday\n12 Aug 2018\n10.30am') 175 | ssd.show() 176 | ``` 177 | 178 | The file `writer_demo.py` illustrates the use of font files with a 128*64 179 | SSD1306 OLED display and the official 180 | [SSD1306 driver](https://github.com/micropython/micropython-lib/tree/master/micropython/drivers/display/ssd1306). 181 | 182 | ### 2.1.1 Static Method 183 | 184 | The `Writer` class exposes the following static method: 185 | 186 | 1. `set_textpos(device, row=None, col=None)`. The `device` is the display 187 | instance. This method determines where on screen subsequent text is to be 188 | rendered. The initial value is (0, 0) - the top left corner. Arguments are in 189 | pixels with positive values representing down and right respectively. The 190 | insertion point defines the top left hand corner of the next character to be 191 | output. 192 | 193 | Where `None` is passed, the setting is left unchanged. 194 | Return: `row`, `col` current settings. 195 | 196 | The insertion point applies to all `Writer` instances having the same device. 197 | The insertion point on a given screen is maintained regardless of the font in 198 | use. 199 | 200 | ### 2.1.2 Constructor 201 | 202 | This takes the following args: 203 | 1. `device` The hardware device driver instance for the screen in use. 204 | 2. `font` A Python font instance. 205 | 3. `verbose=True` If `True` the constructor emits console printout. 206 | 207 | ### 2.1.3 Methods 208 | 209 | 1. `printstring(string, invert=False)`. Renders the string at the current 210 | insertion point. Newline and Tab characters are honoured. If `invert` is 211 | `True` the text is output with foreground and background colors transposed. 212 | 2. `height()` Returns the font height in pixels. 213 | 3. `stringlen(string, oh=False)` Returns the length of a string in pixels. 214 | Appications can use this for right or centre justification. 215 | The `oh` arg is for internal use. If set, the method returns a `bool`, `True` 216 | if the string would overhang the display edge if rendered at the current 217 | insertion point. 218 | 4. `set_clip(row_clip=None, col_clip=None, wrap=None)`. If `row_clip` and/or 219 | `col_clip` are `True`, characters will be clipped if they extend beyond the 220 | boundaries of the physical display. If `col_clip` is `False` characters will 221 | wrap onto the next line. If `row_clip` is `False` the display will, where 222 | necessary, scroll up to ensure the line is rendered. If `wrap` is `True` 223 | word-wrapping will be performed, assuming words are separated by spaces. 224 | If any arg is `None`, that value will be left unchanged. 225 | Returns the current values of `row_clip`, `col_clip` and `wrap`. 226 | 5. `tabsize(value=None)`. If `value` is an integer sets the tab size. Returns 227 | the current tab size (initial default is 4). Tabs only work properly with 228 | fixed pitch fonts. 229 | 230 | ###### [Contents](./WRITER.md#contents) 231 | 232 | ## 2.2 The CWriter class 233 | 234 | This extends the `Writer` class by adding support for color displays. A color 235 | value is an integer whose interpretation is dependent on the display hardware 236 | and device driver. The Python font file uses single bit pixels. On a color 237 | screen these are rendered using foreground and background colors. Display 238 | drivers provide an `rgb` classmethod which converts RGB values to an integer 239 | suitable for the driver. RGB values are integers in range `0 <= c <= 255` (see 240 | example code below). 241 | 242 | ### 2.2.1 Static method 243 | 244 | The `CWriter` class has one static method `create_color`. This is exclusively 245 | for use with 4 bit color display drivers. It populates the driver's color 246 | lookup table. Args: 247 | 1. `ssd` The display instance. 248 | 2. `idx` Color number in range 0 <= idx <= 15. These are arbitrary but by 249 | convention 0 is black and 15 white. 250 | 3. `r` Red value. Values are in range 0 <= red <= 255. 251 | 4. `g` Green value. 252 | 5. `b` Blue value. 253 | 254 | The return value is the `idx` value, hence a color can be defined as 255 | ```python 256 | GREEN = CWriter.create_color(ssd, 1, 0, 255, 0) 257 | ``` 258 | 259 | ### 2.2.2 Constructor 260 | 261 | This takes the following args: 262 | 1. `device` The hardware device driver instance for the screen in use. 263 | 2. `font` A Python font instance. 264 | 3. `fgcolor=None` Foreground color. If `None` a monochrome display is assumed. 265 | 4. `bgcolor=None` Background color. If `None` a monochrome display is assumed. 266 | 5. `verbose=True` If `True` the constructor emits console printout. 267 | 268 | The constructor checks for suitable firmware and also for a compatible device 269 | driver: an `OSError` is raised if these are absent. 270 | 271 | ### 2.2.3 Methods 272 | 273 | All methods of the base class are supported. Additional method: 274 | 1. `setcolor(fgcolor=None, bgcolor=None)`. Sets the foreground and background 275 | colors. If one is `None` that value is left unchanged. If both are `None` the 276 | constructor defaults are restored. Constructor defaults are 1 and 0 277 | for monochrome displays (`Writer`). Returns foreground and background color 278 | values. 279 | 280 | The `printstring` method works as per the base class except that the string is 281 | rendered in foreground color on background color (or reversed if `invert` is 282 | `True`). 283 | 284 | ###### [Contents](./WRITER.md#contents) 285 | 286 | ## 2.3 Example color code 287 | 288 | The following will not work with 4-bit drivers: see [section 2.4](./WRITER.md#24-use-with-4-bit-drivers). 289 | 290 | This demo assumes an SSD1351 OLED connected to a Pyboard D. It will need to be 291 | adapted for other hardware. In order to run this, the following files need to 292 | be copied to the host's filesystem: 293 | * `writer.py` 294 | * `freesans20.py` 295 | * The display driver. This should be installed as per 296 | [this document](https://github.com/peterhinch/micropython-nano-gui/blob/master/DRIVERS.md#12-installation) 297 | to ensure the correct directory structure. 298 | 299 | ```python 300 | import machine 301 | import gc 302 | import time 303 | from writer import CWriter 304 | import freesans20 # Font to use 305 | from drivers.ssd1351.ssd1351 import SSD1351 as SSD # Adapt for other hardware 306 | 307 | # Needed on my Pyboard D PCB to enable supply to the display 308 | pp = machine.Pin('EN_3V3') 309 | pp(1) 310 | time.sleep(1) 311 | 312 | # Adafruit options 313 | # height = 96 # 1.27 inch 96*128 (rows*cols) display 314 | height = 128 # 1.5 inch 128*128 display 315 | 316 | pdc = machine.Pin('Y12', machine.Pin.OUT_PP, value=0) 317 | pcs = machine.Pin('W32', machine.Pin.OUT_PP, value=1) 318 | prst = machine.Pin('Y11', machine.Pin.OUT_PP, value=1) 319 | spi = machine.SPI(2, baudrate=20_000_000) 320 | gc.collect() # Precaution before instantiating framebuf 321 | ssd = SSD(spi, pcs, pdc, prst, height) # Create a display instance 322 | 323 | # Define a few colors (for 4-bit drivers this is done differently) 324 | GREEN = SSD.rgb(0, 255, 0) 325 | RED = SSD.rgb(255,0,0) 326 | BLACK = SSD.rgb(0, 0, 0) 327 | 328 | # Demo drawing geometric shapes using underlying framebuf methods 329 | rhs = ssd.width -1 330 | ssd.line(rhs - 20, 0, rhs, 20, GREEN) 331 | square_side = 10 332 | ssd.fill_rect(rhs - square_side, 0, square_side, square_side, GREEN) 333 | 334 | # Instantiate a writer for a specific font 335 | wri = CWriter(ssd, freesans20) # Can set verbose = False to suppress console output 336 | CWriter.set_textpos(ssd, 0, 0) # In case a previous test has altered this 337 | wri.setcolor(RED, BLACK) # Colors can be set in constructor or changed dynamically 338 | wri.printstring('Sunday\n12 Aug 2018\n10.30am') 339 | ssd.show() 340 | ``` 341 | ## 2.4 Use with 4 bit drivers 342 | 343 | Some color display drivers for larger displays use 4-bit colors: this achieves 344 | a substantial reduction in the size of the frame buffer at the cost of limiting 345 | the number of colors that can be displayed. The driver expands the colors at 346 | run time using a lookup table. 347 | 348 | This means that colors must be defined using the `create_color` static method 349 | described above. 350 | ```python 351 | from machine import SPI, Pin 352 | from writer import CWriter 353 | import freesans20 # Font to use 354 | from drivers.ili93xx.ili9341 import ILI9341 as SSD 355 | 356 | spi = SPI(0, sck=Pin(6), mosi=Pin(7), miso=Pin(4), baudrate=30_000_000) 357 | dc = Pin(8, Pin.OUT, value=0) 358 | cs = Pin(10, Pin.OUT, value=1) 359 | rst = Pin(9, Pin.OUT, value=1) 360 | ssd = SSD(spi, cs, dc, rst) 361 | 362 | # Define a few colors: populates the lookup table 363 | BLACK = CWriter.create_color(ssd, 0, 0, 0, 0) 364 | GREEN = CWriter.create_color(ssd, 1, 0, 255, 0) 365 | RED = CWriter.create_color(ssd, 2, 255, 0, 0) 366 | YELLOW = CWriter.create_color(ssd, 3, 255, 255, 0) 367 | # Demo drawing geometric shapes using underlying framebuf methods 368 | rhs = ssd.width -1 369 | ssd.line(rhs - 20, 0, rhs, 20, GREEN) 370 | square_side = 10 371 | ssd.fill_rect(rhs - square_side, 0, square_side, square_side, GREEN) 372 | 373 | # Instantiate a writer for a specific font 374 | wri = CWriter(ssd, freesans20) # Can set verbose = False to suppress console output 375 | CWriter.set_textpos(ssd, 0, 0) # In case a previous test has altered this 376 | wri.setcolor(RED, BLACK) # Colors can be set in constructor or changed dynamically 377 | wri.printstring('Tuesday\n6th December 2020\n10.30am\n') 378 | wri.setcolor(YELLOW, BLACK) 379 | wri.printstring('Running on a 4-bit driver.') 380 | ssd.show() 381 | ``` 382 | ###### [Contents](./WRITER.md#contents) 383 | 384 | # 3. Icons 385 | 386 | It is possible to create simple icons, for example to create micro-gui 387 | pushbuttons with media playback symbols. Take an arbitrary free font and use a 388 | font editor to replace the glyphs for 'A', 'B', 'C'... with chosen symbols. 389 | Save this modified font under a new name. Then run `font_to_py` to create a 390 | Python font in a chosen size and comprising only those characters (`-c ABCDE`). 391 | Instantiate the buttons with e.g. `text="A"`. 392 | 393 | Alternatively icons can be created as bitmaps and converted to Python font 394 | files as [described here](../icon_fonts/README.md). 395 | -------------------------------------------------------------------------------- /writer/courier20.py: -------------------------------------------------------------------------------- 1 | # Code generated by font-to-py.py. 2 | # Font: Courier Prime.ttf 3 | version = '0.2' 4 | 5 | def height(): 6 | return 20 7 | 8 | def max_width(): 9 | return 14 10 | 11 | def hmap(): 12 | return True 13 | 14 | def reverse(): 15 | return False 16 | 17 | def monospaced(): 18 | return True 19 | 20 | def min_ch(): 21 | return 32 22 | 23 | def max_ch(): 24 | return 126 25 | 26 | _font =\ 27 | b'\x0e\x00\x00\x00\x00\x00\x7c\x00\xfe\x00\xc7\x00\xc3\x00\x03\x00'\ 28 | b'\x07\x00\x1e\x00\x18\x00\x18\x00\x18\x00\x3c\x00\x3c\x00\x18\x00'\ 29 | b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x0e\x00\x00\x00\x00\x00'\ 30 | b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'\ 31 | b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'\ 32 | b'\x00\x00\x00\x00\x0e\x00\x00\x00\x00\x00\x60\x00\x60\x00\x60\x00'\ 33 | b'\x60\x00\x60\x00\x60\x00\x60\x00\x60\x00\x00\x00\x60\x00\xf0\x00'\ 34 | b'\xf0\x00\x60\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x0e\x00'\ 35 | b'\x00\x00\x00\x00\xe6\x00\xe6\x00\x66\x00\x66\x00\x66\x00\x66\x00'\ 36 | b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'\ 37 | b'\x00\x00\x00\x00\x00\x00\x00\x00\x0e\x00\x00\x00\x03\x30\x02\x20'\ 38 | b'\x02\x20\x06\x60\x3f\xf8\x3f\xf8\x0c\xc0\x08\x80\x7f\xf0\xff\xf0'\ 39 | b'\x19\x80\x11\x00\x33\x00\x33\x00\x00\x00\x00\x00\x00\x00\x00\x00'\ 40 | b'\x00\x00\x0e\x00\x0c\x00\x0c\x00\x3d\x80\x7f\x80\xcd\x80\xcc\x80'\ 41 | b'\xec\x00\x7f\x00\x0f\x80\x0c\xc0\xcc\xc0\xcd\xc0\xff\x80\xcf\x00'\ 42 | b'\x0c\x00\x0c\x00\x00\x00\x00\x00\x00\x00\x00\x00\x0e\x00\x00\x00'\ 43 | b'\x00\x00\x38\x00\xfe\x18\xc6\x30\xc6\x60\xfe\xc0\x39\x80\x03\x00'\ 44 | b'\x06\x70\x1d\xfc\x39\x8c\x71\x8c\x61\xfc\x00\x70\x00\x00\x00\x00'\ 45 | b'\x00\x00\x00\x00\x00\x00\x0e\x00\x00\x00\x00\x00\x1e\x00\x3f\x00'\ 46 | b'\x63\x00\x63\x00\x60\x00\x30\x00\x31\xc0\x49\xc0\xc7\x00\xc3\x00'\ 47 | b'\xe3\x00\x7f\xc0\x39\xc0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'\ 48 | b'\x0e\x00\x00\x00\x00\x00\x60\x00\x60\x00\x60\x00\x60\x00\x60\x00'\ 49 | b'\x60\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'\ 50 | b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x0e\x00\x04\x00\x0e\x00'\ 51 | b'\x18\x00\x30\x00\x30\x00\x60\x00\x60\x00\xc0\x00\xc0\x00\xc0\x00'\ 52 | b'\xc0\x00\xc0\x00\xc0\x00\x60\x00\x60\x00\x30\x00\x38\x00\x1c\x00'\ 53 | b'\x0e\x00\x04\x00\x0e\x00\x40\x00\xe0\x00\x30\x00\x18\x00\x18\x00'\ 54 | b'\x0c\x00\x0c\x00\x06\x00\x06\x00\x06\x00\x06\x00\x06\x00\x06\x00'\ 55 | b'\x0c\x00\x0c\x00\x18\x00\x38\x00\x70\x00\xe0\x00\x80\x00\x0e\x00'\ 56 | b'\x00\x00\x00\x00\x0c\x00\x0c\x00\x0c\x00\xed\xc0\x7f\x80\x0c\x00'\ 57 | b'\x1e\x00\x33\x00\x23\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'\ 58 | b'\x00\x00\x00\x00\x00\x00\x00\x00\x0e\x00\x00\x00\x00\x00\x00\x00'\ 59 | b'\x0c\x00\x0c\x00\x0c\x00\x0c\x00\xff\xc0\xff\xc0\x0c\x00\x0c\x00'\ 60 | b'\x0c\x00\x0c\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'\ 61 | b'\x00\x00\x0e\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'\ 62 | b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x78\x00\x70\x00'\ 63 | b'\x60\x00\x60\x00\xc0\x00\xc0\x00\x00\x00\x00\x00\x0e\x00\x00\x00'\ 64 | b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xc0\xff\xc0'\ 65 | b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'\ 66 | b'\x00\x00\x00\x00\x00\x00\x0e\x00\x00\x00\x00\x00\x00\x00\x00\x00'\ 67 | b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x60\x00'\ 68 | b'\xf0\x00\xf0\x00\x60\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'\ 69 | b'\x0e\x00\x00\xc0\x01\x80\x01\x80\x03\x00\x03\x00\x02\x00\x06\x00'\ 70 | b'\x04\x00\x0c\x00\x0c\x00\x18\x00\x18\x00\x30\x00\x30\x00\x20\x00'\ 71 | b'\x60\x00\x40\x00\xc0\x00\x00\x00\x00\x00\x0e\x00\x00\x00\x00\x00'\ 72 | b'\x1e\x00\x3f\x00\x61\x80\xe1\xc0\xc0\xc0\xc0\xc0\xc0\xc0\xc0\xc0'\ 73 | b'\xc0\xc0\xe1\xc0\x61\x80\x3f\x00\x1e\x00\x00\x00\x00\x00\x00\x00'\ 74 | b'\x00\x00\x00\x00\x0e\x00\x00\x00\x00\x00\x0c\x00\x7c\x00\xec\x00'\ 75 | b'\x0c\x00\x0c\x00\x0c\x00\x0c\x00\x0c\x00\x0c\x00\x0c\x00\x0c\x00'\ 76 | b'\xff\xc0\xff\xc0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x0e\x00'\ 77 | b'\x00\x00\x00\x00\x3e\x00\xff\x00\xc3\x80\xc1\x80\x01\x80\x01\x00'\ 78 | b'\x02\x00\x04\x00\x08\x00\x11\x80\x21\x80\xff\x80\xff\x80\x00\x00'\ 79 | b'\x00\x00\x00\x00\x00\x00\x00\x00\x0e\x00\x00\x00\x00\x00\x3e\x00'\ 80 | b'\x7f\x00\x61\x80\x61\x80\x01\x80\x1f\x00\x1f\x00\x03\x80\x01\x80'\ 81 | b'\x01\x80\xc3\x80\xff\x00\x3e\x00\x00\x00\x00\x00\x00\x00\x00\x00'\ 82 | b'\x00\x00\x0e\x00\x00\x00\x00\x00\x03\x00\x07\x00\x0b\x00\x1b\x00'\ 83 | b'\x13\x00\x23\x00\x63\x00\xff\xc0\xff\xe0\x03\x00\x03\x00\x0f\xc0'\ 84 | b'\x0f\xc0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x0e\x00\x00\x00'\ 85 | b'\x00\x00\x7f\x80\x7f\x80\x60\x00\x60\x00\x7e\x00\x7f\x00\x63\x80'\ 86 | b'\x01\x80\x01\x80\x01\x80\xc3\x80\xff\x00\x3c\x00\x00\x00\x00\x00'\ 87 | b'\x00\x00\x00\x00\x00\x00\x0e\x00\x00\x00\x00\x00\x07\x80\x1f\x80'\ 88 | b'\x3c\x00\x70\x00\x60\x00\xcf\x00\xff\x80\xe1\xc0\xc0\xc0\xc0\xc0'\ 89 | b'\x61\xc0\x7f\x80\x1f\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'\ 90 | b'\x0e\x00\x00\x00\x00\x00\xff\x80\xff\x80\xc1\x80\xc1\x00\x03\x00'\ 91 | b'\x02\x00\x06\x00\x06\x00\x0c\x00\x0c\x00\x08\x00\x18\x00\x10\x00'\ 92 | b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x0e\x00\x00\x00\x00\x00'\ 93 | b'\x1e\x00\x3f\x00\x61\x80\x61\x80\x73\x80\x3f\x00\x7f\x00\xe3\x80'\ 94 | b'\xc1\x80\xc1\x80\xe3\x80\x7f\x00\x3e\x00\x00\x00\x00\x00\x00\x00'\ 95 | b'\x00\x00\x00\x00\x0e\x00\x00\x00\x00\x00\x3e\x00\x7f\x80\xe1\x80'\ 96 | b'\xc0\xc0\xc0\xc0\xe1\xc0\x7f\xc0\x3c\xc0\x01\x80\x03\x80\x0f\x00'\ 97 | b'\x7e\x00\x78\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x0e\x00'\ 98 | b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x60\x00\xf0\x00\xf0\x00'\ 99 | b'\x60\x00\x00\x00\x00\x00\x60\x00\xf0\x00\xf0\x00\x60\x00\x00\x00'\ 100 | b'\x00\x00\x00\x00\x00\x00\x00\x00\x0e\x00\x00\x00\x00\x00\x00\x00'\ 101 | b'\x00\x00\x00\x00\x30\x00\x78\x00\x78\x00\x30\x00\x00\x00\x00\x00'\ 102 | b'\x00\x00\x78\x00\x70\x00\x60\x00\x60\x00\xc0\x00\xc0\x00\x00\x00'\ 103 | b'\x00\x00\x0e\x00\x00\x00\x00\x00\x00\x00\x00\x60\x01\xe0\x07\x80'\ 104 | b'\x1e\x00\x78\x00\xe0\x00\x78\x00\x0e\x00\x03\x80\x00\xe0\x00\x40'\ 105 | b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x0e\x00\x00\x00'\ 106 | b'\x00\x00\x00\x00\x00\x00\x00\x00\xff\xc0\xff\xc0\x00\x00\x00\x00'\ 107 | b'\x00\x00\xff\xc0\xff\xc0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'\ 108 | b'\x00\x00\x00\x00\x00\x00\x0e\x00\x00\x00\x00\x00\x00\x00\x80\x00'\ 109 | b'\xe0\x00\x38\x00\x0e\x00\x03\xc0\x00\xc0\x03\x80\x0e\x00\x38\x00'\ 110 | b'\xe0\x00\x80\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'\ 111 | b'\x0e\x00\x00\x00\x00\x00\x7c\x00\xfe\x00\xc7\x00\xc3\x00\x03\x00'\ 112 | b'\x07\x00\x1e\x00\x18\x00\x18\x00\x18\x00\x3c\x00\x3c\x00\x18\x00'\ 113 | b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x0e\x00\x00\x00\x00\x00'\ 114 | b'\x00\x00\x0f\x80\x1f\xc0\x38\xe0\x66\xf0\x6f\xb0\xcd\x30\xd9\x30'\ 115 | b'\xd9\x30\xdb\x70\xdf\xe0\x6c\xc0\x70\x00\x3f\xc0\x0f\x80\x00\x00'\ 116 | b'\x00\x00\x00\x00\x0e\x00\x00\x00\x00\x00\x3f\x00\x3f\x00\x07\x80'\ 117 | b'\x0c\x80\x0c\x80\x18\xc0\x18\x40\x3f\xe0\x3f\xe0\x30\x60\x60\x30'\ 118 | b'\xf8\xf8\xf8\xf8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x0e\x00'\ 119 | b'\x00\x00\x00\x00\xff\x00\xff\xc0\x30\xc0\x30\xc0\x30\xc0\x3f\x80'\ 120 | b'\x3f\xc0\x30\xe0\x30\x60\x30\x60\x30\xe0\xff\xc0\xff\x80\x00\x00'\ 121 | b'\x00\x00\x00\x00\x00\x00\x00\x00\x0e\x00\x00\x00\x00\x00\x1f\x60'\ 122 | b'\x3f\xe0\x70\xe0\x60\x60\xc0\x60\xc0\x40\xc0\x00\xc0\x00\xc0\x00'\ 123 | b'\x60\x00\x70\x60\x3f\xe0\x1f\x80\x00\x00\x00\x00\x00\x00\x00\x00'\ 124 | b'\x00\x00\x0e\x00\x00\x00\x00\x00\xff\x00\xff\x80\x31\xc0\x30\xe0'\ 125 | b'\x30\x60\x30\x60\x30\x60\x30\x60\x30\x60\x30\xe0\x31\xc0\xff\x80'\ 126 | b'\xff\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x0e\x00\x00\x00'\ 127 | b'\x00\x00\xff\xe0\xff\xe0\x30\x60\x33\x60\x33\x00\x3f\x00\x3f\x00'\ 128 | b'\x33\x00\x33\x00\x30\x60\x30\x60\xff\xe0\xff\xe0\x00\x00\x00\x00'\ 129 | b'\x00\x00\x00\x00\x00\x00\x0e\x00\x00\x00\x00\x00\xff\xe0\xff\xe0'\ 130 | b'\x30\x60\x33\x60\x33\x00\x3f\x00\x3f\x00\x33\x00\x33\x00\x30\x00'\ 131 | b'\x30\x00\xfe\x00\xfe\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'\ 132 | b'\x0e\x00\x00\x00\x00\x00\x1e\xc0\x3f\xc0\x71\xc0\x60\xc0\xc0\xc0'\ 133 | b'\xc0\x00\xc0\x00\xc7\xf0\xc7\xf0\x60\xc0\x70\xc0\x3f\xc0\x1f\x80'\ 134 | b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x0e\x00\x00\x00\x00\x00'\ 135 | b'\xfd\xf8\xfd\xf8\x30\x60\x30\x60\x30\x60\x3f\xe0\x3f\xe0\x30\x60'\ 136 | b'\x30\x60\x30\x60\x30\x60\xfd\xf8\xfd\xf8\x00\x00\x00\x00\x00\x00'\ 137 | b'\x00\x00\x00\x00\x0e\x00\x00\x00\x00\x00\xff\xc0\xff\xc0\x0c\x00'\ 138 | b'\x0c\x00\x0c\x00\x0c\x00\x0c\x00\x0c\x00\x0c\x00\x0c\x00\x0c\x00'\ 139 | b'\xff\xc0\xff\xc0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x0e\x00'\ 140 | b'\x00\x00\x00\x00\x3f\xf8\x3f\xf8\x01\x80\x01\x80\x01\x80\x01\x80'\ 141 | b'\x81\x80\xc1\x80\xc1\x80\xc1\x80\xc3\x80\x7f\x00\x3e\x00\x00\x00'\ 142 | b'\x00\x00\x00\x00\x00\x00\x00\x00\x0e\x00\x00\x00\x00\x00\xfc\xf0'\ 143 | b'\xfc\xf0\x30\x40\x31\x80\x33\x00\x34\x00\x3f\x00\x31\x80\x30\x80'\ 144 | b'\x30\xc0\x30\x40\xfc\x70\xfc\x30\x00\x00\x00\x00\x00\x00\x00\x00'\ 145 | b'\x00\x00\x0e\x00\x00\x00\x00\x00\x7f\x00\xff\x00\x18\x00\x18\x00'\ 146 | b'\x18\x00\x18\x00\x18\x00\x18\x30\x18\x30\x18\x30\x18\x30\x7f\xf0'\ 147 | b'\xff\xf0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x0e\x00\x00\x00'\ 148 | b'\x00\x00\xf0\xf0\xf0\xf0\x70\xe0\x79\xe0\x69\x60\x69\x60\x6f\x60'\ 149 | b'\x66\x60\x66\x60\x66\x60\x60\x60\xf9\xf0\xf9\xf0\x00\x00\x00\x00'\ 150 | b'\x00\x00\x00\x00\x00\x00\x0e\x00\x00\x00\x00\x00\xf0\xf8\xf8\xf8'\ 151 | b'\x3c\x30\x34\x30\x32\x30\x32\x30\x31\x30\x31\x30\x30\xb0\x30\x70'\ 152 | b'\x30\x70\x7c\x30\x7c\x30\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'\ 153 | b'\x0e\x00\x00\x00\x00\x00\x1f\x80\x3f\xc0\x70\xe0\x60\x60\xc0\x30'\ 154 | b'\xc0\x30\xc0\x30\xc0\x30\xc0\x30\x60\x60\x70\xe0\x3f\xc0\x1f\x80'\ 155 | b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x0e\x00\x00\x00\x00\x00'\ 156 | b'\x7f\xc0\xff\xe0\x18\x70\x18\x30\x18\x30\x18\x70\x1f\xe0\x1f\xc0'\ 157 | b'\x18\x00\x18\x00\x18\x00\x7f\x00\xff\x00\x00\x00\x00\x00\x00\x00'\ 158 | b'\x00\x00\x00\x00\x0e\x00\x00\x00\x00\x00\x1f\x80\x3f\xc0\x70\xe0'\ 159 | b'\x60\x60\xc0\x30\xc0\x30\xc0\x30\xc0\x30\xc0\x30\x60\x60\x70\xe0'\ 160 | b'\x3f\xc0\x0f\x80\x18\x20\x3f\xe0\x3f\xc0\x20\x00\x00\x00\x0e\x00'\ 161 | b'\x00\x00\x00\x00\xff\x80\xff\xc0\x30\xe0\x30\x60\x30\xe0\x3f\xc0'\ 162 | b'\x3f\x00\x31\x80\x30\xc0\x30\xc0\x30\x60\xfc\x78\xfc\x38\x00\x00'\ 163 | b'\x00\x00\x00\x00\x00\x00\x00\x00\x0e\x00\x00\x00\x00\x00\x3c\xc0'\ 164 | b'\x7f\xc0\xe1\xc0\xc0\xc0\xc0\x00\x70\x00\x1f\x00\x01\x80\x00\xc0'\ 165 | b'\xc0\xc0\xe1\xc0\xff\x80\xdf\x00\x00\x00\x00\x00\x00\x00\x00\x00'\ 166 | b'\x00\x00\x0e\x00\x00\x00\x00\x00\xff\xc0\xff\xc0\xcc\xc0\xcc\xc0'\ 167 | b'\xcc\xc0\xcc\xc0\x0c\x00\x0c\x00\x0c\x00\x0c\x00\x0c\x00\x7f\x80'\ 168 | b'\x7f\x80\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x0e\x00\x00\x00'\ 169 | b'\x00\x00\xfc\xfc\xfc\xfc\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30'\ 170 | b'\x30\x30\x30\x30\x30\x30\x38\x70\x1f\xe0\x0f\xc0\x00\x00\x00\x00'\ 171 | b'\x00\x00\x00\x00\x00\x00\x0e\x00\x00\x00\x00\x00\xfc\x7c\xfc\x7c'\ 172 | b'\x30\x30\x30\x30\x10\x20\x18\x60\x18\x40\x0c\xc0\x0c\xc0\x04\x80'\ 173 | b'\x07\x80\x07\x00\x03\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'\ 174 | b'\x0e\x00\x00\x00\x00\x00\xf8\x7c\xf8\x7c\x60\x18\x63\x18\x23\x10'\ 175 | b'\x23\x90\x37\x90\x37\xb0\x34\xb0\x3c\xf0\x1c\xe0\x18\x60\x18\x60'\ 176 | b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x0e\x00\x00\x00\x00\x00'\ 177 | b'\xf9\xf0\xf9\xf0\x30\xc0\x19\x80\x1f\x80\x0f\x00\x06\x00\x0f\x00'\ 178 | b'\x19\x80\x30\xc0\x60\x60\xf9\xf0\xf9\xf0\x00\x00\x00\x00\x00\x00'\ 179 | b'\x00\x00\x00\x00\x0e\x00\x00\x00\x00\x00\xf9\xf0\xf9\xf0\x30\xc0'\ 180 | b'\x30\xc0\x19\x80\x0f\x00\x0f\x00\x06\x00\x06\x00\x06\x00\x06\x00'\ 181 | b'\x3f\xc0\x3f\xc0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x0e\x00'\ 182 | b'\x00\x00\x00\x00\xff\xc0\xff\xc0\xc1\x80\xc3\x00\x02\x00\x04\x00'\ 183 | b'\x0c\x00\x18\x00\x10\xc0\x20\xc0\x40\xc0\xff\xc0\xff\xc0\x00\x00'\ 184 | b'\x00\x00\x00\x00\x00\x00\x00\x00\x0e\x00\xfc\x00\xfc\x00\xc0\x00'\ 185 | b'\xc0\x00\xc0\x00\xc0\x00\xc0\x00\xc0\x00\xc0\x00\xc0\x00\xc0\x00'\ 186 | b'\xc0\x00\xc0\x00\xc0\x00\xc0\x00\xc0\x00\xc0\x00\xfc\x00\xfc\x00'\ 187 | b'\x00\x00\x0e\x00\xc0\x00\x40\x00\x60\x00\x20\x00\x30\x00\x30\x00'\ 188 | b'\x18\x00\x18\x00\x0c\x00\x0c\x00\x04\x00\x06\x00\x02\x00\x03\x00'\ 189 | b'\x03\x00\x01\x80\x01\x80\x00\xc0\x00\x00\x00\x00\x0e\x00\xfc\x00'\ 190 | b'\xfc\x00\x0c\x00\x0c\x00\x0c\x00\x0c\x00\x0c\x00\x0c\x00\x0c\x00'\ 191 | b'\x0c\x00\x0c\x00\x0c\x00\x0c\x00\x0c\x00\x0c\x00\x0c\x00\x0c\x00'\ 192 | b'\xfc\x00\xfc\x00\x00\x00\x0e\x00\x00\x00\x00\x00\x18\x00\x18\x00'\ 193 | b'\x3c\x00\x24\x00\x66\x00\xc6\x00\xc3\x00\x83\x00\x00\x00\x00\x00'\ 194 | b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'\ 195 | b'\x0e\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'\ 196 | b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'\ 197 | b'\xff\xfc\xff\xfc\x00\x00\x00\x00\x00\x00\x0e\x00\xe0\x00\xf8\x00'\ 198 | b'\x1c\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'\ 199 | b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'\ 200 | b'\x00\x00\x00\x00\x0e\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'\ 201 | b'\x3e\x00\xff\x00\xc1\x80\x3f\x80\x7f\x80\xc1\x80\xc1\x80\xc3\x80'\ 202 | b'\xff\x80\x7c\xe0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x0e\x00'\ 203 | b'\xf0\x00\xf0\x00\x30\x00\x30\x00\x30\x00\x37\xc0\x3f\xe0\x38\x60'\ 204 | b'\x30\x30\x30\x30\x30\x30\x30\x30\x38\x60\xff\xe0\xf3\xc0\x00\x00'\ 205 | b'\x00\x00\x00\x00\x00\x00\x00\x00\x0e\x00\x00\x00\x00\x00\x00\x00'\ 206 | b'\x00\x00\x00\x00\x1f\x60\x7f\xe0\x70\xe0\xc0\x60\xc0\x00\xc0\x00'\ 207 | b'\xc0\x00\x70\x60\x7f\xc0\x1f\x80\x00\x00\x00\x00\x00\x00\x00\x00'\ 208 | b'\x00\x00\x0e\x00\x03\xc0\x03\xc0\x00\xc0\x00\xc0\x00\xc0\x3e\xc0'\ 209 | b'\x7f\xc0\x61\xc0\xc0\xc0\xc0\xc0\xc0\xc0\xc0\xc0\x61\xc0\x7f\xf0'\ 210 | b'\x3c\xf0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x0e\x00\x00\x00'\ 211 | b'\x00\x00\x00\x00\x00\x00\x00\x00\x1f\x00\x7f\x80\x60\xc0\xff\xc0'\ 212 | b'\xff\xc0\xc0\x00\xc0\x00\x60\xc0\x7f\xc0\x1f\x00\x00\x00\x00\x00'\ 213 | b'\x00\x00\x00\x00\x00\x00\x0e\x00\x0f\x80\x1f\xc0\x38\x40\x30\x00'\ 214 | b'\x30\x00\xff\x80\xff\x80\x30\x00\x30\x00\x30\x00\x30\x00\x30\x00'\ 215 | b'\x30\x00\xff\x80\xff\x80\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'\ 216 | b'\x0e\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x1e\x78\x7f\xf8'\ 217 | b'\x60\xe0\xc0\x60\xc0\x60\xc0\x60\xc0\x60\x60\xe0\x7f\xe0\x1e\x60'\ 218 | b'\x00\x60\x20\xe0\x3f\xc0\x1f\x80\x00\x00\x0e\x00\xf0\x00\xf0\x00'\ 219 | b'\x30\x00\x30\x00\x30\x00\x37\xc0\x3f\xe0\x3c\x60\x38\x60\x30\x60'\ 220 | b'\x30\x60\x30\x60\x30\x60\xfd\xf8\xfd\xf8\x00\x00\x00\x00\x00\x00'\ 221 | b'\x00\x00\x00\x00\x0e\x00\x0c\x00\x0c\x00\x0c\x00\x00\x00\x00\x00'\ 222 | b'\x7c\x00\x7c\x00\x0c\x00\x0c\x00\x0c\x00\x0c\x00\x0c\x00\x0c\x00'\ 223 | b'\xff\xc0\xff\xc0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x0e\x00'\ 224 | b'\x03\x00\x03\x00\x03\x00\x00\x00\x00\x00\x7f\x00\x7f\x00\x03\x00'\ 225 | b'\x03\x00\x03\x00\x03\x00\x03\x00\x03\x00\x03\x00\x03\x00\x03\x00'\ 226 | b'\xc3\x00\xfe\x00\x3c\x00\x00\x00\x0e\x00\xf0\x00\xf0\x00\x30\x00'\ 227 | b'\x30\x00\x30\x00\x33\xe0\x33\xe0\x31\x80\x33\x00\x34\x00\x3a\x00'\ 228 | b'\x31\x80\x30\xc0\xfd\xf0\xfd\xf0\x00\x00\x00\x00\x00\x00\x00\x00'\ 229 | b'\x00\x00\x0e\x00\x7c\x00\x7c\x00\x0c\x00\x0c\x00\x0c\x00\x0c\x00'\ 230 | b'\x0c\x00\x0c\x00\x0c\x00\x0c\x00\x0c\x00\x0c\x00\x0c\x00\xff\xc0'\ 231 | b'\xff\xc0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x0e\x00\x00\x00'\ 232 | b'\x00\x00\x00\x00\x00\x00\x00\x00\xf6\x70\xff\xf8\x3b\x98\x33\x18'\ 233 | b'\x33\x18\x33\x18\x33\x18\x33\x18\xfb\x9c\xfb\x9c\x00\x00\x00\x00'\ 234 | b'\x00\x00\x00\x00\x00\x00\x0e\x00\x00\x00\x00\x00\x00\x00\x00\x00'\ 235 | b'\x00\x00\xf3\xc0\xf7\xe0\x38\x60\x38\x60\x30\x60\x30\x60\x30\x60'\ 236 | b'\x30\x60\xfd\xf8\xfd\xf8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'\ 237 | b'\x0e\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x1f\x80\x3f\xc0'\ 238 | b'\x70\xe0\xc0\x30\xc0\x30\xc0\x30\xc0\x30\x70\xe0\x3f\xc0\x1f\x80'\ 239 | b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x0e\x00\x00\x00\x00\x00'\ 240 | b'\x00\x00\x00\x00\x00\x00\xf7\xc0\xff\xe0\x38\x60\x30\x30\x30\x30'\ 241 | b'\x30\x30\x30\x30\x38\x60\x3f\xe0\x37\xc0\x30\x00\x30\x00\xfc\x00'\ 242 | b'\xfc\x00\x00\x00\x0e\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'\ 243 | b'\x3e\xf0\x7f\xf0\x61\xc0\xc0\xc0\xc0\xc0\xc0\xc0\xc0\xc0\x61\xc0'\ 244 | b'\x7f\xc0\x3c\xc0\x00\xc0\x00\xc0\x03\xf0\x03\xf0\x00\x00\x0e\x00'\ 245 | b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xf1\xc0\xf7\xe0\x3e\x40'\ 246 | b'\x38\x00\x30\x00\x30\x00\x30\x00\x30\x00\xff\x00\xff\x00\x00\x00'\ 247 | b'\x00\x00\x00\x00\x00\x00\x00\x00\x0e\x00\x00\x00\x00\x00\x00\x00'\ 248 | b'\x00\x00\x00\x00\x7d\x80\xff\x80\xc1\x80\xc0\x80\x7c\x00\x03\x80'\ 249 | b'\xc0\xc0\xe1\xc0\xff\xc0\xdf\x00\x00\x00\x00\x00\x00\x00\x00\x00'\ 250 | b'\x00\x00\x0e\x00\x00\x00\x30\x00\x30\x00\x30\x00\x30\x00\xff\x80'\ 251 | b'\xff\x80\x30\x00\x30\x00\x30\x00\x30\x00\x30\x00\x30\xc0\x1f\xc0'\ 252 | b'\x0f\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x0e\x00\x00\x00'\ 253 | b'\x00\x00\x00\x00\x00\x00\x00\x00\xf1\xe0\xf1\xe0\x30\x60\x30\x60'\ 254 | b'\x30\x60\x30\x60\x30\xe0\x30\xe0\x3f\xf8\x1e\x78\x00\x00\x00\x00'\ 255 | b'\x00\x00\x00\x00\x00\x00\x0e\x00\x00\x00\x00\x00\x00\x00\x00\x00'\ 256 | b'\x00\x00\xf8\xf0\xf9\xf0\x20\x60\x30\xc0\x10\xc0\x18\x80\x09\x80'\ 257 | b'\x0d\x00\x0f\x00\x06\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'\ 258 | b'\x0e\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xf8\x3c\xf8\x3c'\ 259 | b'\x63\x18\x63\x18\x27\x90\x37\xb0\x34\xb0\x1c\xe0\x1c\xe0\x18\x60'\ 260 | b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x0e\x00\x00\x00\x00\x00'\ 261 | b'\x00\x00\x00\x00\x00\x00\xf9\xf0\xf9\xf0\x30\xc0\x19\x80\x0f\x00'\ 262 | b'\x0f\x00\x19\x80\x30\xc0\xf9\xf0\xf9\xf0\x00\x00\x00\x00\x00\x00'\ 263 | b'\x00\x00\x00\x00\x0e\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'\ 264 | b'\xf8\xf0\xf9\xf0\x30\x60\x30\xc0\x18\xc0\x19\x80\x0d\x80\x07\x00'\ 265 | b'\x07\x00\x06\x00\x0c\x00\x1c\x00\x78\x00\x70\x00\x00\x00\x0e\x00'\ 266 | b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xc0\xff\x80\xc1\x00'\ 267 | b'\xc2\x00\x04\x00\x18\x00\x30\xc0\x60\xc0\xff\xc0\xff\xc0\x00\x00'\ 268 | b'\x00\x00\x00\x00\x00\x00\x00\x00\x0e\x00\x06\x00\x0e\x00\x18\x00'\ 269 | b'\x18\x00\x18\x00\x18\x00\x18\x00\x18\x00\x38\x00\xf0\x00\xf0\x00'\ 270 | b'\x38\x00\x18\x00\x18\x00\x18\x00\x18\x00\x18\x00\x1e\x00\x0e\x00'\ 271 | b'\x00\x00\x0e\x00\xc0\x00\xc0\x00\xc0\x00\xc0\x00\xc0\x00\xc0\x00'\ 272 | b'\xc0\x00\xc0\x00\xc0\x00\xc0\x00\xc0\x00\xc0\x00\xc0\x00\xc0\x00'\ 273 | b'\xc0\x00\xc0\x00\xc0\x00\xc0\x00\xc0\x00\x00\x00\x0e\x00\xc0\x00'\ 274 | b'\xe0\x00\x30\x00\x30\x00\x30\x00\x30\x00\x30\x00\x30\x00\x38\x00'\ 275 | b'\x1e\x00\x1e\x00\x38\x00\x30\x00\x30\x00\x30\x00\x30\x00\x30\x00'\ 276 | b'\xf0\x00\xe0\x00\x00\x00\x0e\x00\x00\x00\x00\x00\x00\x00\x00\x00'\ 277 | b'\x00\x00\x00\x00\x00\x00\x78\x40\xff\xc0\x87\x80\x00\x00\x00\x00'\ 278 | b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'\ 279 | 280 | _index =\ 281 | b'\x00\x00\x2a\x00\x54\x00\x7e\x00\xa8\x00\xd2\x00\xfc\x00\x26\x01'\ 282 | b'\x50\x01\x7a\x01\xa4\x01\xce\x01\xf8\x01\x22\x02\x4c\x02\x76\x02'\ 283 | b'\xa0\x02\xca\x02\xf4\x02\x1e\x03\x48\x03\x72\x03\x9c\x03\xc6\x03'\ 284 | b'\xf0\x03\x1a\x04\x44\x04\x6e\x04\x98\x04\xc2\x04\xec\x04\x16\x05'\ 285 | b'\x40\x05\x6a\x05\x94\x05\xbe\x05\xe8\x05\x12\x06\x3c\x06\x66\x06'\ 286 | b'\x90\x06\xba\x06\xe4\x06\x0e\x07\x38\x07\x62\x07\x8c\x07\xb6\x07'\ 287 | b'\xe0\x07\x0a\x08\x34\x08\x5e\x08\x88\x08\xb2\x08\xdc\x08\x06\x09'\ 288 | b'\x30\x09\x5a\x09\x84\x09\xae\x09\xd8\x09\x02\x0a\x2c\x0a\x56\x0a'\ 289 | b'\x80\x0a\xaa\x0a\xd4\x0a\xfe\x0a\x28\x0b\x52\x0b\x7c\x0b\xa6\x0b'\ 290 | b'\xd0\x0b\xfa\x0b\x24\x0c\x4e\x0c\x78\x0c\xa2\x0c\xcc\x0c\xf6\x0c'\ 291 | b'\x20\x0d\x4a\x0d\x74\x0d\x9e\x0d\xc8\x0d\xf2\x0d\x1c\x0e\x46\x0e'\ 292 | b'\x70\x0e\x9a\x0e\xc4\x0e\xee\x0e\x18\x0f\x42\x0f\x6c\x0f\x96\x0f'\ 293 | b'\xc0\x0f' 294 | 295 | _mvfont = memoryview(_font) 296 | 297 | def _chr_addr(ordch): 298 | offset = 2 * (ordch - 32) 299 | return int.from_bytes(_index[offset:offset + 2], 'little') 300 | 301 | def get_ch(ch): 302 | ordch = ord(ch) 303 | ordch = ordch + 1 if ordch >= 32 and ordch <= 126 else 32 304 | offset = _chr_addr(ordch) 305 | width = int.from_bytes(_font[offset:offset + 2], 'little') 306 | next_offs = _chr_addr(ordch +1) 307 | return _mvfont[offset + 2:next_offs], 20, width 308 | 309 | -------------------------------------------------------------------------------- /writer/font10.py: -------------------------------------------------------------------------------- 1 | # Code generated by font-to-py.py. 2 | # Font: FreeSans.ttf 3 | version = '0.1' 4 | 5 | def height(): 6 | return 17 7 | 8 | def max_width(): 9 | return 17 10 | 11 | def hmap(): 12 | return True 13 | 14 | def reverse(): 15 | return False 16 | 17 | def monospaced(): 18 | return False 19 | 20 | _font =\ 21 | b'\x04\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'\ 22 | b'\x00\x00\x00\x06\x00\xc0\xc0\xc0\xc0\xc0\xc0\xc0\xc0\xc0\xc0\x80'\ 23 | b'\x00\xc0\x00\x00\x00\x00\x06\x00\x00\xf0\xf0\xf0\xa0\x00\x00\x00'\ 24 | b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x09\x00\x00\x00\x00\x00\x19'\ 25 | b'\x00\x19\x00\x13\x00\x7f\x80\x12\x00\x32\x00\x32\x00\xff\x80\x26'\ 26 | b'\x00\x24\x00\x64\x00\x00\x00\x00\x00\x00\x00\x00\x00\x09\x00\x10'\ 27 | b'\x00\x3c\x00\x56\x00\xd3\x00\xd3\x00\xd0\x00\xd0\x00\x3c\x00\x17'\ 28 | b'\x00\x13\x00\xd3\x00\xd6\x00\x7c\x00\x10\x00\x00\x00\x00\x00\x00'\ 29 | b'\x00\x0f\x00\x00\x00\x78\x20\xcc\x40\xcc\x80\xcc\x80\xc9\x00\x31'\ 30 | b'\x00\x02\x78\x04\xcc\x04\xcc\x08\xcc\x08\xcc\x10\x78\x00\x00\x00'\ 31 | b'\x00\x00\x00\x00\x00\x0b\x00\x00\x00\x1e\x00\x33\x00\x33\x00\x33'\ 32 | b'\x00\x1e\x00\x18\x00\x74\xc0\xe6\xc0\xc3\x80\xc1\x80\xe3\x80\x3c'\ 33 | b'\x40\x00\x00\x00\x00\x00\x00\x00\x00\x03\x00\x00\xc0\xc0\xc0\x80'\ 34 | b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x06\x00\x10\x20'\ 35 | b'\x20\x60\x40\xc0\xc0\xc0\xc0\xc0\xc0\x40\x60\x20\x30\x10\x00\x06'\ 36 | b'\x00\x80\xc0\x40\x60\x20\x30\x30\x30\x30\x30\x30\x20\x60\x40\xc0'\ 37 | b'\x80\x00\x07\x00\x20\xa8\x70\x50\x50\x00\x00\x00\x00\x00\x00\x00'\ 38 | b'\x00\x00\x00\x00\x00\x0a\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'\ 39 | b'\x00\x00\x00\x30\x00\x30\x00\x30\x00\xfc\x00\x30\x00\x30\x00\x30'\ 40 | b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x05\x00\x00\x00\x00\x00\x00'\ 41 | b'\x00\x00\x00\x00\x00\x00\x00\xc0\x40\x40\x80\x00\x06\x00\x00\x00'\ 42 | b'\x00\x00\x00\x00\x00\x00\xf0\x00\x00\x00\x00\x00\x00\x00\x00\x04'\ 43 | b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xc0\x00\x00'\ 44 | b'\x00\x00\x05\x00\x08\x08\x10\x10\x10\x20\x20\x20\x40\x40\x40\x80'\ 45 | b'\x80\x00\x00\x00\x00\x09\x00\x00\x00\x3c\x00\x66\x00\x42\x00\xc3'\ 46 | b'\x00\xc3\x00\xc3\x00\xc3\x00\xc3\x00\xc3\x00\x42\x00\x66\x00\x3c'\ 47 | b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x09\x00\x00\x00\x10\x00\x30'\ 48 | b'\x00\xf0\x00\x30\x00\x30\x00\x30\x00\x30\x00\x30\x00\x30\x00\x30'\ 49 | b'\x00\x30\x00\x30\x00\x00\x00\x00\x00\x00\x00\x00\x00\x09\x00\x00'\ 50 | b'\x00\x3c\x00\x66\x00\xc3\x00\xc3\x00\x03\x00\x06\x00\x0c\x00\x38'\ 51 | b'\x00\x60\x00\x40\x00\xc0\x00\xff\x00\x00\x00\x00\x00\x00\x00\x00'\ 52 | b'\x00\x09\x00\x00\x00\x7c\x00\xe7\x00\xc3\x00\x03\x00\x02\x00\x1c'\ 53 | b'\x00\x07\x00\x03\x00\x03\x00\xc3\x00\xe6\x00\x3c\x00\x00\x00\x00'\ 54 | b'\x00\x00\x00\x00\x00\x09\x00\x00\x00\x0c\x00\x0c\x00\x1c\x00\x2c'\ 55 | b'\x00\x2c\x00\x4c\x00\x8c\x00\x8c\x00\xfe\x00\x0c\x00\x0c\x00\x0c'\ 56 | b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x09\x00\x00\x00\x7e\x00\x40'\ 57 | b'\x00\x40\x00\x80\x00\xbc\x00\xe6\x00\x03\x00\x03\x00\x03\x00\xc3'\ 58 | b'\x00\x66\x00\x3c\x00\x00\x00\x00\x00\x00\x00\x00\x00\x09\x00\x00'\ 59 | b'\x00\x3c\x00\x66\x00\x43\x00\xc0\x00\xc0\x00\xfc\x00\xe6\x00\xc3'\ 60 | b'\x00\xc3\x00\xc3\x00\x66\x00\x3c\x00\x00\x00\x00\x00\x00\x00\x00'\ 61 | b'\x00\x09\x00\x00\x00\xff\x00\x03\x00\x02\x00\x06\x00\x04\x00\x0c'\ 62 | b'\x00\x08\x00\x18\x00\x18\x00\x10\x00\x30\x00\x30\x00\x00\x00\x00'\ 63 | b'\x00\x00\x00\x00\x00\x09\x00\x00\x00\x3c\x00\x66\x00\xc3\x00\xc3'\ 64 | b'\x00\x66\x00\x3c\x00\x66\x00\xc3\x00\xc3\x00\xc3\x00\x66\x00\x3c'\ 65 | b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x09\x00\x00\x00\x3c\x00\x66'\ 66 | b'\x00\xc3\x00\xc3\x00\xc3\x00\x67\x00\x3b\x00\x03\x00\x03\x00\xc2'\ 67 | b'\x00\x66\x00\x3c\x00\x00\x00\x00\x00\x00\x00\x00\x00\x04\x00\x00'\ 68 | b'\x00\x00\x00\xc0\x00\x00\x00\x00\x00\x00\x00\xc0\x00\x00\x00\x00'\ 69 | b'\x04\x00\x00\x00\x00\x00\x00\xc0\x00\x00\x00\x00\x00\x00\xc0\x40'\ 70 | b'\x40\x80\x00\x0a\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x03'\ 71 | b'\x00\x0e\x00\x38\x00\xc0\x00\xe0\x00\x38\x00\x07\x00\x01\x00\x00'\ 72 | b'\x00\x00\x00\x00\x00\x00\x00\x0a\x00\x00\x00\x00\x00\x00\x00\x00'\ 73 | b'\x00\x00\x00\x00\x00\x00\x00\xff\x00\x00\x00\x00\x00\xff\x00\x00'\ 74 | b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x0a\x00\x00\x00\x00'\ 75 | b'\x00\x00\x00\x00\x00\x00\x00\xc0\x00\x70\x00\x1c\x00\x03\x00\x07'\ 76 | b'\x00\x1c\x00\xe0\x00\x80\x00\x00\x00\x00\x00\x00\x00\x00\x00\x09'\ 77 | b'\x00\x3c\x00\xc7\x00\xc3\x00\x03\x00\x03\x00\x06\x00\x0c\x00\x08'\ 78 | b'\x00\x18\x00\x18\x00\x00\x00\x00\x00\x18\x00\x00\x00\x00\x00\x00'\ 79 | b'\x00\x00\x00\x11\x00\x07\xe0\x00\x0c\x38\x00\x30\x0c\x00\x20\x06'\ 80 | b'\x00\x63\xb7\x00\x4c\x73\x00\xcc\x63\x00\xd8\x63\x00\xd8\x63\x00'\ 81 | b'\xd8\x46\x00\xdc\xce\x00\x6f\x78\x00\x30\x00\x00\x18\x00\x00\x0f'\ 82 | b'\xe0\x00\x00\x00\x00\x00\x00\x00\x0b\x00\x06\x00\x0e\x00\x0b\x00'\ 83 | b'\x1b\x00\x1b\x00\x11\x80\x31\x80\x31\x80\x3f\xc0\x60\xc0\x60\x40'\ 84 | b'\x40\x60\xc0\x60\x00\x00\x00\x00\x00\x00\x00\x00\x0b\x00\xfe\x00'\ 85 | b'\xc3\x80\xc1\x80\xc1\x80\xc1\x80\xc3\x00\xfe\x00\xc1\x80\xc0\xc0'\ 86 | b'\xc0\xc0\xc0\xc0\xc1\x80\xff\x00\x00\x00\x00\x00\x00\x00\x00\x00'\ 87 | b'\x0c\x00\x1f\x80\x30\xc0\x60\x60\x40\x60\xc0\x00\xc0\x00\xc0\x00'\ 88 | b'\xc0\x00\xc0\x00\x40\x60\x60\x60\x30\xc0\x1f\x80\x00\x00\x00\x00'\ 89 | b'\x00\x00\x00\x00\x0c\x00\xff\x00\xc1\x80\xc0\xc0\xc0\x60\xc0\x60'\ 90 | b'\xc0\x60\xc0\x60\xc0\x60\xc0\x60\xc0\x60\xc0\xc0\xc1\x80\xff\x00'\ 91 | b'\x00\x00\x00\x00\x00\x00\x00\x00\x0b\x00\xff\x00\xc0\x00\xc0\x00'\ 92 | b'\xc0\x00\xc0\x00\xc0\x00\xff\x00\xc0\x00\xc0\x00\xc0\x00\xc0\x00'\ 93 | b'\xc0\x00\xff\x00\x00\x00\x00\x00\x00\x00\x00\x00\x0a\x00\xff\x00'\ 94 | b'\xc0\x00\xc0\x00\xc0\x00\xc0\x00\xc0\x00\xfe\x00\xc0\x00\xc0\x00'\ 95 | b'\xc0\x00\xc0\x00\xc0\x00\xc0\x00\x00\x00\x00\x00\x00\x00\x00\x00'\ 96 | b'\x0d\x00\x0f\xc0\x30\x60\x60\x30\x60\x00\xc0\x00\xc0\x00\xc1\xf0'\ 97 | b'\xc0\x30\xc0\x30\x60\x30\x60\x70\x30\xf0\x0f\x10\x00\x00\x00\x00'\ 98 | b'\x00\x00\x00\x00\x0c\x00\xc0\xc0\xc0\xc0\xc0\xc0\xc0\xc0\xc0\xc0'\ 99 | b'\xc0\xc0\xff\xc0\xc0\xc0\xc0\xc0\xc0\xc0\xc0\xc0\xc0\xc0\xc0\xc0'\ 100 | b'\x00\x00\x00\x00\x00\x00\x00\x00\x05\x00\xc0\xc0\xc0\xc0\xc0\xc0'\ 101 | b'\xc0\xc0\xc0\xc0\xc0\xc0\xc0\x00\x00\x00\x00\x09\x00\x06\x00\x06'\ 102 | b'\x00\x06\x00\x06\x00\x06\x00\x06\x00\x06\x00\x06\x00\x06\x00\xc6'\ 103 | b'\x00\xc6\x00\xc4\x00\x78\x00\x00\x00\x00\x00\x00\x00\x00\x00\x0b'\ 104 | b'\x00\xc0\xc0\xc1\x80\xc3\x00\xc6\x00\xcc\x00\xd8\x00\xfc\x00\xe6'\ 105 | b'\x00\xc6\x00\xc3\x00\xc1\x80\xc1\x80\xc0\xc0\x00\x00\x00\x00\x00'\ 106 | b'\x00\x00\x00\x0a\x00\xc0\x00\xc0\x00\xc0\x00\xc0\x00\xc0\x00\xc0'\ 107 | b'\x00\xc0\x00\xc0\x00\xc0\x00\xc0\x00\xc0\x00\xc0\x00\xfe\x00\x00'\ 108 | b'\x00\x00\x00\x00\x00\x00\x00\x0e\x00\xe0\x38\xe0\x38\xf0\x78\xf0'\ 109 | b'\x78\xd0\x58\xd8\xd8\xd8\xd8\xc8\x98\xcd\x98\xcd\x98\xc5\x18\xc7'\ 110 | b'\x18\xc7\x18\x00\x00\x00\x00\x00\x00\x00\x00\x0c\x00\xe0\x60\xe0'\ 111 | b'\x60\xf0\x60\xd0\x60\xd8\x60\xcc\x60\xc4\x60\xc6\x60\xc3\x60\xc3'\ 112 | b'\x60\xc1\xe0\xc0\xe0\xc0\xe0\x00\x00\x00\x00\x00\x00\x00\x00\x0d'\ 113 | b'\x00\x1f\x80\x30\xc0\x60\x60\xe0\x60\xc0\x30\xc0\x30\xc0\x30\xc0'\ 114 | b'\x30\xc0\x30\xe0\x60\x60\x60\x30\xc0\x1f\x80\x00\x00\x00\x00\x00'\ 115 | b'\x00\x00\x00\x0b\x00\xff\x00\xc1\x80\xc0\xc0\xc0\xc0\xc0\xc0\xc1'\ 116 | b'\x80\xff\x00\xc0\x00\xc0\x00\xc0\x00\xc0\x00\xc0\x00\xc0\x00\x00'\ 117 | b'\x00\x00\x00\x00\x00\x00\x00\x0d\x00\x1f\x80\x30\xc0\x60\x60\xe0'\ 118 | b'\x60\xc0\x30\xc0\x30\xc0\x30\xc0\x30\xc0\x30\xe1\x60\x61\xe0\x30'\ 119 | b'\xc0\x1f\xe0\x00\x20\x00\x00\x00\x00\x00\x00\x0c\x00\xff\x00\xc1'\ 120 | b'\xc0\xc0\xc0\xc0\xc0\xc0\xc0\xc1\x80\xff\x00\xc1\xc0\xc0\xc0\xc0'\ 121 | b'\xc0\xc0\xc0\xc0\xc0\xc0\xe0\x00\x00\x00\x00\x00\x00\x00\x00\x0b'\ 122 | b'\x00\x3f\x00\x61\x80\xc0\xc0\xc0\x00\xc0\x00\x60\x00\x3e\x00\x07'\ 123 | b'\x80\x01\xc0\xc0\xc0\xc0\xc0\x61\x80\x3f\x00\x00\x00\x00\x00\x00'\ 124 | b'\x00\x00\x00\x0b\x00\xff\x00\x18\x00\x18\x00\x18\x00\x18\x00\x18'\ 125 | b'\x00\x18\x00\x18\x00\x18\x00\x18\x00\x18\x00\x18\x00\x18\x00\x00'\ 126 | b'\x00\x00\x00\x00\x00\x00\x00\x0c\x00\xc0\xc0\xc0\xc0\xc0\xc0\xc0'\ 127 | b'\xc0\xc0\xc0\xc0\xc0\xc0\xc0\xc0\xc0\xc0\xc0\xc0\xc0\xc0\xc0\x61'\ 128 | b'\x80\x3f\x00\x00\x00\x00\x00\x00\x00\x00\x00\x0b\x00\xc0\x60\x40'\ 129 | b'\x40\x60\xc0\x60\xc0\x20\x80\x31\x80\x31\x80\x11\x00\x1b\x00\x0b'\ 130 | b'\x00\x0a\x00\x0e\x00\x06\x00\x00\x00\x00\x00\x00\x00\x00\x00\x10'\ 131 | b'\x00\xc1\x83\xc1\x82\x42\x86\x62\xc6\x62\xc6\x62\x44\x24\x44\x24'\ 132 | b'\x6c\x34\x2c\x3c\x28\x18\x38\x18\x38\x18\x18\x00\x00\x00\x00\x00'\ 133 | b'\x00\x00\x00\x0b\x00\x60\x40\x20\xc0\x31\x80\x19\x00\x1b\x00\x0e'\ 134 | b'\x00\x06\x00\x0e\x00\x1b\x00\x11\x80\x31\x80\x60\xc0\x40\x60\x00'\ 135 | b'\x00\x00\x00\x00\x00\x00\x00\x0c\x00\x40\x60\x60\x60\x30\xc0\x30'\ 136 | b'\xc0\x19\x80\x0d\x00\x0f\x00\x06\x00\x06\x00\x06\x00\x06\x00\x06'\ 137 | b'\x00\x06\x00\x00\x00\x00\x00\x00\x00\x00\x00\x0a\x00\xff\x80\x01'\ 138 | b'\x80\x03\x00\x06\x00\x06\x00\x0c\x00\x18\x00\x18\x00\x30\x00\x60'\ 139 | b'\x00\x60\x00\xc0\x00\xff\x80\x00\x00\x00\x00\x00\x00\x00\x00\x05'\ 140 | b'\x00\xe0\xc0\xc0\xc0\xc0\xc0\xc0\xc0\xc0\xc0\xc0\xc0\xc0\xc0\xc0'\ 141 | b'\xc0\xe0\x05\x00\x80\x80\x40\x40\x40\x20\x20\x20\x10\x10\x10\x08'\ 142 | b'\x08\x00\x00\x00\x00\x05\x00\xe0\x60\x60\x60\x60\x60\x60\x60\x60'\ 143 | b'\x60\x60\x60\x60\x60\x60\x60\xe0\x08\x00\x00\x30\x30\x78\x48\x48'\ 144 | b'\x84\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x0a\x00\x00\x00\x00'\ 145 | b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'\ 146 | b'\x00\x00\x00\x00\x00\x00\x00\xff\xc0\x00\x00\x00\x00\x00\x00\x04'\ 147 | b'\x00\xc0\x40\x20\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'\ 148 | b'\x00\x00\x09\x00\x00\x00\x00\x00\x00\x00\x00\x00\x7c\x00\xc6\x00'\ 149 | b'\x06\x00\x06\x00\x7e\x00\xc6\x00\xc6\x00\xce\x00\x77\x00\x00\x00'\ 150 | b'\x00\x00\x00\x00\x00\x00\x09\x00\xc0\x00\xc0\x00\xc0\x00\xc0\x00'\ 151 | b'\xde\x00\xe3\x00\xc1\x80\xc1\x80\xc1\x80\xc1\x80\xc1\x80\xe3\x00'\ 152 | b'\xde\x00\x00\x00\x00\x00\x00\x00\x00\x00\x09\x00\x00\x00\x00\x00'\ 153 | b'\x00\x00\x00\x00\x3c\x00\x66\x00\xc3\x00\xc0\x00\xc0\x00\xc0\x00'\ 154 | b'\xc3\x00\x66\x00\x3c\x00\x00\x00\x00\x00\x00\x00\x00\x00\x0a\x00'\ 155 | b'\x03\x00\x03\x00\x03\x00\x03\x00\x3b\x00\x67\x00\xc3\x00\xc3\x00'\ 156 | b'\xc3\x00\xc3\x00\xc3\x00\x67\x00\x3b\x00\x00\x00\x00\x00\x00\x00'\ 157 | b'\x00\x00\x09\x00\x00\x00\x00\x00\x00\x00\x00\x00\x3c\x00\x66\x00'\ 158 | b'\xc3\x00\xc3\x00\xff\x00\xc0\x00\xc3\x00\x66\x00\x3c\x00\x00\x00'\ 159 | b'\x00\x00\x00\x00\x00\x00\x05\x00\x30\x60\x60\x60\xf0\x60\x60\x60'\ 160 | b'\x60\x60\x60\x60\x60\x00\x00\x00\x00\x09\x00\x00\x00\x00\x00\x00'\ 161 | b'\x00\x00\x00\x3b\x00\x67\x00\xc3\x00\xc3\x00\xc3\x00\xc3\x00\xc3'\ 162 | b'\x00\x67\x00\x3b\x00\x03\x00\x03\x00\xc6\x00\x7c\x00\x09\x00\xc0'\ 163 | b'\x00\xc0\x00\xc0\x00\xc0\x00\xde\x00\xe3\x00\xc3\x00\xc3\x00\xc3'\ 164 | b'\x00\xc3\x00\xc3\x00\xc3\x00\xc3\x00\x00\x00\x00\x00\x00\x00\x00'\ 165 | b'\x00\x04\x00\xc0\x00\x00\x00\xc0\xc0\xc0\xc0\xc0\xc0\xc0\xc0\xc0'\ 166 | b'\x00\x00\x00\x00\x04\x00\x60\x00\x00\x00\x60\x60\x60\x60\x60\x60'\ 167 | b'\x60\x60\x60\x60\x60\x60\xc0\x09\x00\xc0\x00\xc0\x00\xc0\x00\xc0'\ 168 | b'\x00\xc6\x00\xcc\x00\xd8\x00\xf8\x00\xe8\x00\xcc\x00\xc6\x00\xc6'\ 169 | b'\x00\xc3\x00\x00\x00\x00\x00\x00\x00\x00\x00\x04\x00\xc0\xc0\xc0'\ 170 | b'\xc0\xc0\xc0\xc0\xc0\xc0\xc0\xc0\xc0\xc0\x00\x00\x00\x00\x0e\x00'\ 171 | b'\x00\x00\x00\x00\x00\x00\x00\x00\xdd\xe0\xe7\x30\xc6\x30\xc6\x30'\ 172 | b'\xc6\x30\xc6\x30\xc6\x30\xc6\x30\xc6\x30\x00\x00\x00\x00\x00\x00'\ 173 | b'\x00\x00\x09\x00\x00\x00\x00\x00\x00\x00\x00\x00\xde\x00\xe3\x00'\ 174 | b'\xc3\x00\xc3\x00\xc3\x00\xc3\x00\xc3\x00\xc3\x00\xc3\x00\x00\x00'\ 175 | b'\x00\x00\x00\x00\x00\x00\x09\x00\x00\x00\x00\x00\x00\x00\x00\x00'\ 176 | b'\x3c\x00\x66\x00\xc3\x00\xc3\x00\xc3\x00\xc3\x00\xc3\x00\x66\x00'\ 177 | b'\x3c\x00\x00\x00\x00\x00\x00\x00\x00\x00\x09\x00\x00\x00\x00\x00'\ 178 | b'\x00\x00\x00\x00\xde\x00\xe3\x00\xc1\x80\xc1\x80\xc1\x80\xc1\x80'\ 179 | b'\xc1\x80\xe3\x00\xde\x00\xc0\x00\xc0\x00\xc0\x00\x00\x00\x0a\x00'\ 180 | b'\x00\x00\x00\x00\x00\x00\x00\x00\x3b\x00\x67\x00\xc3\x00\xc3\x00'\ 181 | b'\xc3\x00\xc3\x00\xc3\x00\x67\x00\x3b\x00\x03\x00\x03\x00\x03\x00'\ 182 | b'\x00\x00\x06\x00\x00\x00\x00\x00\xd8\xe0\xc0\xc0\xc0\xc0\xc0\xc0'\ 183 | b'\xc0\x00\x00\x00\x00\x08\x00\x00\x00\x00\x00\x7c\xc6\xc0\xc0\x70'\ 184 | b'\x0e\xc6\xc6\x7c\x00\x00\x00\x00\x05\x00\x00\x00\x60\x60\xf0\x60'\ 185 | b'\x60\x60\x60\x60\x60\x60\x70\x00\x00\x00\x00\x09\x00\x00\x00\x00'\ 186 | b'\x00\x00\x00\x00\x00\xc3\x00\xc3\x00\xc3\x00\xc3\x00\xc3\x00\xc3'\ 187 | b'\x00\xc3\x00\xc7\x00\x7b\x00\x00\x00\x00\x00\x00\x00\x00\x00\x08'\ 188 | b'\x00\x00\x00\x00\x00\xc3\x43\x62\x66\x26\x34\x3c\x18\x18\x00\x00'\ 189 | b'\x00\x00\x0c\x00\x00\x00\x00\x00\x00\x00\x00\x00\xc6\x30\x46\x30'\ 190 | b'\x47\x20\x6f\x20\x69\x60\x29\x60\x29\xc0\x39\xc0\x10\xc0\x00\x00'\ 191 | b'\x00\x00\x00\x00\x00\x00\x08\x00\x00\x00\x00\x00\x42\x66\x34\x18'\ 192 | b'\x18\x1c\x24\x66\x43\x00\x00\x00\x00\x08\x00\x00\x00\x00\x00\xc3'\ 193 | b'\x42\x42\x66\x24\x24\x3c\x18\x18\x18\x10\x30\x60\x08\x00\x00\x00'\ 194 | b'\x00\x00\xfe\x0c\x08\x18\x30\x60\x40\xc0\xfe\x00\x00\x00\x00\x06'\ 195 | b'\x00\x30\x60\x60\x60\x60\x60\x60\xe0\xc0\xe0\x60\x60\x60\x60\x60'\ 196 | b'\x60\x30\x04\x00\xc0\xc0\xc0\xc0\xc0\xc0\xc0\xc0\xc0\xc0\xc0\xc0'\ 197 | b'\xc0\xc0\xc0\xc0\x00\x06\x00\xc0\x60\x60\x60\x60\x60\x60\x70\x30'\ 198 | b'\x70\x60\x60\x60\x60\x60\x60\xc0\x09\x00\x00\x00\x00\x00\x00\x00'\ 199 | b'\x00\x00\x00\x00\x00\x00\x62\x00\x9e\x00\x00\x00\x00\x00\x00\x00'\ 200 | b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' 201 | 202 | _index =\ 203 | b'\x00\x00\x13\x00\x26\x00\x39\x00\x5d\x00\x81\x00\xa5\x00\xc9\x00'\ 204 | b'\xdc\x00\xef\x00\x02\x01\x15\x01\x39\x01\x4c\x01\x5f\x01\x72\x01'\ 205 | b'\x85\x01\xa9\x01\xcd\x01\xf1\x01\x15\x02\x39\x02\x5d\x02\x81\x02'\ 206 | b'\xa5\x02\xc9\x02\xed\x02\x00\x03\x13\x03\x37\x03\x5b\x03\x7f\x03'\ 207 | b'\xa3\x03\xd8\x03\xfc\x03\x20\x04\x44\x04\x68\x04\x8c\x04\xb0\x04'\ 208 | b'\xd4\x04\xf8\x04\x0b\x05\x2f\x05\x53\x05\x77\x05\x9b\x05\xbf\x05'\ 209 | b'\xe3\x05\x07\x06\x2b\x06\x4f\x06\x73\x06\x97\x06\xbb\x06\xdf\x06'\ 210 | b'\x03\x07\x27\x07\x4b\x07\x6f\x07\x82\x07\x95\x07\xa8\x07\xbb\x07'\ 211 | b'\xdf\x07\xf2\x07\x16\x08\x3a\x08\x5e\x08\x82\x08\xa6\x08\xb9\x08'\ 212 | b'\xdd\x08\x01\x09\x14\x09\x27\x09\x4b\x09\x5e\x09\x82\x09\xa6\x09'\ 213 | b'\xca\x09\xee\x09\x12\x0a\x25\x0a\x38\x0a\x4b\x0a\x6f\x0a\x82\x0a'\ 214 | b'\xa6\x0a\xb9\x0a\xcc\x0a\xdf\x0a\xf2\x0a\x05\x0b\x18\x0b\x3c\x0b'\ 215 | 216 | _mvfont = memoryview(_font) 217 | 218 | def _chr_addr(ordch): 219 | offset = 2 * (ordch - 32) 220 | return int.from_bytes(_index[offset:offset + 2], 'little') 221 | 222 | def get_ch(ch): 223 | ordch = ord(ch) 224 | ordch = ordch if ordch >= 32 and ordch <= 126 else ord('?') 225 | offset = _chr_addr(ordch) 226 | width = int.from_bytes(_font[offset:offset + 2], 'little') 227 | next_offs = _chr_addr(ordch +1) 228 | return _mvfont[offset + 2:next_offs], 17, width 229 | 230 | -------------------------------------------------------------------------------- /writer/font6.py: -------------------------------------------------------------------------------- 1 | # Code generated by font-to-py.py. 2 | # Font: FreeSans.ttf 3 | version = '0.2' 4 | 5 | def height(): 6 | return 14 7 | 8 | def max_width(): 9 | return 14 10 | 11 | def hmap(): 12 | return True 13 | 14 | def reverse(): 15 | return False 16 | 17 | def monospaced(): 18 | return False 19 | 20 | def min_ch(): 21 | return 32 22 | 23 | def max_ch(): 24 | return 126 25 | 26 | _font =\ 27 | b'\x08\x00\x00\x78\x8c\x84\x04\x18\x30\x20\x20\x00\x20\x00\x00\x00'\ 28 | b'\x04\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'\ 29 | b'\x05\x00\x00\x80\x80\x80\x80\x80\x80\x80\x80\x00\x80\x00\x00\x00'\ 30 | b'\x05\x00\x00\xa0\xa0\xa0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'\ 31 | b'\x08\x00\x00\x00\x12\x14\x7f\x24\x24\xfe\x28\x48\x48\x00\x00\x00'\ 32 | b'\x08\x00\x20\x78\xac\xa4\xa0\xa0\x78\x2c\xa4\xac\x78\x20\x00\x00'\ 33 | b'\x0c\x00\x00\x00\x70\x80\x89\x00\x89\x00\x8a\x00\x72\x00\x04\xe0'\ 34 | b'\x05\x10\x09\x10\x09\x10\x10\xe0\x00\x00\x00\x00\x00\x00\x09\x00'\ 35 | b'\x00\x00\x30\x00\x48\x00\x48\x00\x78\x00\x20\x00\x52\x00\x9e\x00'\ 36 | b'\x8c\x00\x8e\x00\x73\x00\x00\x00\x00\x00\x00\x00\x03\x00\x00\x80'\ 37 | b'\x80\x80\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x05\x00\x00\x20'\ 38 | b'\x40\x40\x80\x80\x80\x80\x80\x80\x80\x40\x40\x20\x05\x00\x00\x80'\ 39 | b'\x40\x40\x20\x20\x20\x20\x20\x20\x20\x40\x40\x80\x05\x00\x00\x20'\ 40 | b'\xf8\x20\x50\x00\x00\x00\x00\x00\x00\x00\x00\x00\x08\x00\x00\x00'\ 41 | b'\x00\x00\x00\x20\x20\xf8\x20\x20\x20\x00\x00\x00\x04\x00\x00\x00'\ 42 | b'\x00\x00\x00\x00\x00\x00\x00\x00\x80\x80\x80\x00\x05\x00\x00\x00'\ 43 | b'\x00\x00\x00\x00\x00\xe0\x00\x00\x00\x00\x00\x00\x04\x00\x00\x00'\ 44 | b'\x00\x00\x00\x00\x00\x00\x00\x00\x80\x00\x00\x00\x04\x00\x00\x10'\ 45 | b'\x10\x20\x20\x20\x40\x40\x40\x80\x80\x00\x00\x00\x08\x00\x00\x78'\ 46 | b'\x48\x84\x84\x84\x84\x84\x84\x48\x78\x00\x00\x00\x08\x00\x00\x20'\ 47 | b'\x60\xe0\x20\x20\x20\x20\x20\x20\x20\x00\x00\x00\x08\x00\x00\x78'\ 48 | b'\xcc\x84\x04\x0c\x18\x60\x40\x80\xfc\x00\x00\x00\x08\x00\x00\x78'\ 49 | b'\xc4\x84\x04\x38\x04\x04\x84\xcc\x78\x00\x00\x00\x08\x00\x00\x08'\ 50 | b'\x18\x38\x28\x48\x88\xfc\x08\x08\x08\x00\x00\x00\x08\x00\x00\x7c'\ 51 | b'\x80\x80\xb8\xcc\x04\x04\x04\x88\x78\x00\x00\x00\x08\x00\x00\x38'\ 52 | b'\x48\x84\x80\xf8\xcc\x84\x84\x4c\x78\x00\x00\x00\x08\x00\x00\xfc'\ 53 | b'\x0c\x08\x10\x10\x20\x20\x20\x40\x40\x00\x00\x00\x08\x00\x00\x78'\ 54 | b'\x84\x84\x84\x78\xcc\x84\x84\xcc\x78\x00\x00\x00\x08\x00\x00\x78'\ 55 | b'\xc8\x84\x84\xcc\x74\x04\x04\x88\x70\x00\x00\x00\x04\x00\x00\x00'\ 56 | b'\x00\x80\x00\x00\x00\x00\x00\x00\x80\x00\x00\x00\x04\x00\x00\x00'\ 57 | b'\x00\x00\x80\x00\x00\x00\x00\x00\x80\x80\x80\x00\x08\x00\x00\x00'\ 58 | b'\x00\x00\x00\x1c\x70\x80\x60\x1c\x04\x00\x00\x00\x08\x00\x00\x00'\ 59 | b'\x00\x00\x00\x00\xfc\x00\xfc\x00\x00\x00\x00\x00\x08\x00\x00\x00'\ 60 | b'\x00\x00\x00\xe0\x38\x06\x1c\x60\x80\x00\x00\x00\x08\x00\x00\x78'\ 61 | b'\x8c\x84\x04\x18\x30\x20\x20\x00\x20\x00\x00\x00\x0e\x00\x00\x00'\ 62 | b'\x07\xc0\x18\x60\x20\x10\x43\x48\x84\xc8\x88\xc8\x88\x88\x89\x90'\ 63 | b'\xc6\xe0\x60\x00\x30\x00\x0f\xc0\x00\x00\x09\x00\x00\x00\x0c\x00'\ 64 | b'\x1c\x00\x14\x00\x16\x00\x32\x00\x22\x00\x7f\x00\x41\x00\x41\x80'\ 65 | b'\xc1\x80\x00\x00\x00\x00\x00\x00\x09\x00\x00\x00\xfc\x00\x82\x00'\ 66 | b'\x82\x00\x82\x00\xfc\x00\x86\x00\x82\x00\x82\x00\x86\x00\xfc\x00'\ 67 | b'\x00\x00\x00\x00\x00\x00\x0a\x00\x00\x00\x3c\x00\x42\x00\x41\x00'\ 68 | b'\x80\x00\x80\x00\x80\x00\x81\x00\xc1\x00\x62\x00\x3c\x00\x00\x00'\ 69 | b'\x00\x00\x00\x00\x0a\x00\x00\x00\xfc\x00\x82\x00\x83\x00\x81\x00'\ 70 | b'\x81\x00\x81\x00\x81\x00\x83\x00\x82\x00\xfc\x00\x00\x00\x00\x00'\ 71 | b'\x00\x00\x09\x00\x00\x00\xfe\x00\x80\x00\x80\x00\x80\x00\xfc\x00'\ 72 | b'\x80\x00\x80\x00\x80\x00\x80\x00\xfe\x00\x00\x00\x00\x00\x00\x00'\ 73 | b'\x08\x00\x00\xfc\x80\x80\x80\xfc\x80\x80\x80\x80\x80\x00\x00\x00'\ 74 | b'\x0b\x00\x00\x00\x1e\x00\x61\x00\x40\x80\x80\x00\x80\x00\x87\x80'\ 75 | b'\x80\x80\xc0\x80\x61\x80\x3e\x80\x00\x00\x00\x00\x00\x00\x0a\x00'\ 76 | b'\x00\x00\x81\x00\x81\x00\x81\x00\x81\x00\xff\x00\x81\x00\x81\x00'\ 77 | b'\x81\x00\x81\x00\x81\x00\x00\x00\x00\x00\x00\x00\x04\x00\x00\x80'\ 78 | b'\x80\x80\x80\x80\x80\x80\x80\x80\x80\x00\x00\x00\x07\x00\x00\x04'\ 79 | b'\x04\x04\x04\x04\x04\x04\x84\x84\x78\x00\x00\x00\x09\x00\x00\x00'\ 80 | b'\x82\x00\x84\x00\x88\x00\x90\x00\xb0\x00\xd8\x00\x88\x00\x84\x00'\ 81 | b'\x86\x00\x82\x00\x00\x00\x00\x00\x00\x00\x08\x00\x00\x80\x80\x80'\ 82 | b'\x80\x80\x80\x80\x80\x80\xfc\x00\x00\x00\x0c\x00\x00\x00\xc1\x80'\ 83 | b'\xc1\x80\xc1\x80\xa2\x80\xa2\x80\xa2\x80\x94\x80\x94\x80\x94\x80'\ 84 | b'\x88\x80\x00\x00\x00\x00\x00\x00\x0a\x00\x00\x00\xc1\x00\xc1\x00'\ 85 | b'\xe1\x00\xb1\x00\x91\x00\x89\x00\x8d\x00\x87\x00\x83\x00\x83\x00'\ 86 | b'\x00\x00\x00\x00\x00\x00\x0b\x00\x00\x00\x3e\x00\x63\x00\xc1\x00'\ 87 | b'\x80\x80\x80\x80\x80\x80\x80\x80\xc1\x00\x63\x00\x3e\x00\x00\x00'\ 88 | b'\x00\x00\x00\x00\x09\x00\x00\x00\xfc\x00\x86\x00\x82\x00\x82\x00'\ 89 | b'\x86\x00\xfc\x00\x80\x00\x80\x00\x80\x00\x80\x00\x00\x00\x00\x00'\ 90 | b'\x00\x00\x0b\x00\x00\x00\x3e\x00\x63\x00\xc1\x00\x80\x80\x80\x80'\ 91 | b'\x80\x80\x80\x80\xc5\x80\x63\x00\x3f\x00\x00\x80\x00\x00\x00\x00'\ 92 | b'\x0a\x00\x00\x00\xfc\x00\x82\x00\x82\x00\x82\x00\x82\x00\xfc\x00'\ 93 | b'\x82\x00\x82\x00\x82\x00\x83\x00\x00\x00\x00\x00\x00\x00\x09\x00'\ 94 | b'\x00\x00\x7c\x00\xc6\x00\x82\x00\xc0\x00\x78\x00\x0e\x00\x02\x00'\ 95 | b'\x82\x00\xc6\x00\x7c\x00\x00\x00\x00\x00\x00\x00\x09\x00\x00\x00'\ 96 | b'\xfe\x00\x10\x00\x10\x00\x10\x00\x10\x00\x10\x00\x10\x00\x10\x00'\ 97 | b'\x10\x00\x10\x00\x00\x00\x00\x00\x00\x00\x0a\x00\x00\x00\x81\x00'\ 98 | b'\x81\x00\x81\x00\x81\x00\x81\x00\x81\x00\x81\x00\x81\x00\xc3\x00'\ 99 | b'\x3c\x00\x00\x00\x00\x00\x00\x00\x09\x00\x00\x00\xc1\x80\x41\x00'\ 100 | b'\x41\x00\x63\x00\x22\x00\x32\x00\x16\x00\x14\x00\x1c\x00\x08\x00'\ 101 | b'\x00\x00\x00\x00\x00\x00\x0d\x00\x00\x00\xc2\x18\x45\x18\x45\x10'\ 102 | b'\x65\x10\x65\xb0\x28\xa0\x28\xa0\x38\xa0\x38\xe0\x10\x40\x00\x00'\ 103 | b'\x00\x00\x00\x00\x09\x00\x00\x00\x41\x00\x63\x00\x32\x00\x14\x00'\ 104 | b'\x0c\x00\x1c\x00\x16\x00\x22\x00\x63\x00\x41\x80\x00\x00\x00\x00'\ 105 | b'\x00\x00\x09\x00\x00\x00\xc1\x80\x63\x00\x22\x00\x36\x00\x14\x00'\ 106 | b'\x08\x00\x08\x00\x08\x00\x08\x00\x08\x00\x00\x00\x00\x00\x00\x00'\ 107 | b'\x09\x00\x00\x00\x7f\x00\x03\x00\x06\x00\x04\x00\x0c\x00\x18\x00'\ 108 | b'\x30\x00\x20\x00\x40\x00\xff\x00\x00\x00\x00\x00\x00\x00\x04\x00'\ 109 | b'\x00\xc0\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\xc0\x04\x00'\ 110 | b'\x00\x80\x80\x40\x40\x40\x20\x20\x20\x10\x10\x00\x00\x00\x04\x00'\ 111 | b'\x00\xc0\x40\x40\x40\x40\x40\x40\x40\x40\x40\x40\x40\xc0\x07\x00'\ 112 | b'\x00\x20\x60\x50\x90\x88\x00\x00\x00\x00\x00\x00\x00\x00\x08\x00'\ 113 | b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\x00\x00\x04\x00'\ 114 | b'\x00\x40\x20\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x08\x00'\ 115 | b'\x00\x00\x00\x78\x84\x04\x04\x7c\x84\x8c\x76\x00\x00\x00\x08\x00'\ 116 | b'\x00\x80\x80\xb8\xcc\x84\x84\x84\x84\xc8\xb8\x00\x00\x00\x07\x00'\ 117 | b'\x00\x00\x00\x78\x44\x80\x80\x80\x80\x44\x78\x00\x00\x00\x08\x00'\ 118 | b'\x00\x02\x02\x3a\x46\x82\x82\x82\x82\x46\x3a\x00\x00\x00\x07\x00'\ 119 | b'\x00\x00\x00\x3c\x44\x82\xfe\x80\x80\x46\x3c\x00\x00\x00\x04\x00'\ 120 | b'\x00\x60\x40\xe0\x40\x40\x40\x40\x40\x40\x40\x00\x00\x00\x08\x00'\ 121 | b'\x00\x00\x00\x3a\x46\x82\x82\x82\x82\x46\x7a\x02\x84\x7c\x08\x00'\ 122 | b'\x00\x80\x80\xb0\xc8\x88\x88\x88\x88\x88\x88\x00\x00\x00\x03\x00'\ 123 | b'\x00\x80\x00\x80\x80\x80\x80\x80\x80\x80\x80\x00\x00\x00\x03\x00'\ 124 | b'\x00\x40\x00\x40\x40\x40\x40\x40\x40\x40\x40\x40\x40\xc0\x07\x00'\ 125 | b'\x00\x80\x80\x88\x90\xa0\xe0\x90\x98\x88\x8c\x00\x00\x00\x03\x00'\ 126 | b'\x00\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x00\x00\x00\x0b\x00'\ 127 | b'\x00\x00\x00\x00\x00\x00\xb7\x00\xcc\x80\x88\x80\x88\x80\x88\x80'\ 128 | b'\x88\x80\x88\x80\x88\x80\x00\x00\x00\x00\x00\x00\x08\x00\x00\x00'\ 129 | b'\x00\xb8\xc4\x84\x84\x84\x84\x84\x84\x00\x00\x00\x07\x00\x00\x00'\ 130 | b'\x00\x38\x44\x82\x82\x82\x82\x44\x38\x00\x00\x00\x08\x00\x00\x00'\ 131 | b'\x00\xb8\xc8\x84\x84\x84\x84\xc8\xb8\x80\x80\x00\x08\x00\x00\x00'\ 132 | b'\x00\x3a\x46\x82\x82\x82\x82\x46\x7a\x02\x02\x00\x05\x00\x00\x00'\ 133 | b'\x00\xa0\xc0\x80\x80\x80\x80\x80\x80\x00\x00\x00\x07\x00\x00\x00'\ 134 | b'\x00\x70\x88\x80\xc0\x70\x08\x88\x70\x00\x00\x00\x04\x00\x00\x00'\ 135 | b'\x40\xe0\x40\x40\x40\x40\x40\x40\x60\x00\x00\x00\x08\x00\x00\x00'\ 136 | b'\x00\x84\x84\x84\x84\x84\x84\x8c\x74\x00\x00\x00\x07\x00\x00\x00'\ 137 | b'\x00\xc6\x44\x44\x6c\x28\x28\x38\x10\x00\x00\x00\x0a\x00\x00\x00'\ 138 | b'\x00\x00\x00\x00\x8c\x40\xcc\xc0\x4c\x80\x5c\x80\x52\x80\x73\x80'\ 139 | b'\x33\x00\x33\x00\x00\x00\x00\x00\x00\x00\x07\x00\x00\x00\x00\x44'\ 140 | b'\x68\x28\x30\x30\x28\x4c\xc4\x00\x00\x00\x07\x00\x00\x00\x00\xc6'\ 141 | b'\x44\x44\x6c\x28\x28\x30\x10\x10\x20\x60\x07\x00\x00\x00\x00\x7c'\ 142 | b'\x0c\x08\x10\x30\x60\x40\xfc\x00\x00\x00\x05\x00\x00\x60\x40\x40'\ 143 | b'\x40\x40\x40\x80\x40\x40\x40\x40\x40\x60\x04\x00\x00\x80\x80\x80'\ 144 | b'\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x05\x00\x00\xc0\x40\x40'\ 145 | b'\x40\x40\x40\x20\x40\x40\x40\x40\x40\xc0\x07\x00\x00\x00\x00\x00'\ 146 | b'\x00\x62\x9e\x00\x00\x00\x00\x00\x00\x00' 147 | 148 | _index =\ 149 | b'\x00\x00\x10\x00\x20\x00\x30\x00\x40\x00\x50\x00\x60\x00\x7e\x00'\ 150 | b'\x9c\x00\xac\x00\xbc\x00\xcc\x00\xdc\x00\xec\x00\xfc\x00\x0c\x01'\ 151 | b'\x1c\x01\x2c\x01\x3c\x01\x4c\x01\x5c\x01\x6c\x01\x7c\x01\x8c\x01'\ 152 | b'\x9c\x01\xac\x01\xbc\x01\xcc\x01\xdc\x01\xec\x01\xfc\x01\x0c\x02'\ 153 | b'\x1c\x02\x2c\x02\x4a\x02\x68\x02\x86\x02\xa4\x02\xc2\x02\xe0\x02'\ 154 | b'\xf0\x02\x0e\x03\x2c\x03\x3c\x03\x4c\x03\x6a\x03\x7a\x03\x98\x03'\ 155 | b'\xb6\x03\xd4\x03\xf2\x03\x10\x04\x2e\x04\x4c\x04\x6a\x04\x88\x04'\ 156 | b'\xa6\x04\xc4\x04\xe2\x04\x00\x05\x1e\x05\x2e\x05\x3e\x05\x4e\x05'\ 157 | b'\x5e\x05\x6e\x05\x7e\x05\x8e\x05\x9e\x05\xae\x05\xbe\x05\xce\x05'\ 158 | b'\xde\x05\xee\x05\xfe\x05\x0e\x06\x1e\x06\x2e\x06\x3e\x06\x5c\x06'\ 159 | b'\x6c\x06\x7c\x06\x8c\x06\x9c\x06\xac\x06\xbc\x06\xcc\x06\xdc\x06'\ 160 | b'\xec\x06\x0a\x07\x1a\x07\x2a\x07\x3a\x07\x4a\x07\x5a\x07\x6a\x07'\ 161 | b'\x7a\x07' 162 | 163 | _mvfont = memoryview(_font) 164 | 165 | def _chr_addr(ordch): 166 | offset = 2 * (ordch - 32) 167 | return int.from_bytes(_index[offset:offset + 2], 'little') 168 | 169 | def get_ch(ch): 170 | ordch = ord(ch) 171 | ordch = ordch + 1 if ordch >= 32 and ordch <= 126 else 32 172 | offset = _chr_addr(ordch) 173 | width = int.from_bytes(_font[offset:offset + 2], 'little') 174 | next_offs = _chr_addr(ordch +1) 175 | return _mvfont[offset + 2:next_offs], 14, width 176 | 177 | -------------------------------------------------------------------------------- /writer/freesans20.py: -------------------------------------------------------------------------------- 1 | # Code generated by font-to-py.py. 2 | # Font: FreeSans.ttf 3 | version = '0.25' 4 | 5 | def height(): 6 | return 20 7 | 8 | def max_width(): 9 | return 20 10 | 11 | def hmap(): 12 | return True 13 | 14 | def reverse(): 15 | return False 16 | 17 | def monospaced(): 18 | return False 19 | 20 | def min_ch(): 21 | return 32 22 | 23 | def max_ch(): 24 | return 126 25 | 26 | _font =\ 27 | b'\x0b\x00\x00\x00\x3c\x00\x7e\x00\xc7\x00\xc3\x00\x03\x00\x03\x00'\ 28 | b'\x06\x00\x0c\x00\x08\x00\x18\x00\x18\x00\x00\x00\x00\x00\x18\x00'\ 29 | b'\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\x05\x00\x00\x00\x00\x00'\ 30 | b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'\ 31 | b'\x07\x00\x00\xc0\xc0\xc0\xc0\xc0\xc0\xc0\xc0\xc0\xc0\xc0\x00\x00'\ 32 | b'\xc0\xc0\x00\x00\x00\x00\x07\x00\x00\x00\xd8\xd8\xd8\xd8\x90\x00'\ 33 | b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x0b\x00\x00\x00'\ 34 | b'\x00\x00\x0c\xc0\x08\x80\x08\x80\x7f\xe0\x7f\xe0\x19\x80\x11\x00'\ 35 | b'\x11\x00\xff\xc0\xff\xc0\x33\x00\x33\x00\x22\x00\x22\x00\x00\x00'\ 36 | b'\x00\x00\x00\x00\x00\x00\x0b\x00\x08\x00\x3e\x00\x7f\x80\xe9\xc0'\ 37 | b'\xc8\xc0\xc8\xc0\xc8\x00\xe8\x00\x7c\x00\x1f\x80\x09\xc0\x08\xc0'\ 38 | b'\xc8\xc0\xe9\xc0\x7f\x80\x3e\x00\x08\x00\x00\x00\x00\x00\x00\x00'\ 39 | b'\x12\x00\x00\x00\x00\x00\x00\x00\x38\x10\x00\x7c\x10\x00\xc6\x20'\ 40 | b'\x00\xc6\x20\x00\xc6\x40\x00\x7c\xc0\x00\x38\x80\x00\x01\x1e\x00'\ 41 | b'\x01\x3f\x00\x02\x73\x80\x02\x61\x80\x04\x73\x80\x04\x3f\x00\x08'\ 42 | b'\x1e\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x0d\x00'\ 43 | b'\x00\x00\x00\x00\x0e\x00\x1f\x00\x31\x80\x31\x80\x31\x80\x1f\x00'\ 44 | b'\x1c\x00\x76\x60\xe3\x60\xc1\xc0\xc0\xc0\xe1\xc0\x7f\x60\x3e\x30'\ 45 | b'\x00\x00\x00\x00\x00\x00\x00\x00\x04\x00\x00\x00\xc0\xc0\xc0\xc0'\ 46 | b'\x80\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x07\x00'\ 47 | b'\x00\x10\x10\x20\x20\x60\x40\xc0\xc0\xc0\xc0\xc0\xc0\xc0\x40\x60'\ 48 | b'\x20\x30\x10\x18\x07\x00\x00\x40\x40\x20\x20\x30\x10\x18\x18\x18'\ 49 | b'\x18\x18\x18\x18\x10\x30\x20\x60\x40\xc0\x08\x00\x00\x20\x20\xf8'\ 50 | b'\x20\x50\x50\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'\ 51 | b'\x0c\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'\ 52 | b'\x18\x00\x18\x00\x18\x00\xff\x00\xff\x00\x18\x00\x18\x00\x18\x00'\ 53 | b'\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\x06\x00\x00\x00\x00\x00'\ 54 | b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xc0\xc0\x40\x40\x80\x00'\ 55 | b'\x07\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xf8\xf8\x00\x00\x00'\ 56 | b'\x00\x00\x00\x00\x00\x00\x05\x00\x00\x00\x00\x00\x00\x00\x00\x00'\ 57 | b'\x00\x00\x00\x00\x00\x00\xc0\xc0\x00\x00\x00\x00\x06\x00\x00\x04'\ 58 | b'\x0c\x08\x08\x18\x10\x10\x30\x20\x20\x60\x40\x40\xc0\x80\x00\x00'\ 59 | b'\x00\x00\x0b\x00\x00\x00\x00\x00\x3e\x00\x7f\x00\x63\x00\xe3\x80'\ 60 | b'\xc1\x80\xc1\x80\xc1\x80\xc1\x80\xc1\x80\xc1\x80\xe3\x80\x63\x00'\ 61 | b'\x7f\x00\x3e\x00\x00\x00\x00\x00\x00\x00\x00\x00\x0b\x00\x00\x00'\ 62 | b'\x00\x00\x10\x00\x30\x00\xf0\x00\xf0\x00\x30\x00\x30\x00\x30\x00'\ 63 | b'\x30\x00\x30\x00\x30\x00\x30\x00\x30\x00\x30\x00\x30\x00\x00\x00'\ 64 | b'\x00\x00\x00\x00\x00\x00\x0b\x00\x00\x00\x00\x00\x3e\x00\x7f\x00'\ 65 | b'\xe3\x80\xc1\x80\x01\x80\x01\x80\x03\x00\x0e\x00\x1c\x00\x30\x00'\ 66 | b'\x60\x00\xc0\x00\xff\x80\xff\x80\x00\x00\x00\x00\x00\x00\x00\x00'\ 67 | b'\x0b\x00\x00\x00\x00\x00\x3e\x00\x7f\x00\xe3\x80\xc1\x80\x01\x80'\ 68 | b'\x0f\x00\x0f\x00\x03\x80\x01\x80\x01\x80\xc1\x80\xe3\x80\x7f\x00'\ 69 | b'\x3e\x00\x00\x00\x00\x00\x00\x00\x00\x00\x0b\x00\x00\x00\x00\x00'\ 70 | b'\x06\x00\x06\x00\x0e\x00\x1e\x00\x16\x00\x26\x00\x46\x00\x46\x00'\ 71 | b'\x86\x00\xff\x00\xff\x00\x06\x00\x06\x00\x06\x00\x00\x00\x00\x00'\ 72 | b'\x00\x00\x00\x00\x0b\x00\x00\x00\x00\x00\x7f\x00\x7f\x00\x60\x00'\ 73 | b'\x60\x00\xde\x00\xff\x00\xe3\x80\x01\x80\x01\x80\x01\x80\x01\x80'\ 74 | b'\xc3\x00\x7f\x00\x3e\x00\x00\x00\x00\x00\x00\x00\x00\x00\x0b\x00'\ 75 | b'\x00\x00\x00\x00\x1e\x00\x3f\x00\x63\x00\x61\x80\xc0\x00\xde\x00'\ 76 | b'\xff\x00\xe3\x80\xc1\x80\xc1\x80\xc1\x80\x63\x80\x7f\x00\x3e\x00'\ 77 | b'\x00\x00\x00\x00\x00\x00\x00\x00\x0b\x00\x00\x00\x00\x00\xff\x80'\ 78 | b'\xff\x80\x01\x00\x03\x00\x02\x00\x06\x00\x04\x00\x0c\x00\x08\x00'\ 79 | b'\x18\x00\x18\x00\x10\x00\x30\x00\x30\x00\x00\x00\x00\x00\x00\x00'\ 80 | b'\x00\x00\x0b\x00\x00\x00\x00\x00\x1c\x00\x3e\x00\x63\x00\x63\x00'\ 81 | b'\x63\x00\x3e\x00\x3e\x00\x63\x00\xc1\x80\xc1\x80\xc1\x80\x63\x00'\ 82 | b'\x7f\x00\x1c\x00\x00\x00\x00\x00\x00\x00\x00\x00\x0b\x00\x00\x00'\ 83 | b'\x00\x00\x3e\x00\x7f\x00\xe3\x00\xc1\x80\xc1\x80\xc1\x80\xe3\x80'\ 84 | b'\x7f\x80\x3d\x80\x01\x80\x03\x00\xe3\x00\x7e\x00\x3c\x00\x00\x00'\ 85 | b'\x00\x00\x00\x00\x00\x00\x05\x00\x00\x00\x00\x00\x00\xc0\xc0\x00'\ 86 | b'\x00\x00\x00\x00\x00\x00\xc0\xc0\x00\x00\x00\x00\x05\x00\x00\x00'\ 87 | b'\x00\x00\x00\x00\xc0\xc0\x00\x00\x00\x00\x00\x00\xc0\xc0\x40\x40'\ 88 | b'\x80\x00\x0c\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'\ 89 | b'\x00\x40\x01\xc0\x07\x00\x3c\x00\xe0\x00\xe0\x00\x78\x00\x0f\x00'\ 90 | b'\x03\xc0\x00\x40\x00\x00\x00\x00\x00\x00\x00\x00\x0c\x00\x00\x00'\ 91 | b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xc0'\ 92 | b'\xff\xc0\x00\x00\x00\x00\xff\xc0\xff\xc0\x00\x00\x00\x00\x00\x00'\ 93 | b'\x00\x00\x00\x00\x00\x00\x0c\x00\x00\x00\x00\x00\x00\x00\x00\x00'\ 94 | b'\x00\x00\x00\x00\x00\x00\xe0\x00\x78\x00\x0e\x00\x03\xc0\x01\xc0'\ 95 | b'\x07\x00\x3c\x00\xf0\x00\x80\x00\x00\x00\x00\x00\x00\x00\x00\x00'\ 96 | b'\x0b\x00\x00\x00\x3c\x00\x7e\x00\xc7\x00\xc3\x00\x03\x00\x03\x00'\ 97 | b'\x06\x00\x0c\x00\x08\x00\x18\x00\x18\x00\x00\x00\x00\x00\x18\x00'\ 98 | b'\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\x14\x00\x00\x00\x00\x03'\ 99 | b'\xf0\x00\x0f\xfc\x00\x1e\x0f\x00\x38\x03\x80\x71\xe1\x80\x63\xe9'\ 100 | b'\xc0\x67\x18\xc0\xce\x18\xc0\xcc\x18\xc0\xcc\x10\xc0\xcc\x31\x80'\ 101 | b'\xce\x73\x80\x67\xff\x00\x63\x9e\x00\x30\x00\x00\x3c\x00\x00\x0f'\ 102 | b'\xf8\x00\x03\xf0\x00\x00\x00\x00\x0d\x00\x00\x00\x07\x00\x07\x00'\ 103 | b'\x07\x80\x0d\x80\x0d\x80\x08\xc0\x18\xc0\x18\xc0\x10\x60\x3f\xe0'\ 104 | b'\x3f\xe0\x30\x30\x60\x30\x60\x38\xc0\x18\x00\x00\x00\x00\x00\x00'\ 105 | b'\x00\x00\x0d\x00\x00\x00\xff\x00\xff\x80\xc1\xc0\xc0\xc0\xc0\xc0'\ 106 | b'\xc1\xc0\xff\x00\xff\x80\xc0\xc0\xc0\x60\xc0\x60\xc0\x60\xc0\xe0'\ 107 | b'\xff\xc0\xff\x80\x00\x00\x00\x00\x00\x00\x00\x00\x0e\x00\x00\x00'\ 108 | b'\x0f\x80\x3f\xe0\x70\x60\x60\x30\xe0\x00\xc0\x00\xc0\x00\xc0\x00'\ 109 | b'\xc0\x00\xc0\x00\xe0\x30\x60\x70\x70\x60\x3f\xe0\x0f\x80\x00\x00'\ 110 | b'\x00\x00\x00\x00\x00\x00\x0e\x00\x00\x00\xff\x00\xff\x80\xc1\xc0'\ 111 | b'\xc0\xc0\xc0\x60\xc0\x60\xc0\x60\xc0\x60\xc0\x60\xc0\x60\xc0\x60'\ 112 | b'\xc0\xc0\xc1\xc0\xff\x80\xff\x00\x00\x00\x00\x00\x00\x00\x00\x00'\ 113 | b'\x0d\x00\x00\x00\xff\xc0\xff\xc0\xc0\x00\xc0\x00\xc0\x00\xc0\x00'\ 114 | b'\xff\x80\xff\x80\xc0\x00\xc0\x00\xc0\x00\xc0\x00\xc0\x00\xff\xc0'\ 115 | b'\xff\xc0\x00\x00\x00\x00\x00\x00\x00\x00\x0c\x00\x00\x00\xff\x80'\ 116 | b'\xff\x80\xc0\x00\xc0\x00\xc0\x00\xc0\x00\xff\x00\xff\x00\xc0\x00'\ 117 | b'\xc0\x00\xc0\x00\xc0\x00\xc0\x00\xc0\x00\xc0\x00\x00\x00\x00\x00'\ 118 | b'\x00\x00\x00\x00\x0f\x00\x00\x00\x0f\xc0\x3f\xf0\x38\x30\x60\x18'\ 119 | b'\x60\x00\xc0\x00\xc0\x00\xc1\xf8\xc1\xf8\xc0\x18\xe0\x18\x60\x38'\ 120 | b'\x78\x78\x3f\xd8\x0f\x88\x00\x00\x00\x00\x00\x00\x00\x00\x0e\x00'\ 121 | b'\x00\x00\xc0\x60\xc0\x60\xc0\x60\xc0\x60\xc0\x60\xc0\x60\xff\xe0'\ 122 | b'\xff\xe0\xc0\x60\xc0\x60\xc0\x60\xc0\x60\xc0\x60\xc0\x60\xc0\x60'\ 123 | b'\x00\x00\x00\x00\x00\x00\x00\x00\x06\x00\x00\xc0\xc0\xc0\xc0\xc0'\ 124 | b'\xc0\xc0\xc0\xc0\xc0\xc0\xc0\xc0\xc0\xc0\x00\x00\x00\x00\x0b\x00'\ 125 | b'\x00\x00\x03\x00\x03\x00\x03\x00\x03\x00\x03\x00\x03\x00\x03\x00'\ 126 | b'\x03\x00\x03\x00\x03\x00\xc3\x00\xc3\x00\xe7\x00\x7e\x00\x3c\x00'\ 127 | b'\x00\x00\x00\x00\x00\x00\x00\x00\x0d\x00\x00\x00\xc0\x60\xc0\xc0'\ 128 | b'\xc1\x80\xc3\x00\xc6\x00\xcc\x00\xdc\x00\xf6\x00\xe6\x00\xc3\x00'\ 129 | b'\xc1\x80\xc1\x80\xc0\xc0\xc0\x60\xc0\x60\x00\x00\x00\x00\x00\x00'\ 130 | b'\x00\x00\x0b\x00\x00\x00\xc0\x00\xc0\x00\xc0\x00\xc0\x00\xc0\x00'\ 131 | b'\xc0\x00\xc0\x00\xc0\x00\xc0\x00\xc0\x00\xc0\x00\xc0\x00\xc0\x00'\ 132 | b'\xff\x80\xff\x80\x00\x00\x00\x00\x00\x00\x00\x00\x11\x00\x00\x00'\ 133 | b'\x00\xe0\x1c\x00\xe0\x1c\x00\xf0\x3c\x00\xf0\x3c\x00\xd0\x2c\x00'\ 134 | b'\xd8\x6c\x00\xd8\x6c\x00\xc8\x4c\x00\xcc\xcc\x00\xcc\xcc\x00\xc4'\ 135 | b'\x8c\x00\xc6\x8c\x00\xc7\x8c\x00\xc3\x0c\x00\xc3\x0c\x00\x00\x00'\ 136 | b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x0f\x00\x00\x00\xe0\x60'\ 137 | b'\xe0\x60\xf0\x60\xf0\x60\xd8\x60\xd8\x60\xcc\x60\xc4\x60\xc6\x60'\ 138 | b'\xc2\x60\xc3\x60\xc1\xe0\xc1\xe0\xc0\xe0\xc0\xe0\x00\x00\x00\x00'\ 139 | b'\x00\x00\x00\x00\x10\x00\x00\x00\x0f\xc0\x1f\xe0\x38\x70\x60\x18'\ 140 | b'\x60\x1c\xc0\x0c\xc0\x0c\xc0\x0c\xc0\x0c\xc0\x0c\x60\x1c\x60\x18'\ 141 | b'\x38\x70\x1f\xe0\x0f\xc0\x00\x00\x00\x00\x00\x00\x00\x00\x0d\x00'\ 142 | b'\x00\x00\xff\x00\xff\x80\xc1\xc0\xc0\xc0\xc0\xc0\xc0\xc0\xc1\xc0'\ 143 | b'\xff\x80\xff\x00\xc0\x00\xc0\x00\xc0\x00\xc0\x00\xc0\x00\xc0\x00'\ 144 | b'\x00\x00\x00\x00\x00\x00\x00\x00\x10\x00\x00\x00\x0f\xc0\x1f\xe0'\ 145 | b'\x38\x70\x60\x18\x60\x1c\xc0\x0c\xc0\x0c\xc0\x0c\xc0\x0c\xc0\x0c'\ 146 | b'\x60\x18\x60\xd8\x38\x70\x1f\xf8\x0f\x98\x00\x08\x00\x00\x00\x00'\ 147 | b'\x00\x00\x0e\x00\x00\x00\xff\x80\xff\xc0\xc0\xe0\xc0\x60\xc0\x60'\ 148 | b'\xc0\x60\xc0\xc0\xff\x80\xff\xc0\xc0\xe0\xc0\x60\xc0\x60\xc0\x60'\ 149 | b'\xc0\x60\xc0\x70\x00\x00\x00\x00\x00\x00\x00\x00\x0d\x00\x00\x00'\ 150 | b'\x1f\x80\x7f\xe0\xe0\x70\xc0\x30\xc0\x00\xe0\x00\x78\x00\x3f\x80'\ 151 | b'\x03\xe0\x00\x70\xc0\x30\xc0\x30\x70\x60\x7f\xe0\x1f\x80\x00\x00'\ 152 | b'\x00\x00\x00\x00\x00\x00\x0d\x00\x00\x00\xff\xc0\xff\xc0\x0c\x00'\ 153 | b'\x0c\x00\x0c\x00\x0c\x00\x0c\x00\x0c\x00\x0c\x00\x0c\x00\x0c\x00'\ 154 | b'\x0c\x00\x0c\x00\x0c\x00\x0c\x00\x00\x00\x00\x00\x00\x00\x00\x00'\ 155 | b'\x0e\x00\x00\x00\xc0\x60\xc0\x60\xc0\x60\xc0\x60\xc0\x60\xc0\x60'\ 156 | b'\xc0\x60\xc0\x60\xc0\x60\xc0\x60\xc0\x60\xc0\x60\x60\xc0\x7f\xc0'\ 157 | b'\x1f\x00\x00\x00\x00\x00\x00\x00\x00\x00\x0d\x00\x00\x00\xc0\x30'\ 158 | b'\x60\x30\x60\x30\x20\x20\x30\x60\x30\x60\x10\x40\x18\xc0\x18\xc0'\ 159 | b'\x08\x80\x0d\x80\x0d\x80\x07\x00\x07\x00\x07\x00\x00\x00\x00\x00'\ 160 | b'\x00\x00\x00\x00\x13\x00\x00\x00\x00\xc0\xc0\xc0\x60\xe0\xc0\x60'\ 161 | b'\xe0\xc0\x61\xe0\xc0\x61\xb1\x80\x31\xb1\x80\x31\xb1\x80\x33\x11'\ 162 | b'\x80\x33\x19\x00\x13\x1b\x00\x1f\x1b\x00\x1e\x0b\x00\x1e\x0e\x00'\ 163 | b'\x0e\x0e\x00\x0c\x06\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'\ 164 | b'\x00\x00\x0d\x00\x00\x00\x60\x30\x30\x70\x30\x60\x18\xc0\x0c\xc0'\ 165 | b'\x0d\x80\x07\x00\x07\x00\x07\x00\x0d\x80\x18\xc0\x18\xe0\x30\x60'\ 166 | b'\x70\x30\x60\x38\x00\x00\x00\x00\x00\x00\x00\x00\x0e\x00\x00\x00'\ 167 | b'\x60\x18\x70\x38\x30\x30\x18\x60\x18\x60\x0c\xc0\x0f\xc0\x07\x80'\ 168 | b'\x03\x00\x03\x00\x03\x00\x03\x00\x03\x00\x03\x00\x03\x00\x00\x00'\ 169 | b'\x00\x00\x00\x00\x00\x00\x0c\x00\x00\x00\xff\xe0\xff\xe0\x00\xc0'\ 170 | b'\x01\x80\x03\x80\x03\x00\x06\x00\x0c\x00\x1c\x00\x38\x00\x30\x00'\ 171 | b'\x60\x00\xc0\x00\xff\xe0\xff\xe0\x00\x00\x00\x00\x00\x00\x00\x00'\ 172 | b'\x06\x00\x00\xe0\xe0\xc0\xc0\xc0\xc0\xc0\xc0\xc0\xc0\xc0\xc0\xc0'\ 173 | b'\xc0\xc0\xc0\xc0\xe0\xe0\x06\x00\x00\x80\xc0\x40\x40\x60\x20\x20'\ 174 | b'\x30\x10\x10\x18\x08\x08\x0c\x04\x00\x00\x00\x00\x06\x00\x00\xe0'\ 175 | b'\xe0\x60\x60\x60\x60\x60\x60\x60\x60\x60\x60\x60\x60\x60\x60\x60'\ 176 | b'\xe0\xe0\x09\x00\x00\x00\x00\x00\x18\x00\x38\x00\x28\x00\x2c\x00'\ 177 | b'\x64\x00\x46\x00\xc2\x00\x82\x00\x00\x00\x00\x00\x00\x00\x00\x00'\ 178 | b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x0c\x00\x00\x00'\ 179 | b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'\ 180 | b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xf0'\ 181 | b'\x00\x00\x00\x00\x00\x00\x05\x00\x00\xc0\x60\x30\x00\x00\x00\x00'\ 182 | b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x0b\x00\x00\x00'\ 183 | b'\x00\x00\x00\x00\x00\x00\x00\x00\x3e\x00\xff\x80\xc1\x80\x01\x80'\ 184 | b'\x01\x80\x3f\x80\xf1\x80\xc1\x80\xc3\x80\xff\xc0\x78\xc0\x00\x00'\ 185 | b'\x00\x00\x00\x00\x00\x00\x0b\x00\x00\x00\xc0\x00\xc0\x00\xc0\x00'\ 186 | b'\xc0\x00\xdf\x00\xff\x80\xe1\x80\xc0\xc0\xc0\xc0\xc0\xc0\xc0\xc0'\ 187 | b'\xc0\xc0\xe1\x80\xff\x80\xde\x00\x00\x00\x00\x00\x00\x00\x00\x00'\ 188 | b'\x0a\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x1e\x00\x7f\x00'\ 189 | b'\x61\x80\xc0\x00\xc0\x00\xc0\x00\xc0\x00\xc1\x80\x63\x80\x7f\x00'\ 190 | b'\x3e\x00\x00\x00\x00\x00\x00\x00\x00\x00\x0b\x00\x00\x00\x01\x80'\ 191 | b'\x01\x80\x01\x80\x01\x80\x3d\x80\x7f\x80\x63\x80\xc1\x80\xc1\x80'\ 192 | b'\xc1\x80\xc1\x80\xc1\x80\x63\x80\x7f\x80\x3d\x80\x00\x00\x00\x00'\ 193 | b'\x00\x00\x00\x00\x0b\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'\ 194 | b'\x3e\x00\x7f\x00\x63\x00\xc1\x80\xff\x80\xff\x80\xc0\x00\xc0\x00'\ 195 | b'\x63\x80\x7f\x00\x3e\x00\x00\x00\x00\x00\x00\x00\x00\x00\x06\x00'\ 196 | b'\x00\x30\x70\x60\x60\xf0\xf0\x60\x60\x60\x60\x60\x60\x60\x60\x60'\ 197 | b'\x00\x00\x00\x00\x0b\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'\ 198 | b'\x3d\x80\x7f\x80\x63\x80\xc1\x80\xc1\x80\xc1\x80\xc1\x80\xc1\x80'\ 199 | b'\x63\x80\x7f\x80\x3d\x80\x01\x80\xc3\x80\x7f\x00\x3e\x00\x0b\x00'\ 200 | b'\x00\x00\xc0\x00\xc0\x00\xc0\x00\xc0\x00\xdf\x00\xdf\x80\xe3\x80'\ 201 | b'\xc1\x80\xc1\x80\xc1\x80\xc1\x80\xc1\x80\xc1\x80\xc1\x80\xc1\x80'\ 202 | b'\x00\x00\x00\x00\x00\x00\x00\x00\x04\x00\x00\xc0\xc0\x00\x00\xc0'\ 203 | b'\xc0\xc0\xc0\xc0\xc0\xc0\xc0\xc0\xc0\xc0\x00\x00\x00\x00\x05\x00'\ 204 | b'\x00\x30\x30\x00\x00\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30'\ 205 | b'\x30\x30\xf0\xe0\x0a\x00\x00\x00\xc0\x00\xc0\x00\xc0\x00\xc0\x00'\ 206 | b'\xc3\x00\xc6\x00\xcc\x00\xd8\x00\xf8\x00\xec\x00\xce\x00\xc6\x00'\ 207 | b'\xc3\x00\xc3\x00\xc1\x80\x00\x00\x00\x00\x00\x00\x00\x00\x04\x00'\ 208 | b'\x00\xc0\xc0\xc0\xc0\xc0\xc0\xc0\xc0\xc0\xc0\xc0\xc0\xc0\xc0\xc0'\ 209 | b'\x00\x00\x00\x00\x10\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'\ 210 | b'\xde\x78\xfe\xfc\xe3\x8c\xc3\x0c\xc3\x0c\xc3\x0c\xc3\x0c\xc3\x0c'\ 211 | b'\xc3\x0c\xc3\x0c\xc3\x0c\x00\x00\x00\x00\x00\x00\x00\x00\x0b\x00'\ 212 | b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xcf\x00\xdf\x80\xe3\x80'\ 213 | b'\xc1\x80\xc1\x80\xc1\x80\xc1\x80\xc1\x80\xc1\x80\xc1\x80\xc1\x80'\ 214 | b'\x00\x00\x00\x00\x00\x00\x00\x00\x0b\x00\x00\x00\x00\x00\x00\x00'\ 215 | b'\x00\x00\x00\x00\x3e\x00\x7f\x00\x63\x00\xc1\x80\xc1\x80\xc1\x80'\ 216 | b'\xc1\x80\xc1\x80\x63\x00\x7f\x00\x3e\x00\x00\x00\x00\x00\x00\x00'\ 217 | b'\x00\x00\x0b\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xde\x00'\ 218 | b'\xff\x80\xe1\x80\xc0\xc0\xc0\xc0\xc0\xc0\xc0\xc0\xc0\xc0\xe1\x80'\ 219 | b'\xff\x80\xde\x00\xc0\x00\xc0\x00\xc0\x00\x00\x00\x0b\x00\x00\x00'\ 220 | b'\x00\x00\x00\x00\x00\x00\x00\x00\x3d\x80\x7f\x80\x63\x80\xc1\x80'\ 221 | b'\xc1\x80\xc1\x80\xc1\x80\xc1\x80\x63\x80\x7f\x80\x3d\x80\x01\x80'\ 222 | b'\x01\x80\x01\x80\x00\x00\x07\x00\x00\x00\x00\x00\x00\xd8\xf8\xe0'\ 223 | b'\xc0\xc0\xc0\xc0\xc0\xc0\xc0\xc0\x00\x00\x00\x00\x0a\x00\x00\x00'\ 224 | b'\x00\x00\x00\x00\x00\x00\x00\x00\x3c\x00\x7f\x00\xc3\x00\xc0\x00'\ 225 | b'\xf0\x00\x7e\x00\x0f\x00\x03\x00\xc3\x00\xfe\x00\x7c\x00\x00\x00'\ 226 | b'\x00\x00\x00\x00\x00\x00\x06\x00\x00\x00\x00\x60\x60\xf0\xf0\x60'\ 227 | b'\x60\x60\x60\x60\x60\x60\x70\x70\x00\x00\x00\x00\x0b\x00\x00\x00'\ 228 | b'\x00\x00\x00\x00\x00\x00\x00\x00\xc1\x80\xc1\x80\xc1\x80\xc1\x80'\ 229 | b'\xc1\x80\xc1\x80\xc1\x80\xc1\x80\xe3\x80\xfd\x80\x79\x80\x00\x00'\ 230 | b'\x00\x00\x00\x00\x00\x00\x0a\x00\x00\x00\x00\x00\x00\x00\x00\x00'\ 231 | b'\x00\x00\xc0\xc0\x61\x80\x61\x80\x61\x00\x23\x00\x33\x00\x32\x00'\ 232 | b'\x16\x00\x1e\x00\x1c\x00\x0c\x00\x00\x00\x00\x00\x00\x00\x00\x00'\ 233 | b'\x0e\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xc3\x0c\xc3\x8c'\ 234 | b'\x63\x8c\x67\x88\x66\x98\x24\xd8\x34\xd0\x3c\xd0\x3c\x70\x18\x70'\ 235 | b'\x18\x60\x00\x00\x00\x00\x00\x00\x00\x00\x0a\x00\x00\x00\x00\x00'\ 236 | b'\x00\x00\x00\x00\x00\x00\x61\x80\x63\x00\x33\x00\x1e\x00\x1c\x00'\ 237 | b'\x0c\x00\x1c\x00\x16\x00\x33\x00\x63\x00\x41\x80\x00\x00\x00\x00'\ 238 | b'\x00\x00\x00\x00\x0a\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'\ 239 | b'\xc0\x80\x41\x80\x61\x80\x61\x00\x23\x00\x33\x00\x32\x00\x16\x00'\ 240 | b'\x1c\x00\x1c\x00\x0c\x00\x08\x00\x18\x00\x78\x00\x70\x00\x0a\x00'\ 241 | b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\x00\xff\x00\x06\x00'\ 242 | b'\x06\x00\x0c\x00\x18\x00\x30\x00\x60\x00\xc0\x00\xff\x00\xff\x00'\ 243 | b'\x00\x00\x00\x00\x00\x00\x00\x00\x07\x00\x00\x18\x38\x30\x30\x30'\ 244 | b'\x30\x30\x30\x70\xc0\x70\x30\x30\x30\x30\x30\x30\x38\x18\x05\x00'\ 245 | b'\x00\xc0\xc0\xc0\xc0\xc0\xc0\xc0\xc0\xc0\xc0\xc0\xc0\xc0\xc0\xc0'\ 246 | b'\xc0\xc0\xc0\xc0\x07\x00\x00\xc0\xe0\x60\x60\x60\x60\x60\x60\x70'\ 247 | b'\x18\x70\x60\x60\x60\x60\x60\x60\xe0\xc0\x0a\x00\x00\x00\x00\x00'\ 248 | b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x60\x00\xf1\x00\x9f\x00'\ 249 | b'\x06\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'\ 250 | b'\x00\x00\x00\x00' 251 | 252 | _index =\ 253 | b'\x00\x00\x2a\x00\x2a\x00\x40\x00\x40\x00\x56\x00\x56\x00\x6c\x00'\ 254 | b'\x6c\x00\x96\x00\x96\x00\xc0\x00\xc0\x00\xfe\x00\xfe\x00\x28\x01'\ 255 | b'\x28\x01\x3e\x01\x3e\x01\x54\x01\x54\x01\x6a\x01\x6a\x01\x80\x01'\ 256 | b'\x80\x01\xaa\x01\xaa\x01\xc0\x01\xc0\x01\xd6\x01\xd6\x01\xec\x01'\ 257 | b'\xec\x01\x02\x02\x02\x02\x2c\x02\x2c\x02\x56\x02\x56\x02\x80\x02'\ 258 | b'\x80\x02\xaa\x02\xaa\x02\xd4\x02\xd4\x02\xfe\x02\xfe\x02\x28\x03'\ 259 | b'\x28\x03\x52\x03\x52\x03\x7c\x03\x7c\x03\xa6\x03\xa6\x03\xbc\x03'\ 260 | b'\xbc\x03\xd2\x03\xd2\x03\xfc\x03\xfc\x03\x26\x04\x26\x04\x50\x04'\ 261 | b'\x50\x04\x7a\x04\x7a\x04\xb8\x04\xb8\x04\xe2\x04\xe2\x04\x0c\x05'\ 262 | b'\x0c\x05\x36\x05\x36\x05\x60\x05\x60\x05\x8a\x05\x8a\x05\xb4\x05'\ 263 | b'\xb4\x05\xde\x05\xde\x05\x08\x06\x08\x06\x1e\x06\x1e\x06\x48\x06'\ 264 | b'\x48\x06\x72\x06\x72\x06\x9c\x06\x9c\x06\xda\x06\xda\x06\x04\x07'\ 265 | b'\x04\x07\x2e\x07\x2e\x07\x58\x07\x58\x07\x82\x07\x82\x07\xac\x07'\ 266 | b'\xac\x07\xd6\x07\xd6\x07\x00\x08\x00\x08\x2a\x08\x2a\x08\x54\x08'\ 267 | b'\x54\x08\x92\x08\x92\x08\xbc\x08\xbc\x08\xe6\x08\xe6\x08\x10\x09'\ 268 | b'\x10\x09\x26\x09\x26\x09\x3c\x09\x3c\x09\x52\x09\x52\x09\x7c\x09'\ 269 | b'\x7c\x09\xa6\x09\xa6\x09\xbc\x09\xbc\x09\xe6\x09\xe6\x09\x10\x0a'\ 270 | b'\x10\x0a\x3a\x0a\x3a\x0a\x64\x0a\x64\x0a\x8e\x0a\x8e\x0a\xa4\x0a'\ 271 | b'\xa4\x0a\xce\x0a\xce\x0a\xf8\x0a\xf8\x0a\x0e\x0b\x0e\x0b\x24\x0b'\ 272 | b'\x24\x0b\x4e\x0b\x4e\x0b\x64\x0b\x64\x0b\x8e\x0b\x8e\x0b\xb8\x0b'\ 273 | b'\xb8\x0b\xe2\x0b\xe2\x0b\x0c\x0c\x0c\x0c\x36\x0c\x36\x0c\x4c\x0c'\ 274 | b'\x4c\x0c\x76\x0c\x76\x0c\x8c\x0c\x8c\x0c\xb6\x0c\xb6\x0c\xe0\x0c'\ 275 | b'\xe0\x0c\x0a\x0d\x0a\x0d\x34\x0d\x34\x0d\x5e\x0d\x5e\x0d\x88\x0d'\ 276 | b'\x88\x0d\x9e\x0d\x9e\x0d\xb4\x0d\xb4\x0d\xca\x0d\xca\x0d\xf4\x0d'\ 277 | 278 | _mvfont = memoryview(_font) 279 | 280 | def get_ch(ch): 281 | ordch = ord(ch) 282 | ordch = ordch + 1 if ordch >= 32 and ordch <= 126 else 32 283 | idx_offs = 4 * (ordch - 32) 284 | offset = int.from_bytes(_index[idx_offs : idx_offs + 2], 'little') 285 | next_offs = int.from_bytes(_index[idx_offs + 2 : idx_offs + 4], 'little') 286 | width = int.from_bytes(_font[offset:offset + 2], 'little') 287 | return _mvfont[offset + 2:next_offs], 20, width 288 | 289 | -------------------------------------------------------------------------------- /writer/images/IMG_2861.JPG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/peterhinch/micropython-font-to-py/c24761448e6ef1c40716b9b2b629e6fa37b2c9d2/writer/images/IMG_2861.JPG -------------------------------------------------------------------------------- /writer/images/IMG_2866.JPG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/peterhinch/micropython-font-to-py/c24761448e6ef1c40716b9b2b629e6fa37b2c9d2/writer/images/IMG_2866.JPG -------------------------------------------------------------------------------- /writer/images/fields.JPG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/peterhinch/micropython-font-to-py/c24761448e6ef1c40716b9b2b629e6fa37b2c9d2/writer/images/fields.JPG -------------------------------------------------------------------------------- /writer/images/fonts.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/peterhinch/micropython-font-to-py/c24761448e6ef1c40716b9b2b629e6fa37b2c9d2/writer/images/fonts.png -------------------------------------------------------------------------------- /writer/images/mixed.JPG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/peterhinch/micropython-font-to-py/c24761448e6ef1c40716b9b2b629e6fa37b2c9d2/writer/images/mixed.JPG -------------------------------------------------------------------------------- /writer/images/rjust.JPG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/peterhinch/micropython-font-to-py/c24761448e6ef1c40716b9b2b629e6fa37b2c9d2/writer/images/rjust.JPG -------------------------------------------------------------------------------- /writer/old_versions/writer_fw_compatible.py: -------------------------------------------------------------------------------- 1 | # writer.py Implements the Writer class. 2 | # Handles colour, word wrap and tab stops 3 | 4 | # V0.4.3 Aug 2021 Support for fast blit to color displays (PR7682). 5 | # V0.4.0 Jan 2021 Improved handling of word wrap and line clip. Upside-down 6 | # rendering no longer supported: delegate to device driver. 7 | # V0.3.5 Sept 2020 Fast rendering option for color displays 8 | 9 | # Released under the MIT License (MIT). See LICENSE. 10 | # Copyright (c) 2019-2021 Peter Hinch 11 | 12 | # A Writer supports rendering text to a Display instance in a given font. 13 | # Multiple Writer instances may be created, each rendering a font to the 14 | # same Display object. 15 | 16 | # Timings were run on a pyboard D SF6W comparing slow and fast rendering 17 | # and averaging over multiple characters. Proportional fonts were used. 18 | # 20 pixel high font, timings were 5.44ms/467μs, gain 11.7 (freesans20). 19 | # 10 pixel high font, timings were 1.76ms/396μs, gain 4.36 (arial10). 20 | 21 | 22 | import framebuf 23 | from uctypes import bytearray_at, addressof 24 | from sys import implementation 25 | import os 26 | 27 | __version__ = (0, 4, 3) 28 | 29 | def buildcheck(device): 30 | if not hasattr(device, 'palette'): 31 | return False 32 | i0, i1, _ = implementation[1] 33 | if i0 > 1 or i1 > 16: 34 | return True 35 | # After release of V1.17 require that build. Until then check for date. 36 | # TODO simplify this once V1.17 is released. 37 | try: 38 | datestring = os.uname()[3] 39 | date = datestring.split(' on')[1] 40 | date = date.lstrip()[:10] 41 | idate = tuple([int(x) for x in date.split('-')]) 42 | return idate >= (2021, 8, 25) 43 | except AttributeError: 44 | return False 45 | 46 | 47 | fast_mode = False # False for mono displays although actually these render fast 48 | 49 | class DisplayState(): 50 | def __init__(self): 51 | self.text_row = 0 52 | self.text_col = 0 53 | 54 | def _get_id(device): 55 | if not isinstance(device, framebuf.FrameBuffer): 56 | raise ValueError('Device must be derived from FrameBuffer.') 57 | return id(device) 58 | 59 | # Basic Writer class for monochrome displays 60 | class Writer(): 61 | 62 | state = {} # Holds a display state for each device 63 | 64 | @staticmethod 65 | def set_textpos(device, row=None, col=None): 66 | devid = _get_id(device) 67 | if devid not in Writer.state: 68 | Writer.state[devid] = DisplayState() 69 | s = Writer.state[devid] # Current state 70 | if row is not None: 71 | if row < 0 or row >= device.height: 72 | raise ValueError('row is out of range') 73 | s.text_row = row 74 | if col is not None: 75 | if col < 0 or col >= device.width: 76 | raise ValueError('col is out of range') 77 | s.text_col = col 78 | return s.text_row, s.text_col 79 | 80 | def __init__(self, device, font, verbose=True): 81 | self.devid = _get_id(device) 82 | self.device = device 83 | if self.devid not in Writer.state: 84 | Writer.state[self.devid] = DisplayState() 85 | self.font = font 86 | if font.height() >= device.height or font.max_width() >= device.width: 87 | raise ValueError('Font too large for screen') 88 | # Allow to work with reverse or normal font mapping 89 | if font.hmap(): 90 | self.map = framebuf.MONO_HMSB if font.reverse() else framebuf.MONO_HLSB 91 | else: 92 | raise ValueError('Font must be horizontally mapped.') 93 | if verbose: 94 | fstr = 'Orientation: Horizontal. Reversal: {}. Width: {}. Height: {}.' 95 | print(fstr.format(font.reverse(), device.width, device.height)) 96 | print('Start row = {} col = {}'.format(self._getstate().text_row, self._getstate().text_col)) 97 | self.screenwidth = device.width # In pixels 98 | self.screenheight = device.height 99 | self.bgcolor = 0 # Monochrome background and foreground colors 100 | self.fgcolor = 1 101 | self.row_clip = False # Clip or scroll when screen fullt 102 | self.col_clip = False # Clip or new line when row is full 103 | self.wrap = True # Word wrap 104 | self.cpos = 0 105 | self.tab = 4 106 | 107 | self.glyph = None # Current char 108 | self.char_height = 0 109 | self.char_width = 0 110 | self.clip_width = 0 111 | 112 | def _getstate(self): 113 | return Writer.state[self.devid] 114 | 115 | def _newline(self): 116 | s = self._getstate() 117 | height = self.font.height() 118 | s.text_row += height 119 | s.text_col = 0 120 | margin = self.screenheight - (s.text_row + height) 121 | y = self.screenheight + margin 122 | if margin < 0: 123 | if not self.row_clip: 124 | self.device.scroll(0, margin) 125 | self.device.fill_rect(0, y, self.screenwidth, abs(margin), self.bgcolor) 126 | s.text_row += margin 127 | 128 | def set_clip(self, row_clip=None, col_clip=None, wrap=None): 129 | if row_clip is not None: 130 | self.row_clip = row_clip 131 | if col_clip is not None: 132 | self.col_clip = col_clip 133 | if wrap is not None: 134 | self.wrap = wrap 135 | return self.row_clip, self.col_clip, self.wrap 136 | 137 | @property 138 | def height(self): # Property for consistency with device 139 | return self.font.height() 140 | 141 | def printstring(self, string, invert=False): 142 | # word wrapping. Assumes words separated by single space. 143 | q = string.split('\n') 144 | last = len(q) - 1 145 | for n, s in enumerate(q): 146 | if s: 147 | self._printline(s, invert) 148 | if n != last: 149 | self._printchar('\n') 150 | 151 | def _printline(self, string, invert): 152 | rstr = None 153 | if self.wrap and self.stringlen(string, True): # Length > self.screenwidth 154 | pos = 0 155 | lstr = string[:] 156 | while self.stringlen(lstr, True): # Length > self.screenwidth 157 | pos = lstr.rfind(' ') 158 | lstr = lstr[:pos].rstrip() 159 | if pos > 0: 160 | rstr = string[pos + 1:] 161 | string = lstr 162 | 163 | for char in string: 164 | self._printchar(char, invert) 165 | if rstr is not None: 166 | self._printchar('\n') 167 | self._printline(rstr, invert) # Recurse 168 | 169 | def stringlen(self, string, oh=False): 170 | sc = self._getstate().text_col # Start column 171 | wd = self.screenwidth 172 | l = 0 173 | for char in string[:-1]: 174 | _, _, char_width = self.font.get_ch(char) 175 | l += char_width 176 | if oh and l + sc > wd: 177 | return True # All done. Save time. 178 | char = string[-1] 179 | _, _, char_width = self.font.get_ch(char) 180 | if oh and l + sc + char_width > wd: 181 | l += self._truelen(char) # Last char might have blank cols on RHS 182 | else: 183 | l += char_width # Public method. Return same value as old code. 184 | return l + sc > wd if oh else l 185 | 186 | # Return the printable width of a glyph less any blank columns on RHS 187 | def _truelen(self, char): 188 | glyph, ht, wd = self.font.get_ch(char) 189 | div, mod = divmod(wd, 8) 190 | gbytes = div + 1 if mod else div # No. of bytes per row of glyph 191 | mc = 0 # Max non-blank column 192 | data = glyph[(wd - 1) // 8] # Last byte of row 0 193 | for row in range(ht): # Glyph row 194 | for col in range(wd -1, -1, -1): # Glyph column 195 | gbyte, gbit = divmod(col, 8) 196 | if gbit == 0: # Next glyph byte 197 | data = glyph[row * gbytes + gbyte] 198 | if col <= mc: 199 | break 200 | if data & (1 << (7 - gbit)): # Pixel is lit (1) 201 | mc = col # Eventually gives rightmost lit pixel 202 | break 203 | if mc + 1 == wd: 204 | break # All done: no trailing space 205 | print('Truelen', char, wd, mc + 1) # TEST 206 | return mc + 1 207 | 208 | def _get_char(self, char, recurse): 209 | if not recurse: # Handle tabs 210 | if char == '\n': 211 | self.cpos = 0 212 | elif char == '\t': 213 | nspaces = self.tab - (self.cpos % self.tab) 214 | if nspaces == 0: 215 | nspaces = self.tab 216 | while nspaces: 217 | nspaces -= 1 218 | self._printchar(' ', recurse=True) 219 | self.glyph = None # All done 220 | return 221 | 222 | self.glyph = None # Assume all done 223 | if char == '\n': 224 | self._newline() 225 | return 226 | glyph, char_height, char_width = self.font.get_ch(char) 227 | s = self._getstate() 228 | np = None # Allow restriction on printable columns 229 | if s.text_row + char_height > self.screenheight: 230 | if self.row_clip: 231 | return 232 | self._newline() 233 | oh = s.text_col + char_width - self.screenwidth # Overhang (+ve) 234 | if oh > 0: 235 | if self.col_clip or self.wrap: 236 | np = char_width - oh # No. of printable columns 237 | if np <= 0: 238 | return 239 | else: 240 | self._newline() 241 | self.glyph = glyph 242 | self.char_height = char_height 243 | self.char_width = char_width 244 | self.clip_width = char_width if np is None else np 245 | 246 | # Method using blitting. Efficient rendering for monochrome displays. 247 | # Tested on SSD1306. Invert is for black-on-white rendering. 248 | def _printchar(self, char, invert=False, recurse=False): 249 | s = self._getstate() 250 | self._get_char(char, recurse) 251 | if self.glyph is None: 252 | return # All done 253 | buf = bytearray(self.glyph) 254 | if invert: 255 | for i, v in enumerate(buf): 256 | buf[i] = 0xFF & ~ v 257 | fbc = framebuf.FrameBuffer(buf, self.clip_width, self.char_height, self.map) 258 | self.device.blit(fbc, s.text_col, s.text_row) 259 | s.text_col += self.char_width 260 | self.cpos += 1 261 | 262 | def tabsize(self, value=None): 263 | if value is not None: 264 | self.tab = value 265 | return self.tab 266 | 267 | def setcolor(self, *_): 268 | return self.fgcolor, self.bgcolor 269 | 270 | # Writer for colour displays. 271 | class CWriter(Writer): 272 | 273 | 274 | def __init__(self, device, font, fgcolor=None, bgcolor=None, verbose=True): 275 | super().__init__(device, font, verbose) 276 | global fast_mode 277 | fast_mode = buildcheck(device) 278 | if bgcolor is not None: # Assume monochrome. 279 | self.bgcolor = bgcolor 280 | if fgcolor is not None: 281 | self.fgcolor = fgcolor 282 | self.def_bgcolor = self.bgcolor 283 | self.def_fgcolor = self.fgcolor 284 | self._printchar = self._pchfast if fast_mode else self._pchslow 285 | verbose and print('Render {} using fast mode'.format('is' if fast_mode else 'not')) 286 | 287 | def _pchfast(self, char, invert=False, recurse=False): 288 | s = self._getstate() 289 | self._get_char(char, recurse) 290 | if self.glyph is None: 291 | return # All done 292 | buf = bytearray_at(addressof(self.glyph), len(self.glyph)) 293 | fbc = framebuf.FrameBuffer(buf, self.clip_width, self.char_height, self.map) 294 | palette = self.device.palette 295 | palette.bg(self.fgcolor if invert else self.bgcolor) 296 | palette.fg(self.bgcolor if invert else self.fgcolor) 297 | 298 | self.device.blit(fbc, s.text_col, s.text_row, -1, palette) 299 | s.text_col += self.char_width 300 | self.cpos += 1 301 | 302 | def _pchslow(self, char, invert=False, recurse=False): 303 | s = self._getstate() 304 | self._get_char(char, recurse) 305 | if self.glyph is None: 306 | return # All done 307 | char_height = self.char_height 308 | char_width = self.char_width 309 | clip_width = self.clip_width 310 | 311 | div, mod = divmod(char_width, 8) 312 | gbytes = div + 1 if mod else div # No. of bytes per row of glyph 313 | device = self.device 314 | fgcolor = self.bgcolor if invert else self.fgcolor 315 | bgcolor = self.fgcolor if invert else self.bgcolor 316 | drow = s.text_row # Destination row 317 | wcol = s.text_col # Destination column of character start 318 | for srow in range(char_height): # Source row 319 | for scol in range(clip_width): # Source column 320 | # Destination column: add writer column 321 | dcol = wcol + scol 322 | gbyte, gbit = divmod(scol, 8) 323 | if gbit == 0: # Next glyph byte 324 | data = self.glyph[srow * gbytes + gbyte] 325 | pixel = fgcolor if data & (1 << (7 - gbit)) else bgcolor 326 | device.pixel(dcol, drow, pixel) 327 | drow += 1 328 | if drow >= self.screenheight or drow < 0: 329 | break 330 | s.text_col += char_width 331 | self.cpos += 1 332 | 333 | def setcolor(self, fgcolor=None, bgcolor=None): 334 | if fgcolor is None and bgcolor is None: 335 | self.fgcolor = self.def_fgcolor 336 | self.bgcolor = self.def_bgcolor 337 | else: 338 | if fgcolor is not None: 339 | self.fgcolor = fgcolor 340 | if bgcolor is not None: 341 | self.bgcolor = bgcolor 342 | return self.fgcolor, self.bgcolor 343 | -------------------------------------------------------------------------------- /writer/old_versions/writer_minimal.py: -------------------------------------------------------------------------------- 1 | # writer_minimal.py Implements the Writer class. 2 | # Minimal version for SSD1306 3 | # V0.22 Peter Hinch 3rd Jan 2018: Supports new SSD1306 driver. 4 | 5 | # The MIT License (MIT) 6 | # 7 | # Copyright (c) 2016 Peter Hinch 8 | # 9 | # Permission is hereby granted, free of charge, to any person obtaining a copy 10 | # of this software and associated documentation files (the "Software"), to deal 11 | # in the Software without restriction, including without limitation the rights 12 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | # copies of the Software, and to permit persons to whom the Software is 14 | # furnished to do so, subject to the following conditions: 15 | # 16 | # The above copyright notice and this permission notice shall be included in 17 | # all copies or substantial portions of the Software. 18 | # 19 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 25 | # THE SOFTWARE. 26 | 27 | # A Writer supports rendering text to a Display instance in a given font. 28 | # Multiple Writer instances may be created, each rendering a font to the 29 | # same Display object. 30 | 31 | import framebuf 32 | 33 | class Writer(): 34 | text_row = 0 # attributes common to all Writer instances 35 | text_col = 0 36 | row_clip = False # Clip or scroll when screen full 37 | col_clip = False # Clip or new line when row is full 38 | 39 | @classmethod 40 | def set_textpos(cls, row, col): 41 | cls.text_row = row 42 | cls.text_col = col 43 | 44 | @classmethod 45 | def set_clip(cls, row_clip, col_clip): 46 | cls.row_clip = row_clip 47 | cls.col_clip = col_clip 48 | 49 | def __init__(self, device, font, verbose=True): 50 | self.device = device 51 | self.font = font 52 | # Allow to work with any font mapping 53 | if font.hmap(): 54 | self.map = framebuf.MONO_HMSB if font.reverse() else framebuf.MONO_HLSB 55 | else: 56 | raise ValueError('Font must be horizontally mapped.') 57 | if verbose: 58 | print('Orientation: {} Reversal: {}'.format('horiz' if font.hmap() else 'vert', font.reverse())) 59 | self.screenwidth = device.width # In pixels 60 | self.screenheight = device.height 61 | 62 | def _newline(self): 63 | height = self.font.height() 64 | Writer.text_row += height 65 | Writer.text_col = 0 66 | margin = self.screenheight - (Writer.text_row + height) 67 | if margin < 0: 68 | if not Writer.row_clip: 69 | self.device.scroll(0, margin) 70 | Writer.text_row += margin 71 | 72 | def printstring(self, string): 73 | for char in string: 74 | self._printchar(char) 75 | 76 | # Method using blitting. Efficient rendering for monochrome displays. 77 | # Tested on SSD1306. Invert is for black-on-white rendering. 78 | def _printchar(self, char, invert=False): 79 | if char == '\n': 80 | self._newline() 81 | return 82 | glyph, char_height, char_width = self.font.get_ch(char) 83 | if Writer.text_row + char_height > self.screenheight: 84 | if Writer.row_clip: 85 | return 86 | self._newline() 87 | if Writer.text_col + char_width > self.screenwidth: 88 | if Writer.col_clip: 89 | return 90 | else: 91 | self._newline() 92 | buf = bytearray(glyph) 93 | if invert: 94 | for i, v in enumerate(buf): 95 | buf[i] = 0xFF & ~ v 96 | fbc = framebuf.FrameBuffer(buf, char_width, char_height, self.map) 97 | self.device.blit(fbc, Writer.text_col, Writer.text_row) 98 | Writer.text_col += char_width 99 | -------------------------------------------------------------------------------- /writer/ssd1306_setup.py: -------------------------------------------------------------------------------- 1 | # ssd1306_setup.py Demo pogram for rendering arbitrary fonts to an SSD1306 OLED display. 2 | # Device initialisation 3 | 4 | # The MIT License (MIT) 5 | # 6 | # Copyright (c) 2016 Peter Hinch 7 | # 8 | # Permission is hereby granted, free of charge, to any person obtaining a copy 9 | # of this software and associated documentation files (the "Software"), to deal 10 | # in the Software without restriction, including without limitation the rights 11 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | # copies of the Software, and to permit persons to whom the Software is 13 | # furnished to do so, subject to the following conditions: 14 | # 15 | # The above copyright notice and this permission notice shall be included in 16 | # all copies or substantial portions of the Software. 17 | # 18 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 24 | # THE SOFTWARE. 25 | 26 | 27 | # https://learn.adafruit.com/monochrome-oled-breakouts/wiring-128x32-spi-oled-display 28 | # https://www.proto-pic.co.uk/monochrome-128x32-oled-graphic-display.html 29 | 30 | # V0.3 12th Aug 2018 31 | 32 | import machine 33 | from ssd1306 import SSD1306_SPI, SSD1306_I2C 34 | 35 | WIDTH = const(128) 36 | HEIGHT = const(64) 37 | 38 | def setup(use_spi=False, soft=True): 39 | if use_spi: 40 | # Pyb SSD 41 | # 3v3 Vin 42 | # Gnd Gnd 43 | # X1 DC 44 | # X2 CS 45 | # X3 Rst 46 | # X6 CLK 47 | # X8 DATA 48 | pdc = machine.Pin('X1', machine.Pin.OUT_PP) 49 | pcs = machine.Pin('X2', machine.Pin.OUT_PP) 50 | prst = machine.Pin('X3', machine.Pin.OUT_PP) 51 | if soft: 52 | spi = machine.SPI(sck=machine.Pin('X6'), mosi=machine.Pin('X8'), miso=machine.Pin('X7')) 53 | else: 54 | spi = machine.SPI(1) 55 | ssd = SSD1306_SPI(WIDTH, HEIGHT, spi, pdc, prst, pcs) 56 | else: # I2C 57 | # Pyb SSD 58 | # 3v3 Vin 59 | # Gnd Gnd 60 | # Y9 CLK 61 | # Y10 DATA 62 | if soft: 63 | pscl = machine.Pin('Y9', machine.Pin.OPEN_DRAIN) 64 | psda = machine.Pin('Y10', machine.Pin.OPEN_DRAIN) 65 | i2c = machine.I2C(scl=pscl, sda=psda) 66 | else: 67 | i2c = machine.I2C(2) 68 | ssd = SSD1306_I2C(WIDTH, HEIGHT, i2c) 69 | return ssd 70 | -------------------------------------------------------------------------------- /writer/writer.py: -------------------------------------------------------------------------------- 1 | # writer.py Implements the Writer class. 2 | # Handles colour, word wrap and tab stops 3 | 4 | # V0.5.2 May 2025 Fix bug whereby glyph clipping might be attempted. 5 | # V0.5.1 Dec 2022 Support 4-bit color display drivers. 6 | # V0.5.0 Sep 2021 Color now requires firmware >= 1.17. 7 | # V0.4.3 Aug 2021 Support for fast blit to color displays (PR7682). 8 | # V0.4.0 Jan 2021 Improved handling of word wrap and line clip. Upside-down 9 | # rendering no longer supported: delegate to device driver. 10 | # V0.3.5 Sept 2020 Fast rendering option for color displays 11 | 12 | # Released under the MIT License (MIT). See LICENSE. 13 | # Copyright (c) 2019-2021 Peter Hinch 14 | 15 | # A Writer supports rendering text to a Display instance in a given font. 16 | # Multiple Writer instances may be created, each rendering a font to the 17 | # same Display object. 18 | 19 | # Timings were run on a pyboard D SF6W comparing slow and fast rendering 20 | # and averaging over multiple characters. Proportional fonts were used. 21 | # 20 pixel high font, timings were 5.44ms/467μs, gain 11.7 (freesans20). 22 | # 10 pixel high font, timings were 1.76ms/396μs, gain 4.36 (arial10). 23 | 24 | 25 | import framebuf 26 | from uctypes import bytearray_at, addressof 27 | 28 | __version__ = (0, 5, 2) 29 | 30 | 31 | class DisplayState: 32 | def __init__(self): 33 | self.text_row = 0 34 | self.text_col = 0 35 | 36 | 37 | def _get_id(device): 38 | if not isinstance(device, framebuf.FrameBuffer): 39 | raise ValueError("Device must be derived from FrameBuffer.") 40 | return id(device) 41 | 42 | 43 | # Basic Writer class for monochrome displays 44 | class Writer: 45 | 46 | state = {} # Holds a display state for each device 47 | 48 | @staticmethod 49 | def set_textpos(device, row=None, col=None): 50 | devid = _get_id(device) 51 | if devid not in Writer.state: 52 | Writer.state[devid] = DisplayState() 53 | s = Writer.state[devid] # Current state 54 | if row is not None: 55 | if row < 0 or row >= device.height: 56 | raise ValueError("row is out of range") 57 | s.text_row = row 58 | if col is not None: 59 | if col < 0 or col >= device.width: 60 | raise ValueError("col is out of range") 61 | s.text_col = col 62 | return s.text_row, s.text_col 63 | 64 | def __init__(self, device, font, verbose=True): 65 | self.devid = _get_id(device) 66 | self.device = device 67 | if self.devid not in Writer.state: 68 | Writer.state[self.devid] = DisplayState() 69 | self.font = font 70 | if font.height() >= device.height or font.max_width() >= device.width: 71 | raise ValueError("Font too large for screen") 72 | # Allow to work with reverse or normal font mapping 73 | if font.hmap(): 74 | self.map = framebuf.MONO_HMSB if font.reverse() else framebuf.MONO_HLSB 75 | else: 76 | raise ValueError("Font must be horizontally mapped.") 77 | if verbose: 78 | fstr = "Orientation: Horizontal. Reversal: {}. Width: {}. Height: {}." 79 | print(fstr.format(font.reverse(), device.width, device.height)) 80 | print( 81 | "Start row = {} col = {}".format( 82 | self._getstate().text_row, self._getstate().text_col 83 | ) 84 | ) 85 | self.screenwidth = device.width # In pixels 86 | self.screenheight = device.height 87 | self.bgcolor = 0 # Monochrome background and foreground colors 88 | self.fgcolor = 1 89 | self.row_clip = False # Clip or scroll when screen fullt 90 | self.col_clip = False # Clip or new line when row is full 91 | self.wrap = True # Word wrap 92 | self.cpos = 0 93 | self.tab = 4 94 | 95 | self.glyph = None # Current char 96 | self.char_height = 0 97 | self.char_width = 0 98 | 99 | def _getstate(self): 100 | return Writer.state[self.devid] 101 | 102 | def _newline(self): 103 | s = self._getstate() 104 | height = self.font.height() 105 | s.text_row += height 106 | s.text_col = 0 107 | margin = self.screenheight - (s.text_row + height) 108 | y = self.screenheight + margin 109 | if margin < 0: 110 | if not self.row_clip: 111 | self.device.scroll(0, margin) 112 | self.device.fill_rect(0, y, self.screenwidth, abs(margin), self.bgcolor) 113 | s.text_row += margin 114 | 115 | def set_clip(self, row_clip=None, col_clip=None, wrap=None): 116 | if row_clip is not None: 117 | self.row_clip = row_clip 118 | if col_clip is not None: 119 | self.col_clip = col_clip 120 | if wrap is not None: 121 | self.wrap = wrap 122 | return self.row_clip, self.col_clip, self.wrap 123 | 124 | @property 125 | def height(self): # Property for consistency with device 126 | return self.font.height() 127 | 128 | def printstring(self, string, invert=False): 129 | # word wrapping. Assumes words separated by single space. 130 | q = string.split("\n") 131 | last = len(q) - 1 132 | for n, s in enumerate(q): 133 | if s: 134 | self._printline(s, invert) 135 | if n != last: 136 | self._printchar("\n") 137 | 138 | def _printline(self, string, invert): 139 | rstr = None 140 | if self.wrap and self.stringlen(string, True): # Length > self.screenwidth 141 | pos = 0 142 | lstr = string[:] 143 | while self.stringlen(lstr, True): # Length > self.screenwidth 144 | pos = lstr.rfind(" ") 145 | lstr = lstr[:pos].rstrip() 146 | if pos > 0: 147 | rstr = string[pos + 1 :] 148 | string = lstr 149 | 150 | for char in string: 151 | self._printchar(char, invert) 152 | if rstr is not None: 153 | self._printchar("\n") 154 | self._printline(rstr, invert) # Recurse 155 | 156 | def stringlen(self, string, oh=False): 157 | if not len(string): 158 | return 0 159 | sc = self._getstate().text_col # Start column 160 | wd = self.screenwidth 161 | l = 0 162 | for char in string[:-1]: 163 | _, _, char_width = self.font.get_ch(char) 164 | l += char_width 165 | if oh and l + sc > wd: 166 | return True # All done. Save time. 167 | char = string[-1] 168 | _, _, char_width = self.font.get_ch(char) 169 | if oh and l + sc + char_width > wd: 170 | l += self._truelen(char) # Last char might have blank cols on RHS 171 | else: 172 | l += char_width # Public method. Return same value as old code. 173 | return l + sc > wd if oh else l 174 | 175 | # Return the printable width of a glyph less any blank columns on RHS 176 | def _truelen(self, char): 177 | glyph, ht, wd = self.font.get_ch(char) 178 | div, mod = divmod(wd, 8) 179 | gbytes = div + 1 if mod else div # No. of bytes per row of glyph 180 | mc = 0 # Max non-blank column 181 | data = glyph[(wd - 1) // 8] # Last byte of row 0 182 | for row in range(ht): # Glyph row 183 | for col in range(wd - 1, -1, -1): # Glyph column 184 | gbyte, gbit = divmod(col, 8) 185 | if gbit == 0: # Next glyph byte 186 | data = glyph[row * gbytes + gbyte] 187 | if col <= mc: 188 | break 189 | if data & (1 << (7 - gbit)): # Pixel is lit (1) 190 | mc = col # Eventually gives rightmost lit pixel 191 | break 192 | if mc + 1 == wd: 193 | break # All done: no trailing space 194 | # print('Truelen', char, wd, mc + 1) # TEST 195 | return mc + 1 196 | 197 | def _get_char(self, char, recurse): 198 | if not recurse: # Handle tabs 199 | if char == "\n": 200 | self.cpos = 0 201 | elif char == "\t": 202 | nspaces = self.tab - (self.cpos % self.tab) 203 | if nspaces == 0: 204 | nspaces = self.tab 205 | while nspaces: 206 | nspaces -= 1 207 | self._printchar(" ", recurse=True) 208 | self.glyph = None # All done 209 | return 210 | 211 | self.glyph = None # Assume all done 212 | if char == "\n": 213 | self._newline() 214 | return 215 | glyph, char_height, char_width = self.font.get_ch(char) 216 | s = self._getstate() 217 | if s.text_row + char_height > self.screenheight: 218 | if self.row_clip: 219 | return 220 | self._newline() 221 | if s.text_col + char_width - self.screenwidth > 0: # Glyph would overhang 222 | if self.col_clip or self.wrap: 223 | return # Can't clip a glyph: discard 224 | else: 225 | self._newline() 226 | self.glyph = glyph 227 | self.char_height = char_height 228 | self.char_width = char_width 229 | 230 | # Method using blitting. Efficient rendering for monochrome displays. 231 | # Tested on SSD1306. Invert is for black-on-white rendering. 232 | def _printchar(self, char, invert=False, recurse=False): 233 | s = self._getstate() 234 | self._get_char(char, recurse) 235 | if self.glyph is None: 236 | return # All done 237 | buf = bytearray(self.glyph) 238 | if invert: 239 | for i, v in enumerate(buf): 240 | buf[i] = 0xFF & ~v 241 | fbc = framebuf.FrameBuffer(buf, self.char_width, self.char_height, self.map) 242 | self.device.blit(fbc, s.text_col, s.text_row) 243 | s.text_col += self.char_width 244 | self.cpos += 1 245 | 246 | def tabsize(self, value=None): 247 | if value is not None: 248 | self.tab = value 249 | return self.tab 250 | 251 | def setcolor(self, *_): 252 | return self.fgcolor, self.bgcolor 253 | 254 | 255 | # Writer for colour displays. 256 | class CWriter(Writer): 257 | @staticmethod 258 | def create_color(ssd, idx, r, g, b): 259 | c = ssd.rgb(r, g, b) 260 | if not hasattr(ssd, "lut"): 261 | return c 262 | if not 0 <= idx <= 15: 263 | raise ValueError("Color nos must be 0..15") 264 | x = idx << 1 265 | ssd.lut[x] = c & 0xFF 266 | ssd.lut[x + 1] = c >> 8 267 | return idx 268 | 269 | def __init__(self, device, font, fgcolor=None, bgcolor=None, verbose=True): 270 | if not hasattr(device, "palette"): 271 | raise OSError("Incompatible device driver.") 272 | 273 | super().__init__(device, font, verbose) 274 | if bgcolor is not None: # Assume monochrome. 275 | self.bgcolor = bgcolor 276 | if fgcolor is not None: 277 | self.fgcolor = fgcolor 278 | self.def_bgcolor = self.bgcolor 279 | self.def_fgcolor = self.fgcolor 280 | 281 | def _printchar(self, char, invert=False, recurse=False): 282 | s = self._getstate() 283 | self._get_char(char, recurse) 284 | if self.glyph is None: 285 | return # All done 286 | buf = bytearray_at(addressof(self.glyph), len(self.glyph)) 287 | fbc = framebuf.FrameBuffer(buf, self.char_width, self.char_height, self.map) 288 | palette = self.device.palette 289 | palette.bg(self.fgcolor if invert else self.bgcolor) 290 | palette.fg(self.bgcolor if invert else self.fgcolor) 291 | self.device.blit(fbc, s.text_col, s.text_row, -1, palette) 292 | s.text_col += self.char_width 293 | self.cpos += 1 294 | 295 | def setcolor(self, fgcolor=None, bgcolor=None): 296 | if fgcolor is None and bgcolor is None: 297 | self.fgcolor = self.def_fgcolor 298 | self.bgcolor = self.def_bgcolor 299 | else: 300 | if fgcolor is not None: 301 | self.fgcolor = fgcolor 302 | if bgcolor is not None: 303 | self.bgcolor = bgcolor 304 | return self.fgcolor, self.bgcolor 305 | -------------------------------------------------------------------------------- /writer/writer_demo.py: -------------------------------------------------------------------------------- 1 | # writer_demo.py Demo pogram for rendering arbitrary fonts to an SSD1306 OLED display. 2 | # Illustrates a minimal example. Requires ssd1306_setup.py which contains 3 | # wiring details. 4 | 5 | # The MIT License (MIT) 6 | # 7 | # Copyright (c) 2018 Peter Hinch 8 | # 9 | # Permission is hereby granted, free of charge, to any person obtaining a copy 10 | # of this software and associated documentation files (the "Software"), to deal 11 | # in the Software without restriction, including without limitation the rights 12 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | # copies of the Software, and to permit persons to whom the Software is 14 | # furnished to do so, subject to the following conditions: 15 | # 16 | # The above copyright notice and this permission notice shall be included in 17 | # all copies or substantial portions of the Software. 18 | # 19 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 25 | # THE SOFTWARE. 26 | 27 | 28 | # https://learn.adafruit.com/monochrome-oled-breakouts/wiring-128x32-spi-oled-display 29 | # https://www.proto-pic.co.uk/monochrome-128x32-oled-graphic-display.html 30 | 31 | # V0.3 13th Aug 2018 32 | 33 | import machine 34 | from ssd1306_setup import WIDTH, HEIGHT, setup 35 | from writer import Writer 36 | 37 | # Font 38 | import freesans20 39 | 40 | def test(use_spi=False): 41 | ssd = setup(use_spi) # Create a display instance 42 | rhs = WIDTH -1 43 | ssd.line(rhs - 20, 0, rhs, 20, 1) 44 | square_side = 10 45 | ssd.fill_rect(rhs - square_side, 0, square_side, square_side, 1) 46 | 47 | wri = Writer(ssd, freesans20) 48 | Writer.set_textpos(ssd, 0, 0) # verbose = False to suppress console output 49 | wri.printstring('Sunday\n') 50 | wri.printstring('12 Aug 2018\n') 51 | wri.printstring('10.30am') 52 | ssd.show() 53 | 54 | print('Test assumes a 128*64 (w*h) display. Edit WIDTH and HEIGHT in ssd1306_setup.py for others.') 55 | print('Device pinouts are comments in ssd1306_setup.py.') 56 | print('Issue:') 57 | print('writer_demo.test() for an I2C connected device.') 58 | print('writer_demo.test(True) for an SPI connected device.') 59 | -------------------------------------------------------------------------------- /writer/writer_gui.py: -------------------------------------------------------------------------------- 1 | # writer_gui.py Displayable objects based on the Writer and CWriter classes 2 | # V0.3 Peter Hinch 26th Aug 2018 3 | 4 | # The MIT License (MIT) 5 | # 6 | # Copyright (c) 2018 Peter Hinch 7 | # 8 | # Permission is hereby granted, free of charge, to any person obtaining a copy 9 | # of this software and associated documentation files (the "Software"), to deal 10 | # in the Software without restriction, including without limitation the rights 11 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | # copies of the Software, and to permit persons to whom the Software is 13 | # furnished to do so, subject to the following conditions: 14 | # 15 | # The above copyright notice and this permission notice shall be included in 16 | # all copies or substantial portions of the Software. 17 | # 18 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 24 | # THE SOFTWARE. 25 | 26 | # Base class for a displayable object. Subclasses must implement .show() and .value() 27 | # Has position, colors and border definition. 28 | # border: False no border None use bgcolor, int: treat as color 29 | 30 | from writer import Writer 31 | import framebuf 32 | 33 | def _circle(dev, x0, y0, r, color): # Single pixel circle 34 | x = -r 35 | y = 0 36 | err = 2 -2*r 37 | while x <= 0: 38 | dev.pixel(x0 -x, y0 +y, color) 39 | dev.pixel(x0 +x, y0 +y, color) 40 | dev.pixel(x0 +x, y0 -y, color) 41 | dev.pixel(x0 -x, y0 -y, color) 42 | e2 = err 43 | if (e2 <= y): 44 | y += 1 45 | err += y*2 +1 46 | if (-x == y and e2 <= x): 47 | e2 = 0 48 | if (e2 > x): 49 | x += 1 50 | err += x*2 +1 51 | 52 | def circle(dev, x0, y0, r, color, width =1): # Draw circle 53 | x0, y0, r = int(x0), int(y0), int(r) 54 | for r in range(r, r -width, -1): 55 | dev._circle(x0, y0, r, color) 56 | 57 | def fillcircle(dev, x0, y0, r, color): # Draw filled circle 58 | x0, y0, r = int(x0), int(y0), int(r) 59 | x = -r 60 | y = 0 61 | err = 2 -2*r 62 | while x <= 0: 63 | dev.line(x0 -x, y0 -y, x0 -x, y0 +y, color) 64 | dev.line(x0 +x, y0 -y, x0 +x, y0 +y, color) 65 | e2 = err 66 | if (e2 <= y): 67 | y +=1 68 | err += y*2 +1 69 | if (-x == y and e2 <= x): 70 | e2 = 0 71 | if (e2 > x): 72 | x += 1 73 | err += x*2 +1 74 | 75 | 76 | class DObject(): 77 | def __init__(self, writer, row, col, height, width, fgcolor, bgcolor, bordercolor): 78 | writer.set_clip(True, True, False) # Disable scrolling text 79 | self.writer = writer 80 | device = writer.device 81 | self.device = device 82 | if row < 0: 83 | row = 0 84 | self.warning() 85 | elif row + height >= device.height: 86 | row = device.height - height - 1 87 | self.warning() 88 | if col < 0: 89 | col = 0 90 | self.warning() 91 | elif col + width >= device.width: 92 | row = device.width - width - 1 93 | self.warning() 94 | self.row = row 95 | self.col = col 96 | self.width = width 97 | self.height = height 98 | self._value = None # Type depends on context but None means don't display. 99 | # Current colors 100 | if fgcolor is None: 101 | fgcolor = writer.fgcolor 102 | if bgcolor is None: 103 | bgcolor = writer.bgcolor 104 | if bordercolor is None: 105 | bordercolor = fgcolor 106 | self.fgcolor = fgcolor 107 | self.bgcolor = bgcolor 108 | # bordercolor is False if no border is to be drawn 109 | self.bdcolor = bordercolor 110 | # Default colors allow restoration after dynamic change 111 | self.def_fgcolor = fgcolor 112 | self.def_bgcolor = bgcolor 113 | self.def_bdcolor = bordercolor 114 | # has_border is True if a border was drawn 115 | self.has_border = False 116 | 117 | def warning(self): 118 | print('Warning: attempt to create {} outside screen dimensions.'.format(self.__class__.__name__)) 119 | 120 | # Draw a border if .bdcolor specifies a color. If False, erase an existing border 121 | def show(self): 122 | wri = self.writer 123 | dev = wri.device 124 | if isinstance(self.bdcolor, bool): # No border 125 | if self.has_border: # Border exists: erase it 126 | dev.rect(self.col - 2, self.row - 2, self.width + 4, self.height + 4, self.bgcolor) 127 | self.has_border = False 128 | elif self.bdcolor: # Border is required 129 | dev.rect(self.col - 2, self.row - 2, self.width + 4, self.height + 4, self.bdcolor) 130 | self.has_border = True 131 | 132 | def value(self, v = None): 133 | if v is not None: 134 | self._value = v 135 | return self._value 136 | 137 | # text: str display string int save width 138 | class Label(DObject): 139 | def __init__(self, writer, row, col, text, invert=False, fgcolor=None, bgcolor=None, bordercolor=False): 140 | # Determine width of object 141 | if isinstance(text, int): 142 | width = text 143 | text = None 144 | else: 145 | width = writer.stringlen(text) 146 | height = writer.height 147 | super().__init__(writer, row, col, height, width, fgcolor, bgcolor, bordercolor) 148 | if text is not None: 149 | self.value(text, invert) 150 | 151 | def value(self, text=None, invert=False, fgcolor=None, bgcolor=None, bordercolor=None): 152 | txt = super().value(text) 153 | # Redraw even if no text supplied: colors may have changed. 154 | self.invert = invert 155 | self.fgcolor = self.def_fgcolor if fgcolor is None else fgcolor 156 | self.bgcolor = self.def_bgcolor if bgcolor is None else bgcolor 157 | if bordercolor is False: 158 | self.def_bdcolor = False 159 | self.bdcolor = self.def_bdcolor if bordercolor is None else bordercolor 160 | self.show() 161 | return txt 162 | 163 | def show(self): 164 | txt = super().value() 165 | if txt is None: # No content to draw. Future use. 166 | return 167 | super().show() # Draw or erase border 168 | wri = self.writer 169 | dev = self.device 170 | wri.setcolor(self.fgcolor, self.bgcolor) 171 | dev.fill_rect(self.col, self.row, self.width, wri.height, wri.bgcolor) # Blank text field 172 | Writer.set_textpos(dev, self.row, self.col) 173 | wri.setcolor(self.fgcolor, self.bgcolor) 174 | wri.printstring(txt, self.invert) 175 | wri.setcolor() # Restore defaults 176 | 177 | class Meter(DObject): 178 | def __init__(self, writer, row, col, *, height=50, width=10, 179 | fgcolor=None, bgcolor=None, pointercolor=None, bordercolor=None, 180 | divisions=5, legends=None, value=None): 181 | super().__init__(writer, row, col, height, width, fgcolor, bgcolor, bordercolor) 182 | self.divisions = divisions 183 | self.legends = legends 184 | self.pointercolor = pointercolor if pointercolor is not None else self.fgcolor 185 | self.value(value) 186 | 187 | def value(self, n=None): 188 | if n is None: 189 | return super().value() 190 | n = super().value(min(1, max(0, n))) 191 | self.show() 192 | return n 193 | 194 | def show(self): 195 | super().show() # Draw or erase border 196 | val = super().value() 197 | wri = self.writer 198 | dev = self.device 199 | width = self.width 200 | height = self.height 201 | legends = self.legends 202 | x0 = self.col 203 | x1 = self.col + width 204 | y0 = self.row 205 | y1 = self.row + height 206 | dev.fill_rect(self.col, self.row, width, height, self.bgcolor) # Blank field 207 | if self.divisions > 0: 208 | dy = height / (self.divisions) # Tick marks 209 | for tick in range(self.divisions + 1): 210 | ypos = int(y0 + dy * tick) 211 | dev.hline(x0 + 2, ypos, x1 - x0 - 4, self.fgcolor) 212 | 213 | if legends is not None: # Legends 214 | dy = 0 if len(legends) <= 1 else height / (len(legends) -1) 215 | yl = y1 - wri.height / 2 # Start at bottom 216 | for legend in legends: 217 | Label(wri, int(yl), x1 + 4, legend) 218 | yl -= dy 219 | 220 | y = int(y1 - val * height) # y position of slider 221 | dev.hline(x0, y, width, self.pointercolor) # Draw pointer 222 | 223 | 224 | class LED(DObject): 225 | def __init__(self, writer, row, col, *, height=15, 226 | fgcolor=None, bgcolor=None, bordercolor=None, legend=None): 227 | super().__init__(writer, row, col, height, height, fgcolor, bgcolor, bordercolor) 228 | self.legend = legend 229 | self.radius = self.height // 2 230 | 231 | def color(self, c): 232 | self.fgcolor = c 233 | self.show() 234 | 235 | def show(self): 236 | super().show() 237 | wri = self.writer 238 | dev = self.device 239 | r = self.radius 240 | fillcircle(dev, self.col + r, self.row + r, r, self.fgcolor) 241 | if self.legend is not None: 242 | Label(wri, self.row + self.height - wri.height, self.col + self.width + 1, self.legend) 243 | -------------------------------------------------------------------------------- /writer/writer_tests.py: -------------------------------------------------------------------------------- 1 | # ssd1306_test.py Demo program for rendering arbitrary fonts to an SSD1306 OLED display. 2 | 3 | # The MIT License (MIT) 4 | # 5 | # Copyright (c) 2018 Peter Hinch 6 | # 7 | # Permission is hereby granted, free of charge, to any person obtaining a copy 8 | # of this software and associated documentation files (the "Software"), to deal 9 | # in the Software without restriction, including without limitation the rights 10 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | # copies of the Software, and to permit persons to whom the Software is 12 | # furnished to do so, subject to the following conditions: 13 | # 14 | # The above copyright notice and this permission notice shall be included in 15 | # all copies or substantial portions of the Software. 16 | # 17 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 23 | # THE SOFTWARE. 24 | 25 | 26 | # https://learn.adafruit.com/monochrome-oled-breakouts/wiring-128x32-spi-oled-display 27 | # https://www.proto-pic.co.uk/monochrome-128x32-oled-graphic-display.html 28 | 29 | # V0.3 12th Aug 2018 30 | 31 | import machine 32 | import utime 33 | import uos 34 | from ssd1306_setup import WIDTH, HEIGHT, setup 35 | from writer import Writer, CWriter 36 | from writer_gui import Label, Meter 37 | 38 | # Fonts 39 | import freesans20 40 | import courier20 as fixed 41 | import font6 as small 42 | import arial10 43 | 44 | def inverse(use_spi=False, soft=True): 45 | ssd = setup(use_spi, soft) # Create a display instance 46 | rhs = WIDTH -1 47 | ssd.line(rhs - 20, 0, rhs, 20, 1) 48 | square_side = 10 49 | ssd.fill_rect(rhs - square_side, 0, square_side, square_side, 1) 50 | 51 | Writer.set_textpos(ssd, 0, 0) # In case previous tests have altered it 52 | wri = Writer(ssd, freesans20, verbose=False) 53 | wri.set_clip(False, False, False) # Char wrap 54 | wri.printstring('Sunday\n') 55 | wri.printstring('12 Aug 2018\n') 56 | wri.printstring('10.30am', True) # Inverse text 57 | ssd.show() 58 | 59 | def scroll(use_spi=False, soft=True): 60 | ssd = setup(use_spi, soft) # Create a display instance 61 | rhs = WIDTH -1 62 | ssd.line(rhs - 20, 0, rhs, 20, 1) 63 | square_side = 10 64 | ssd.fill_rect(rhs - square_side, 0, square_side, square_side, 1) 65 | 66 | Writer.set_textpos(ssd, 0, 0) # In case previous tests have altered it 67 | wri = Writer(ssd, freesans20, verbose=False) 68 | wri.set_clip(False, False, False) # Char wrap 69 | wri.printstring('Sunday\n') 70 | wri.printstring('12 Aug 2018\n') 71 | wri.printstring('10.30am') 72 | for x in range(5): 73 | ssd.show() 74 | utime.sleep(2) 75 | wri.printstring('\nCount = {:2d}'.format(x)) 76 | ssd.show() 77 | utime.sleep(2) 78 | wri.printstring('\nDone.') 79 | ssd.show() 80 | 81 | def usd_scroll(use_spi=False, soft=True): 82 | ssd = setup(use_spi, soft) # Create a display instance 83 | # Only CWriter can do usd 84 | CWriter.invert_display(ssd) 85 | CWriter.set_textpos(ssd, 0, 0) 86 | wri = CWriter(ssd, freesans20, verbose=False) 87 | wri.set_clip(False, False, False) # Char wrap 88 | 89 | wri.printstring('Sunday\n') 90 | wri.printstring('12 Aug 2018\n') 91 | wri.printstring('10.30am') 92 | for x in range(5): 93 | ssd.show() 94 | utime.sleep(2) 95 | wri.printstring('\nCount = {:2d}'.format(x)) 96 | ssd.show() 97 | utime.sleep(2) 98 | wri.printstring('\nDone.') 99 | ssd.show() 100 | CWriter.invert_display(ssd, False) # For subsequent tests 101 | 102 | def usd(use_spi=False, soft=True): 103 | ssd = setup(use_spi, soft) # Create a display instance 104 | # Only CWriter can do usd 105 | CWriter.invert_display(ssd) 106 | CWriter.set_textpos(ssd, 0, 0) 107 | wri = CWriter(ssd, freesans20, verbose=False) 108 | wri.set_clip(False, False, False) # Char wrap 109 | wri.printstring('Sunday\n') 110 | wri.printstring('12 Aug 2018\n') 111 | wri.printstring('10.30am') 112 | ssd.show() 113 | CWriter.invert_display(ssd, False) # For subsequent tests 114 | 115 | def rjust(use_spi=False, soft=True): 116 | ssd = setup(use_spi, soft) # Create a display instance 117 | Writer.set_textpos(ssd, 0, 0) # Previous tests may have altered it 118 | wri = Writer(ssd, freesans20, verbose=False) 119 | wri.set_clip(False, False, False) # Char wrap 120 | 121 | my_str = 'Sunday\n' 122 | l = wri.stringlen(my_str) 123 | Writer.set_textpos(ssd, col = WIDTH - l) 124 | wri.printstring(my_str) 125 | 126 | my_str = '12 Aug 2018\n' 127 | l = wri.stringlen(my_str) 128 | Writer.set_textpos(ssd, col = WIDTH - l) 129 | wri.printstring(my_str) 130 | 131 | my_str = '10.30am' 132 | l = wri.stringlen(my_str) 133 | Writer.set_textpos(ssd, col = WIDTH - l) 134 | wri.printstring(my_str) 135 | ssd.show() 136 | 137 | def fonts(use_spi=False, soft=True): 138 | ssd = setup(use_spi, soft) # Create a display instance 139 | Writer.set_textpos(ssd, 0, 0) # In case previous tests have altered it 140 | wri = Writer(ssd, freesans20, verbose=False) 141 | wri.set_clip(False, False, False) # Char wrap 142 | wri_f = Writer(ssd, small, verbose=False) 143 | wri_f.set_clip(False, False, False) # Char wrap 144 | wri_f.printstring('Sunday\n') 145 | wri.printstring('12 Aug 2018\n') 146 | wri.printstring('10.30am') 147 | ssd.show() 148 | 149 | def tabs(use_spi=False, soft=True): 150 | ssd = setup(use_spi, soft) # Create a display instance 151 | Writer.set_textpos(ssd, 0, 0) # In case previous tests have altered it 152 | wri = Writer(ssd, fixed, verbose=False) 153 | wri.set_clip(False, False, False) # Char wrap 154 | wri.printstring('1\t2\n') 155 | wri.printstring('111\t22\n') 156 | wri.printstring('1111\t1') 157 | ssd.show() 158 | 159 | def usd_tabs(use_spi=False, soft=True): 160 | ssd = setup(use_spi, soft) # Create a display instance 161 | CWriter.invert_display(ssd) 162 | CWriter.set_textpos(ssd, 0, 0) 163 | wri = CWriter(ssd, fixed, verbose=False) 164 | wri.set_clip(False, False, False) # Char wrap 165 | wri.printstring('1\t2\n') 166 | wri.printstring('111\t22\n') 167 | wri.printstring('1111\t1') 168 | ssd.show() 169 | CWriter.invert_display(ssd, False) # For subsequent tests 170 | 171 | def wrap(use_spi=False, soft=True): 172 | ssd = setup(use_spi, soft) # Create a display instance 173 | Writer.set_textpos(ssd, 0, 0) # In case previous tests have altered it 174 | wri = Writer(ssd, freesans20, verbose=False) 175 | wri.set_clip(False, False, True) # Word wrap 176 | wri.printstring('the quick brown fox jumps over') 177 | ssd.show() 178 | 179 | def dual(use_spi=False, soft=True): 180 | ssd0 = setup(False, soft) # I2C display 181 | ssd1 = setup(True, False) # SPI instance 182 | Writer.set_textpos(ssd0, 0, 0) # In case previous tests have altered it 183 | wri0 = Writer(ssd0, small, verbose=False) 184 | wri0.set_clip(False, False, False) 185 | Writer.set_textpos(ssd1, 0, 0) # In case previous tests have altered it 186 | wri1 = Writer(ssd1, small, verbose=False) 187 | wri1.set_clip(False, False, False) 188 | 189 | nfields = [] 190 | dy = small.height() + 6 191 | col = 15 192 | for n, wri in enumerate((wri0, wri1)): 193 | nfields.append([]) 194 | y = 2 195 | for txt in ('X:', 'Y:', 'Z:'): 196 | Label(wri, y, 0, txt) 197 | nfields[n].append(Label(wri, y, col, wri.stringlen('99.99'), True)) 198 | y += dy 199 | 200 | for _ in range(10): 201 | for n, wri in enumerate((wri0, wri1)): 202 | for field in nfields[n]: 203 | value = int.from_bytes(uos.urandom(3),'little')/167772 204 | field.value('{:5.2f}'.format(value)) 205 | wri.device.show() 206 | utime.sleep(1) 207 | for wri in (wri0, wri1): 208 | Label(wri, 0, 64, ' DONE ', True) 209 | wri.device.show() 210 | 211 | 212 | tstr = '''Test assumes a 128*64 (w*h) display. Edit WIDTH and HEIGHT in ssd1306_setup.py for others. 213 | Device pinouts are comments in ssd1306_setup.py. 214 | All tests take two boolean args: 215 | use_spi = False. Set True for SPI connected device 216 | soft=True set False to use hardware I2C/SPI. Hardware I2C option currently fails with official SSD1306 driver. 217 | 218 | Available tests: 219 | inverse() Show black on white text. 220 | scroll() Illustrate scrolling 221 | usd() Upside-down display. 222 | usd_scroll() Upside-down scroll test. 223 | rjust() Right justification. 224 | fonts() Two fonts. 225 | tabs() Tab stops. 226 | usd_tabs() Upside-down tabs. 227 | wrap() Word wrapping 228 | dual() Test two displays on one host.''' 229 | 230 | print(tstr) 231 | --------------------------------------------------------------------------------