Browse Source

doc tooling: vendor punkdoc-mono web font (OFL subset of Cascadia Mono 2404.23) + -assets embed|link|none in punknative generator

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.com
master
Julian Noble 1 day ago
parent
commit
c18ca90a70
  1. 94
      src/scriptapps/tools/fontprep/LICENSE-cascadia-OFL.txt
  2. 70
      src/scriptapps/tools/fontprep/README.md
  3. 109
      src/scriptapps/tools/fontprep/make_punkdoc_mono.py
  4. BIN
      src/scriptapps/tools/fontprep/punkdoc-mono.woff2
  5. 21
      src/scriptapps/tools/fontprep/punkdoc-unicodes.txt
  6. 57
      src/scriptapps/tools/punkargs_punknative.tcl

94
src/scriptapps/tools/fontprep/LICENSE-cascadia-OFL.txt

@ -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.

70
src/scriptapps/tools/fontprep/README.md

@ -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.

109
src/scriptapps/tools/fontprep/make_punkdoc_mono.py

@ -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()

BIN
src/scriptapps/tools/fontprep/punkdoc-mono.woff2

Binary file not shown.

21
src/scriptapps/tools/fontprep/punkdoc-unicodes.txt

@ -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

57
src/scriptapps/tools/punkargs_punknative.tcl

@ -4,10 +4,18 @@
# synopsis conventions ([...] optionals, <type> placeholders) - bypassing
# doctools entirely (doctools stays Tcl-standard; no man-page output here).
#
# usage: tclsh punkargs_punknative.tcl <package> <outdir> ?-boxmap none|light?
# usage: tclsh punkargs_punknative.tcl <package> <outdir> ?-boxmap none|light? ?-assets embed|link|none?
# -boxmap none (default): faithful heavy box-drawing glyphs
# -boxmap light: map heavy/mixed box chars to the light set (portability
# fallback for viewers with only minimal mono fonts; _lightbox suffix)
# -assets embed (default): data-uri the pinned punkdoc-mono web font
# (fontprep/punkdoc-mono.woff2) into the page - fully self-contained
# single file; every glyph (incl. octants/legacy computing) renders
# identically on any machine (~106KB page overhead)
# -assets link: reference punkdoc-mono.woff2 relatively and copy it to
# <outdir> - one cached font shared by a whole generated doc set
# -assets none: no font shipped; rely on the viewer's installed fonts
# via the css stack (octants/legacy-computing glyphs may be missing)
# writes: <outdir>/<pkg>_punknative.html terminal-faithful rendering:
# punk::args::usage ANSI output converted to styled HTML spans
# (dark terminal theme, box-drawing tables, colours, italics)
@ -54,6 +62,7 @@ lassign $argv pkgname outdir
#Verified 2026-07-13: heavy renders aligned in chrome + firefox with the
#pre-level font stack.
set boxmap none
set assets embed
foreach {k v} [lrange $argv 2 end] {
switch -- $k {
-boxmap {
@ -63,8 +72,15 @@ foreach {k v} [lrange $argv 2 end] {
}
set boxmap $v
}
-assets {
if {$v ni {embed link none}} {
puts stderr "-assets must be embed, link or none (got '$v')"
exit 2
}
set assets $v
}
default {
puts stderr "unknown option '$k' (known: -boxmap)"
puts stderr "unknown option '$k' (known: -boxmap -assets)"
exit 2
}
}
@ -292,15 +308,44 @@ puts "punk-native docgen: [llength $cmd_ids] command definitions for $pkgname $p
# ---------------------------------------------------------------------------
# html: terminal-faithful - ansi2html of punk::args::usage per command
# ---------------------------------------------------------------------------
#pinned web font (see fontprep/README.md): guarantees every glyph in the punk
#domain - incl. unicode 16 octants and legacy computing blocks that no
#commonly-installed font provides. Variable (wght 200-700) so bold spans use
#real bold. OFL-subset of Cascadia Mono renamed punkdoc-mono (RFN condition).
set fontfile [file join [file dirname [file dirname [file normalize [info script]/__]]] fontprep punkdoc-mono.woff2]
set fontface ""
if {$assets ne "none"} {
if {![file exists $fontfile]} {
puts stderr "WARNING: -assets $assets requested but $fontfile not found - falling back to -assets none"
set assets none
} else {
if {$assets eq "embed"} {
set fd [open $fontfile rb]
set fontb64 [binary encode base64 [read $fd]]
close $fd
set fontsrc "data:font/woff2;base64,$fontb64"
} else {
#link mode: ship the font next to the page(s) - shared+cached
file copy -force $fontfile [file join $outdir punkdoc-mono.woff2]
set fontsrc "punkdoc-mono.woff2"
}
set fontface "@font-face {font-family:\"punkdoc-mono\"; src:url($fontsrc) format(\"woff2\"); font-weight:200 700; font-display:block;}\n"
}
}
set html ""
append html "<!DOCTYPE html>\n<html lang=\"en\"><head><meta charset=\"utf-8\">\n"
append html "<title>$pkgname $pkgversion - punk::args reference</title>\n"
append html "<style>\n"
#font stack: fonts with FULL box-drawing coverage (incl. heavy + mixed-weight
#chars) first - Consolas lacks the heavy set, causing per-glyph fallback at
#wrong widths (overlong border lines, esp. in chrome). lang=en above also
#steers chrome away from CJK ambiguous-width (double-width) fallback.
append html $fontface
#fallback stack: fonts with FULL box-drawing coverage (incl. heavy +
#mixed-weight chars) first - Courier New (firefox's default mono) lacks the
#heavy set. lang=en above steers chrome away from CJK ambiguous-width
#(double-width) fallback for box chars when no stack font matches.
set fontstack "'Cascadia Mono','Cascadia Code','DejaVu Sans Mono','Noto Sans Mono','JetBrains Mono','Liberation Mono',Consolas,Menlo,monospace"
if {$fontface ne ""} {
set fontstack "'punkdoc-mono',$fontstack"
}
append html "body {background:$ansi2html::default_bg; color:$ansi2html::default_fg; font-family:$fontstack;}\n"
append html "h1,h2 {font-family:inherit; color:#e5e510;}\n"
#IMPORTANT: pre needs its own font-family - the UA stylesheet's

Loading…
Cancel
Save