Browse Source
src/scriptapps/tools/fontprep/: pinned monospace web font for the punk-native html doc pipeline, closing the glyph-coverage gap the probe identified - unicode 16 octants (U+1CC00-1CEBF) and Symbols for Legacy Computing (U+1FB00-1FBFF) needed by ansicat/textblock rendering are absent from commonly-installed monospace fonts (even system Cascadia copies are commonly pre-2404 and lack them). - punkdoc-mono.woff2: glyph subset of Cascadia Mono v2404.23 VARIABLE ttf (wght axis 200-700 kept - true bold from one ~80KB file; 1561 cmap codepoints incl. full CP437 repertoire, box/blocks/geometric, braille, legacy computing + octants). Coverage verified 157/160 against the measured glyph inventory - identical to the full source font (misses are deliberate: em-space + fullwidth forms are converter-normalization/fallback territory). - renaming is REQUIRED by licence: Cascadia's SIL OFL 1.1 declares Reserved Font Name "Cascadia Code" and subsetting is modification. Name table is surgical: family records renamed, Microsoft copyright preserved verbatim (OFL requirement), trademark record removed (a renamed copy must not assert Microsoft's mark), OFL statement/url in licence records, provenance credit in the description record. - LICENSE-cascadia-OFL.txt (from the v2404.23 tag), punkdoc-unicodes.txt (whole-block ranges with headroom), make_punkdoc_mono.py (fonttools subset+ rename script), README.md (full provenance: source/output sha256s, exact command, regeneration steps). Python fonttools is one-time prep tooling only - the doc build never invokes it; generators just read the .woff2. Track licence in the G-063 inventory. punkargs_punknative.tcl: new -assets option consuming the vendored font: embed (default) = data-uri @font-face, fully self-contained page; link = copy punkdoc-mono.woff2 beside the output and reference relatively (shared+cached across a doc set); none = css stack only, with warn-and-degrade when the asset is missing. font-weight 200 700 descriptor, punkdoc-mono prepended to the fallback stack. Assisted-by: harness=claude; primary-model=claude-fable-5; api-location=anthropic.commaster
6 changed files with 345 additions and 6 deletions
@ -0,0 +1,94 @@ |
|||||||
|
Copyright (c) 2019 - Present, Microsoft Corporation, |
||||||
|
with Reserved Font Name Cascadia Code. |
||||||
|
|
||||||
|
This Font Software is licensed under the SIL Open Font License, Version 1.1. |
||||||
|
This license is copied below, and is also available with a FAQ at: |
||||||
|
http://scripts.sil.org/OFL |
||||||
|
|
||||||
|
|
||||||
|
----------------------------------------------------------- |
||||||
|
SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 |
||||||
|
----------------------------------------------------------- |
||||||
|
|
||||||
|
PREAMBLE |
||||||
|
The goals of the Open Font License (OFL) are to stimulate worldwide |
||||||
|
development of collaborative font projects, to support the font creation |
||||||
|
efforts of academic and linguistic communities, and to provide a free and |
||||||
|
open framework in which fonts may be shared and improved in partnership |
||||||
|
with others. |
||||||
|
|
||||||
|
The OFL allows the licensed fonts to be used, studied, modified and |
||||||
|
redistributed freely as long as they are not sold by themselves. The |
||||||
|
fonts, including any derivative works, can be bundled, embedded, |
||||||
|
redistributed and/or sold with any software provided that any reserved |
||||||
|
names are not used by derivative works. The fonts and derivatives, |
||||||
|
however, cannot be released under any other type of license. The |
||||||
|
requirement for fonts to remain under this license does not apply |
||||||
|
to any document created using the fonts or their derivatives. |
||||||
|
|
||||||
|
DEFINITIONS |
||||||
|
"Font Software" refers to the set of files released by the Copyright |
||||||
|
Holder(s) under this license and clearly marked as such. This may |
||||||
|
include source files, build scripts and documentation. |
||||||
|
|
||||||
|
"Reserved Font Name" refers to any names specified as such after the |
||||||
|
copyright statement(s). |
||||||
|
|
||||||
|
"Original Version" refers to the collection of Font Software components as |
||||||
|
distributed by the Copyright Holder(s). |
||||||
|
|
||||||
|
"Modified Version" refers to any derivative made by adding to, deleting, |
||||||
|
or substituting -- in part or in whole -- any of the components of the |
||||||
|
Original Version, by changing formats or by porting the Font Software to a |
||||||
|
new environment. |
||||||
|
|
||||||
|
"Author" refers to any designer, engineer, programmer, technical |
||||||
|
writer or other person who contributed to the Font Software. |
||||||
|
|
||||||
|
PERMISSION & CONDITIONS |
||||||
|
Permission is hereby granted, free of charge, to any person obtaining |
||||||
|
a copy of the Font Software, to use, study, copy, merge, embed, modify, |
||||||
|
redistribute, and sell modified and unmodified copies of the Font |
||||||
|
Software, subject to the following conditions: |
||||||
|
|
||||||
|
1) Neither the Font Software nor any of its individual components, |
||||||
|
in Original or Modified Versions, may be sold by itself. |
||||||
|
|
||||||
|
2) Original or Modified Versions of the Font Software may be bundled, |
||||||
|
redistributed and/or sold with any software, provided that each copy |
||||||
|
contains the above copyright notice and this license. These can be |
||||||
|
included either as stand-alone text files, human-readable headers or |
||||||
|
in the appropriate machine-readable metadata fields within text or |
||||||
|
binary files as long as those fields can be easily viewed by the user. |
||||||
|
|
||||||
|
3) No Modified Version of the Font Software may use the Reserved Font |
||||||
|
Name(s) unless explicit written permission is granted by the corresponding |
||||||
|
Copyright Holder. This restriction only applies to the primary font name as |
||||||
|
presented to the users. |
||||||
|
|
||||||
|
4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font |
||||||
|
Software shall not be used to promote, endorse or advertise any |
||||||
|
Modified Version, except to acknowledge the contribution(s) of the |
||||||
|
Copyright Holder(s) and the Author(s) or with their explicit written |
||||||
|
permission. |
||||||
|
|
||||||
|
5) The Font Software, modified or unmodified, in part or in whole, |
||||||
|
must be distributed entirely under this license, and must not be |
||||||
|
distributed under any other license. The requirement for fonts to |
||||||
|
remain under this license does not apply to any document created |
||||||
|
using the Font Software. |
||||||
|
|
||||||
|
TERMINATION |
||||||
|
This license becomes null and void if any of the above conditions are |
||||||
|
not met. |
||||||
|
|
||||||
|
DISCLAIMER |
||||||
|
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, |
||||||
|
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF |
||||||
|
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT |
||||||
|
OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE |
||||||
|
COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, |
||||||
|
INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL |
||||||
|
DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING |
||||||
|
FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM |
||||||
|
OTHER DEALINGS IN THE FONT SOFTWARE. |
||||||
@ -0,0 +1,70 @@ |
|||||||
|
# fontprep - punkdoc-mono web font for the punk-native documentation pipeline |
||||||
|
|
||||||
|
`punkdoc-mono.woff2` is the pinned monospace web font consumed by the |
||||||
|
punk-native html doc generation (`../punkargs_punknative.tcl` and its |
||||||
|
successors). It exists because the ansi->html rendering of punk::args usage |
||||||
|
tables, textblock output and cp437/ansi art (src/testansi) needs glyphs - |
||||||
|
Unicode 16 octants (U+1CC00-1CEBF) and Symbols for Legacy Computing |
||||||
|
(U+1FB00-1FBFF) in particular - that are absent from the monospace fonts |
||||||
|
commonly installed on reader machines. Coverage was measured empirically: |
||||||
|
see the glyph inventory probe notes below. |
||||||
|
|
||||||
|
## What it is |
||||||
|
|
||||||
|
A glyph subset of **Cascadia Mono v2404.23** (variable font, `wght` axis |
||||||
|
200-700 retained so browsers render true bold from this single file), |
||||||
|
renamed to **punkdoc-mono**. |
||||||
|
|
||||||
|
- Renaming is REQUIRED, not cosmetic: Cascadia's SIL OFL 1.1 licence declares |
||||||
|
the Reserved Font Name "Cascadia Code", and a modified version (subsetting |
||||||
|
is modification) must not use it. `punkdoc-mono` avoids the Cascadia name |
||||||
|
entirely. The font's embedded name table carries the original Microsoft |
||||||
|
copyright (nameID 0, preserved verbatim as the OFL requires), the OFL |
||||||
|
licence statement/url (nameID 13/14), and a provenance description |
||||||
|
(nameID 10). The original trademark record was removed (a renamed copy must |
||||||
|
not assert Microsoft's trademark). |
||||||
|
- Licence: SIL OFL 1.1 - full text in `LICENSE-cascadia-OFL.txt` (fetched from |
||||||
|
the v2404.23 source tag). The OFL applies to the font only; it places no |
||||||
|
conditions on punkshell (BSD) or on documents rendered with the font. |
||||||
|
Track in the project licence inventory (G-063). |
||||||
|
- No italic face is included: browsers synthesize oblique for italic spans |
||||||
|
(advance widths are preserved, so terminal-grid alignment holds). |
||||||
|
|
||||||
|
## Provenance (reproducibility) |
||||||
|
|
||||||
|
- Source: https://github.com/microsoft/cascadia-code/releases/download/v2404.23/CascadiaCode-2404.23.zip |
||||||
|
- release zip sha256: A911410626C0E09D03FA3FDDA827188FDA96607DF50FECC3C5FEE5906E33251B |
||||||
|
- input file within zip: ttf/CascadiaMono.ttf (variable) |
||||||
|
- input ttf sha256: 056F33BA2D9DA954750320EC6ECFB7D83CFB5F93AD593722170CD4498F5D4E95 |
||||||
|
- Tooling: python fonttools 4.63.0 + brotli (one-time prep only - the doc |
||||||
|
BUILD never invokes python; it just reads/copies/base64s the .woff2) |
||||||
|
- Command: |
||||||
|
`python make_punkdoc_mono.py CascadiaMono.ttf punkdoc-unicodes.txt <outdir>` |
||||||
|
- Output punkdoc-mono.woff2 sha256: 3476B6C12B3306D25A787C01882B0F484C3E11C3E05A59969065B199056641A4 |
||||||
|
(1561 cmap codepoints, 4319 glyphs, 79780 bytes) |
||||||
|
|
||||||
|
## Subset ranges |
||||||
|
|
||||||
|
`punkdoc-unicodes.txt` - whole blocks (with headroom) covering the demand |
||||||
|
measured 2026-07-13 by the glyph inventory probe (scratch trial folder |
||||||
|
argdoc2man-trial-2026-07-13: glyph_inventory.tcl over src/testansi decoded as |
||||||
|
ansicat does + textblock::periodic + all textblock frametypes + |
||||||
|
punk::args::usage = 160 unique non-ascii codepoints), plus the full CP437 |
||||||
|
repertoire and braille (U+2800-28FF) for future plot rendering. |
||||||
|
Verified: the subset covers 157/160 of the measured inventory - identical to |
||||||
|
the full source font. The 3 misses are deliberate non-goals: em-space U+2003 |
||||||
|
(converters should normalize exotic spaces to plain spaces - terminals treat |
||||||
|
them as one cell) and fullwidth forms U+FF30/FF31 (inherently double-width; |
||||||
|
CJK-font fallback territory). |
||||||
|
|
||||||
|
## Regenerating (when the glyph domain grows) |
||||||
|
|
||||||
|
1. Re-run the glyph inventory probe over the current content; if new |
||||||
|
codepoints fall outside `punkdoc-unicodes.txt`, widen the ranges. |
||||||
|
2. `pip install fonttools brotli` (any machine; a venv is fine) |
||||||
|
3. Fetch the pinned Cascadia release (or a newer one - update shas here) |
||||||
|
4. Run the command above; verify coverage (e.g. via |
||||||
|
Windows.Media.GlyphTypeface against the inventory - see font_coverage.ps1 |
||||||
|
in the trial scratch folder) and that name records contain no "Cascadia" |
||||||
|
outside the nameID 10 provenance credit. |
||||||
|
5. Commit the new .woff2 together with updated shas in this README. |
||||||
@ -0,0 +1,109 @@ |
|||||||
|
#!/usr/bin/env python3 |
||||||
|
"""make_punkdoc_mono.py - one-time font-prep for the punkshell doc pipeline. |
||||||
|
|
||||||
|
Subsets the Cascadia Mono VARIABLE font (weight axis kept, so browsers get |
||||||
|
true bold from one file) to the punkdoc unicode ranges, renames the family to |
||||||
|
'punkdoc-mono' (REQUIRED: Cascadia's OFL declares Reserved Font Name |
||||||
|
'Cascadia Code' - a modified version must not use the reserved name), and |
||||||
|
writes woff2 (for the web) + ttf (for cmap verification tooling). |
||||||
|
|
||||||
|
usage: python make_punkdoc_mono.py <CascadiaMono.ttf> <unicodes.txt> <outdir> |
||||||
|
""" |
||||||
|
import sys |
||||||
|
import os |
||||||
|
from fontTools import version as fonttools_version |
||||||
|
from fontTools.ttLib import TTFont |
||||||
|
from fontTools.subset import Subsetter, Options |
||||||
|
|
||||||
|
SRC_FAMILY_NAMES = ["Cascadia Mono", "CascadiaMono", "Cascadia Code", "CascadiaCode"] |
||||||
|
NEW_FAMILY = "punkdoc-mono" |
||||||
|
|
||||||
|
def parse_unicodes(path): |
||||||
|
cps = set() |
||||||
|
with open(path, encoding="utf-8") as f: |
||||||
|
for line in f: |
||||||
|
line = line.strip() |
||||||
|
if not line or line.startswith("#"): |
||||||
|
continue |
||||||
|
if "-" in line: |
||||||
|
lo, hi = line.split("-") |
||||||
|
cps.update(range(int(lo, 16), int(hi, 16) + 1)) |
||||||
|
else: |
||||||
|
cps.add(int(line, 16)) |
||||||
|
return cps |
||||||
|
|
||||||
|
# name records that carry the FAMILY NAME and must be renamed (OFL Reserved |
||||||
|
# Font Name requirement). Copyright (0), designer/vendor credits (8,9,11,12) |
||||||
|
# are preserved untouched - the OFL requires the copyright notice be retained. |
||||||
|
FAMILY_NAME_IDS = (1, 3, 4, 6, 16, 17, 21, 22, 25) |
||||||
|
OFL_ID13 = ("This Font Software is licensed under the SIL Open Font License, " |
||||||
|
"Version 1.1.") |
||||||
|
PROVENANCE_ID10 = ("punkdoc-mono is a glyph subset of Cascadia Mono v2404.23 " |
||||||
|
"(c) Microsoft Corporation, renamed as required by the SIL " |
||||||
|
"OFL 1.1 Reserved Font Name condition. Prepared for the " |
||||||
|
"punkshell documentation pipeline.") |
||||||
|
|
||||||
|
def rename(font): |
||||||
|
name = font["name"] |
||||||
|
keep = [] |
||||||
|
for rec in name.names: |
||||||
|
# drop the trademark record entirely: the original text is about the |
||||||
|
# Cascadia name (which this font no longer uses) and a renamed copy |
||||||
|
# must not assert Microsoft's trademark over the new name |
||||||
|
if rec.nameID == 7: |
||||||
|
continue |
||||||
|
if rec.nameID in FAMILY_NAME_IDS: |
||||||
|
s = rec.toUnicode() |
||||||
|
for old in SRC_FAMILY_NAMES: |
||||||
|
s = s.replace(old, NEW_FAMILY) |
||||||
|
s = s.replace(old.replace(" ", ""), NEW_FAMILY) |
||||||
|
if rec.nameID == 6: |
||||||
|
s = s.replace(" ", "") |
||||||
|
rec.string = s |
||||||
|
elif rec.nameID == 13: |
||||||
|
rec.string = OFL_ID13 |
||||||
|
keep.append(rec) |
||||||
|
name.names = keep |
||||||
|
# embedded provenance/description (mentioning the source font as credit is |
||||||
|
# permitted - the RFN restriction is on naming the font, not attribution) |
||||||
|
for platformID, platEncID, langID in ((3, 1, 0x409), (1, 0, 0)): |
||||||
|
name.setName(PROVENANCE_ID10, 10, platformID, platEncID, langID) |
||||||
|
|
||||||
|
def main(): |
||||||
|
src, unifile, outdir = sys.argv[1], sys.argv[2], sys.argv[3] |
||||||
|
os.makedirs(outdir, exist_ok=True) |
||||||
|
cps = parse_unicodes(unifile) |
||||||
|
print(f"fonttools {fonttools_version}; requested codepoints: {len(cps)}") |
||||||
|
|
||||||
|
font = TTFont(src) |
||||||
|
opts = Options() |
||||||
|
opts.layout_features = ["*"] # keep default layout behaviour |
||||||
|
opts.name_IDs = ["*"] # keep all name records (license notice ids too) |
||||||
|
opts.name_legacy = True |
||||||
|
opts.name_languages = ["*"] |
||||||
|
opts.notdef_outline = True |
||||||
|
opts.recalc_bounds = True |
||||||
|
opts.recalc_average_width = True |
||||||
|
opts.prune_unicode_ranges = True |
||||||
|
subsetter = Subsetter(options=opts) |
||||||
|
subsetter.populate(unicodes=cps) |
||||||
|
subsetter.subset(font) |
||||||
|
|
||||||
|
covered = set() |
||||||
|
for table in font["cmap"].tables: |
||||||
|
covered.update(table.cmap.keys()) |
||||||
|
print(f"glyphs in subset: {font['maxp'].numGlyphs}; cmap codepoints: {len(covered)}") |
||||||
|
|
||||||
|
rename(font) |
||||||
|
|
||||||
|
ttf_path = os.path.join(outdir, "punkdoc-mono.ttf") |
||||||
|
font.save(ttf_path) |
||||||
|
print(f"wrote {ttf_path} ({os.path.getsize(ttf_path)} bytes)") |
||||||
|
|
||||||
|
font.flavor = "woff2" |
||||||
|
woff2_path = os.path.join(outdir, "punkdoc-mono.woff2") |
||||||
|
font.save(woff2_path) |
||||||
|
print(f"wrote {woff2_path} ({os.path.getsize(woff2_path)} bytes)") |
||||||
|
|
||||||
|
if __name__ == "__main__": |
||||||
|
main() |
||||||
Binary file not shown.
@ -0,0 +1,21 @@ |
|||||||
|
# punkdoc-mono subset ranges - whole blocks covering (with headroom) the |
||||||
|
# glyph demand measured by glyph_inventory.tcl over src/testansi + |
||||||
|
# textblock::periodic/frames + punk::args::usage (2026-07-13, 160 codepoints) |
||||||
|
# plus the full CP437 repertoire and braille for future plot rendering. |
||||||
|
0020-007E |
||||||
|
00A0-00FF |
||||||
|
0180-024F |
||||||
|
0370-03FF |
||||||
|
2000-206F |
||||||
|
2070-209F |
||||||
|
20A0-20CF |
||||||
|
2190-21FF |
||||||
|
2200-22FF |
||||||
|
2300-23FF |
||||||
|
2400-243F |
||||||
|
2500-25FF |
||||||
|
2600-26FF |
||||||
|
2700-27BF |
||||||
|
2800-28FF |
||||||
|
1CC00-1CEBF |
||||||
|
1FB00-1FBFF |
||||||
Loading…
Reference in new issue