Flutter Engine
The Flutter Engine
gen_test_font.py
Go to the documentation of this file.
1#!/usr/bin/env python3
2#
3# Copyright 2013 The Flutter Authors. All rights reserved.
4# Use of this source code is governed by a BSD-style license that can be
5# found in the LICENSE file.
6
7import sys, math
8from operator import itemgetter
9from itertools import groupby
10import unicodedata
11import fontforge
12
13NAME = "FlutterTest"
14# Turn off auto-hinting and enable manual hinting. FreeType skips auto-hinting
15# if the font's family name is in a hard-coded "tricky" font list.
16TRICKY_NAME = "MingLiU"
17EM = 1024
18DESCENT = -EM // 4
19ASCENT = EM + DESCENT
20# -143 and 20 are the underline location and width Ahem uses.
21UPOS = -143 * 1000 // EM
22UWIDTH = 20 * 1000 // EM
23
24### Font Metadata and Metrics
25
26font = fontforge.font()
27font.familyname = TRICKY_NAME
28font.fullname = NAME
29font.fontname = NAME
30
31# This sets the relevant fields in the os2 table and hhea table.
32font.ascent = ASCENT
33font.descent = -DESCENT
34font.upos = UPOS
35font.uwidth = UWIDTH
36font.hhea_linegap = 0
37font.os2_typolinegap = 0
38
39font.horizontalBaseline = (
40 ("hang", "ideo", "romn"),
41 (
42 ("latn", "romn", (ASCENT, DESCENT, 0), ()),
43 ("grek", "romn", (ASCENT, DESCENT, 0), ()),
44 ("hani", "ideo", (ASCENT, DESCENT, 0), ()),
45 ),
46)
47
48### TrueType Hinting
49
50# Hints are ignored on macOS.
51#
52# These hints only **vertically** adjust the outlines, for better vertical
53# alignment in golden tests. They don't affect the font or the glyphs' public
54# metrics available to the framework, so they typically don't affect non-golden
55# tests.
56#
57# The hinting goals are:
58#
59# 1. Aligning the key points on glyph outlines between glyphs, when different
60# types of glyphs are placed side by side. E.g., for a given point size, "p"
61# and "É" should never overlap vertically, and "p" and "x" should be
62# bottom-aligned.
63#
64# 2. Aligning the top and the bottom of the "x" glyph with the background. With
65# point size = 14, since the em square's y-extent is 3.5 px (256 * 14 / 1024)
66# below the baseline and 10.5 px above the baseline, the glyph's CBOX will be
67# "rounded out" (3.5 -> 4, 10.5 -> 11). So "x" is going to be misaligned with
68# the background by +0.5 px when rasterized without proper grid-fitting.
69
70# Allocate space in cvt.
71font.cvt = [0]
72# gcd is used to avoid overflowing, this works for the current ASCENT and EM value.
73gcd = math.gcd(ASCENT, EM)
74# The control value program is for computing the y-offset (in pixels) to move
75# the embox's top edge to grid. The end result will be stored to CVT entry 0.
76# CVT[0] = (pointSize * ASCENT / EM) - ceil(pointSize * ASCENT / EM)
77prep_program = f"""
78 RTG
79 PUSHW_1
80 0
81 MPS
82 PUSHW_1
83 {(ASCENT << 6) // gcd}
84 MUL
85 PUSHW_1
86 {EM // gcd}
87 DIV
88 DUP
89 CEILING
90 SUB
91 WCVTP
92"""
93font.setTableData("prep", fontforge.parseTTInstrs(prep_program))
94
95
96def glyph_program(glyph):
97 # Shift Zone 1 by CVT[0]. In FreeType SHZ actually shifts the zone zp2
98 # points to, instead of top of the stack. That's probably a bug.
99 instructions = """
100 SVTCA[0]
101 PUSHB_4
102 0
103 0
104 0
105 0
106 SZPS
107 MIRP[0000]
108 SRP2
109 PUSHB_3
110 1
111 1
112 1
113 SZP2
114 SHZ[0]
115 SZPS
116 """
117
118 # Round To Grid every on-curve point, but ignore those who are on the ASCENT
119 # or DESCENT line. This step keeps "p" (ascent flushed) and "É" (descent
120 # flushed)'s y extents from overlapping each other.
121 for index, point in enumerate([p for contour in glyph.foreground for p in contour]):
122 if point.y not in [ASCENT, DESCENT]:
123 instructions += f"""
124 PUSHB_1
125 {index}
126 MDAP[1]
127 """
128 return fontforge.parseTTInstrs(instructions)
129
130
131### Creating Glyphs Outlines
132
133
134def square_glyph(glyph):
135 pen = glyph.glyphPen()
136 # Counter Clockwise
137 pen.moveTo((0, DESCENT))
138 pen.lineTo((0, ASCENT))
139 pen.lineTo((EM, ASCENT))
140 pen.lineTo((EM, DESCENT))
141 pen.closePath()
142 glyph.ttinstrs = glyph_program(glyph)
143
144
146 pen = glyph.glyphPen()
147 pen.moveTo((0, DESCENT))
148 pen.lineTo((0, 0))
149 pen.lineTo((EM, 0))
150 pen.lineTo((EM, DESCENT))
151 pen.closePath()
152 glyph.ttinstrs = glyph_program(glyph)
153
154
156 pen = glyph.glyphPen()
157 pen.moveTo((0, 0))
158 pen.lineTo((0, ASCENT))
159 pen.lineTo((EM, ASCENT))
160 pen.lineTo((EM, 0))
161 pen.closePath()
162 glyph.ttinstrs = glyph_program(glyph)
163
164
165def not_def_glyph(glyph):
166 pen = glyph.glyphPen()
167 # Counter Clockwise for the outer contour.
168 pen.moveTo((EM // 8, 0))
169 pen.lineTo((EM // 8, ASCENT))
170 pen.lineTo((EM - EM // 8, ASCENT))
171 pen.lineTo((EM - EM // 8, 0))
172 pen.closePath()
173 # Clockwise, inner contour.
174 pen.moveTo((EM // 4, EM // 8))
175 pen.lineTo((EM - EM // 4, EM // 8))
176 pen.lineTo((EM - EM // 4, ASCENT - EM // 8))
177 pen.lineTo((EM // 4, ASCENT - EM // 8))
178 pen.closePath()
179 glyph.ttinstrs = glyph_program(glyph)
180
181
182def unicode_range(fromUnicode, throughUnicode):
183 return range(fromUnicode, throughUnicode + 1)
184
185
186square_codepoints = [
187 codepoint for l in [
188 unicode_range(0x21, 0x26),
189 unicode_range(0x28, 0x6F),
190 unicode_range(0x71, 0x7E),
191 unicode_range(0xA1, 0xC8),
192 unicode_range(0xCA, 0xFF),
193 [0x131],
194 unicode_range(0x152, 0x153),
195 [0x178, 0x192],
196 unicode_range(0x2C6, 0x2C7),
197 [0x2C9],
198 unicode_range(0x2D8, 0x2DD),
199 [0x394, 0x3A5, 0x3A7, 0x3A9, 0x3BC, 0x3C0],
200 unicode_range(0x2013, 0x2014),
201 unicode_range(0x2018, 0x201A),
202 unicode_range(0x201C, 0x201E),
203 unicode_range(0x2020, 0x2022),
204 [0x2026, 0x2030],
205 unicode_range(0x2039, 0x203A),
206 [0x2044, 0x2122, 0x2126, 0x2202, 0x2206, 0x220F],
207 unicode_range(0x2211, 0x2212),
208 unicode_range(0x2219, 0x221A),
209 [0x221E, 0x222B, 0x2248, 0x2260],
210 unicode_range(0x2264, 0x2265),
211 [
212 0x22F2, 0x25CA, 0x3007, 0x4E00, 0x4E03, 0x4E09, 0x4E5D, 0x4E8C, 0x4E94, 0x516B, 0x516D,
213 0x5341, 0x56D7, 0x56DB, 0x571F, 0x6728, 0x6C34, 0x706B, 0x91D1
214 ],
215 unicode_range(0xF000, 0xF002),
216 ] for codepoint in l
217] + [0x70] + [ord(c) for c in "中文测试文本是否正确"]
218
219no_path_codepoints = [
220 #(codepoint, advance %)
221 (0x0020, 1),
222 (0x00A0, 1),
223 (0x2003, 1),
224 (0x3000, 1),
225 (0x2002, 1 / 2),
226 (0x2004, 1 / 3),
227 (0x2005, 1 / 4),
228 (0x2006, 1 / 6),
229 (0x2009, 1 / 5),
230 (0x200A, 1 / 10),
231 (0xFEFF, 0),
232 (0x200B, 0),
233 (0x200C, 0),
234 (0x200D, 0),
235]
236
237
238def create_glyph(name, contour):
239 glyph = font.createChar(-1, name)
240 contour(glyph)
241 glyph.width = EM
242 return glyph
243
244
245if square_codepoints:
246 create_glyph("Square", square_glyph).altuni = square_codepoints
247create_glyph("Ascent Flushed", ascent_flushed_glyph).unicode = 0x70
248create_glyph("Descent Flushed", descent_flushed_glyph).unicode = 0xC9
249create_glyph(".notdef", not_def_glyph).unicode = -1
250
251
252def create_no_path_glyph(codepoint, advance_percentage):
253 name = "Zero Advance" if advance_percentage == 0 else (
254 "Full Advance" if advance_percentage == 1 else f"1/{(int)(1/advance_percentage)} Advance"
255 )
256 no_path_glyph = font.createChar(codepoint, name)
257 no_path_glyph.width = (int)(EM * advance_percentage)
258 return no_path_glyph
259
260
261for (codepoint, advance_percentage) in no_path_codepoints:
262 if (codepoint in square_codepoints):
263 raise ValueError(f"{hex(codepoint)} is occupied.")
264 create_no_path_glyph(codepoint, advance_percentage)
265
266font.generate(sys.argv[1] if len(sys.argv) >= 2 else "test_font.ttf")
267
268### Printing Glyph Map Stats
269
270scripts = set()
271for glyph in font.glyphs():
272 if glyph.unicode >= 0:
273 scripts.add(fontforge.scriptFromUnicode(glyph.unicode))
274 for codepoint, _, _ in glyph.altuni or []:
275 scripts.add(fontforge.scriptFromUnicode(codepoint))
276script_list = list(scripts)
277script_list.sort()
278
279print(f"| \ Script <br />Glyph | {' | '.join(script_list)} |")
280print(" | :--- " + " | :----: " * len(script_list) + "|")
281
282for glyph in font.glyphs():
283 if glyph.unicode < 0 and not glyph.altuni:
284 continue
285 glyph_mapping = {}
286 if glyph.unicode >= 0:
287 glyph_mapping[fontforge.scriptFromUnicode(glyph.unicode)] = [glyph.unicode]
288 for codepoint, _, _ in glyph.altuni or []:
289 script = fontforge.scriptFromUnicode(codepoint)
290 if script in glyph_mapping:
291 glyph_mapping[script].append(codepoint)
292 else:
293 glyph_mapping[script] = [codepoint]
294
295 codepoints_by_script = [glyph_mapping.get(script, []) for script in script_list]
296
298 if not codepoints:
299 return ""
300 codepoints.sort()
301 codepoint_ranges = [
302 list(map(itemgetter(1), group))
303 for key, group in groupby(enumerate(codepoints), lambda x: x[0] - x[1])
304 ]
305 characters = [chr(c) for c in codepoints]
306
307 def map_char(c):
308 if c == "`":
309 return "`` ` ``"
310 if c == "|":
311 return "`\\|`"
312 if c.isprintable() and (not c.isspace()):
313 return f"`{c}`"
314 return "`<" + unicodedata.name(c, hex(ord(c))) + ">`"
315
316 full_list = " ".join([map_char(c) for c in characters])
317 return "**codepoint(s):** " + ", ".join([
318 f"{hex(r[0])}-{hex(r[-1])}" if len(r) > 1 else hex(r[0]) for r in codepoint_ranges
319 ]) + "<br />" + "**character(s):** " + full_list
320
321 print(
322 f"| {glyph.glyphname} | {' | '.join([describe_codepoint_range(l) for l in codepoints_by_script])} |"
323 )
static void append(char **dst, size_t *count, const char *src, size_t n)
Definition: editor.cpp:211
DEF_SWITCHES_START aot vmservice shared library Name of the *so containing AOT compiled Dart assets for launching the service isolate vm snapshot The VM snapshot data that will be memory mapped as read only SnapshotAssetPath must be present isolate snapshot The isolate snapshot data that will be memory mapped as read only SnapshotAssetPath must be present cache dir Path to the cache directory This is different from the persistent_cache_path in embedder which is used for Skia shader cache icu native lib Path to the library file that exports the ICU data vm service The hostname IP address on which the Dart VM Service should be served If not set
Definition: switches.h:76
def describe_codepoint_range(codepoints)
def create_no_path_glyph(codepoint, advance_percentage)
def unicode_range(fromUnicode, throughUnicode)
def not_def_glyph(glyph)
def glyph_program(glyph)
def descent_flushed_glyph(glyph)
def create_glyph(name, contour)
def ascent_flushed_glyph(glyph)
def square_glyph(glyph)
Creating Glyphs Outlines.
def print(*args, **kwargs)
Definition: run_tests.py:49
SI auto map(std::index_sequence< I... >, Fn &&fn, const Args &... args) -> skvx::Vec< sizeof...(I), decltype(fn(args[0]...))>
Definition: SkVx.h:680
static SkString join(const CommandLineFlags::StringArray &)
Definition: skpbench.cpp:741