ttf-commissioner-hinted

LOW
maintainer gaougalos 0 votes scanned 2026-10-26 15:45:54.756943
View on AUR
Why flagged

Uploaded within the last 14 days with 2 or fewer community votes — little peer review so far.

Triggered rules

Low Few votes, recently uploaded zero_votes_recent

Uploaded within the last 14 days with 2 or fewer community votes — little peer review so far.

PKGBUILD

1# Maintainer: gaou-piou <i.am.piou@gmail.com>
2pkgname=ttf-commissioner-hinted
3_commver=1.012
4_nfver=3.5.1
5pkgver="${_commver}"
6pkgrel=1
7pkgdesc='Commissioner, a low-contrast humanist sans with Flare/Volume/Weight axes and a separate italic, re-hinted with ttfautohint for FreeType (optionally static / Nerd Fonts patched)'
8arch=('any')
9url='https://github.com/kosbarts/Commissioner'
10license=('OFL-1.1')
11makedepends=('python' 'python-fonttools' 'fontforge' 'parallel' 'ttfautohint')
12options=(!strip)
13provides=('ttf-commissioner')
14conflicts=('ttf-commissioner' 'ttf-commissioner-variable' 'otf-commissioner')
15source=(
16 "Commissioner-Roman.ttf::https://raw.githubusercontent.com/kosbarts/Commissioner/${_commver}/fonts/variable/Commissioner%5BFLAR%2CVOLM%2Cwght%5D.ttf"
17 "Commissioner-Italic.ttf::https://raw.githubusercontent.com/kosbarts/Commissioner/${_commver}/fonts/variable/Commissioner-Italic%5BFLAR%2CVOLM%2Cwght%5D.ttf"
18 "OFL.txt::https://raw.githubusercontent.com/kosbarts/Commissioner/${_commver}/OFL.txt"
19 "font-patcher-${_nfver}.zip::https://github.com/ryanoasis/nerd-fonts/releases/download/v${_nfver}/FontPatcher.zip"
20 "make_statics.py"
21 "set_gasp.py"
22 "measure.py"
23 "79-commissioner-families.conf"
24 "README.md"
25 "FEATURES.md"
26)
27noextract=("font-patcher-${_nfver}.zip")
28sha256sums=('63ce7044195585bc41476193be7be0312e07ec6685921eb141c6eabfb0aee3b6'
29 'f14e729502d1bcb7873830791d1de58c366d403846eb680a4a1e1e293d6953f6'
30 '4a7d88c77b4bc39ff84f2e058ba8f015636c50ce33588a86d9da97defe519933'
31 '42bcb32145499a35732274c7fc48deb434ad0d2e0e118f98527c1479c6fa251a'
32 'eed086f8bce3b73ef93475f0706e0978c8070a3bddd47a1e1ce74223beffbe27'
33 '0b0464b268525569b5fa28b4735791efb22f22829ad178629776ae3ea82d7f99'
34 '373d859f527adcf80933f9741af0595e708f591bdde5f2f01896aebd7d488750'
35 'f09f7f4a7dbcc2b18c1bafeae56c5611d995066f0b1cbcfe84bb9890edc66d4f'
36 '7c40d541e807419c658a49839d1815b6965a5c14c287739560c85e936280ec65'
37 '3f86a8d1100a69364b1035189143950de9c9963b217184048808ae29e88a6129')
38
39# ── What this package does ──────────────────────────────────────────────────
40#
41# Commissioner (Kostas Bartsokas) is a low-contrast humanist sans in three
42# "voices" that are axis settings, not separate designs: the default grotesque,
43# Flare (FLAR 100, glyphic serifs on the terminals) and Loud (FLAR 100 +
44# VOLM 100, the same serifs as wedges). Verified against the designer's own
45# static TTFs by rendering both at 200 px.
46#
47# The 1.012 release ships TWO variable fonts:
48#
49# Commissioner[FLAR,VOLM,wght].ttf roman, 9 instances, 3 axes
50# Commissioner-Italic[FLAR,VOLM,wght].ttf italic, 9 instances, 3 axes
51#
52# The italic file is the roman sheared 12 degrees - measured pixel-identical
53# to the third variable font in the repository,
54# Commissioner[FLAR,VOLM,slnt,wght].ttf at slnt=-12, in every weight, in
55# Latin, Greek and Cyrillic. That 4-axis file is NOT what this package
56# installs, for one reason: it is stale. Google Fonts ships it, but it is
57# still version 1.001 built with gftools 0.9.23 (1123 glyphs, 983 code points),
58# it is not in the 1.012 release archive at all, and it is missing everything
59# 1.012 added - tnum (tabular figures, also for + - = < > etc.), ss01 (the
60# alternates of G with a bar), the arbitrary-fraction machinery (numr/dnom/
61# frac), and the Greek OpenType fixes. The roman+italic pair is 1.012 in both
62# files (1172 glyphs, 994 code points). What you lose is a continuous slnt
63# axis; pango, Firefox and Chromium all ask for the italic style and get the
64# italic file, so nothing in a desktop notices.
65#
66# Neither upstream file is hinted: no fpgm, no cvt, zero glyph instructions, a
67# 7-byte prep. At hintstyle hintmedium FreeType therefore treats the face as
68# hinted and runs no autohinter, i.e. GTK (manual mode, font-hinting medium)
69# shows Commissioner completely unhinted. That is measurable: hintmedium on
70# the unhinted font is identical to FT_LOAD_NO_HINTING, glyph for glyph.
71# Upstream's 54 prebuilt statics, by contrast, ARE hinted (name table: "Version
72# 1.012; ttfautohint (v1.8.4.16-eb64)") - so the 54-file AUR package
73# ttf-commissioner is hinted and the variable font is not.
74#
75# Measured with FreeType 2.15.0, word "Hamburgefonstiv Αλφάβητο 0123".
76# glued = i j ï ä ö ü ë ё ϊ ϋ ΐ ΰ with no pixel row under 25% coverage between
77# mark and letter body, 10-20 px, 165 cases per weight, fewer is better.
78# dev = summed coverage difference to an 8x supersampled unhinted render, % of
79# its ink, best 1/8 px alignment, 12/13/14/16 px, lower is closer to the design:
80#
81# glued 100/400/700/900 dev 100/400/700/900
82# unhinted (hintmedium) 0/ 6/ 38/129 4.1/ 2.7/ 1.5/ 1.0
83# hintslight (autohinter) 0/ 0/ 6/ 34 12.5/11.7/ 8.4/ 7.0
84# bytecode natural (n) 0/ 8/ 8/ 72 12.1/11.7/ 7.9/ 6.5 <- default
85# bytecode balanced (q) 0/ 7/ 8/ 72 12.3/11.8/ 7.9/ 6.4
86# bytecode sharp (s) 0/ 8/ 8/ 70 54.1/12.3/10.5/ 7.2
87# bytecode light (n, 12-48) 0/ 8/ 8/ 72 12.1/11.7/ 7.9/ 6.5
88# static, hinted per weight 0/ 5/ 8/129 11.0/11.2/ 8.0/ 6.4
89# upstream's own statics 0/ 0/ 7/123 11.9/11.9/ 8.8/10.7
90#
91# Unlike Geologica, whose x-height grows 80 units across the axis and whose
92# bytecode therefore snaps every weight to the Thin master's blue zones, this
93# family barely moves: x-height 992 (Thin) to 1016 (Black) font units at 2000
94# upem, cap height 1426 in every instance, ascender 1478-1500. So the two
95# renderers land on the same pixel: the mean vertical shift of x, o and H is
96# identical to the hundredth at 13, 14 and 16 px in all nine weights, and
97# differs only at 12 px, by 0.4 px. What still differs is the dot metric:
98# FreeType's light autohinter keeps the dot of i off the stem where ttfautohint
99# cannot, because a one-pixel gap at 10-14 px in Black is below what bytecode
100# hinting can express. That is why hintslight - the Arch default, and what the
101# README recommends - remains the best single setting, and the bytecode this
102# package adds is for the desktops that run hintmedium/hintfull, where the
103# unhinted upstream font renders with no hinting at all.
104#
105# Note also that ttfautohint's --increase-x-height is silently ignored on a
106# variable font: -x 0, -x 30 and -x 60 produce byte-identical rendering (still
107# 8 glued at wght 400, 72 at 900). It does work on the statics, where it
108# changes the Regular from 5 glued to 0 for 0.1 of deviation - see the
109# "balanced" preset in the README's static section.
110#
111# FreeType only runs TrueType bytecode at hintstyle hintmedium/hintfull. This
112# package ships (not enabled) /usr/share/fontconfig/conf.avail/80-$pkgname.conf
113# which turns bytecode hinting on for the installed families only, and
114# excludes Chromium/Electron by prgname (at device scale 1 they round every
115# advance at hintmedium). It only affects clients that read hinting from
116# fontconfig (Firefox, Chromium, Qt, kitty/foot): GTK3/GTK4 in manual mode take
117# the hint style from gsettings and ignore fontconfig's hintstyle. See
118# README.md for the recommended desktop settings, which for Commissioner are
119# hintslight plus stem darkening (+39% ink on Thin, +23% on ExtraLight, +10%
120# on Regular - the light end of this family needs it).
121#
122# ── Build-time options ───────────────────────────────────────────────────────
123#
124# Run from a terminal, prepare() asks for COMM_VARIANT, NERD_PATCH, HINTING and
125# HINT_PRESET (Enter keeps the default). Any of them already set in the
126# environment is not asked. Without a TTY nothing is asked and the
127# environment / defaults below apply. Answers are saved to $srcdir/.build_opts.
128#
129# COMM_VARIANT variable|static (default: variable)
130# variable the roman and the italic variable font: every axis
131# stays usable
132# static one TTF per weight (and italic), cut with the
133# fontTools instancer; each one is hinted on its own,
134# so its blue zones match that exact weight
135#
136# STATIC_WEIGHTS "100 ... 900" wght values to cut (default: all nine:
137# 100 200 300 400 500 600 700 800 900)
138# STATIC_ITALIC true/false also cut the italic, default true
139# STATIC_VOICES "default flare loud" voices to cut, default "default":
140# default FLAR 0, VOLM 0 family "Commissioner"
141# flare FLAR 100, VOLM 0 family "Commissioner Flare"
142# loud FLAR 100, VOLM 100 family "Commissioner Loud"
143# All three = 54 files, the same set the designer ships, but
144# hinted per weight instead of all with one set of flags.
145#
146# NERD_PATCH true/false Patch with Nerd Fonts glyphs (default: false).
147# font-patcher drops variable axes, so this forces
148# COMM_VARIANT=static. Family becomes
149# "Commissioner Nerd Font (Mono|Propo)".
150#
151# HINTING true/false Hint with ttfautohint (default: true). false installs
152# the fonts as upstream ships them: no hinting at all
153# at hintmedium, the autohinter at hintslight.
154#
155# HINT_PRESET name Named bundle of ttfautohint flags (default: natural)
156# ("Linux" = the stem letter FreeType v40 actually uses)
157# natural stem=nnn (Linux: n) range 8-48 x-height 0 (closest to
158# the outline; best
159# measured preset)
160# balanced stem=qsq (Linux: q) range 8-48 x-height 14 (ttfautohint
161# defaults; on the
162# VF the x-height
163# part is ignored,
164# on statics it
165# opens the dots at
166# weight 400)
167# sharp stem=sss (Linux: s) range 6-48 x-height 14 (highest
168# contrast, distorts
169# Thin: dev 54.1)
170# light stem=nnn (Linux: n) range 12-48 x-height 0 (HiDPI; <12px
171# left alone)
172# custom use the individual HINT_* vars below verbatim
173#
174# Individual ttfautohint knobs (override the preset; empty = use preset value):
175# HINT_MODE nnn|qqq|qsq|sss 3-char --stem-width-mode (grayscale/GDI/DW)
176# On Linux only the 3rd letter has an effect.
177# HINT_RANGE_MIN N --hinting-range-min
178# HINT_RANGE_MAX N --hinting-range-max
179# HINT_LIMIT N --hinting-limit (default: HINT_RANGE_MAX; 0 = no limit)
180# HINT_XHEIGHT N --increase-x-height (0 = disable; no effect on the
181# variable fonts, see above)
182# HINT_XSNAP_EXC STR --x-height-snapping-exceptions (unset = none)
183# HINT_DEFAULT_SCRIPT STR --default-script (default: latn)
184# HINT_FALLBACK_SCRIPT STR --fallback-script (default: latn; Nerd-patched
185# builds always use none plus --fallback-scaling)
186# HINT_WINCOMPAT true/false --windows-compatibility (two extra blue zones
187# for GDI ClearType; Windows/Wine only)
188# HINT_TTFA_TABLE true/false --ttfa-table (default: false)
189# HINT_FAMILY_SUFFIX STR -F/--family-suffix, e.g. " Hinted"
190#
191# HINT_INTEGER_PPEM true/false Set head.flags bit 3 ("ppem is always an
192# integer"), which ttfautohint leaves clear and fontbakery
193# wants set for hinted fonts (default: false). On Linux
194# the flag does not change the ppem FreeType picks
195# (measured: 11 px at 8 pt / 96 dpi with the bit clear
196# and set); it only feeds the hinter's integer
197# assumption, which moves ink by up to 3.5%. Enable it
198# to satisfy fontbakery and match Windows GDI.
199#
200# GASP_MODE keep|sized|smooth|gridfit (default: keep; FreeType ignores gasp,
201# Windows uses it)
202# keep leave the gasp table as ttfautohint wrote it
203# sized MS strategy: <=8 grayscale, 9-16 gridfit, 17+ both
204# smooth all sizes anti-aliased ({0xFFFF: 15})
205# gridfit all sizes gridfit+grayscale ({0xFFFF: 3})
206#
207# Examples:
208# makepkg -si (asks, when run from a terminal)
209# makepkg -si </dev/null (never asks: defaults only)
210# COMM_VARIANT=static makepkg -si
211# COMM_VARIANT=static STATIC_VOICES="default flare loud" makepkg -si
212# COMM_VARIANT=static STATIC_ITALIC=false STATIC_VOICES=flare makepkg -si
213# HINT_PRESET=balanced makepkg -si
214# NERD_PATCH=true STATIC_WEIGHTS="400 500 700" makepkg -si
215# ────────────────────────────────────────────────────────────────────────────
216
217_opt_keys=(COMM_VARIANT STATIC_WEIGHTS STATIC_ITALIC STATIC_VOICES NERD_PATCH
218 HINTING HINT_PRESET HINT_MODE HINT_RANGE_MIN HINT_RANGE_MAX
219 HINT_LIMIT HINT_XHEIGHT HINT_XSNAP_EXC HINT_DEFAULT_SCRIPT
220 HINT_FALLBACK_SCRIPT HINT_WINCOMPAT HINT_TTFA_TABLE
221 HINT_FAMILY_SUFFIX HINT_INTEGER_PPEM GASP_MODE)
222
223# _ask_yn QUESTION DEFAULT(y|n) -> sets _yn to true/false
224_ask_yn() {
225 local _ans
226 read -r -p " $1? [$2] " _ans || _ans=""
227 [[ "${_ans:-$2}" =~ ^[yY] ]] && _yn=true || _yn=false
228}
229
230# _ask_choice TITLE DEFAULT KEY:description ... -> sets _choice to a KEY.
231# Enter (or EOF) keeps DEFAULT; anything else must be a listed number.
232_ask_choice() {
233 local _title="$1" _default="$2"; shift 2
234 local -a _items=("$@")
235 local _i _ans
236 echo " $_title:"
237 for _i in "${!_items[@]}"; do
238 printf " %d) %-12s %s%s\n" "$((_i + 1))" "${_items[$_i]%%:*}" "${_items[$_i]#*:}" \
239 "$([[ "${_items[$_i]%%:*}" == "$_default" ]] && echo " (default)")"
240 done
241 while :; do
242 read -r -p " > " _ans || _ans=""
243 if [[ -z "$_ans" ]]; then _choice="$_default"; return; fi
244 if [[ "$_ans" =~ ^[0-9]+$ ]] && (( _ans >= 1 && _ans <= ${#_items[@]} )); then
245 _choice="${_items[$((_ans - 1))]%%:*}"; return
246 fi
247 echo " Enter 1-${#_items[@]}, or press Enter for $_default."
248 done
249}
250
251# Called once, from prepare(): ask for whatever the environment has not set,
252# but only on a terminal, then save every set option to .build_opts.
253_prompt_options() {
254 if [[ -t 0 ]]; then
255 echo ""
256 echo " $pkgname: build options (Enter = default)"
257 echo ""
258 if [[ -z "${NERD_PATCH:-}" ]]; then
259 _ask_yn "Patch with Nerd Fonts glyphs (forces static fonts)" n; NERD_PATCH=$_yn
260 fi
261 if [[ "$NERD_PATCH" != true && -z "${COMM_VARIANT:-}" ]]; then
262 _ask_choice "Font set" variable \
263 "variable:roman + italic font, all axes (recommended)" \
264 "static:one file per weight/italic/voice, hinted per weight"
265 COMM_VARIANT=$_choice
266 fi
267 if [[ -z "${HINTING:-}" ]]; then
268 _ask_yn "Hint with ttfautohint (n = unhinted, as upstream)" y; HINTING=$_yn
269 fi
270 if [[ "$HINTING" == true && -z "${HINT_PRESET:-}" ]]; then
271 _ask_choice "Hinting preset" natural \
272 "natural:stem n, 8-48px, no x-height boost (measured best)" \
273 "balanced:stem q, 8-48px (ttfautohint defaults, opens the dots)" \
274 "sharp:stem s, 6-48px (highest contrast, distorts Thin)" \
275 "light:stem n, 12-48px, no x-height boost (HiDPI)" \
276 "custom:use the HINT_* environment variables"
277 HINT_PRESET=$_choice
278 fi
279 echo ""
280 else
281 echo "==> Non-interactive build (no TTY): using environment variables / defaults."
282 fi
283
284 local _k
285 : > "$srcdir/.build_opts"
286 for _k in "${_opt_keys[@]}"; do
287 # "set" rather than non-empty: HINT_XSNAP_EXC="" (no exceptions) differs
288 # from HINT_XSNAP_EXC unset (ttfautohint's default).
289 if [[ "${!_k+set}" == set ]]; then
290 printf '%s=%s\n' "$_k" "${!_k}" >> "$srcdir/.build_opts"
291 fi
292 done
293}
294
295# Load .build_opts (whitelisted keys only), then apply defaults and validate.
296# makepkg runs build() and package() in separate shells, so both call this.
297_resolve_options() {
298 local _k _v
299 if [[ ! -f "$srcdir/.build_opts" ]]; then
300 echo "Error: $srcdir/.build_opts missing; run a full makepkg (prepare() writes it)." >&2
301 exit 1
302 fi
303 while IFS='=' read -r _k _v; do
304 if [[ " ${_opt_keys[*]} " == *" $_k "* ]]; then
305 printf -v "$_k" '%s' "$_v"
306 fi
307 done < "$srcdir/.build_opts"
308
309 COMM_VARIANT="${COMM_VARIANT:-variable}"
310 STATIC_WEIGHTS="${STATIC_WEIGHTS:-100 200 300 400 500 600 700 800 900}"
311 STATIC_ITALIC="${STATIC_ITALIC:-true}"
312 STATIC_VOICES="${STATIC_VOICES:-default}"
313 NERD_PATCH="${NERD_PATCH:-false}"
314 HINTING="${HINTING:-true}"
315 HINT_PRESET="${HINT_PRESET:-natural}"
316 GASP_MODE="${GASP_MODE:-keep}"
317 HINT_WINCOMPAT="${HINT_WINCOMPAT:-false}"
318 HINT_TTFA_TABLE="${HINT_TTFA_TABLE:-false}"
319 HINT_INTEGER_PPEM="${HINT_INTEGER_PPEM:-false}"
320 HINT_DEFAULT_SCRIPT="${HINT_DEFAULT_SCRIPT:-latn}"
321 HINT_FALLBACK_SCRIPT="${HINT_FALLBACK_SCRIPT:-latn}"
322
323 case "$COMM_VARIANT" in
324 variable|static) ;;
325 *) echo "Error: COMM_VARIANT='$COMM_VARIANT' invalid (variable|static)." >&2; exit 1 ;;
326 esac
327 for _v in STATIC_ITALIC NERD_PATCH HINTING HINT_WINCOMPAT HINT_TTFA_TABLE HINT_INTEGER_PPEM; do
328 case "${!_v}" in
329 true|false) ;;
330 *) echo "Error: $_v='${!_v}' must be true or false." >&2; exit 1 ;;
331 esac
332 done
333 # font-patcher drops fvar/gvar, so patching always works on statics.
334 if [[ "$NERD_PATCH" == true && "$COMM_VARIANT" == variable ]]; then
335 echo "==> Note: the variable font cannot be Nerd-patched; using COMM_VARIANT=static."
336 COMM_VARIANT=static
337 fi
338 for _v in $STATIC_WEIGHTS; do
339 case "$_v" in
340 100|200|300|400|500|600|700|800|900) ;;
341 *) echo "Error: STATIC_WEIGHTS: '$_v' is not 100, 200, ... 900." >&2; exit 1 ;;
342 esac
343 done
344 local _voice _seen=" "
345 for _voice in $STATIC_VOICES; do
346 case "$_voice" in
347 default|flare|loud) ;;
348 *) echo "Error: STATIC_VOICES: '$_voice' is not default, flare or loud." >&2; exit 1 ;;
349 esac
350 if [[ "$_seen" == *" $_voice "* ]]; then
351 echo "Error: STATIC_VOICES: '$_voice' listed twice." >&2; exit 1
352 fi
353 _seen+="$_voice "
354 done
355
356 local _m _lo _hi _xh
357 case "$HINT_PRESET" in
358 natural) _m=nnn; _lo=8; _hi=48; _xh=0 ;;
359 balanced) _m=qsq; _lo=8; _hi=48; _xh=14 ;;
360 sharp) _m=sss; _lo=6; _hi=48; _xh=14 ;;
361 light) _m=nnn; _lo=12; _hi=48; _xh=0 ;;
362 custom) _m=nnn; _lo=8; _hi=48; _xh=0 ;;
363 *)
364 echo "Error: unknown HINT_PRESET='$HINT_PRESET'." >&2
365 echo " Valid: natural balanced sharp light custom" >&2
366 exit 1 ;;
367 esac
368 # A preset never overrides a value the user set explicitly.
369 HINT_MODE="${HINT_MODE:-$_m}"
370 HINT_RANGE_MIN="${HINT_RANGE_MIN:-$_lo}"
371 HINT_RANGE_MAX="${HINT_RANGE_MAX:-$_hi}"
372 HINT_LIMIT="${HINT_LIMIT:-$HINT_RANGE_MAX}"
373 HINT_XHEIGHT="${HINT_XHEIGHT:-$_xh}"
374
375 if [[ ! "$HINT_MODE" =~ ^[nqs]{3}$ ]]; then
376 echo "Error: HINT_MODE='$HINT_MODE' must be 3 chars from n/q/s (e.g. nnn)." >&2
377 exit 1
378 fi
379 for _v in HINT_RANGE_MIN HINT_RANGE_MAX HINT_LIMIT HINT_XHEIGHT; do
380 if [[ ! "${!_v}" =~ ^[0-9]+$ ]]; then
381 echo "Error: $_v='${!_v}' must be a non-negative integer." >&2; exit 1
382 fi
383 done
384 if (( HINT_RANGE_MIN > HINT_RANGE_MAX )); then
385 echo "Error: HINT_RANGE_MIN ($HINT_RANGE_MIN) > HINT_RANGE_MAX ($HINT_RANGE_MAX)." >&2
386 exit 1
387 fi
388 for _v in HINT_DEFAULT_SCRIPT HINT_FALLBACK_SCRIPT; do
389 if [[ ! "${!_v}" =~ ^[a-z0-9]{3,4}$ ]]; then
390 echo "Error: $_v='${!_v}' must be a script tag (e.g. latn, none)." >&2; exit 1
391 fi
392 done
393 case "$GASP_MODE" in
394 keep|sized|smooth|gridfit) ;;
395 *) echo "Error: GASP_MODE='$GASP_MODE' invalid (keep|sized|smooth|gridfit)." >&2; exit 1 ;;
396 esac
397}
398
399prepare() {
400 _prompt_options
401 _resolve_options # validate now, so a typo fails before the long steps
402
403 rm -rf "$srcdir/font-patcher"
404 if [[ "$NERD_PATCH" == true ]]; then
405 mkdir -p "$srcdir/font-patcher"
406 bsdtar xf "$srcdir/font-patcher-${_nfver}.zip" -C "$srcdir/font-patcher"
407 fi
408}
409
410build() {
411 _resolve_options
412
413 echo "==> Options: variant=$COMM_VARIANT nerd=$NERD_PATCH hinting=$HINTING"
414 if [[ "$COMM_VARIANT" == static ]]; then
415 echo "==> Statics: weights='$STATIC_WEIGHTS' italic=$STATIC_ITALIC voices='$STATIC_VOICES'"
416 fi
417 if [[ "$HINTING" == true ]]; then
418 echo "==> ttfautohint: preset=$HINT_PRESET stem=$HINT_MODE" \
419 "range=$HINT_RANGE_MIN-$HINT_RANGE_MAX limit=$HINT_LIMIT x-height=$HINT_XHEIGHT" \
420 "x-snap-exc='${HINT_XSNAP_EXC-<default>}' gasp=$GASP_MODE integer-ppem=$HINT_INTEGER_PPEM" \
421 "ttfa-table=$HINT_TTFA_TABLE${HINT_FAMILY_SUFFIX:+ suffix='$HINT_FAMILY_SUFFIX'}"
422 fi
423
424 rm -rf "$srcdir/selected" "$srcdir/patched" "$srcdir/hinted"
425 mkdir -p "$srcdir/selected" "$srcdir/patched" "$srcdir/hinted"
426
427 # ── Step 1: select ──────────────────────────────────────────────────────────
428 if [[ "$COMM_VARIANT" == variable ]]; then
429 # A plain filename: brackets in file names upset some tools and globs.
430 install -m644 "$srcdir/Commissioner-Roman.ttf" "$srcdir/Commissioner-Italic.ttf" \
431 "$srcdir/selected/"
432 else
433 echo "==> Cutting static instances..."
434 local -a _jobs=()
435 local _src _w _voice _flar _volm
436 for _src in Roman Italic; do
437 [[ "$_src" == Italic && "$STATIC_ITALIC" != true ]] && continue
438 for _w in $STATIC_WEIGHTS; do
439 for _voice in $STATIC_VOICES; do
440 case "$_voice" in
441 default) _flar=0; _volm=0 ;;
442 flare) _flar=100; _volm=0 ;;
443 loud) _flar=100; _volm=100 ;;
444 esac
445 _jobs+=("$_w $_flar $_volm $_src")
446 done
447 done
448 done
449 printf '%s\n' "${_jobs[@]}" | parallel --will-cite -j"$(nproc)" --colsep ' ' \
450 python "$srcdir/make_statics.py" --wght {1} --flar {2} --volm {3} \
451 -o "$srcdir/selected" "$srcdir/Commissioner-{4}.ttf"
452 fi
453 local _count
454 _count=$(find "$srcdir/selected" -name '*.ttf' | wc -l)
455 echo "==> Selected $_count font file(s)."
456 if (( _count == 0 )); then
457 echo "Error: no fonts produced." >&2; exit 1
458 fi
459
460 local _hintsrc="$srcdir/selected"
461
462 # ── Step 2: Nerd Fonts patching ─────────────────────────────────────────────
463 # Patch first, hint afterwards: font-patcher does not keep hint bytecode, and
464 # ttfautohint then sees the final glyph set including the icons.
465 if [[ "$NERD_PATCH" == true ]]; then
466 echo "==> Patching with Nerd Fonts glyphs (this takes a while)..."
467 find "$srcdir/selected" -type f | sort | \
468 parallel --will-cite -j"$(nproc)" python "$srcdir/font-patcher/font-patcher" \
469 --variable-width-glyphs -q -c {} -out "$srcdir/patched" '>/dev/null'
470 _count=$(find "$srcdir/patched" -maxdepth 1 -name '*.ttf' | wc -l)
471 if (( _count == 0 )); then
472 echo "Error: font-patcher produced no output. See messages above." >&2; exit 1
473 fi
474 echo "==> Patched $_count font(s)."
475 _hintsrc="$srcdir/patched"
476 fi
477
478 if [[ "$HINTING" != true ]]; then
479 return 0
480 fi
481
482 # ── Step 3: ttfautohint ─────────────────────────────────────────────────────
483 # On a variable font ttfautohint keeps fvar/gvar/STAT/MVAR and every named
484 # instance, but its blue zones come from the default master (Thin) and its
485 # --increase-x-height is ignored. On a static it hints that exact instance.
486 local -a _ta_opts=(
487 --hinting-range-min="$HINT_RANGE_MIN"
488 --hinting-range-max="$HINT_RANGE_MAX"
489 --hinting-limit="$HINT_LIMIT"
490 --increase-x-height="$HINT_XHEIGHT"
491 --stem-width-mode="$HINT_MODE"
492 --default-script="$HINT_DEFAULT_SCRIPT"
493 --ignore-restrictions
494 )
495 if [[ "$NERD_PATCH" == true ]]; then
496 # Icons would otherwise get Latin blue zones: scale them instead.
497 _ta_opts+=(--fallback-script=none --fallback-scaling)
498 else
499 _ta_opts+=(--fallback-script="$HINT_FALLBACK_SCRIPT")
500 fi
501 # Honour an explicit empty HINT_XSNAP_EXC (= no exceptions); omit if unset.
502 if [[ "${HINT_XSNAP_EXC+set}" == set ]]; then
503 _ta_opts+=(--x-height-snapping-exceptions="$HINT_XSNAP_EXC")
504 fi
505 if [[ "$HINT_WINCOMPAT" == true ]]; then _ta_opts+=(--windows-compatibility); fi
506 if [[ -n "${HINT_FAMILY_SUFFIX:-}" ]]; then _ta_opts+=(--family-suffix="$HINT_FAMILY_SUFFIX"); fi
507 if [[ "$HINT_TTFA_TABLE" == true ]]; then _ta_opts+=(--ttfa-table); else _ta_opts+=(--no-info); fi
508
509 # Serial on purpose: GNU parallel rebuilds the command line from its
510 # arguments, so a knob whose value contains a space (HINT_FAMILY_SUFFIX="
511 # Hinted", HINT_XSNAP_EXC="10-12 14") is split and ttfautohint prints its
512 # usage and fails. Hinting one font takes well under a second.
513 echo "==> Hinting with ttfautohint..."
514 local _f
515 for _f in "$_hintsrc"/*.ttf; do
516 ttfautohint "${_ta_opts[@]}" "$_f" "$srcdir/hinted/${_f##*/}"
517 done
518
519 if [[ "$HINT_INTEGER_PPEM" == true ]]; then
520 echo "==> Setting head.flags bit 3 (integer ppem)..."
521 python - "$srcdir/hinted/"*.ttf <<'PY'
522import sys
523from fontTools.ttLib import TTFont
524for path in sys.argv[1:]:
525 font = TTFont(path)
526 font["head"].flags |= 1 << 3
527 font.save(path)
528PY
529 fi
530
531 if [[ "$GASP_MODE" != keep ]]; then
532 echo "==> Applying gasp mode '$GASP_MODE'..."
533 python "$srcdir/set_gasp.py" "$GASP_MODE" "$srcdir/hinted/"*.ttf
534 fi
535}
536
537package() {
538 _resolve_options
539
540 local _out="$srcdir/selected"
541 if [[ "$HINTING" == true ]]; then
542 _out="$srcdir/hinted"
543 elif [[ "$NERD_PATCH" == true ]]; then
544 _out="$srcdir/patched"
545 fi
546 if ! compgen -G "$_out/*.ttf" >/dev/null; then
547 echo "Error: no fonts to install in $_out." >&2; exit 1
548 fi
549 local _fontdir="$pkgdir/usr/share/fonts/commissioner"
550 install -Dm644 -t "$_fontdir" "$_out/"*.ttf
551 install -Dm644 "$srcdir/OFL.txt" "$pkgdir/usr/share/licenses/$pkgname/OFL.txt"
552
553 local _avail="$pkgdir/usr/share/fontconfig/conf.avail"
554 install -d "$_avail"
555
556 # Family names for the FLAR/VOLM voices. Variable build only: the statics
557 # have no axes, and with STATIC_VOICES="default flare loud" they are already
558 # "Commissioner", "Commissioner Flare" and "Commissioner Loud".
559 if [[ "$COMM_VARIANT" == variable ]]; then
560 install -Dm644 -t "$_avail" "$srcdir/79-commissioner-families.conf"
561 fi
562
563 # Opt-in rendering tweak (see the header): bytecode hinting for the families
564 # this build installed. Shipped in conf.avail and deliberately not enabled.
565 # The family list is read from the fonts, so Nerd Fonts names and family
566 # suffixes are covered.
567 if [[ "$HINTING" == true ]]; then
568 local _fam _conf="$_avail/80-$pkgname.conf"
569 local _chromium_prgnames=(chrome chromium brave helium electron code slack signal-desktop vesktop)
570 local _prg _prgtests=""
571 for _prg in "${_chromium_prgnames[@]}"; do
572 _prgtests+=" <test qual=\"all\" name=\"prgname\" compare=\"not_eq\"><string>$_prg</string></test>"$'\n'
573 done
574 {
575 echo '<?xml version="1.0" encoding="UTF-8"?>'
576 echo '<!DOCTYPE fontconfig SYSTEM "urn:fontconfig:fonts.dtd">'
577 echo "<!-- Installed by $pkgname. Use the ttfautohint bytecode hints in these"
578 echo " fonts rather than FreeType's light autohinter. hintmedium, not"
579 echo " hintfull: same rendering in cairo/pango, and Blink keeps subpixel"
580 echo " positioning at hintmedium but drops it at hintfull."
581 echo " Chromium and Electron apps are excluded: at device scale 1 they use"
582 echo " linear advances only for hintslight/none, so hintmedium would round"
583 echo " every glyph advance to a whole pixel (uneven letter gaps)."
584 echo ""
585 echo " Commissioner does not need this file as badly as Geologica does:"
586 echo " its x-height barely moves with the weight, so the bytecode and the"
587 echo " autohinter snap to the same pixel at 13-16 px. What the bytecode"
588 echo " cannot do is keep the dot of i off the stem at 10-14 px in Bold and"
589 echo " Black (72 glued cases at wght 900 against the autohinter's 34), and"
590 echo " that is the argument for staying on hintstyle hintslight. See"
591 echo " README.md section 2. -->"
592 echo '<fontconfig>'
593 # One <match> per family: several <test>s in one <match> are ANDed, so
594 # the prgname tests exclude every listed app. qual="all" makes a test
595 # pass when prgname is unset. prgname is the executable's basename.
596 while IFS= read -r _fam; do
597 _fam="${_fam//&/&amp;}"; _fam="${_fam//</&lt;}"
598 cat <<EOF
599 <match target="font">
600 <test name="family" compare="eq"><string>$_fam</string></test>
601${_prgtests%$'\n'}
602 <edit name="hinting" mode="assign"><bool>true</bool></edit>
603 <edit name="autohint" mode="assign"><bool>false</bool></edit>
604 <edit name="hintstyle" mode="assign"><const>hintmedium</const></edit>
605 </match>
606EOF
607 done < <(python - "$_fontdir/"*.ttf <<'PY' | sort -u
608import sys
609from fontTools.ttLib import TTFont
610for path in sys.argv[1:]:
611 name = TTFont(path, lazy=True)["name"]
612 print(name.getDebugName(16) or name.getDebugName(1))
613PY
614)
615 echo '</fontconfig>'
616 } > "$_conf"
617 chmod 644 "$_conf"
618 fi
619
620 install -Dm644 -t "$pkgdir/usr/share/doc/$pkgname" "$srcdir/README.md" "$srcdir/FEATURES.md"
621}
622

Scan history

Scanned at (UTC)SeverityRules
2026-10-26 15:45:54 Low 1

Report a package

Reports go to the AURWatch maintainer (one person) and are read by hand. No login required.

0 / 4000
Your suggestion