blob: f24185caab1dcd7fdda92b5ca8df006cbb2ff959 [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
Peter A. Bigot8b4d5292019-07-09 17:09:20 -050016PRINTABLE_MAX = 126
Henrik Brix Andersen9e8c9ca2018-11-08 20:47:39 +010017
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
Peter A. Bigot9a0d9e32019-07-15 10:05:10 -050023 width, height = image.size
Henrik Brix Andersen9e8c9ca2018-11-08 20:47:39 +010024 if args.dump:
25 blackwhite.save("{}_{}.png".format(args.name, charcode))
26
27 if PRINTABLE_MIN <= charcode <= PRINTABLE_MAX:
28 char = " ({:c})".format(charcode)
29 else:
30 char = ""
31
32 args.output.write("""\t/* {:d}{} */\n\t{{\n""".format(charcode, char))
33
Peter A. Bigot9a0d9e32019-07-15 10:05:10 -050034 for col in range(0, width):
Henrik Brix Andersen9e8c9ca2018-11-08 20:47:39 +010035 args.output.write("\t\t")
Peter A. Bigot9a0d9e32019-07-15 10:05:10 -050036 for octet in range(0, int(height / 8)):
Henrik Brix Andersen9e8c9ca2018-11-08 20:47:39 +010037 value = ""
38 for bit in range(0, 8):
39 row = octet * 8 + bit
40 if pixels[col, row]:
41 value = "0" + value
42 else:
43 value = "1" + value
44 args.output.write("0x{:02x},".format(int(value, 2)))
45 args.output.write("\n")
46 args.output.write("\t},\n")
47
48def extract_font_glyphs():
49 """Extract font glyphs from a TrueType/OpenType font file"""
50 font = ImageFont.truetype(args.input, args.size)
Peter A. Bigot9a0d9e32019-07-15 10:05:10 -050051
52 # Figure out the bounding box for the desired glyphs
53 fw_max = 0
54 fh_max = 0
Henrik Brix Andersen9e8c9ca2018-11-08 20:47:39 +010055 for i in range(args.first, args.last + 1):
Peter A. Bigot9a0d9e32019-07-15 10:05:10 -050056 fw, fh = font.getsize(chr(i))
57 if fw > fw_max:
58 fw_max = fw
59 if fh > fh_max:
60 fh_max = fh
61
62 # Round the vtiled length up to pack into bytes.
63 width = fw_max
64 height = 8 * int((fh_max + args.y_offset + 7) / 8)
65
66 # Diagnose inconsistencies with arguments
67 if width != args.width:
68 raise Exception('text width {} mismatch with -x {}'.format(width, args.width))
69 if height != args.height:
70 raise Exception('text height {} mismatch with -y {}'.format(height, args.height))
71
72 for i in range(args.first, args.last + 1):
73 image = Image.new('1', (width, height), 'white')
Henrik Brix Andersen9e8c9ca2018-11-08 20:47:39 +010074 draw = ImageDraw.Draw(image)
Peter A. Bigot9a0d9e32019-07-15 10:05:10 -050075
76 fw, fh = draw.textsize(chr(i), font=font)
77
78 xpos = 0
79 if args.center_x:
80 xpos = (width - fw) / 2 + 1
81 ypos = args.y_offset
82
83 draw.text((xpos, ypos), chr(i), font=font)
Henrik Brix Andersen9e8c9ca2018-11-08 20:47:39 +010084 generate_element(image, i)
85
86def extract_image_glyphs():
87 """Extract font glyphs from an image file"""
88 image = Image.open(args.input)
89
90 x_offset = 0
91 for i in range(args.first, args.last + 1):
92 glyph = image.crop((x_offset, 0, x_offset + args.width, args.height))
93 generate_element(glyph, i)
94 x_offset += args.width
95
96def generate_header():
97 """Generate CFB font header file"""
Henrik Brix Andersen9e8c9ca2018-11-08 20:47:39 +010098
Marc Herbert6f98db62019-06-11 15:50:04 -070099 zephyr_base = os.environ.get('ZEPHYR_BASE', "")
100
Ulf Magnusson0d39a102019-09-06 11:13:19 +0200101 clean_cmd = []
Marc Herbert6f98db62019-06-11 15:50:04 -0700102 for arg in sys.argv:
103 if arg.startswith("--bindir"):
104 # Drop. Assumes --bindir= was passed with '=' sign.
105 continue
Peter A. Bigot98a8e292019-07-09 17:24:46 -0500106 if args.bindir and arg.startswith(args.bindir):
Marc Herbert6f98db62019-06-11 15:50:04 -0700107 # +1 to also strip '/' or '\' separator
108 striplen = min(len(args.bindir)+1, len(arg))
109 clean_cmd.append(arg[striplen:])
110 continue
111
112 clean_cmd.append(arg.replace(zephyr_base, '"${ZEPHYR_BASE}"'))
113
Henrik Brix Andersen9e8c9ca2018-11-08 20:47:39 +0100114 args.output.write("""/*
115 * This file was automatically generated using the following command:
116 * {cmd}
117 *
118 */
119
Henrik Brix Andersen9e8c9ca2018-11-08 20:47:39 +0100120#include <zephyr.h>
121#include <display/cfb.h>
122
Peter A. Bigotf14b19b2019-07-18 06:14:51 -0500123static const u8_t cfb_font_{name:s}_{width:d}{height:d}[{elem:d}][{b:.0f}] = {{\n"""
Marc Herbert6f98db62019-06-11 15:50:04 -0700124 .format(cmd=" ".join(clean_cmd),
Henrik Brix Andersen9e8c9ca2018-11-08 20:47:39 +0100125 name=args.name,
126 width=args.width,
127 height=args.height,
128 elem=args.last - args.first + 1,
129 b=args.width / 8 * args.height))
130
131 if args.type == "font":
132 extract_font_glyphs()
133 elif args.type == "image":
134 extract_image_glyphs()
135 elif args.input.name.lower().endswith((".otf", ".otc", ".ttf", ".ttc")):
136 extract_font_glyphs()
137 else:
138 extract_image_glyphs()
139
140 args.output.write("""
141}};
142
143FONT_ENTRY_DEFINE({name}_{width}{height},
144 {width},
145 {height},
146 CFB_FONT_MONO_VPACKED,
147 cfb_font_{name}_{width}{height},
148 {first},
149 {last}
150);
Peter A. Bigotf14b19b2019-07-18 06:14:51 -0500151""" .format(name=args.name, width=args.width, height=args.height,
152 first=args.first, last=args.last))
Henrik Brix Andersen9e8c9ca2018-11-08 20:47:39 +0100153
154def parse_args():
155 """Parse arguments"""
156 global args
157 parser = argparse.ArgumentParser(
158 description="Character Frame Buffer (CFB) font header file generator",
159 formatter_class=argparse.RawDescriptionHelpFormatter)
160
161 parser.add_argument(
162 "-d", "--dump", action="store_true",
163 help="dump generated CFB font elements as images for preview")
164
165 group = parser.add_argument_group("input arguments")
166 group.add_argument(
167 "-i", "--input", required=True, type=argparse.FileType('rb'), metavar="FILE",
168 help="TrueType/OpenType file or image input file")
169 group.add_argument(
170 "-t", "--type", default="auto", choices=["auto", "font", "image"],
171 help="Input file type (default: %(default)s)")
172
173 group = parser.add_argument_group("font arguments")
174 group.add_argument(
175 "-s", "--size", type=int, default=10, metavar="POINTS",
176 help="TrueType/OpenType font size in points (default: %(default)s)")
177
178 group = parser.add_argument_group("output arguments")
179 group.add_argument(
180 "-o", "--output", type=argparse.FileType('w'), default="-", metavar="FILE",
181 help="CFB font header file (default: stdout)")
182 group.add_argument(
Peter A. Bigot98a8e292019-07-09 17:24:46 -0500183 "--bindir", type=str,
Marc Herbert6f98db62019-06-11 15:50:04 -0700184 help="CMAKE_BINARY_DIR for pure logging purposes. No trailing slash.")
185 group.add_argument(
Henrik Brix Andersen9e8c9ca2018-11-08 20:47:39 +0100186 "-x", "--width", required=True, type=int,
187 help="width of the CFB font elements in pixels")
188 group.add_argument(
189 "-y", "--height", required=True, type=int, choices=range(8, 128, 8),
190 help="height of the CFB font elements in pixels")
191 group.add_argument(
192 "-n", "--name", default="custom",
193 help="name of the CFB font entry (default: %(default)s)")
194 group.add_argument(
195 "--first", type=int, default=PRINTABLE_MIN, metavar="CHARCODE",
196 help="character code mapped to the first CFB font element (default: %(default)s)")
197 group.add_argument(
198 "--last", type=int, default=PRINTABLE_MAX, metavar="CHARCODE",
199 help="character code mapped to the last CFB font element (default: %(default)s)")
Peter A. Bigot9a0d9e32019-07-15 10:05:10 -0500200 group.add_argument(
201 "--center-x", action='store_true',
202 help="center character glyphs horizontally")
203 group.add_argument(
204 "--y-offset", type=int, default=0,
205 help="vertical offset for character glyphs (default: %(default)s)")
Henrik Brix Andersen9e8c9ca2018-11-08 20:47:39 +0100206
207 args = parser.parse_args()
208
209def main():
210 """Parse arguments and generate CFB font header file"""
211 parse_args()
212 generate_header()
213
214if __name__ == "__main__":
215 main()