···11-21Copyright (c) 2026, Sajid Anwar.
3243This Font Software is licensed under the SIL Open Font License, Version 1.1.
54This license is copied below, and is also available with a FAQ at:
66-https\://openfontlicense.org
55+https://openfontlicense.org
76
8798\----------------------------------------------------------------------
···105104INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
106105DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
107106FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
108108-OTHER DEALINGS IN THE FONT SOFTWARE.
107107+OTHER DEALINGS IN THE FONT SOFTWARE.
+30-32
dist/README.md
···11-21# Baseline Diagnostic Font
3243## Overview
···8798## Baselines and Metrics
1091111-| Baseline/Metric | Coordinate | BASE Value | OS/2 Value | hhea Value |
1212-|------------------------|------------|------------|----------------|------------|
1313-| ascent | 800 | | sTypoAscender | ascent |
1414-| ideographic-over | 750 | idtp | | |
1515-| hanging | 650 | hang | | |
1616-| ideographic-face-over | 650 | icft | | |
1717-| cap-height | 550 | | sCapHeight | |
1818-| math | 450 | math | | |
1919-| /central/ | 350 | | | |
2020-| /em-middle/ | 300 | | | |
2121-| x-height | 250 | | sxHeight | |
2222-| /x-middle/ | 150 | | | |
2323-| alphabetic | 50 | romn | | |
2424-| ideographic-face-under | 50 | icfb | | |
2525-| /zero/ | 0 | | | |
2626-| ideographic-under | -50 | ideo | | |
2727-| descent | -200 | | sTypoDescender | descent |
1010+| Baseline/Metric | Coordinate | BASE Value | OS/2 Value | hhea Value |
1111+|-----------------|------------|------------|------------|------------|
1212+| ascent | 800 | | sTypoAscender | ascent |
1313+| ideographic-over | 750 | idtp | | |
1414+| hanging | 650 | hang | | |
1515+| ideographic-face-over | 650 | icft | | |
1616+| cap-height | 550 | | sCapHeight | |
1717+| math | 450 | math | | |
1818+| /central/ | 350 | | | |
1919+| /em-middle/ | 300 | | | |
2020+| x-height | 250 | | sxHeight | |
2121+| /x-middle/ | 150 | | | |
2222+| alphabetic | 50 | romn | | |
2323+| ideographic-face-under | 50 | icfb | | |
2424+| /zero/ | 0 | | | |
2525+| ideographic-under | -50 | ideo | | |
2626+| descent | -200 | | sTypoDescender | descent |
28272929-The `BaselineDiagnosticAlphabeticZero` variant is the same as `BaselineDiagnostic`,
3030-except the alphabetic baseline is at the common value of 0. This also
3131-results in the x-middle baseline being at 125.
2828+The `BaselineDiagnosticAlphabeticZero` variant is the same as `BaselineDiagnostic`, except `alphabetic` moves to 0; `x-middle` moves to 125.
32293330## Glyphs
3431···4340Each baseline pair has two variants: a **layout** glyph (opaque filled rectangle
4441between the two baselines) and a **labeled** glyph (lines with text labels, like `X`).
45424646-| Pair | Layout | Layout codepoint | Labeled | Labeled codepoint |
4747-|-------------------------------|--------|------------------|---------|-------------------|
4848-| X-height + Alphabetic | `x` | U+0078 | `χ` | U+03C7 |
4949-| Cap-height + Alphabetic | `B` | U+0042 | `β` | U+03B2 |
5050-| Ideo em-box (idtp + ideo) | `口` | U+53E3 | `日` | U+65E5 |
5151-| Ideo face (icft + icfb) | `中` | U+4E2D | `田` | U+7530 |
5252-| Hanging + Alphabetic | `अ` | U+0905 | `आ` | U+0906 |
5353-| Math + Alphabetic | `+` | U+002B | `±` | U+00B1 |
4343+| Pair | Layout | Layout codepoint | Labeled | Labeled codepoint |
4444+|------|--------|------------------|---------|-------------------|
4545+| x-height + alphabetic | `x` | U+0078 | `χ` | U+03C7 |
4646+| cap-height + alphabetic | `B` | U+0042 | `β` | U+03B2 |
4747+| ideographic-over + ideographic-under | `口` | U+53E3 | `日` | U+65E5 |
4848+| ideographic-face-over + ideographic-face-under | `中` | U+4E2D | `田` | U+7530 |
4949+| hanging + alphabetic | `अ` | U+0905 | `आ` | U+0906 |
5050+| math + alphabetic | `+` | U+002B | `±` | U+00B1 |
54515552### Em-box glyphs
56535754| Variant | Glyph | Codepoint |
5855|---------|-------|-----------|
5959-| Filled | `█` | U+2588 |
6060-| Outline | `□` | U+25A1 |
5656+| Filled | `█` | U+2588 |
5757+| Outline | `□` | U+25A1 |
61586259## Source and Downloads
6060+6361Both the source code and built font files can be found in the [`@sajidanwar.com/baseline-diagnostic-font`][tangled-repo]
6462repository on [Tangled][tangled-home] or the [`kbhomes/baseline-diagnostic-font`][github-repo]
6563repository on [GitHub][github-home].
···7876[SIL Open Font License, Version 1.1][ofl-1.1], and is available at `LICENSE.txt`.
79778078[noto-sans-mono]: https://fonts.google.com/noto/specimen/Noto+Sans+Mono/license
8181-[ofl-1.1]: https://openfontlicense.org/open-font-license-official-text/
7979+[ofl-1.1]: https://openfontlicense.org/open-font-license-official-text/
···11import re
22from font import Font, FontBaseline, FontBaselineStyle, FontGlyph, FontGlyphKind, build_baselines_font
33+from jinja2 import Environment, FileSystemLoader
34from textwrap import dedent, indent
45from typing import Dict, List
66+77+AUTHOR = "Sajid Anwar"
5869def main():
710 glyphs = [
···82858386 write_font_files(fonts)
8487 write_font_stylesheet(fonts)
8585- write_font_readme()
8888+ write_font_html(fonts)
8989+ write_font_readme(fonts)
8690 write_font_license()
87918892···150154 print(f"Wrote stylesheet at {out_path}")
151155152156153153-# TODO: Generate this README and tables programmatically from the fonts.
154154-def write_font_readme():
155155- out_path = "dist/README.md"
156156- with open(out_path, "w") as f:
157157- f.write(dedent(r'''
158158- # Baseline Diagnostic Font
157157+def prepare_template_data(fonts: List[Font]) -> dict:
158158+ font = fonts[0]
159159+ font_az = fonts[1]
159160160160- ## Overview
161161+ dfn_tooltips = {
162162+ 'central': 'Computed at halfway between ideographic-under and ideographic-over',
163163+ 'em-middle': 'Computed at halfway between ascent and descent',
164164+ 'x-middle': 'Computed at halfway between alphabetic and x-height',
165165+ 'zero': 'Zero coordinate',
166166+ }
161167162162- Font that can be used for validating baseline alignments. Given the embedded
163163- text in the font, this should be used with very large font sizes.
168168+ seen: Dict[str, dict] = {}
169169+ baseline_table = []
170170+ for b in font.baselines:
171171+ if b.id not in seen:
172172+ entry = {'id': b.id, 'position': b.position, 'base': '', 'os2': '', 'hhea': '', 'tooltip': dfn_tooltips.get(b.id)}
173173+ seen[b.id] = entry
174174+ baseline_table.append(entry)
175175+ if b.table == 'BASE': seen[b.id]['base'] = b.name
176176+ elif b.table == 'OS/2': seen[b.id]['os2'] = b.name
177177+ elif b.table == 'hhea': seen[b.id]['hhea'] = b.name
164178165165- ## Baselines and Metrics
179179+ az_positions = {}
180180+ for b in font_az.baselines:
181181+ if b.id not in az_positions:
182182+ az_positions[b.id] = b.position
183183+ az_diffs = [{'id': e['id'], 'position': az_positions[e['id']]}
184184+ for e in baseline_table
185185+ if e['id'] in az_positions and az_positions[e['id']] != e['position']]
186186+ az_diffs.sort(key=lambda baseline: baseline['id'])
166187167167- | Baseline/Metric | Coordinate | BASE Value | OS/2 Value | hhea Value |
168168- |------------------------|------------|------------|----------------|------------|
169169- | ascent | 800 | | sTypoAscender | ascent |
170170- | ideographic-over | 750 | idtp | | |
171171- | hanging | 650 | hang | | |
172172- | ideographic-face-over | 650 | icft | | |
173173- | cap-height | 550 | | sCapHeight | |
174174- | math | 450 | math | | |
175175- | /central/ | 350 | | | |
176176- | /em-middle/ | 300 | | | |
177177- | x-height | 250 | | sxHeight | |
178178- | /x-middle/ | 150 | | | |
179179- | alphabetic | 50 | romn | | |
180180- | ideographic-face-under | 50 | icfb | | |
181181- | /zero/ | 0 | | | |
182182- | ideographic-under | -50 | ideo | | |
183183- | descent | -200 | | sTypoDescender | descent |
188188+ pair_map: Dict[tuple, dict] = {}
189189+ pair_order = []
190190+ for g in font.glyphs:
191191+ if g.baseline_ids:
192192+ key = tuple(g.baseline_ids)
193193+ if key not in pair_map:
194194+ pair_map[key] = {'ids': list(key), 'layout': None, 'labeled': None}
195195+ pair_order.append(key)
196196+ glyph_data = {'char': g.char, 'codepoint': f'U+{ord(g.char):04X}'}
197197+ if g.kind == FontGlyphKind.PAIR_LAYOUT: pair_map[key]['layout'] = glyph_data
198198+ elif g.kind == FontGlyphKind.PAIR_LABELED: pair_map[key]['labeled'] = glyph_data
184199185185- The `BaselineDiagnosticAlphabeticZero` variant is the same as `BaselineDiagnostic`,
186186- except the alphabetic baseline is at the common value of 0. This also
187187- results in the x-middle baseline being at 125.
200200+ def embox_data(kind):
201201+ g = next((g for g in font.glyphs if g.kind == kind), None)
202202+ return {'char': g.char, 'codepoint': f'U+{ord(g.char):04X}'} if g else None
188203189189- ## Glyphs
204204+ return {
205205+ 'font_name': font.name,
206206+ 'font_az_name': font_az.name,
207207+ 'baseline_table': baseline_table,
208208+ 'az_diffs': az_diffs,
209209+ 'pairs': [pair_map[k] for k in pair_order],
210210+ 'embox_filled': embox_data(FontGlyphKind.EMBOX_FILLED),
211211+ 'embox_outline': embox_data(FontGlyphKind.EMBOX_OUTLINE),
212212+ }
190213191191- ### Diagnostic glyph
192214193193- | Glyph | Codepoint | Description |
194194- |-------|-----------|-------------|
195195- | `X` | U+0058 | All baselines drawn with labels |
215215+def _jinja_env():
216216+ return Environment(loader=FileSystemLoader('templates'), trim_blocks=True, lstrip_blocks=True)
196217197197- ### Pair glyphs
198218199199- Each baseline pair has two variants: a **layout** glyph (opaque filled rectangle
200200- between the two baselines) and a **labeled** glyph (lines with text labels, like `X`).
201201-202202- | Pair | Layout | Layout codepoint | Labeled | Labeled codepoint |
203203- |-------------------------------|--------|------------------|---------|-------------------|
204204- | X-height + Alphabetic | `x` | U+0078 | `χ` | U+03C7 |
205205- | Cap-height + Alphabetic | `B` | U+0042 | `β` | U+03B2 |
206206- | Ideo em-box (idtp + ideo) | `口` | U+53E3 | `日` | U+65E5 |
207207- | Ideo face (icft + icfb) | `中` | U+4E2D | `田` | U+7530 |
208208- | Hanging + Alphabetic | `अ` | U+0905 | `आ` | U+0906 |
209209- | Math + Alphabetic | `+` | U+002B | `±` | U+00B1 |
210210-211211- ### Em-box glyphs
212212-213213- | Variant | Glyph | Codepoint |
214214- |---------|-------|-----------|
215215- | Filled | `█` | U+2588 |
216216- | Outline | `□` | U+25A1 |
217217-218218- ## Source and Downloads
219219- Both the source code and built font files can be found in the [`@sajidanwar.com/baseline-diagnostic-font`][tangled-repo]
220220- repository on [Tangled][tangled-home] or the [`kbhomes/baseline-diagnostic-font`][github-repo]
221221- repository on [GitHub][github-home].
222222-223223- This font is built using Python with the [fonttools](https://fonttools.readthedocs.io/en/latest/) library.
224224-225225- [tangled-repo]: https://tangled.org/sajidanwar.com/baseline-diagnostic-font
226226- [tangled-home]: https://tangled.org/
227227- [github-repo]: https://github.com/kbhomes/baseline-diagnostic-font
228228- [github-home]: https://github.com/
229229-230230- ## License
219219+def write_font_html(fonts: List[Font]):
220220+ out_path = "dist/index.html"
221221+ data = prepare_template_data(fonts)
222222+ html = _jinja_env().get_template('index.html.jinja').render(**data)
223223+ with open(out_path, 'w') as f:
224224+ f.write(html)
225225+ print(f"Wrote HTML at {out_path}")
231226232232- This font contains [Noto Sans Mono][noto-sans-mono] glyphs in the rendering
233233- of its baseline labels. Like that font, this font is licensed under the
234234- [SIL Open Font License, Version 1.1][ofl-1.1], and is available at `LICENSE.txt`.
235227236236- [noto-sans-mono]: https://fonts.google.com/noto/specimen/Noto+Sans+Mono/license
237237- [ofl-1.1]: https://openfontlicense.org/open-font-license-official-text/
238238- '''))
239239- print(f"Wrote README at {out_path}")
228228+def write_font_readme(fonts: List[Font]):
229229+ out_path = "dist/README.md"
230230+ data = prepare_template_data(fonts)
231231+ md = _jinja_env().get_template('README.md.jinja').render(**data)
232232+ with open(out_path, 'w') as f:
233233+ f.write(md)
234234+ print(f"Wrote README at {out_path}")
240235241236242237def write_font_license():
243238 out_path = "dist/LICENSE.md"
244244- with open(out_path, "w") as f:
245245- f.write(dedent(r'''
246246- Copyright (c) 2026, Sajid Anwar.
247247-248248- This Font Software is licensed under the SIL Open Font License, Version 1.1.
249249- This license is copied below, and is also available with a FAQ at:
250250- https\://openfontlicense.org
251251-
252252-253253- \----------------------------------------------------------------------
254254-255255- #### SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
256256-257257- \----------------------------------------------------------------------
258258-259259-
260260-261261- PREAMBLE
262262- -----------
263263-264264- The goals of the Open Font License (OFL) are to stimulate worldwide
265265- development of collaborative font projects, to support the font creation
266266- efforts of academic and linguistic communities, and to provide a free and
267267- open framework in which fonts may be shared and improved in partnership
268268- with others.
269269-270270- The OFL allows the licensed fonts to be used, studied, modified and
271271- redistributed freely as long as they are not sold by themselves. The
272272- fonts, including any derivative works, can be bundled, embedded,
273273- redistributed and/or sold with any software provided that any reserved
274274- names are not used by derivative works. The fonts and derivatives,
275275- however, cannot be released under any other type of license. The
276276- requirement for fonts to remain under this license does not apply
277277- to any document created using the fonts or their derivatives.
278278-279279- DEFINITIONS
280280- -----------
281281-282282- "Font Software" refers to the set of files released by the Copyright
283283- Holder(s) under this license and clearly marked as such. This may
284284- include source files, build scripts and documentation.
285285-286286- "Reserved Font Name" refers to any names specified as such after the
287287- copyright statement(s).
288288-289289- "Original Version" refers to the collection of Font Software components as
290290- distributed by the Copyright Holder(s).
291291-292292- "Modified Version" refers to any derivative made by adding to, deleting,
293293- or substituting -- in part or in whole -- any of the components of the
294294- Original Version, by changing formats or by porting the Font Software to a
295295- new environment.
296296-297297- "Author" refers to any designer, engineer, programmer, technical
298298- writer or other person who contributed to the Font Software.
299299-300300- PERMISSION & CONDITIONS
301301- -----------
302302-303303- Permission is hereby granted, free of charge, to any person obtaining
304304- a copy of the Font Software, to use, study, copy, merge, embed, modify,
305305- redistribute, and sell modified and unmodified copies of the Font
306306- Software, subject to the following conditions:
307307-308308- 1) Neither the Font Software nor any of its individual components,
309309- in Original or Modified Versions, may be sold by itself.
310310-311311- 2) Original or Modified Versions of the Font Software may be bundled,
312312- redistributed and/or sold with any software, provided that each copy
313313- contains the above copyright notice and this license. These can be
314314- included either as stand-alone text files, human-readable headers or
315315- in the appropriate machine-readable metadata fields within text or
316316- binary files as long as those fields can be easily viewed by the user.
317317-318318- 3) No Modified Version of the Font Software may use the Reserved Font
319319- Name(s) unless explicit written permission is granted by the corresponding
320320- Copyright Holder. This restriction only applies to the primary font name as
321321- presented to the users.
322322-323323- 4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
324324- Software shall not be used to promote, endorse or advertise any
325325- Modified Version, except to acknowledge the contribution(s) of the
326326- Copyright Holder(s) and the Author(s) or with their explicit written
327327- permission.
328328-329329- 5) The Font Software, modified or unmodified, in part or in whole,
330330- must be distributed entirely under this license, and must not be
331331- distributed under any other license. The requirement for fonts to
332332- remain under this license does not apply to any document created
333333- using the Font Software.
334334-335335- TERMINATION
336336- -----------
337337-338338- This license becomes null and void if any of the above conditions are
339339- not met.
340340-341341- DISCLAIMER
342342- -----------
343343-344344- THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
345345- EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
346346- MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
347347- OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
348348- COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
349349- INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
350350- DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
351351- FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
352352- OTHER DEALINGS IN THE FONT SOFTWARE.
353353- '''))
354354- print(f"Wrote OFL 1.1 license at {out_path}")
239239+ md = _jinja_env().get_template('LICENSE.md.jinja').render(author=AUTHOR)
240240+ with open(out_path, 'w') as f:
241241+ f.write(md)
242242+ print(f"Wrote OFL 1.1 license at {out_path}")
355243356244def dashing(value: str):
357245 return re.sub(r'(?<!^)(?=[A-Z])', '-', value).lower()
···11+Copyright (c) 2026, {{ author }}.
22+33+This Font Software is licensed under the SIL Open Font License, Version 1.1.
44+This license is copied below, and is also available with a FAQ at:
55+https://openfontlicense.org
66+
77+88+\----------------------------------------------------------------------
99+1010+#### SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
1111+1212+\----------------------------------------------------------------------
1313+1414+
1515+1616+PREAMBLE
1717+-----------
1818+1919+The goals of the Open Font License (OFL) are to stimulate worldwide
2020+development of collaborative font projects, to support the font creation
2121+efforts of academic and linguistic communities, and to provide a free and
2222+open framework in which fonts may be shared and improved in partnership
2323+with others.
2424+2525+The OFL allows the licensed fonts to be used, studied, modified and
2626+redistributed freely as long as they are not sold by themselves. The
2727+fonts, including any derivative works, can be bundled, embedded,
2828+redistributed and/or sold with any software provided that any reserved
2929+names are not used by derivative works. The fonts and derivatives,
3030+however, cannot be released under any other type of license. The
3131+requirement for fonts to remain under this license does not apply
3232+to any document created using the fonts or their derivatives.
3333+3434+DEFINITIONS
3535+-----------
3636+3737+"Font Software" refers to the set of files released by the Copyright
3838+Holder(s) under this license and clearly marked as such. This may
3939+include source files, build scripts and documentation.
4040+4141+"Reserved Font Name" refers to any names specified as such after the
4242+copyright statement(s).
4343+4444+"Original Version" refers to the collection of Font Software components as
4545+distributed by the Copyright Holder(s).
4646+4747+"Modified Version" refers to any derivative made by adding to, deleting,
4848+or substituting -- in part or in whole -- any of the components of the
4949+Original Version, by changing formats or by porting the Font Software to a
5050+new environment.
5151+5252+"Author" refers to any designer, engineer, programmer, technical
5353+writer or other person who contributed to the Font Software.
5454+5555+PERMISSION & CONDITIONS
5656+-----------
5757+5858+Permission is hereby granted, free of charge, to any person obtaining
5959+a copy of the Font Software, to use, study, copy, merge, embed, modify,
6060+redistribute, and sell modified and unmodified copies of the Font
6161+Software, subject to the following conditions:
6262+6363+1) Neither the Font Software nor any of its individual components,
6464+in Original or Modified Versions, may be sold by itself.
6565+6666+2) Original or Modified Versions of the Font Software may be bundled,
6767+redistributed and/or sold with any software, provided that each copy
6868+contains the above copyright notice and this license. These can be
6969+included either as stand-alone text files, human-readable headers or
7070+in the appropriate machine-readable metadata fields within text or
7171+binary files as long as those fields can be easily viewed by the user.
7272+7373+3) No Modified Version of the Font Software may use the Reserved Font
7474+Name(s) unless explicit written permission is granted by the corresponding
7575+Copyright Holder. This restriction only applies to the primary font name as
7676+presented to the users.
7777+7878+4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
7979+Software shall not be used to promote, endorse or advertise any
8080+Modified Version, except to acknowledge the contribution(s) of the
8181+Copyright Holder(s) and the Author(s) or with their explicit written
8282+permission.
8383+8484+5) The Font Software, modified or unmodified, in part or in whole,
8585+must be distributed entirely under this license, and must not be
8686+distributed under any other license. The requirement for fonts to
8787+remain under this license does not apply to any document created
8888+using the Font Software.
8989+9090+TERMINATION
9191+-----------
9292+9393+This license becomes null and void if any of the above conditions are
9494+not met.
9595+9696+DISCLAIMER
9797+-----------
9898+9999+THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
100100+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
101101+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
102102+OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
103103+COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
104104+INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
105105+DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
106106+FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
107107+OTHER DEALINGS IN THE FONT SOFTWARE.
+68
templates/README.md.jinja
···11+# Baseline Diagnostic Font
22+33+## Overview
44+55+Font that can be used for validating baseline alignments. Given the embedded
66+text in the font, this should be used with very large font sizes.
77+88+## Baselines and Metrics
99+1010+| Baseline/Metric | Coordinate | BASE Value | OS/2 Value | hhea Value |
1111+|-----------------|------------|------------|------------|------------|
1212+{% for b in baseline_table %}
1313+| {% if b.tooltip %}/{{ b.id }}/{% else %}{{ b.id }}{% endif %} | {{ b.position }} | {{ b.base }} | {{ b.os2 }} | {{ b.hhea }} |
1414+{% endfor %}
1515+1616+The `{{ font_az_name }}` variant is the same as `{{ font_name }}`, except {% for diff in az_diffs %}`{{ diff.id }}` moves to {{ diff.position }}{% if not loop.last %}; {% endif %}{% endfor %}.
1717+1818+## Glyphs
1919+2020+### Diagnostic glyph
2121+2222+| Glyph | Codepoint | Description |
2323+|-------|-----------|-------------|
2424+| `X` | U+0058 | All baselines drawn with labels |
2525+2626+### Pair glyphs
2727+2828+Each baseline pair has two variants: a **layout** glyph (opaque filled rectangle
2929+between the two baselines) and a **labeled** glyph (lines with text labels, like `X`).
3030+3131+| Pair | Layout | Layout codepoint | Labeled | Labeled codepoint |
3232+|------|--------|------------------|---------|-------------------|
3333+{% for pair in pairs %}
3434+| {{ pair.ids | join(' + ') }} | {% if pair.layout %}`{{ pair.layout.char }}`{% endif %} | {% if pair.layout %}{{ pair.layout.codepoint }}{% endif %} | {% if pair.labeled %}`{{ pair.labeled.char }}`{% endif %} | {% if pair.labeled %}{{ pair.labeled.codepoint }}{% endif %} |
3535+{% endfor %}
3636+3737+### Em-box glyphs
3838+3939+| Variant | Glyph | Codepoint |
4040+|---------|-------|-----------|
4141+{% if embox_filled %}
4242+| Filled | `{{ embox_filled.char }}` | {{ embox_filled.codepoint }} |
4343+{% endif %}
4444+{% if embox_outline %}
4545+| Outline | `{{ embox_outline.char }}` | {{ embox_outline.codepoint }} |
4646+{% endif %}
4747+4848+## Source and Downloads
4949+5050+Both the source code and built font files can be found in the [`@sajidanwar.com/baseline-diagnostic-font`][tangled-repo]
5151+repository on [Tangled][tangled-home] or the [`kbhomes/baseline-diagnostic-font`][github-repo]
5252+repository on [GitHub][github-home].
5353+5454+This font is built using Python with the [fonttools](https://fonttools.readthedocs.io/en/latest/) library.
5555+5656+[tangled-repo]: https://tangled.org/sajidanwar.com/baseline-diagnostic-font
5757+[tangled-home]: https://tangled.org/
5858+[github-repo]: https://github.com/kbhomes/baseline-diagnostic-font
5959+[github-home]: https://github.com/
6060+6161+## License
6262+6363+This font contains [Noto Sans Mono][noto-sans-mono] glyphs in the rendering
6464+of its baseline labels. Like that font, this font is licensed under the
6565+[SIL Open Font License, Version 1.1][ofl-1.1], and is available at `LICENSE.txt`.
6666+6767+[noto-sans-mono]: https://fonts.google.com/noto/specimen/Noto+Sans+Mono/license
6868+[ofl-1.1]: https://openfontlicense.org/open-font-license-official-text/
+278
templates/index.html.jinja
···11+<!DOCTYPE html>
22+<html lang="en">
33+<head>
44+ <meta charset="utf-8">
55+ <meta name="viewport" content="width=device-width, initial-scale=1">
66+ <link rel="stylesheet" href="./baseline-diagnostic-font.css">
77+ <style>
88+ body {
99+ width: 80ch;
1010+ margin: auto;
1111+ font: 16px/1.5 sans-serif;
1212+ padding: 0;
1313+ box-sizing: border-box;
1414+ }
1515+1616+ code {
1717+ background: #f8f8f8;
1818+ border: 1px dotted #ccc;
1919+ border-radius: 0.25em;
2020+ padding: 0.25em;
2121+ font-weight: bold;
2222+ font-size: smaller;
2323+ }
2424+2525+ dfn {
2626+ text-decoration: 2px dotted underline;
2727+ cursor: help;
2828+ }
2929+3030+ #preview, #embox-preview {
3131+ display: flex;
3232+ justify-content: space-evenly;
3333+ text-align: center;
3434+ }
3535+3636+ table {
3737+ text-align: center;
3838+ font-family: monospace;
3939+ border-collapse: collapse;
4040+ margin: 1em auto;
4141+ }
4242+4343+ table td, table th {
4444+ padding: 0.25em 0.5em;
4545+ vertical-align: middle;
4646+ }
4747+4848+ th {
4949+ background: #f8f8f8;
5050+ }
5151+5252+ #baselines-table {
5353+ text-align: left;
5454+ }
5555+5656+ #baselines-table td:nth-child(2) {
5757+ text-align: right;
5858+ }
5959+6060+ .pair-cell {
6161+ text-align: center;
6262+ }
6363+6464+ .glyph-render {
6565+ background: #fff;
6666+ display: inline-block;
6767+ margin-bottom: 10px;
6868+ }
6969+7070+ .glyph-render.layout {
7171+ background: repeating-conic-gradient(#eee 0 25%, #0000 0 50%) 50% / 20px 20px;
7272+ }
7373+7474+ .pair-name {
7575+ display: inline-flex;
7676+ flex-direction: column;
7777+ gap: 1px;
7878+ background: black;
7979+ }
8080+8181+ .pair-name span {
8282+ background: white;
8383+ padding: 4px;
8484+ }
8585+8686+ .font-switch {
8787+ display: flex;
8888+ margin-bottom: 0.75em;
8989+ font-family: monospace;
9090+ font-size: 0.75em;
9191+ justify-content: center;
9292+ }
9393+9494+ .font-switch input[type="radio"] {
9595+ display: none;
9696+ }
9797+9898+ .font-switch label span {
9999+ display: block;
100100+ padding: 0.3em 0.75em;
101101+ border: 1px solid #ccc;
102102+ cursor: pointer;
103103+ background: #f8f8f8;
104104+ user-select: none;
105105+ }
106106+107107+ .font-switch label:first-child span {
108108+ border-radius: 4px 0 0 4px;
109109+ }
110110+111111+ .font-switch label:last-child span {
112112+ border-radius: 0 4px 4px 0;
113113+ margin-left: -1px;
114114+ }
115115+116116+ .font-switch input[type="radio"]:checked + span {
117117+ font-weight: bold;
118118+ border-color: #000;
119119+ background: white;
120120+ position: relative;
121121+ }
122122+123123+ .font-switch input[type="radio"]:checked + span:after {
124124+ content: ' ✔';
125125+ font-weight: 900;
126126+ }
127127+128128+ #pair-section:has([value="{{ font_name }}"]:checked) .glyph-render {
129129+ font-family: "{{ font_name }}";
130130+ }
131131+132132+ #pair-section:has([value="{{ font_az_name }}"]:checked) .glyph-render {
133133+ font-family: "{{ font_az_name }}";
134134+ }
135135+ </style>
136136+</head>
137137+<body>
138138+ <h1>Baseline Diagnostic Font</h1>
139139+140140+ <h2>Overview</h2>
141141+ <p>
142142+ Font that can be used for validating baseline alignments. Given the embedded
143143+ text in the font, this should be used with very large font sizes.
144144+ </p>
145145+146146+ <h2>Baselines and Metrics</h2>
147147+ <table id="baselines-table" border="1">
148148+ <thead>
149149+ <tr>
150150+ <th>Baseline/Metric</th>
151151+ <th>Coordinate</th>
152152+ <th><code>BASE</code> Value</th>
153153+ <th><code>OS/2</code> Value</th>
154154+ <th><code>hhea</code> Value</th>
155155+ </tr>
156156+ </thead>
157157+ {% for b in baseline_table %}
158158+ <tr>
159159+ <td>{% if b.tooltip %}<dfn title="{{ b.tooltip }}">{{ b.id }}</dfn>{% else %}{{ b.id }}{% endif %}</td>
160160+ <td>{{ b.position }}</td>
161161+ <td>{{ b.base }}</td>
162162+ <td>{{ b.os2 }}</td>
163163+ <td>{{ b.hhea }}</td>
164164+ </tr>
165165+ {% endfor %}
166166+ </table>
167167+ <p>
168168+ The <code>{{ font_az_name }}</code> variant is the same as
169169+ <code>{{ font_name }}</code>, except
170170+ {% for diff in az_diffs %}<code>{{ diff.id }}</code> moves to {{ diff.position }}{% if not loop.last %}; {% endif %}{% endfor %}.
171171+ </p>
172172+173173+ <h2>Glyphs</h2>
174174+175175+ <h3>Diagnostic glyph</h3>
176176+ <p>
177177+ <code>X</code> (U+0058) draws all baselines with labels. Use at large font sizes.
178178+ </p>
179179+ <div id="preview">
180180+ <div>
181181+ <p><span class="glyph-render" style="font: 250px/1 '{{ font_name }}'">X</span></p>
182182+ <em><code>{{ font_name }}</code> at 250px</em>
183183+ </div>
184184+ <div>
185185+ <p><span class="glyph-render" style="font: 250px/1 '{{ font_az_name }}'">X</span></p>
186186+ <em><code>{{ font_az_name }}</code> at 250px</em>
187187+ </div>
188188+ </div>
189189+190190+ <h3>Baseline pair glyphs</h3>
191191+ <p>
192192+ Each pair has a <strong>layout</strong> variant (opaque fill between the two
193193+ baselines) and a <strong>labeled</strong> variant (lines with labels, like
194194+ <code>X</code>). Labeled variants also show dashed reference lines for
195195+ <dfn title="Zero coordinate">zero</dfn> and
196196+ <dfn title="Computed at halfway between ascent and descent">em-middle</dfn>.
197197+ </p>
198198+ <div id="pair-section">
199199+ <div class="font-switch">
200200+ <label><input type="radio" name="pair-font" value="{{ font_name }}" checked><span>{{ font_name }}</span></label>
201201+ <label><input type="radio" name="pair-font" value="{{ font_az_name }}"><span>{{ font_az_name }}</span></label>
202202+ </div>
203203+ <table id="pair-table" border="1">
204204+ <thead>
205205+ <tr><th>Pair</th><th>Layout</th><th>Labeled</th></tr>
206206+ </thead>
207207+ {% for pair in pairs %}
208208+ <tr>
209209+ <td>
210210+ <div class="pair-name">
211211+ {% for id in pair.ids %}<span>{{ id }}</span>{% endfor %}
212212+ </div>
213213+ </td>
214214+ {% if pair.layout %}
215215+ <td class="pair-cell">
216216+ <span class="glyph-render layout" style="font-size: 150px; line-height: 1">{{ pair.layout.char }}</span>
217217+ <br><code>{{ pair.layout.char }}</code> ({{ pair.layout.codepoint }})
218218+ </td>
219219+ {% else %}
220220+ <td></td>
221221+ {% endif %}
222222+ {% if pair.labeled %}
223223+ <td class="pair-cell">
224224+ <span class="glyph-render" style="font-size: 150px; line-height: 1">{{ pair.labeled.char }}</span>
225225+ <br><code>{{ pair.labeled.char }}</code> ({{ pair.labeled.codepoint }})
226226+ </td>
227227+ {% else %}
228228+ <td></td>
229229+ {% endif %}
230230+ </tr>
231231+ {% endfor %}
232232+ </table>
233233+ </div>
234234+235235+ <h3>Em-box glyphs</h3>
236236+ <p>
237237+ Reference glyphs spanning the full em-box from ascent to descent.
238238+ Use them as solid or outlined background blocks without any baseline markings.
239239+ </p>
240240+ <div id="embox-preview">
241241+ {% if embox_filled %}
242242+ <div>
243243+ <p><span class="glyph-render" style="font: 150px/1 '{{ font_name }}'">{{ embox_filled.char }}</span></p>
244244+ <em><code>{{ embox_filled.char }}</code> ({{ embox_filled.codepoint }}) — Filled</em>
245245+ </div>
246246+ {% endif %}
247247+ {% if embox_outline %}
248248+ <div>
249249+ <p><span class="glyph-render" style="font: 150px/1 '{{ font_name }}'">{{ embox_outline.char }}</span></p>
250250+ <em><code>{{ embox_outline.char }}</code> ({{ embox_outline.codepoint }}) — Outline</em>
251251+ </div>
252252+ {% endif %}
253253+ </div>
254254+255255+ <h2>Source and Downloads</h2>
256256+ <p>
257257+ Both the source code and built font files can be found in the
258258+ <a href="https://tangled.org/sajidanwar.com/baseline-diagnostic-font"><code>@sajidanwar.com/baseline-diagnostic-font</code></a>
259259+ repository on <a href="https://tangled.org">Tangled</a> or the
260260+ <a href="https://github.com/kbhomes/baseline-diagnostic-font"><code>kbhomes/baseline-diagnostic-font</code></a>
261261+ repository on <a href="https://github.com/">GitHub</a>.
262262+ </p>
263263+ <p>
264264+ This font is built using Python with the
265265+ <a href="https://fonttools.readthedocs.io/en/latest/">fonttools</a> library.
266266+ </p>
267267+268268+ <h2>License</h2>
269269+ <p>
270270+ This font contains <a href="https://fonts.google.com/noto/specimen/Noto+Sans+Mono/license">Noto Sans Mono</a>
271271+ glyphs in the rendering of its baseline labels. Like that font, this font is
272272+ licensed under the
273273+ <a href="https://openfontlicense.org/open-font-license-official-text/">SIL Open Font License, Version 1.1</a>,
274274+ and is available in the
275275+ <a href="https://tangled.org/sajidanwar.com/baseline-diagnostic-font/blob/main/dist/LICENSE.md">source repository</a>.
276276+ </p>
277277+</body>
278278+</html>