blob: c19e7e0f9eb70c8aaba3fb6bed5590f9c129da32 [file] [log] [blame]
Henrik Brix Andersen9e8c9ca2018-11-08 20:47:39 +01001#!/usr/bin/env python3
2#
3# Copyright (c) 2018 Henrik Brix Andersen <henrik@brixandersen.dk>
4#
5# SPDX-License-Identifier: Apache-2.0
6
7import argparse
Marc Herbert6f98db62019-06-11 15:50:04 -07008import os
Henrik Brix Andersen9e8c9ca2018-11-08 20:47:39 +01009import sys
10
11from PIL import ImageFont
12from PIL import Image
13from PIL import ImageDraw
14
15PRINTABLE_MIN = 32
16PRINTABLE_MAX = 127
17
18def generate_element(image, charcode):
19 """Generate CFB font element for a given character code from an image"""
20 blackwhite = image.convert("1", dither=Image.NONE)
21 pixels = blackwhite.load()
22
23 if args.dump:
24 blackwhite.save("{}_{}.png".format(args.name, charcode))
25
26 if PRINTABLE_MIN <= charcode <= PRINTABLE_MAX:
27 char = " ({:c})".format(charcode)
28 else:
29 char = ""
30
31 args.output.write("""\t/* {:d}{} */\n\t{{\n""".format(charcode, char))
32
33 for col in range(0, args.width):
34 args.output.write("\t\t")
35 for octet in range(0, int(args.height / 8)):
36 value = ""
37 for bit in range(0, 8):
38 row = octet * 8 + bit
39 if pixels[col, row]:
40 value = "0" + value
41 else:
42 value = "1" + value
43 args.output.write("0x{:02x},".format(int(value, 2)))
44 args.output.write("\n")
45 args.output.write("\t},\n")
46
47def extract_font_glyphs():
48 """Extract font glyphs from a TrueType/OpenType font file"""
49 font = ImageFont.truetype(args.input, args.size)
50 for i in range(args.first, args.last + 1):
51 image = Image.new("RGB", (args.width, args.height), (255, 255, 255))
52 draw = ImageDraw.Draw(image)
53 draw.text((0, 0), chr(i), (0, 0, 0), font=font)
54 generate_element(image, i)
55
56def extract_image_glyphs():
57 """Extract font glyphs from an image file"""
58 image = Image.open(args.input)
59
60 x_offset = 0
61 for i in range(args.first, args.last + 1):
62 glyph = image.crop((x_offset, 0, x_offset + args.width, args.height))
63 generate_element(glyph, i)
64 x_offset += args.width
65
66def generate_header():
67 """Generate CFB font header file"""
68 guard = "__CFB_FONT_{:s}_{:d}{:d}_H__".format(args.name.upper(), args.width,
69 args.height)
70
Marc Herbert6f98db62019-06-11 15:50:04 -070071 zephyr_base = os.environ.get('ZEPHYR_BASE', "")
72
73 clean_cmd = [ ]
74 for arg in sys.argv:
75 if arg.startswith("--bindir"):
76 # Drop. Assumes --bindir= was passed with '=' sign.
77 continue
78 if arg.startswith(args.bindir):
79 # +1 to also strip '/' or '\' separator
80 striplen = min(len(args.bindir)+1, len(arg))
81 clean_cmd.append(arg[striplen:])
82 continue
83
84 clean_cmd.append(arg.replace(zephyr_base, '"${ZEPHYR_BASE}"'))
85
Henrik Brix Andersen9e8c9ca2018-11-08 20:47:39 +010086 args.output.write("""/*
87 * This file was automatically generated using the following command:
88 * {cmd}
89 *
90 */
91
92#ifndef {guard}
93#define {guard}
94
95#include <zephyr.h>
96#include <display/cfb.h>
97
98const u8_t cfb_font_{name:s}_{width:d}{height:d}[{elem:d}][{b:.0f}] = {{\n"""
Marc Herbert6f98db62019-06-11 15:50:04 -070099 .format(cmd=" ".join(clean_cmd),
Henrik Brix Andersen9e8c9ca2018-11-08 20:47:39 +0100100 guard=guard,
101 name=args.name,
102 width=args.width,
103 height=args.height,
104 elem=args.last - args.first + 1,
105 b=args.width / 8 * args.height))
106
107 if args.type == "font":
108 extract_font_glyphs()
109 elif args.type == "image":
110 extract_image_glyphs()
111 elif args.input.name.lower().endswith((".otf", ".otc", ".ttf", ".ttc")):
112 extract_font_glyphs()
113 else:
114 extract_image_glyphs()
115
116 args.output.write("""
117}};
118
119FONT_ENTRY_DEFINE({name}_{width}{height},
120 {width},
121 {height},
122 CFB_FONT_MONO_VPACKED,
123 cfb_font_{name}_{width}{height},
124 {first},
125 {last}
126);
127
128#endif /* {guard} */""" .format(name=args.name, width=args.width,
129 height=args.height, first=args.first,
130 last=args.last, guard=guard))
131
132def parse_args():
133 """Parse arguments"""
134 global args
135 parser = argparse.ArgumentParser(
136 description="Character Frame Buffer (CFB) font header file generator",
137 formatter_class=argparse.RawDescriptionHelpFormatter)
138
139 parser.add_argument(
140 "-d", "--dump", action="store_true",
141 help="dump generated CFB font elements as images for preview")
142
143 group = parser.add_argument_group("input arguments")
144 group.add_argument(
145 "-i", "--input", required=True, type=argparse.FileType('rb'), metavar="FILE",
146 help="TrueType/OpenType file or image input file")
147 group.add_argument(
148 "-t", "--type", default="auto", choices=["auto", "font", "image"],
149 help="Input file type (default: %(default)s)")
150
151 group = parser.add_argument_group("font arguments")
152 group.add_argument(
153 "-s", "--size", type=int, default=10, metavar="POINTS",
154 help="TrueType/OpenType font size in points (default: %(default)s)")
155
156 group = parser.add_argument_group("output arguments")
157 group.add_argument(
158 "-o", "--output", type=argparse.FileType('w'), default="-", metavar="FILE",
159 help="CFB font header file (default: stdout)")
160 group.add_argument(
Marc Herbert6f98db62019-06-11 15:50:04 -0700161 "--bindir", type=str, default="",
162 help="CMAKE_BINARY_DIR for pure logging purposes. No trailing slash.")
163 group.add_argument(
Henrik Brix Andersen9e8c9ca2018-11-08 20:47:39 +0100164 "-x", "--width", required=True, type=int,
165 help="width of the CFB font elements in pixels")
166 group.add_argument(
167 "-y", "--height", required=True, type=int, choices=range(8, 128, 8),
168 help="height of the CFB font elements in pixels")
169 group.add_argument(
170 "-n", "--name", default="custom",
171 help="name of the CFB font entry (default: %(default)s)")
172 group.add_argument(
173 "--first", type=int, default=PRINTABLE_MIN, metavar="CHARCODE",
174 help="character code mapped to the first CFB font element (default: %(default)s)")
175 group.add_argument(
176 "--last", type=int, default=PRINTABLE_MAX, metavar="CHARCODE",
177 help="character code mapped to the last CFB font element (default: %(default)s)")
178
179 args = parser.parse_args()
180
181def main():
182 """Parse arguments and generate CFB font header file"""
183 parse_args()
184 generate_header()
185
186if __name__ == "__main__":
187 main()