Font that can be used for validating baseline alignments. sajidanwar.com/misc/baseline-diagnostic-font/
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

Generate CSS variables for baseline positioning

+168 -71
+42
dist/baseline-diagnostic-font.css
··· 9 9 src: url('./BaselineDiagnostic.ttf') format('opentype'); 10 10 } 11 11 12 + :root { 13 + /** 14 + * Variables representing the positions of the given baselines/metrics from the top 15 + * of the em-box as a percentage of the em-height. The top of the em-box (ascent) has 16 + * a position of 0, and the bottom of the em-box (descent) has a position of 1. 17 + */ 18 + --baseline-diagnostic-ascent: calc(1 - (800 + 200) / 1000); 19 + --baseline-diagnostic-ideographic-over: calc(1 - (750 + 200) / 1000); 20 + --baseline-diagnostic-hanging: calc(1 - (650 + 200) / 1000); 21 + --baseline-diagnostic-cap-height: calc(1 - (550 + 200) / 1000); 22 + --baseline-diagnostic-math: calc(1 - (450 + 200) / 1000); 23 + --baseline-diagnostic-central: calc(1 - (350 + 200) / 1000); 24 + --baseline-diagnostic-em-middle: calc(1 - (300 + 200) / 1000); 25 + --baseline-diagnostic-x-height: calc(1 - (250 + 200) / 1000); 26 + --baseline-diagnostic-x-middle: calc(1 - (150 + 200) / 1000); 27 + --baseline-diagnostic-alphabetic: calc(1 - (50 + 200) / 1000); 28 + --baseline-diagnostic-zero: calc(1 - (0 + 200) / 1000); 29 + --baseline-diagnostic-ideographic-under: calc(1 - (-50 + 200) / 1000); 30 + --baseline-diagnostic-descent: calc(1 - (-200 + 200) / 1000); 31 + } 32 + 12 33 @font-face { 13 34 /** 14 35 * Same as the "BaselineDiagnostic" font, but uses the common alphabetic baseline ··· 17 38 font-family: "BaselineDiagnosticAlphabeticZero"; 18 39 src: url('./BaselineDiagnosticAlphabeticZero.ttf') format('opentype'); 19 40 } 41 + 42 + :root { 43 + /** 44 + * Variables representing the positions of the given baselines/metrics from the top 45 + * of the em-box as a percentage of the em-height. The top of the em-box (ascent) has 46 + * a position of 0, and the bottom of the em-box (descent) has a position of 1. 47 + */ 48 + --baseline-diagnostic-alphabetic-zero-ascent: calc(1 - (800 + 200) / 1000); 49 + --baseline-diagnostic-alphabetic-zero-ideographic-over: calc(1 - (750 + 200) / 1000); 50 + --baseline-diagnostic-alphabetic-zero-hanging: calc(1 - (650 + 200) / 1000); 51 + --baseline-diagnostic-alphabetic-zero-cap-height: calc(1 - (550 + 200) / 1000); 52 + --baseline-diagnostic-alphabetic-zero-math: calc(1 - (450 + 200) / 1000); 53 + --baseline-diagnostic-alphabetic-zero-central: calc(1 - (350 + 200) / 1000); 54 + --baseline-diagnostic-alphabetic-zero-em-middle: calc(1 - (300 + 200) / 1000); 55 + --baseline-diagnostic-alphabetic-zero-x-height: calc(1 - (250 + 200) / 1000); 56 + --baseline-diagnostic-alphabetic-zero-x-middle: calc(1 - (125 + 200) / 1000); 57 + --baseline-diagnostic-alphabetic-zero-alphabetic: calc(1 - (0 + 200) / 1000); 58 + --baseline-diagnostic-alphabetic-zero-zero: calc(1 - (0 + 200) / 1000); 59 + --baseline-diagnostic-alphabetic-zero-ideographic-under: calc(1 - (-50 + 200) / 1000); 60 + --baseline-diagnostic-alphabetic-zero-descent: calc(1 - (-200 + 200) / 1000); 61 + }
+12 -5
font.py
··· 26 26 27 27 @dataclass 28 28 class FontBaseline: 29 - type: Union[Literal["BASE", "OS/2", "hhea"], None] 29 + id: str 30 + position: int 31 + table: Union[Literal["BASE", "OS/2", "hhea"], None] 30 32 name: str 31 33 label: str 32 - position: int 33 34 style: FontBaselineStyle 35 + 36 + @dataclass 37 + class Font: 38 + name: str 39 + description: str 40 + baselines: List[FontBaseline] 34 41 35 42 36 43 @dataclass ··· 196 203 "X": diag_glyph_pen.glyph() 197 204 }) 198 205 199 - os2_values = dict((base.name, base.position) for base in baselines if base.type == "OS/2") 200 - hhea_values = dict((base.name, base.position) for base in baselines if base.type == "hhea") 206 + os2_values = dict((base.name, base.position) for base in baselines if base.table == "OS/2") 207 + hhea_values = dict((base.name, base.position) for base in baselines if base.table == "hhea") 201 208 202 209 style_name = "Regular" 203 210 fb.setupPost() ··· 216 223 fb.setupHead(unitsPerEm=EM_SIZE) 217 224 218 225 font = fb.font 219 - bases = list(sorted(filter(lambda base: base.type == "BASE", baselines), key=lambda base: base.name)) 226 + bases = list(sorted(filter(lambda base: base.table == "BASE", baselines), key=lambda base: base.name)) 220 227 base_names = list(base.name for base in bases) 221 228 222 229 base_table = otTables.BASE()
+114 -66
main.py
··· 1 - from font import FontBaseline, FontBaselineStyle, build_baselines_font 2 - from textwrap import dedent 1 + import re 2 + from font import Font, FontBaseline, FontBaselineStyle, build_baselines_font 3 + from textwrap import dedent, indent 4 + from typing import Dict, List 3 5 4 6 def main(): 5 - build_baselines_font( 6 - "BaselineDiagnostic", 7 - "dist/BaselineDiagnostic.ttf", 8 - [ 9 - FontBaseline("OS/2", "sTypoAscender", None, 800, None), 10 - FontBaseline("hhea", "ascent", None, 800, None), 11 - FontBaseline("BASE", "idtp", "IDEOGRAPHIC-OVER", 750, FontBaselineStyle.SOLID), 12 - FontBaseline("BASE", "hang", "HANGING", 650, FontBaselineStyle.SOLID), 13 - FontBaseline("OS/2", "sCapHeight", "CAP-HEIGHT", 550, FontBaselineStyle.SOLID), 14 - FontBaseline("BASE", "math", "MATH", 450, FontBaselineStyle.SOLID), 15 - FontBaseline(None, None, "CENTRAL", 350, FontBaselineStyle.SOLID), 16 - FontBaseline(None, None, None, 300, FontBaselineStyle.DASHED), 17 - FontBaseline("OS/2", "sxHeight", "X-HEIGHT", 250, FontBaselineStyle.SOLID), 18 - FontBaseline(None, None, "X-MIDDLE", 150, FontBaselineStyle.SOLID), 19 - FontBaseline("BASE", "romn", "ALPHABETIC", 50, FontBaselineStyle.SOLID), 20 - FontBaseline(None, None, None, 0, FontBaselineStyle.DASHED), 21 - FontBaseline("BASE", "ideo", "IDEOGRAPHIC-UNDER", -50, FontBaselineStyle.SOLID), 22 - FontBaseline("OS/2", "sTypoDescender", None, -200, None), 23 - FontBaseline("hhea", "descent", None, -200, None), 7 + fonts = [] 8 + fonts.append(Font( 9 + name="BaselineDiagnostic", 10 + description=(dedent("""\ 11 + Font that can be used for validating baseline alignments. Given the embedded 12 + text in the font, this should be used with very large font sizes. There are 13 + two glyphs in the font.""")), 14 + baselines=[ 15 + FontBaseline("ascent", 800, "OS/2", "sTypoAscender", None, None), 16 + FontBaseline("ascent", 800, "hhea", "ascent", None, None), 17 + FontBaseline("ideographic-over", 750, "BASE", "idtp", "IDEOGRAPHIC-OVER", FontBaselineStyle.SOLID), 18 + FontBaseline("hanging", 650, "BASE", "hang", "HANGING", FontBaselineStyle.SOLID), 19 + FontBaseline("cap-height", 550, "OS/2", "sCapHeight", "CAP-HEIGHT", FontBaselineStyle.SOLID), 20 + FontBaseline("math", 450, "BASE", "math", "MATH", FontBaselineStyle.SOLID), 21 + FontBaseline("central", 350, None, None, "CENTRAL", FontBaselineStyle.SOLID), 22 + FontBaseline("em-middle", 300, None, None, None, FontBaselineStyle.DASHED), 23 + FontBaseline("x-height", 250, "OS/2", "sxHeight", "X-HEIGHT", FontBaselineStyle.SOLID), 24 + FontBaseline("x-middle", 150, None, None, "X-MIDDLE", FontBaselineStyle.SOLID), 25 + FontBaseline("alphabetic", 50, "BASE", "romn", "ALPHABETIC", FontBaselineStyle.SOLID), 26 + FontBaseline("zero", 0, None, None, None, FontBaselineStyle.DASHED), 27 + FontBaseline("ideographic-under", -50, "BASE", "ideo", "IDEOGRAPHIC-UNDER", FontBaselineStyle.SOLID), 28 + FontBaseline("descent", -200, "OS/2", "sTypoDescender", None, None), 29 + FontBaseline("descent", -200, "hhea", "descent", None, None), 24 30 ] 25 - ) 26 - 27 - # Same as the "BaselineDiagnostic" font, but uses the common alphabetic 28 - # baseline of 0. This also results in the x-middle baseline being at 125. 29 - 30 - build_baselines_font( 31 - "BaselineDiagnosticAlphabeticZero", 32 - "dist/BaselineDiagnosticAlphabeticZero.ttf", 33 - [ 34 - FontBaseline("OS/2", "sTypoAscender", None, 800, None), 35 - FontBaseline("hhea", "ascent", None, 800, None), 36 - FontBaseline("BASE", "idtp", "IDEOGRAPHIC-OVER", 750, FontBaselineStyle.SOLID), 37 - FontBaseline("BASE", "hang", "HANGING", 650, FontBaselineStyle.SOLID), 38 - FontBaseline("OS/2", "sCapHeight", "CAP-HEIGHT", 550, FontBaselineStyle.SOLID), 39 - FontBaseline("BASE", "math", "MATH", 450, FontBaselineStyle.SOLID), 40 - FontBaseline(None, None, "CENTRAL", 350, FontBaselineStyle.SOLID), 41 - FontBaseline(None, None, None, 300, FontBaselineStyle.DASHED), 42 - FontBaseline("OS/2", "sxHeight", "X-HEIGHT", 250, FontBaselineStyle.SOLID), 43 - FontBaseline(None, None, "X-MIDDLE", 125, FontBaselineStyle.SOLID), 44 - FontBaseline("BASE", "romn", None, 0, FontBaselineStyle.DASHED), 45 - FontBaseline("BASE", "ideo", "IDEOGRAPHIC-UNDER", -50, FontBaselineStyle.SOLID), 46 - FontBaseline("OS/2", "sTypoDescender", None, -200, None), 47 - FontBaseline("hhea", "descent", None, -200, None), 31 + )) 32 + fonts.append(Font( 33 + name="BaselineDiagnosticAlphabeticZero", 34 + description=(dedent("""\ 35 + Same as the "BaselineDiagnostic" font, but uses the common alphabetic baseline 36 + of 0. This also results in the x-middle baseline being at 125.""")), 37 + baselines=[ 38 + FontBaseline("ascent", 800, "OS/2", "sTypoAscender", None, None), 39 + FontBaseline("ascent", 800, "hhea", "ascent", None, None), 40 + FontBaseline("ideographic-over", 750, "BASE", "idtp", "IDEOGRAPHIC-OVER", FontBaselineStyle.SOLID), 41 + FontBaseline("hanging", 650, "BASE", "hang", "HANGING", FontBaselineStyle.SOLID), 42 + FontBaseline("cap-height", 550, "OS/2", "sCapHeight", "CAP-HEIGHT", FontBaselineStyle.SOLID), 43 + FontBaseline("math", 450, "BASE", "math", "MATH", FontBaselineStyle.SOLID), 44 + FontBaseline("central", 350, None, None, "CENTRAL", FontBaselineStyle.SOLID), 45 + FontBaseline("em-middle", 300, None, None, None, FontBaselineStyle.DASHED), 46 + FontBaseline("x-height", 250, "OS/2", "sxHeight", "X-HEIGHT", FontBaselineStyle.SOLID), 47 + FontBaseline("x-middle", 125, None, None, "X-MIDDLE", FontBaselineStyle.SOLID), 48 + FontBaseline("alphabetic", 0, "BASE", "romn", None, FontBaselineStyle.DASHED), 49 + FontBaseline("zero", 0, None, None, None, None), 50 + FontBaseline("ideographic-under", -50, "BASE", "ideo", "IDEOGRAPHIC-UNDER", FontBaselineStyle.SOLID), 51 + FontBaseline("descent", -200, "OS/2", "sTypoDescender", None, None), 52 + FontBaseline("descent", -200, "hhea", "descent", None, None), 48 53 ] 49 - ) 54 + )) 50 55 51 - write_font_stylesheet() 56 + write_font_files(fonts) 57 + write_font_stylesheet(fonts) 52 58 write_font_readme() 53 59 write_font_license() 54 60 55 61 56 - def write_font_stylesheet(): 62 + def write_font_files(fonts: List[Font]): 63 + for font in fonts: 64 + build_baselines_font(font.name, f'dist/{font.name}.ttf', font.baselines) 65 + 66 + 67 + def write_font_stylesheet(fonts: List[Font]): 57 68 out_path = "dist/baseline-diagnostic-font.css" 58 69 with open(out_path, "w") as f: 59 - f.write(dedent(r''' 60 - @font-face { 61 - /** 62 - * Font that can be used for validating baseline alignments. Given the embedded 63 - * text in the font, this should be used with very large font sizes. There are 64 - * two glyphs in the font. 65 - */ 66 - font-family: "BaselineDiagnostic"; 67 - src: url('./BaselineDiagnostic.ttf') format('opentype'); 68 - } 70 + for font in fonts: 71 + template = dedent(''' 72 + @font-face {{ 73 + /** 74 + {description} 75 + */ 76 + font-family: "{name}"; 77 + src: url('./{name}.ttf') format('opentype'); 78 + }} 69 79 70 - @font-face { 71 - /** 72 - * Same as the "BaselineDiagnostic" font, but uses the common alphabetic baseline 73 - * of 0. This also results in the x-middle baseline being at 125. 74 - */ 75 - font-family: "BaselineDiagnosticAlphabeticZero"; 76 - src: url('./BaselineDiagnosticAlphabeticZero.ttf') format('opentype'); 77 - } 78 - ''')) 80 + :root {{ 81 + /** 82 + * Variables representing the positions of the given baselines/metrics from the top 83 + * of the em-box as a percentage of the em-height. The top of the em-box (ascent) has 84 + * a position of 0, and the bottom of the em-box (descent) has a position of 1. 85 + */ 86 + {variables} 87 + }} 88 + ''') 89 + description = indent(font.description, ' * ') 90 + positions: Dict[str, int] = {} 91 + ascent = None 92 + descent = None 93 + 94 + for baseline in font.baselines: 95 + if baseline.id in positions and positions[baseline.id] != baseline.position: 96 + raise ValueError(f"Baseline metric {baseline.id} has different position values") 97 + if baseline.id == 'ascent': 98 + ascent = baseline.position 99 + if baseline.id == 'descent': 100 + descent = -1 * baseline.position 101 + positions[baseline.id] = baseline.position 102 + 103 + if not ascent or not descent: 104 + raise ValueError(f"Required ascent / descent but got {ascent} / {descent}") 105 + 106 + variables = [] 107 + for baseline, position in positions.items(): 108 + variables.append( 109 + "--{font_name}-{baseline}: calc(1 - ({position} + {descent}) / {height});".format( 110 + font_name=dashing(font.name), 111 + baseline=baseline, 112 + position=position, 113 + descent=descent, 114 + height=(ascent + descent), 115 + ) 116 + ) 117 + 118 + f.write(template.format( 119 + name=font.name, 120 + description=description, 121 + variables=indent('\n'.join(variables), ' '), 122 + )) 79 123 print(f"Wrote stylesheet at {out_path}") 80 124 81 125 126 + # TODO: Generate this README and tables programmatically from the fonts. 82 127 def write_font_readme(): 83 128 out_path = "dist/README.md" 84 129 with open(out_path, "w") as f: ··· 253 298 OTHER DEALINGS IN THE FONT SOFTWARE. 254 299 ''')) 255 300 print(f"Wrote OFL 1.1 license at {out_path}") 301 + 302 + def dashing(value: str): 303 + return re.sub(r'(?<!^)(?=[A-Z])', '-', value).lower() 256 304 257 305 if __name__ == "__main__": 258 306 main()