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.

Add ideographic face baselines, pair layout/label glyphs, em-box glyphs

+252 -101
dist/BaselineDiagnostic.ttf

This is a binary file and will not be displayed.

dist/BaselineDiagnosticAlphabeticZero.ttf

This is a binary file and will not be displayed.

+49 -22
dist/README.md
··· 4 4 ## Overview 5 5 6 6 Font that can be used for validating baseline alignments. Given the embedded 7 - text in the font, this should be used with very large font sizes. There are 8 - two glyphs in the font: 7 + text in the font, this should be used with very large font sizes. 9 8 10 - - `X` (U+0058) which has all baselines drawn 11 - - `.notdef` (for all other characters) which is an empty box 9 + ## Baselines and Metrics 12 10 13 - It has the following baselines: 11 + | Baseline/Metric | Coordinate | BASE Value | OS/2 Value | hhea Value | 12 + |------------------------|------------|------------|----------------|------------| 13 + | ascent | 800 | | sTypoAscender | ascent | 14 + | ideographic-over | 750 | idtp | | | 15 + | hanging | 650 | hang | | | 16 + | ideographic-face-over | 650 | icft | | | 17 + | cap-height | 550 | | sCapHeight | | 18 + | math | 450 | math | | | 19 + | /central/ | 350 | | | | 20 + | /em-middle/ | 300 | | | | 21 + | x-height | 250 | | sxHeight | | 22 + | /x-middle/ | 150 | | | | 23 + | alphabetic | 50 | romn | | | 24 + | ideographic-face-under | 50 | icfb | | | 25 + | /zero/ | 0 | | | | 26 + | ideographic-under | -50 | ideo | | | 27 + | descent | -200 | | sTypoDescender | descent | 14 28 15 - | Baseline/Metric | Coordinate | BASE Value | OS/2 Value | hhea Value | 16 - |-------------------|------------|------------|----------------|------------| 17 - | ascent | 800 | | sTypoAscender | ascent | 18 - | ideographic-over | 750 | idtp | | | 19 - | hanging | 650 | hang | | | 20 - | cap-height | 550 | | sCapHeight | | 21 - | math | 450 | math | | | 22 - | /central/ | 350 | | | | 23 - | /em-middle/ | 300 | | | | 24 - | x-height | 250 | | sxHeight | | 25 - | /x-middle/ | 150 | | | | 26 - | alphabetic | 50 | romn | | | 27 - | /zero/ | | | | | 28 - | ideographic-under | -50 | ideo | | | 29 - | descent | -200 | | sTypoDescender | descent | 30 - 31 - The `BaselineDiagnosticAlphabeticZero` variant is the same as `Baseline`, 29 + The `BaselineDiagnosticAlphabeticZero` variant is the same as `BaselineDiagnostic`, 32 30 except the alphabetic baseline is at the common value of 0. This also 33 31 results in the x-middle baseline being at 125. 32 + 33 + ## Glyphs 34 + 35 + ### Diagnostic glyph 36 + 37 + | Glyph | Codepoint | Description | 38 + |-------|-----------|-------------| 39 + | `X` | U+0058 | All baselines drawn with labels | 40 + 41 + ### Pair glyphs 42 + 43 + Each baseline pair has two variants: a **layout** glyph (opaque filled rectangle 44 + between the two baselines) and a **labeled** glyph (lines with text labels, like `X`). 45 + 46 + | Pair | Layout | Layout codepoint | Labeled | Labeled codepoint | 47 + |-------------------------------|--------|------------------|---------|-------------------| 48 + | X-height + Alphabetic | `x` | U+0078 | `χ` | U+03C7 | 49 + | Cap-height + Alphabetic | `B` | U+0042 | `β` | U+03B2 | 50 + | Ideo em-box (idtp + ideo) | `口` | U+53E3 | `日` | U+65E5 | 51 + | Ideo face (icft + icfb) | `中` | U+4E2D | `田` | U+7530 | 52 + | Hanging + Alphabetic | `अ` | U+0905 | `आ` | U+0906 | 53 + | Math + Alphabetic | `+` | U+002B | `±` | U+00B1 | 54 + 55 + ### Em-box glyphs 56 + 57 + | Variant | Glyph | Codepoint | 58 + |---------|-------|-----------| 59 + | Filled | `█` | U+2588 | 60 + | Outline | `□` | U+25A1 | 34 61 35 62 ## Source and Downloads 36 63 Both the source code and built font files can be found in the [`@sajidanwar.com/baseline-diagnostic-font`][tangled-repo]
+4
dist/baseline-diagnostic-font.css
··· 18 18 --baseline-diagnostic-ascent: calc(1 - (800 + 200) / 1000); 19 19 --baseline-diagnostic-ideographic-over: calc(1 - (750 + 200) / 1000); 20 20 --baseline-diagnostic-hanging: calc(1 - (650 + 200) / 1000); 21 + --baseline-diagnostic-ideographic-face-over: calc(1 - (650 + 200) / 1000); 21 22 --baseline-diagnostic-cap-height: calc(1 - (550 + 200) / 1000); 22 23 --baseline-diagnostic-math: calc(1 - (450 + 200) / 1000); 23 24 --baseline-diagnostic-central: calc(1 - (350 + 200) / 1000); ··· 25 26 --baseline-diagnostic-x-height: calc(1 - (250 + 200) / 1000); 26 27 --baseline-diagnostic-x-middle: calc(1 - (150 + 200) / 1000); 27 28 --baseline-diagnostic-alphabetic: calc(1 - (50 + 200) / 1000); 29 + --baseline-diagnostic-ideographic-face-under: calc(1 - (50 + 200) / 1000); 28 30 --baseline-diagnostic-zero: calc(1 - (0 + 200) / 1000); 29 31 --baseline-diagnostic-ideographic-under: calc(1 - (-50 + 200) / 1000); 30 32 --baseline-diagnostic-descent: calc(1 - (-200 + 200) / 1000); ··· 48 50 --baseline-diagnostic-alphabetic-zero-ascent: calc(1 - (800 + 200) / 1000); 49 51 --baseline-diagnostic-alphabetic-zero-ideographic-over: calc(1 - (750 + 200) / 1000); 50 52 --baseline-diagnostic-alphabetic-zero-hanging: calc(1 - (650 + 200) / 1000); 53 + --baseline-diagnostic-alphabetic-zero-ideographic-face-over: calc(1 - (650 + 200) / 1000); 51 54 --baseline-diagnostic-alphabetic-zero-cap-height: calc(1 - (550 + 200) / 1000); 52 55 --baseline-diagnostic-alphabetic-zero-math: calc(1 - (450 + 200) / 1000); 53 56 --baseline-diagnostic-alphabetic-zero-central: calc(1 - (350 + 200) / 1000); ··· 55 58 --baseline-diagnostic-alphabetic-zero-x-height: calc(1 - (250 + 200) / 1000); 56 59 --baseline-diagnostic-alphabetic-zero-x-middle: calc(1 - (125 + 200) / 1000); 57 60 --baseline-diagnostic-alphabetic-zero-alphabetic: calc(1 - (0 + 200) / 1000); 61 + --baseline-diagnostic-alphabetic-zero-ideographic-face-under: calc(1 - (50 + 200) / 1000); 58 62 --baseline-diagnostic-alphabetic-zero-zero: calc(1 - (0 + 200) / 1000); 59 63 --baseline-diagnostic-alphabetic-zero-ideographic-under: calc(1 - (-50 + 200) / 1000); 60 64 --baseline-diagnostic-alphabetic-zero-descent: calc(1 - (-200 + 200) / 1000);
+89 -19
font.py
··· 1 1 import os 2 - from dataclasses import dataclass 3 - from typing import List, Literal, Union 2 + from dataclasses import dataclass, field 3 + from enum import auto, Enum 4 + from typing import List, Literal, Optional, Union 4 5 5 6 from fontTools.fontBuilder import FontBuilder 6 7 from fontTools.pens.transformPen import TransformPen ··· 30 31 label: str 31 32 style: FontBaselineStyle 32 33 34 + 35 + class FontGlyphKind(Enum): 36 + EMBOX_FILLED = auto() 37 + EMBOX_OUTLINE = auto() 38 + PAIR_LAYOUT = auto() 39 + PAIR_LABELED = auto() 40 + 41 + 42 + @dataclass 43 + class FontGlyph: 44 + char: str 45 + kind: FontGlyphKind 46 + baseline_ids: Optional[List[str]] = None 47 + 48 + 33 49 @dataclass 34 50 class Font: 35 51 name: str 36 52 description: str 37 53 baselines: List[FontBaseline] 54 + glyphs: List[FontGlyph] = field(default_factory=list) 38 55 39 56 40 57 @dataclass ··· 174 191 ) 175 192 176 193 177 - def build_baselines_font(font_name: str, out_path: str, baselines: List[FontBaseline]): 194 + def build_baselines_font(font: Font, out_path: str): 195 + baselines = font.baselines 178 196 ascent = next(baseline.position for baseline in baselines if baseline.id == 'ascent') 179 197 descent = next(baseline.position for baseline in baselines if baseline.id == 'descent') 180 198 em_size = ascent - descent ··· 182 200 if not ascent or not descent: 183 201 raise ValueError(f"Required ascent / descent but got {ascent} / {descent}") 184 202 185 - empty_glyph_pen = TTGlyphPen(None) 186 - draw_bordered_rectangle(empty_glyph_pen, 0, descent, em_size, ascent, BORDER_WIDTH) 203 + baseline_by_id = {} 204 + for baseline in baselines: 205 + if baseline.id not in baseline_by_id: 206 + baseline_by_id[baseline.id] = baseline 207 + 208 + # .notdef: bordered rectangle 209 + notdef_glyph_pen = TTGlyphPen(None) 210 + draw_bordered_rectangle(notdef_glyph_pen, 0, descent, em_size, ascent, BORDER_WIDTH) 187 211 212 + # X: all baselines with style drawn 188 213 diag_glyph_pen = TTGlyphPen(None) 189 214 draw_bordered_rectangle(diag_glyph_pen, 0, descent, em_size, ascent, BORDER_WIDTH) 215 + 190 216 label_font = TTFont("./support/noto/NotoSansMono-Bold.ttf") 217 + 191 218 for baseline in baselines: 192 219 if baseline.style: 193 220 draw_baseline( ··· 200 227 stroke_width=baseline.style.stroke_width, 201 228 ) 202 229 230 + glyph_order = [".notdef", "X"] 231 + char_map = {ord("X"): "X"} 232 + glyf_table = {".notdef": notdef_glyph_pen.glyph(), "X": diag_glyph_pen.glyph()} 233 + h_metrics = {".notdef": (em_size, 0), "X": (em_size, 0)} 234 + 235 + for glyph in font.glyphs: 236 + cp = ord(glyph.char) 237 + name = f"uni{cp:04X}" if cp <= 0xFFFF else f"u{cp:05X}" 238 + pen = TTGlyphPen(None) 239 + 240 + if glyph.kind == FontGlyphKind.EMBOX_FILLED: 241 + draw_rectangle(pen, 0, descent, em_size, ascent) 242 + 243 + elif glyph.kind == FontGlyphKind.EMBOX_OUTLINE: 244 + draw_bordered_rectangle(pen, 0, descent, em_size, ascent, BORDER_WIDTH) 245 + 246 + elif glyph.kind == FontGlyphKind.PAIR_LAYOUT: 247 + b1 = baseline_by_id[glyph.baseline_ids[0]] 248 + b2 = baseline_by_id[glyph.baseline_ids[1]] 249 + lower = min(b1.position, b2.position) 250 + upper = max(b1.position, b2.position) 251 + draw_rectangle(pen, 0, lower, em_size, upper) 252 + 253 + elif glyph.kind == FontGlyphKind.PAIR_LABELED: 254 + draw_bordered_rectangle(pen, 0, descent, em_size, ascent, BORDER_WIDTH) 255 + for b in baselines: 256 + if b.style and b.label is None: 257 + draw_baseline(pen, label_font, b.position, em_size, None, 258 + style=b.style.stroke_style, stroke_width=b.style.stroke_width) 259 + for bid in glyph.baseline_ids: 260 + b = baseline_by_id[bid] 261 + style = b.style if b.style else FontBaselineStyle.SOLID 262 + draw_baseline( 263 + pen, 264 + label_font, 265 + b.position, 266 + em_size, 267 + b.label, 268 + style=style.stroke_style, 269 + stroke_width=style.stroke_width, 270 + ) 271 + 272 + glyph_order.append(name) 273 + char_map[cp] = name 274 + glyf_table[name] = pen.glyph() 275 + h_metrics[name] = (em_size, 0) 276 + 203 277 fb = FontBuilder(em_size, isTTF=True) 204 - fb.setupGlyphOrder([".notdef", "X"]) 205 - fb.setupCharacterMap({ ord("X"): "X" }) 206 - fb.setupGlyf({ 207 - ".notdef": empty_glyph_pen.glyph(), 208 - "X": diag_glyph_pen.glyph() 209 - }) 278 + fb.setupGlyphOrder(glyph_order) 279 + fb.setupCharacterMap(char_map) 280 + fb.setupGlyf(glyf_table) 210 281 211 282 os2_values = dict((base.name, base.position) for base in baselines if base.table == "OS/2") 212 283 hhea_values = dict((base.name, base.position) for base in baselines if base.table == "hhea") ··· 216 287 fb.setupPost() 217 288 fb.setupNameTable({ 218 289 "copyright": "Copyright (c) 2026, Sajid Anwar", 219 - "familyName": font_name, 290 + "familyName": font.name, 220 291 "styleName": style_name, 221 - "uniqueFontIdentifier": f"{font_name}-{style_name}", 222 - "fullName": f"{font_name}-{style_name}", 223 - "psName": f"{font_name}-{style_name}", 292 + "uniqueFontIdentifier": f"{font.name}-{style_name}", 293 + "fullName": f"{font.name}-{style_name}", 294 + "psName": f"{font.name}-{style_name}", 224 295 "version": "Version 1.0", 225 296 }) 226 - fb.setupHorizontalMetrics({".notdef": (em_size, 0), "X": (em_size, 0)}) 297 + fb.setupHorizontalMetrics(h_metrics) 227 298 fb.setupOS2(**os2_values) 228 299 fb.setupHorizontalHeader(**hhea_values) 229 300 fb.setupVerticalHeader(**vhea_values) 230 301 fb.setupHead(unitsPerEm=em_size) 231 302 232 - font = fb.font 233 303 bases = list(sorted(filter(lambda base: base.table == "BASE", baselines), key=lambda base: base.name)) 234 304 base_names = list(base.name for base in bases) 235 305 ··· 259 329 base_script.BaseScript.BaseValues.BaseCoord = base_coords 260 330 base_table.HorizAxis.BaseScriptList.BaseScriptRecord = [base_script] 261 331 base_table.VertAxis.BaseScriptList.BaseScriptRecord = [base_script] 262 - font["BASE"] = newTable("BASE") 263 - font["BASE"].table = base_table 332 + fb.font["BASE"] = newTable("BASE") 333 + fb.font["BASE"].table = base_table 264 334 265 335 os.makedirs(os.path.dirname(out_path), exist_ok=True) 266 336 fb.save(out_path)
+110 -60
main.py
··· 1 1 import re 2 - from font import Font, FontBaseline, FontBaselineStyle, build_baselines_font 2 + from font import Font, FontBaseline, FontBaselineStyle, FontGlyph, FontGlyphKind, build_baselines_font 3 3 from textwrap import dedent, indent 4 4 from typing import Dict, List 5 5 6 6 def main(): 7 + glyphs = [ 8 + FontGlyph("x", FontGlyphKind.PAIR_LAYOUT, ["x-height", "alphabetic"]), 9 + FontGlyph("χ", FontGlyphKind.PAIR_LABELED, ["x-height", "alphabetic"]), 10 + FontGlyph("B", FontGlyphKind.PAIR_LAYOUT, ["cap-height", "alphabetic"]), 11 + FontGlyph("β", FontGlyphKind.PAIR_LABELED, ["cap-height", "alphabetic"]), 12 + FontGlyph("口", FontGlyphKind.PAIR_LAYOUT, ["ideographic-over", "ideographic-under"]), 13 + FontGlyph("日", FontGlyphKind.PAIR_LABELED, ["ideographic-over", "ideographic-under"]), 14 + FontGlyph("中", FontGlyphKind.PAIR_LAYOUT, ["ideographic-face-over", "ideographic-face-under"]), 15 + FontGlyph("田", FontGlyphKind.PAIR_LABELED, ["ideographic-face-over", "ideographic-face-under"]), 16 + FontGlyph("अ", FontGlyphKind.PAIR_LAYOUT, ["hanging", "alphabetic"]), 17 + FontGlyph("आ", FontGlyphKind.PAIR_LABELED, ["hanging", "alphabetic"]), 18 + FontGlyph("+", FontGlyphKind.PAIR_LAYOUT, ["math", "alphabetic"]), 19 + FontGlyph("±", FontGlyphKind.PAIR_LABELED, ["math", "alphabetic"]), 20 + FontGlyph("█", FontGlyphKind.EMBOX_FILLED), 21 + FontGlyph("□", FontGlyphKind.EMBOX_OUTLINE), 22 + ] 23 + 7 24 fonts = [] 8 25 fonts.append(Font( 9 26 name="BaselineDiagnostic", ··· 12 29 text in the font, this should be used with very large font sizes. There are 13 30 two glyphs in the font.""")), 14 31 baselines=[ 15 - FontBaseline("ascent", 800, "OS/2", "sTypoAscender", None, None), 16 - FontBaseline("ascent", 800, "hhea", "ascent", None, None), 17 - FontBaseline("ascent", 800, "vhea", "ascent", None, None), 18 - FontBaseline("ideographic-over", 750, "BASE", "idtp", "IDEOGRAPHIC-OVER", FontBaselineStyle.SOLID), 19 - FontBaseline("hanging", 650, "BASE", "hang", "HANGING", FontBaselineStyle.SOLID), 20 - FontBaseline("cap-height", 550, "OS/2", "sCapHeight", "CAP-HEIGHT", FontBaselineStyle.SOLID), 21 - FontBaseline("math", 450, "BASE", "math", "MATH", FontBaselineStyle.SOLID), 22 - FontBaseline("central", 350, None, None, "CENTRAL", FontBaselineStyle.SOLID), 23 - FontBaseline("em-middle", 300, None, None, None, FontBaselineStyle.DASHED), 24 - FontBaseline("x-height", 250, "OS/2", "sxHeight", "X-HEIGHT", FontBaselineStyle.SOLID), 25 - FontBaseline("x-middle", 150, None, None, "X-MIDDLE", FontBaselineStyle.SOLID), 26 - FontBaseline("alphabetic", 50, "BASE", "romn", "ALPHABETIC", FontBaselineStyle.SOLID), 27 - FontBaseline("zero", 0, None, None, None, FontBaselineStyle.DASHED), 28 - FontBaseline("ideographic-under", -50, "BASE", "ideo", "IDEOGRAPHIC-UNDER", FontBaselineStyle.SOLID), 29 - FontBaseline("descent", -200, "OS/2", "sTypoDescender", None, None), 30 - FontBaseline("descent", -200, "hhea", "descent", None, None), 31 - FontBaseline("descent", -200, "vhea", "descent", None, None), 32 - ] 32 + FontBaseline("ascent", 800, "OS/2", "sTypoAscender", None, None), 33 + FontBaseline("ascent", 800, "hhea", "ascent", None, None), 34 + FontBaseline("ascent", 800, "vhea", "ascent", None, None), 35 + FontBaseline("ideographic-over", 750, "BASE", "idtp", "IDEOGRAPHIC-OVER", FontBaselineStyle.SOLID), 36 + FontBaseline("hanging", 650, "BASE", "hang", "HANGING", FontBaselineStyle.SOLID), 37 + FontBaseline("ideographic-face-over", 650, "BASE", "icft", "IDEO-FACE-OVER", None), 38 + FontBaseline("cap-height", 550, "OS/2", "sCapHeight", "CAP-HEIGHT", FontBaselineStyle.SOLID), 39 + FontBaseline("math", 450, "BASE", "math", "MATH", FontBaselineStyle.SOLID), 40 + FontBaseline("central", 350, None, None, "CENTRAL", FontBaselineStyle.SOLID), 41 + FontBaseline("em-middle", 300, None, None, None, FontBaselineStyle.DASHED), 42 + FontBaseline("x-height", 250, "OS/2", "sxHeight", "X-HEIGHT", FontBaselineStyle.SOLID), 43 + FontBaseline("x-middle", 150, None, None, "X-MIDDLE", FontBaselineStyle.SOLID), 44 + FontBaseline("alphabetic", 50, "BASE", "romn", "ALPHABETIC", FontBaselineStyle.SOLID), 45 + FontBaseline("ideographic-face-under", 50, "BASE", "icfb", "IDEO-FACE-UNDER", None), 46 + FontBaseline("zero", 0, None, None, None, FontBaselineStyle.DASHED), 47 + FontBaseline("ideographic-under", -50, "BASE", "ideo", "IDEOGRAPHIC-UNDER", FontBaselineStyle.SOLID), 48 + FontBaseline("descent", -200, "OS/2", "sTypoDescender", None, None), 49 + FontBaseline("descent", -200, "hhea", "descent", None, None), 50 + FontBaseline("descent", -200, "vhea", "descent", None, None), 51 + ], 52 + glyphs=glyphs, 33 53 )) 34 54 fonts.append(Font( 35 55 name="BaselineDiagnosticAlphabeticZero", ··· 37 57 Same as the "BaselineDiagnostic" font, but uses the common alphabetic baseline 38 58 of 0. This also results in the x-middle baseline being at 125.""")), 39 59 baselines=[ 40 - FontBaseline("ascent", 800, "OS/2", "sTypoAscender", None, None), 41 - FontBaseline("ascent", 800, "hhea", "ascent", None, None), 42 - FontBaseline("ascent", 800, "vhea", "ascent", None, None), 43 - FontBaseline("ideographic-over", 750, "BASE", "idtp", "IDEOGRAPHIC-OVER", FontBaselineStyle.SOLID), 44 - FontBaseline("hanging", 650, "BASE", "hang", "HANGING", FontBaselineStyle.SOLID), 45 - FontBaseline("cap-height", 550, "OS/2", "sCapHeight", "CAP-HEIGHT", FontBaselineStyle.SOLID), 46 - FontBaseline("math", 450, "BASE", "math", "MATH", FontBaselineStyle.SOLID), 47 - FontBaseline("central", 350, None, None, "CENTRAL", FontBaselineStyle.SOLID), 48 - FontBaseline("em-middle", 300, None, None, None, FontBaselineStyle.DASHED), 49 - FontBaseline("x-height", 250, "OS/2", "sxHeight", "X-HEIGHT", FontBaselineStyle.SOLID), 50 - FontBaseline("x-middle", 125, None, None, "X-MIDDLE", FontBaselineStyle.SOLID), 51 - FontBaseline("alphabetic", 0, "BASE", "romn", None, FontBaselineStyle.DASHED), 52 - FontBaseline("zero", 0, None, None, None, None), 53 - FontBaseline("ideographic-under", -50, "BASE", "ideo", "IDEOGRAPHIC-UNDER", FontBaselineStyle.SOLID), 54 - FontBaseline("descent", -200, "OS/2", "sTypoDescender", None, None), 55 - FontBaseline("descent", -200, "hhea", "descent", None, None), 56 - FontBaseline("descent", -200, "vhea", "descent", None, None), 57 - ] 60 + FontBaseline("ascent", 800, "OS/2", "sTypoAscender", None, None), 61 + FontBaseline("ascent", 800, "hhea", "ascent", None, None), 62 + FontBaseline("ascent", 800, "vhea", "ascent", None, None), 63 + FontBaseline("ideographic-over", 750, "BASE", "idtp", "IDEOGRAPHIC-OVER", FontBaselineStyle.SOLID), 64 + FontBaseline("hanging", 650, "BASE", "hang", "HANGING", FontBaselineStyle.SOLID), 65 + FontBaseline("ideographic-face-over", 650, "BASE", "icft", "IDEO-FACE-OVER", None), 66 + FontBaseline("cap-height", 550, "OS/2", "sCapHeight", "CAP-HEIGHT", FontBaselineStyle.SOLID), 67 + FontBaseline("math", 450, "BASE", "math", "MATH", FontBaselineStyle.SOLID), 68 + FontBaseline("central", 350, None, None, "CENTRAL", FontBaselineStyle.SOLID), 69 + FontBaseline("em-middle", 300, None, None, None, FontBaselineStyle.DASHED), 70 + FontBaseline("x-height", 250, "OS/2", "sxHeight", "X-HEIGHT", FontBaselineStyle.SOLID), 71 + FontBaseline("x-middle", 125, None, None, "X-MIDDLE", FontBaselineStyle.SOLID), 72 + FontBaseline("alphabetic", 0, "BASE", "romn", None, FontBaselineStyle.DASHED), 73 + FontBaseline("ideographic-face-under", 50, "BASE", "icfb", "IDEO-FACE-UNDER", None), 74 + FontBaseline("zero", 0, None, None, None, None), 75 + FontBaseline("ideographic-under", -50, "BASE", "ideo", "IDEOGRAPHIC-UNDER", FontBaselineStyle.SOLID), 76 + FontBaseline("descent", -200, "OS/2", "sTypoDescender", None, None), 77 + FontBaseline("descent", -200, "hhea", "descent", None, None), 78 + FontBaseline("descent", -200, "vhea", "descent", None, None), 79 + ], 80 + glyphs=glyphs, 58 81 )) 59 82 60 83 write_font_files(fonts) ··· 65 88 66 89 def write_font_files(fonts: List[Font]): 67 90 for font in fonts: 68 - build_baselines_font(font.name, f'dist/{font.name}.ttf', font.baselines) 91 + build_baselines_font(font, f'dist/{font.name}.ttf') 69 92 70 93 71 94 def write_font_stylesheet(fonts: List[Font]): ··· 137 160 ## Overview 138 161 139 162 Font that can be used for validating baseline alignments. Given the embedded 140 - text in the font, this should be used with very large font sizes. There are 141 - two glyphs in the font: 163 + text in the font, this should be used with very large font sizes. 142 164 143 - - `X` (U+0058) which has all baselines drawn 144 - - `.notdef` (for all other characters) which is an empty box 165 + ## Baselines and Metrics 145 166 146 - It has the following baselines: 167 + | Baseline/Metric | Coordinate | BASE Value | OS/2 Value | hhea Value | 168 + |------------------------|------------|------------|----------------|------------| 169 + | ascent | 800 | | sTypoAscender | ascent | 170 + | ideographic-over | 750 | idtp | | | 171 + | hanging | 650 | hang | | | 172 + | ideographic-face-over | 650 | icft | | | 173 + | cap-height | 550 | | sCapHeight | | 174 + | math | 450 | math | | | 175 + | /central/ | 350 | | | | 176 + | /em-middle/ | 300 | | | | 177 + | x-height | 250 | | sxHeight | | 178 + | /x-middle/ | 150 | | | | 179 + | alphabetic | 50 | romn | | | 180 + | ideographic-face-under | 50 | icfb | | | 181 + | /zero/ | 0 | | | | 182 + | ideographic-under | -50 | ideo | | | 183 + | descent | -200 | | sTypoDescender | descent | 147 184 148 - | Baseline/Metric | Coordinate | BASE Value | OS/2 Value | hhea Value | 149 - |-------------------|------------|------------|----------------|------------| 150 - | ascent | 800 | | sTypoAscender | ascent | 151 - | ideographic-over | 750 | idtp | | | 152 - | hanging | 650 | hang | | | 153 - | cap-height | 550 | | sCapHeight | | 154 - | math | 450 | math | | | 155 - | /central/ | 350 | | | | 156 - | /em-middle/ | 300 | | | | 157 - | x-height | 250 | | sxHeight | | 158 - | /x-middle/ | 150 | | | | 159 - | alphabetic | 50 | romn | | | 160 - | /zero/ | | | | | 161 - | ideographic-under | -50 | ideo | | | 162 - | descent | -200 | | sTypoDescender | descent | 163 - 164 - The `BaselineDiagnosticAlphabeticZero` variant is the same as `Baseline`, 185 + The `BaselineDiagnosticAlphabeticZero` variant is the same as `BaselineDiagnostic`, 165 186 except the alphabetic baseline is at the common value of 0. This also 166 187 results in the x-middle baseline being at 125. 188 + 189 + ## Glyphs 190 + 191 + ### Diagnostic glyph 192 + 193 + | Glyph | Codepoint | Description | 194 + |-------|-----------|-------------| 195 + | `X` | U+0058 | All baselines drawn with labels | 196 + 197 + ### Pair glyphs 198 + 199 + Each baseline pair has two variants: a **layout** glyph (opaque filled rectangle 200 + between the two baselines) and a **labeled** glyph (lines with text labels, like `X`). 201 + 202 + | Pair | Layout | Layout codepoint | Labeled | Labeled codepoint | 203 + |-------------------------------|--------|------------------|---------|-------------------| 204 + | X-height + Alphabetic | `x` | U+0078 | `χ` | U+03C7 | 205 + | Cap-height + Alphabetic | `B` | U+0042 | `β` | U+03B2 | 206 + | Ideo em-box (idtp + ideo) | `口` | U+53E3 | `日` | U+65E5 | 207 + | Ideo face (icft + icfb) | `中` | U+4E2D | `田` | U+7530 | 208 + | Hanging + Alphabetic | `अ` | U+0905 | `आ` | U+0906 | 209 + | Math + Alphabetic | `+` | U+002B | `±` | U+00B1 | 210 + 211 + ### Em-box glyphs 212 + 213 + | Variant | Glyph | Codepoint | 214 + |---------|-------|-----------| 215 + | Filled | `█` | U+2588 | 216 + | Outline | `□` | U+25A1 | 167 217 168 218 ## Source and Downloads 169 219 Both the source code and built font files can be found in the [`@sajidanwar.com/baseline-diagnostic-font`][tangled-repo]