From 793c805f4f8da7db2234ff896d1d403eefe01f22 Mon Sep 17 00:00:00 2001 From: Peter Hinch Date: Thu, 6 Sep 2018 11:38:52 +0100 Subject: [PATCH] Add --iterate argument. --- FONT_TO_PY.md | 6 ++++++ font_to_py.py | 27 +++++++++++++++++++++------ writer/DRIVERS.md | 25 ++++++++++++++++++++++++- 3 files changed, 51 insertions(+), 7 deletions(-) diff --git a/FONT_TO_PY.md b/FONT_TO_PY.md index 6570d5b..94aeede 100644 --- a/FONT_TO_PY.md +++ b/FONT_TO_PY.md @@ -56,6 +56,7 @@ Example usage to produce a file `myfont.py` with height of 23 pixels: * -l or --largest Ordinal value of largest character to be stored. Default 126. * -e or --errchar Ordinal value of character to be rendered if an attempt is made to display an out-of-range character. Default 63 (ASCII "?"). + * -i or --iterate Specialist use. See below. * -c or --charset Option to restrict the characters in the font to a specific set. See below. * -k or --charset_file Obtain the character set from a file. Typical use is @@ -85,6 +86,11 @@ value > 127) from ttf files is unreliable. If the expected results are not achieved, use an otf font. However I have successfully created the Cyrillic font from a `ttf`. Perhaps not all fonts are created equal... +The `-i` or `--iterate` argument. For specialist applications. Specifying this +causes a generator function `glyphs` to be included in the Python font file. A +generator instantiated with this will yield `bitmap`, `height`, and `width` for +every glyph in the font. + ### Output The specified height is a target. The algorithm gets as close to the target diff --git a/font_to_py.py b/font_to_py.py index c751474..76a4351 100755 --- a/font_to_py.py +++ b/font_to_py.py @@ -261,6 +261,7 @@ class Font(dict): self.minchar = minchar self.maxchar = maxchar self.monospaced = monospaced + self.defchar = defchar # .def_charset is requested charset or '' if -c was not specified self.def_charset = charset # .charset has all defined characters with '' for those in range but undefined. @@ -369,7 +370,7 @@ class Font(dict): STR01 = """# Code generated by font-to-py.py. # Font: {}{} -version = '0.25' +version = '0.26' """ STR02 = """_mvfont = memoryview(_font) @@ -390,7 +391,7 @@ def write_func(stream, name, arg): # filename, size, minchar=32, maxchar=126, monospaced=False, defchar=ord('?'): -def write_font(op_path, font_path, height, monospaced, hmap, reverse, minchar, maxchar, defchar, charset): +def write_font(op_path, font_path, height, monospaced, hmap, reverse, minchar, maxchar, defchar, charset, iterate): try: fnt = Font(font_path, height, minchar, maxchar, monospaced, defchar, charset) except freetype.ft_errors.FT_Exception: @@ -398,17 +399,24 @@ def write_font(op_path, font_path, height, monospaced, hmap, reverse, minchar, m return False try: with open(op_path, 'w') as stream: - write_data(stream, fnt, font_path, hmap, reverse) + write_data(stream, fnt, font_path, hmap, reverse, iterate) except OSError: print("Can't open", op_path, 'for writing') return False return True +STR03 = ''' +def glyphs(): + for c in """{}""": + yield c, get_ch(c) -def write_data(stream, fnt, font_path, hmap, reverse): +''' + +def write_data(stream, fnt, font_path, hmap, reverse, iterate): height = fnt.height # Actual height, not target height minchar = fnt.minchar maxchar = fnt.maxchar + defchar = fnt.defchar charset = fnt.def_charset st = '' if charset == '' else ' Char set: {}'.format(charset) stream.write(STR01.format(os.path.split(font_path)[1], st)) @@ -420,6 +428,8 @@ def write_data(stream, fnt, font_path, hmap, reverse): write_func(stream, 'monospaced', fnt.monospaced) write_func(stream, 'min_ch', minchar) write_func(stream, 'max_ch', maxchar) + if iterate: + stream.write(STR03.format(''.join(fnt.pop_charset))) data, index = fnt.build_arrays(hmap, reverse) bw_font = ByteWriter(stream, '_font') bw_font.odata(data) @@ -427,7 +437,7 @@ def write_data(stream, fnt, font_path, hmap, reverse): bw_index = ByteWriter(stream, '_index') bw_index.odata(index) bw_index.eot() - stream.write(STR02.format(minchar, maxchar, minchar, minchar, height)) + stream.write(STR02.format(minchar, maxchar, defchar, minchar, height)) # BINARY OUTPUT # hmap reverse magic bytes @@ -493,6 +503,8 @@ if __name__ == "__main__": help='Fixed width (monospaced) font') parser.add_argument('-b', '--binary', action='store_true', help='Produce binary (random access) font file.') + parser.add_argument('-i', '--iterate', action='store_true', + help='Include generator function to iterate over character set.') parser.add_argument('-s', '--smallest', type = int, @@ -566,10 +578,13 @@ if __name__ == "__main__": sys.exit(1) else: cset = args.charset + cs = {c for c in cset if c.isprintable()} - {args.errchar} # dedupe and remove default char + cs = sorted(list(cs)) + cset = ''.join(cs) # Back to string print('Writing Python font file.') if not write_font(args.outfile, args.infile, args.height, args.fixed, args.xmap, args.reverse, args.smallest, args.largest, - args.errchar, cset): + args.errchar, cset, args.iterate): sys.exit(1) print(args.outfile, 'written successfully.') diff --git a/writer/DRIVERS.md b/writer/DRIVERS.md index 8cd661c..806a8b4 100644 --- a/writer/DRIVERS.md +++ b/writer/DRIVERS.md @@ -140,7 +140,8 @@ def get_ch(ch): return mvfont[offset + 2, next_offset], height, width ``` -`height` and `width` are specified in bits (pixels). +`height` and `width` are specified in bits (pixels). See Appendix 1 for extra +code in fonts created with the `--iterate` arg. In the case of monospaced fonts the `max_width` function returns the width of every character. For variable pitch fonts it returns the width of the widest @@ -235,3 +236,25 @@ Fonts created with the `font_to_py` utility have been extensively tested with each of the mapping options. They are used with drivers for SSD1306 OLEDs, SSD1963 LCD displays, the official LCD160CR and the Digital Artists 2.7 inch e-paper display. + +# Appendix 1. The -i --iterate argument + +This specialist arg causes extra code to be included in the font file, to +provide for iterating over all the glyphs in the file. The following sample of +the extra code assumes a font comprising '0123456789:' + +```python +def glyphs(): + for c in """0123456789:""": + yield c, get_ch(c) +``` + +Typical usage under CPython 3 (for a font `cyrillic.py`) is + +```python +import cyrillic +res = [] +for glyph in cyrillic.glyphs(): + res.append(list(glyph)) # Each element is [char, glyph, height, width] +``` +