ttf-geologica-hinted
LOW
maintainer gaougalos
0 votes
scanned 2026-10-26 15:45:54.756943
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>
2
pkgname=ttf-geologica-hinted
3
_geover=1.010
4
_geocommit=685f38d7c9e86b0c8530204c97ddcaf6558dd17b
5
_nfver=3.5.1
6
pkgver="${_geover}"
7
pkgrel=2
8
pkgdesc='Geologica, a humanist-geometric sans with Sharp/Cursive/Slant/Weight axes, re-hinted with ttfautohint for FreeType (optionally static / Nerd Fonts patched)'
9
arch=('any')
10
url='https://github.com/googlefonts/geologica'
11
license=('OFL-1.1')
12
makedepends=('python' 'python-fonttools' 'fontforge' 'parallel' 'ttfautohint')
13
options=(!strip)
14
provides=('ttf-geologica')
15
conflicts=('ttf-geologica')
16
source=(
17
"GeologicaVariable.ttf::https://raw.githubusercontent.com/googlefonts/geologica/${_geocommit}/fonts/variable/Geologica%5BCRSV%2CSHRP%2Cslnt%2Cwght%5D.ttf"
18
"OFL.txt::https://raw.githubusercontent.com/googlefonts/geologica/${_geocommit}/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-geologica-families.conf"
24
"README.md"
25
"FEATURES.md"
26
)
27
noextract=("font-patcher-${_nfver}.zip")
28
sha256sums=('9124d9e88ac6c11d761f35241713a51d68e2c4ebedce0edaca834717a00959ec'
29
'778186245840aea0e60bec6a46e7fb1442e0cd78e41afeadffcd3e8824b379e0'
30
'42bcb32145499a35732274c7fc48deb434ad0d2e0e118f98527c1479c6fa251a'
31
'53d9a3969632b52c7afc5b1968aab4b583c7933641ce3394735705a0cb8e450b'
32
'0b0464b268525569b5fa28b4735791efb22f22829ad178629776ae3ea82d7f99'
33
'cfc6aa1b72cd6649d102a0a97ad1be0fcfc12476fc75a9d8251b6afd2a724fe1'
34
'be02c70c77195b258b9333d32c7eabfbc3e19d74232bd4f31e2b1a02d8424f0c'
35
'169e2ad429d88f819c39764dcd68d57d5da4778c1720d32e843890b39210648c'
36
'1acf0491c2a831a87e1cb09e1a8151dabc02bdf80e33807c7b52ed07be2e5028')
37
38
# ── What this package does ──────────────────────────────────────────────────
39
#
40
# Geologica (Monokrom, 2023) ships as ONE variable font with four axes:
41
# wght 100-900 (default 100!), SHRP 0-100 (sharpness), CRSV 0-1 (cursive
42
# a/g), slnt 0..-12 (oblique). Upstream is ARCHIVED and has no releases: this
43
# package pins the variable font to the commit Google Fonts took it from, so
44
# the source never changes underneath us.
45
#
46
# Upstream ships NO hinting: no fpgm, no cvt, 0 glyph instructions, a 7-byte
47
# prep. At hintstyle hintmedium FreeType then treats the face as hinted and
48
# does not run its autohinter, i.e. GTK (manual mode, font-hinting medium)
49
# shows Geologica completely unhinted. Measured on FreeType 2.15.0, word
50
# "Hamburgefonstiv Αλφάβητο 0123", Regular/Medium/SemiBold, 10-20 px, the
51
# i/j/ï/ä/ö/ü/ë/ё/ϊ/ϋ/ΐ/ΰ dots and diaereses touching the letter body:
52
#
53
# unhinted (hintmedium) 8/ 25/ 44/ 74 glued at wght 100/400/700/900
54
# hintslight (autohinter) 0/ 2/ 7/ 0 glued
55
# bytecode "natural" (n) 0/ 5/ 18/ 22 glued
56
# bytecode "balanced" (q) 0/ 5/ 14/ 10 glued
57
# bytecode "sharp" (s) 0/ 0/ 18/ 22 glued
58
#
59
# and the deviation from the designed outline (summed coverage difference to
60
# an 8x supersampled unhinted render, % of its ink, best 1/8 px alignment,
61
# 12/13/14/16 px; lower is closer to the design):
62
#
63
# unhinted 3.5/ 0.8/ / (≈0: nothing is snapped)
64
# hintslight (autohinter) 12.3/12.1/10.7/ 7.5
65
# bytecode natural 12.5/11.6/10.7/10.6 <- default
66
# bytecode balanced 13.6/14.7/11.2/11.0
67
# bytecode sharp 21.9/12.7/11.6/11.4
68
# static, hinted per weight 11.7/11.5/10.2/ 7.5
69
#
70
# Two things make Geologica different from the sibling font packages:
71
#
72
# 1. Its default master is Thin, not Regular (fvar wght default 100, so the
73
# fonts are even named "Geologica Thin"). ttfautohint takes its blue zones
74
# from that default master, but Geologica's x-height grows with weight:
75
# 960 (Thin) 990 (Regular) 1020 (Bold) 1040 (Black) font units at 2000 upem,
76
# and SHRP=100 pushes ascenders/descenders 40 units further. The bytecode
77
# therefore snaps every weight to Thin's zones: fine at 100-400 px, up to
78
# 0.4 px off at 700-900, which is why "sharp"/"balanced" measure worse and
79
# why the static build (one hint pass per weight) beats the VF at 700-900.
80
# FreeType's own light autohinter recomputes its zones from the outline it
81
# is given, so it tracks every weight and axis value - it is the best
82
# renderer for Geologica, and it needs no package at all: it is what
83
# hintstyle hintslight (the Arch default) already does. The bytecode this
84
# package adds is for the many desktops that run hintmedium/hintfull, where
85
# the unhinted upstream font renders with no hinting whatsoever.
86
#
87
# 2. pango maps the italic style to the FULL named instance (slnt=-12 AND
88
# CRSV=1, the cursive a/g), and does not double-slant it: "Geologica
89
# Italic 72" measures 11.96 degrees and is pixel-identical to
90
# "@slnt=-12,CRSV=1". No fontconfig rule is needed, unlike Roboto Flex.
91
# fontconfig exposes all 18 named instances as styles of "Geologica", and
92
# pango takes wght from the requested weight: "Geologica 12" is
93
# pixel-identical to "@wght=400", "Geologica Bold" to "@wght=700". The
94
# Thin default therefore does not leak into applications. Browsers are the
95
# exception: they map italic to slnt only (CSS has no CRSV), so add
96
# font-feature-settings: "ss01" for the cursive forms - ss01 is
97
# pixel-identical to CRSV=1 (verified with pango 1.58).
98
#
99
# FreeType only runs TrueType bytecode at hintstyle hintmedium/hintfull. This
100
# package ships (not enabled) /usr/share/fontconfig/conf.avail/80-$pkgname.conf
101
# which turns bytecode hinting on for the installed families only, and
102
# excludes Chromium/Electron by prgname (at device scale 1 they round every
103
# advance at hintmedium). It only affects clients that read hinting from
104
# fontconfig (Firefox, Chromium, Qt, kitty/foot): GTK3/GTK4 in manual mode take
105
# the hint style from gsettings and ignore fontconfig's hintstyle. See
106
# README.md for the recommended desktop settings, which for Geologica are
107
# hintslight + stem darkening, not hintmedium.
108
#
109
# ── Build-time options ───────────────────────────────────────────────────────
110
#
111
# Run from a terminal, prepare() asks for GEO_VARIANT, NERD_PATCH, HINTING and
112
# HINT_PRESET (Enter keeps the default). Any of them already set in the
113
# environment is not asked. Without a TTY nothing is asked and the
114
# environment / defaults below apply. Answers are saved to $srcdir/.build_opts.
115
#
116
# GEO_VARIANT variable|static (default: variable)
117
# variable the single 4-axis font: every axis stays usable
118
# static one TTF per weight (and italic), cut with the
119
# fontTools instancer; each one is hinted on its own,
120
# so its blue zones match that exact weight
121
#
122
# STATIC_WEIGHTS "100 ... 900" wght values to cut (default: all nine:
123
# 100 200 300 400 500 600 700 800 900)
124
# STATIC_ITALIC true/false also cut the italic (CRSV=1, slnt=-12),
125
# default true
126
# STATIC_SHRP 0|100 cut the normal or the Sharp (SHRP=100) design;
127
# Sharp statics become the family
128
# "Geologica Sharp", as in the Monokrom release
129
#
130
# NERD_PATCH true/false Patch with Nerd Fonts glyphs (default: false).
131
# font-patcher drops variable axes, so this forces
132
# GEO_VARIANT=static. Family becomes
133
# "Geologica Nerd Font (Mono|Propo)".
134
#
135
# HINTING true/false Hint with ttfautohint (default: true). false installs
136
# the fonts as upstream ships them: no hinting at all
137
# at hintmedium, the autohinter at hintslight.
138
#
139
# HINT_PRESET name Named bundle of ttfautohint flags (default: natural)
140
# ("Linux" = the stem letter FreeType v40 actually uses)
141
# natural stem=nnn (Linux: n) range 8-48 x-height 0 (closest to
142
# the outline; best
143
# measured preset)
144
# balanced stem=qsq (Linux: q) range 8-48 x-height 14 (ttfautohint
145
# defaults; opens the
146
# dots at 700-900)
147
# sharp stem=sss (Linux: s) range 6-48 x-height 14 (highest
148
# contrast, distorts
149
# Thin badly: 21.9)
150
# light stem=nnn (Linux: n) range 12-48 x-height 0 (HiDPI; <12px
151
# left alone)
152
# custom use the individual HINT_* vars below verbatim
153
#
154
# Individual ttfautohint knobs (override the preset; empty = use preset value):
155
# HINT_MODE nnn|qqq|qsq|sss 3-char --stem-width-mode (grayscale/GDI/DW)
156
# On Linux only the 3rd letter has an effect.
157
# HINT_RANGE_MIN N --hinting-range-min
158
# HINT_RANGE_MAX N --hinting-range-max
159
# HINT_LIMIT N --hinting-limit (default: HINT_RANGE_MAX; 0 = no limit)
160
# HINT_XHEIGHT N --increase-x-height (0 = disable)
161
# HINT_XSNAP_EXC STR --x-height-snapping-exceptions (unset = none)
162
# HINT_DEFAULT_SCRIPT STR --default-script (default: latn)
163
# HINT_FALLBACK_SCRIPT STR --fallback-script (default: latn; Nerd-patched
164
# builds always use none plus --fallback-scaling)
165
# HINT_WINCOMPAT true/false --windows-compatibility (two extra blue zones
166
# for GDI ClearType; Windows/Wine only)
167
# HINT_TTFA_TABLE true/false --ttfa-table (default: false)
168
# HINT_FAMILY_SUFFIX STR -F/--family-suffix, e.g. " Hinted"
169
#
170
# HINT_INTEGER_PPEM true/false Set head.flags bit 3 ("ppem is always an
171
# integer"), which ttfautohint leaves clear and fontbakery
172
# wants set for hinted fonts (default: false). On Linux
173
# it is not cosmetic: FreeType then rounds fractional
174
# sizes up to whole pixels, measured +19% ink at 8 pt
175
# and +16% at 11 pt (96 dpi) on this font, so text is
176
# bigger and line breaks shift. Enable it only to match
177
# Windows GDI, not for Unix desktops.
178
#
179
# GASP_MODE keep|sized|smooth|gridfit (default: keep; FreeType ignores gasp,
180
# Windows uses it)
181
# keep leave the gasp table as ttfautohint wrote it
182
# sized MS strategy: <=8 grayscale, 9-16 gridfit, 17+ both
183
# smooth all sizes anti-aliased ({0xFFFF: 15})
184
# gridfit all sizes gridfit+grayscale ({0xFFFF: 3})
185
#
186
# Examples:
187
# makepkg -si (asks, when run from a terminal)
188
# makepkg -si </dev/null (never asks: defaults only)
189
# GEO_VARIANT=static makepkg -si
190
# GEO_VARIANT=static STATIC_SHRP=100 STATIC_ITALIC=false makepkg -si
191
# HINT_PRESET=balanced makepkg -si
192
# NERD_PATCH=true STATIC_WEIGHTS="400 500 700" makepkg -si
193
# ────────────────────────────────────────────────────────────────────────────
194
195
_opt_keys=(GEO_VARIANT STATIC_WEIGHTS STATIC_ITALIC STATIC_SHRP NERD_PATCH
196
HINTING HINT_PRESET HINT_MODE HINT_RANGE_MIN HINT_RANGE_MAX
197
HINT_LIMIT HINT_XHEIGHT HINT_XSNAP_EXC HINT_DEFAULT_SCRIPT
198
HINT_FALLBACK_SCRIPT HINT_WINCOMPAT HINT_TTFA_TABLE
199
HINT_FAMILY_SUFFIX HINT_INTEGER_PPEM GASP_MODE)
200
201
# _ask_yn QUESTION DEFAULT(y|n) -> sets _yn to true/false
202
_ask_yn() {
203
local _ans
204
read -r -p " $1? [$2] " _ans || _ans=""
205
[[ "${_ans:-$2}" =~ ^[yY] ]] && _yn=true || _yn=false
206
}
207
208
# _ask_choice TITLE DEFAULT KEY:description ... -> sets _choice to a KEY.
209
# Enter (or EOF) keeps DEFAULT; anything else must be a listed number.
210
_ask_choice() {
211
local _title="$1" _default="$2"; shift 2
212
local -a _items=("$@")
213
local _i _ans
214
echo " $_title:"
215
for _i in "${!_items[@]}"; do
216
printf " %d) %-12s %s%s\n" "$((_i + 1))" "${_items[$_i]%%:*}" "${_items[$_i]#*:}" \
217
"$([[ "${_items[$_i]%%:*}" == "$_default" ]] && echo " (default)")"
218
done
219
while :; do
220
read -r -p " > " _ans || _ans=""
221
if [[ -z "$_ans" ]]; then _choice="$_default"; return; fi
222
if [[ "$_ans" =~ ^[0-9]+$ ]] && (( _ans >= 1 && _ans <= ${#_items[@]} )); then
223
_choice="${_items[$((_ans - 1))]%%:*}"; return
224
fi
225
echo " Enter 1-${#_items[@]}, or press Enter for $_default."
226
done
227
}
228
229
# Called once, from prepare(): ask for whatever the environment has not set,
230
# but only on a terminal, then save every set option to .build_opts.
231
_prompt_options() {
232
if [[ -t 0 ]]; then
233
echo ""
234
echo " $pkgname: build options (Enter = default)"
235
echo ""
236
if [[ -z "${NERD_PATCH:-}" ]]; then
237
_ask_yn "Patch with Nerd Fonts glyphs (forces static fonts)" n; NERD_PATCH=$_yn
238
fi
239
if [[ "$NERD_PATCH" != true && -z "${GEO_VARIANT:-}" ]]; then
240
_ask_choice "Font set" variable \
241
"variable:one 4-axis font (recommended)" \
242
"static:one file per weight/italic, hinted per weight"
243
GEO_VARIANT=$_choice
244
fi
245
if [[ -z "${HINTING:-}" ]]; then
246
_ask_yn "Hint with ttfautohint (n = unhinted, as upstream)" y; HINTING=$_yn
247
fi
248
if [[ "$HINTING" == true && -z "${HINT_PRESET:-}" ]]; then
249
_ask_choice "Hinting preset" natural \
250
"natural:stem n, 8-48px, no x-height boost (measured best)" \
251
"balanced:stem q, 8-48px (ttfautohint defaults, opens the dots)" \
252
"sharp:stem s, 6-48px (highest contrast, distorts Thin)" \
253
"light:stem n, 12-48px, no x-height boost (HiDPI)" \
254
"custom:use the HINT_* environment variables"
255
HINT_PRESET=$_choice
256
fi
257
echo ""
258
else
259
echo "==> Non-interactive build (no TTY): using environment variables / defaults."
260
fi
261
262
local _k
263
: > "$srcdir/.build_opts"
264
for _k in "${_opt_keys[@]}"; do
265
# "set" rather than non-empty: HINT_XSNAP_EXC="" (no exceptions) differs
266
# from HINT_XSNAP_EXC unset (ttfautohint's default).
267
if [[ "${!_k+set}" == set ]]; then
268
printf '%s=%s\n' "$_k" "${!_k}" >> "$srcdir/.build_opts"
269
fi
270
done
271
}
272
273
# Load .build_opts (whitelisted keys only), then apply defaults and validate.
274
# makepkg runs build() and package() in separate shells, so both call this.
275
_resolve_options() {
276
local _k _v
277
if [[ ! -f "$srcdir/.build_opts" ]]; then
278
echo "Error: $srcdir/.build_opts missing; run a full makepkg (prepare() writes it)." >&2
279
exit 1
280
fi
281
while IFS='=' read -r _k _v; do
282
if [[ " ${_opt_keys[*]} " == *" $_k "* ]]; then
283
printf -v "$_k" '%s' "$_v"
284
fi
285
done < "$srcdir/.build_opts"
286
287
GEO_VARIANT="${GEO_VARIANT:-variable}"
288
STATIC_WEIGHTS="${STATIC_WEIGHTS:-100 200 300 400 500 600 700 800 900}"
289
STATIC_ITALIC="${STATIC_ITALIC:-true}"
290
STATIC_SHRP="${STATIC_SHRP:-0}"
291
NERD_PATCH="${NERD_PATCH:-false}"
292
HINTING="${HINTING:-true}"
293
HINT_PRESET="${HINT_PRESET:-natural}"
294
GASP_MODE="${GASP_MODE:-keep}"
295
HINT_WINCOMPAT="${HINT_WINCOMPAT:-false}"
296
HINT_TTFA_TABLE="${HINT_TTFA_TABLE:-false}"
297
HINT_INTEGER_PPEM="${HINT_INTEGER_PPEM:-false}"
298
HINT_DEFAULT_SCRIPT="${HINT_DEFAULT_SCRIPT:-latn}"
299
HINT_FALLBACK_SCRIPT="${HINT_FALLBACK_SCRIPT:-latn}"
300
301
case "$GEO_VARIANT" in
302
variable|static) ;;
303
*) echo "Error: GEO_VARIANT='$GEO_VARIANT' invalid (variable|static)." >&2; exit 1 ;;
304
esac
305
for _v in STATIC_ITALIC NERD_PATCH HINTING HINT_WINCOMPAT HINT_TTFA_TABLE HINT_INTEGER_PPEM; do
306
case "${!_v}" in
307
true|false) ;;
308
*) echo "Error: $_v='${!_v}' must be true or false." >&2; exit 1 ;;
309
esac
310
done
311
# font-patcher drops fvar/gvar, so patching always works on statics.
312
if [[ "$NERD_PATCH" == true && "$GEO_VARIANT" == variable ]]; then
313
echo "==> Note: the variable font cannot be Nerd-patched; using GEO_VARIANT=static."
314
GEO_VARIANT=static
315
fi
316
for _v in $STATIC_WEIGHTS; do
317
case "$_v" in
318
100|200|300|400|500|600|700|800|900) ;;
319
*) echo "Error: STATIC_WEIGHTS: '$_v' is not 100, 200, ... 900." >&2; exit 1 ;;
320
esac
321
done
322
case "$STATIC_SHRP" in
323
0|100) ;;
324
*) echo "Error: STATIC_SHRP='$STATIC_SHRP' must be 0 or 100." >&2; exit 1 ;;
325
esac
326
327
local _m _lo _hi _xh
328
case "$HINT_PRESET" in
329
natural) _m=nnn; _lo=8; _hi=48; _xh=0 ;;
330
balanced) _m=qsq; _lo=8; _hi=48; _xh=14 ;;
331
sharp) _m=sss; _lo=6; _hi=48; _xh=14 ;;
332
light) _m=nnn; _lo=12; _hi=48; _xh=0 ;;
333
custom) _m=nnn; _lo=8; _hi=48; _xh=0 ;;
334
*)
335
echo "Error: unknown HINT_PRESET='$HINT_PRESET'." >&2
336
echo " Valid: natural balanced sharp light custom" >&2
337
exit 1 ;;
338
esac
339
# A preset never overrides a value the user set explicitly.
340
HINT_MODE="${HINT_MODE:-$_m}"
341
HINT_RANGE_MIN="${HINT_RANGE_MIN:-$_lo}"
342
HINT_RANGE_MAX="${HINT_RANGE_MAX:-$_hi}"
343
HINT_LIMIT="${HINT_LIMIT:-$HINT_RANGE_MAX}"
344
HINT_XHEIGHT="${HINT_XHEIGHT:-$_xh}"
345
346
if [[ ! "$HINT_MODE" =~ ^[nqs]{3}$ ]]; then
347
echo "Error: HINT_MODE='$HINT_MODE' must be 3 chars from n/q/s (e.g. nnn)." >&2
348
exit 1
349
fi
350
for _v in HINT_RANGE_MIN HINT_RANGE_MAX HINT_LIMIT HINT_XHEIGHT; do
351
if [[ ! "${!_v}" =~ ^[0-9]+$ ]]; then
352
echo "Error: $_v='${!_v}' must be a non-negative integer." >&2; exit 1
353
fi
354
done
355
if (( HINT_RANGE_MIN > HINT_RANGE_MAX )); then
356
echo "Error: HINT_RANGE_MIN ($HINT_RANGE_MIN) > HINT_RANGE_MAX ($HINT_RANGE_MAX)." >&2
357
exit 1
358
fi
359
for _v in HINT_DEFAULT_SCRIPT HINT_FALLBACK_SCRIPT; do
360
if [[ ! "${!_v}" =~ ^[a-z0-9]{3,4}$ ]]; then
361
echo "Error: $_v='${!_v}' must be a script tag (e.g. latn, none)." >&2; exit 1
362
fi
363
done
364
case "$GASP_MODE" in
365
keep|sized|smooth|gridfit) ;;
366
*) echo "Error: GASP_MODE='$GASP_MODE' invalid (keep|sized|smooth|gridfit)." >&2; exit 1 ;;
367
esac
368
}
369
370
prepare() {
371
_prompt_options
372
_resolve_options # validate now, so a typo fails before the long steps
373
374
rm -rf "$srcdir/font-patcher"
375
if [[ "$NERD_PATCH" == true ]]; then
376
mkdir -p "$srcdir/font-patcher"
377
bsdtar xf "$srcdir/font-patcher-${_nfver}.zip" -C "$srcdir/font-patcher"
378
fi
379
}
380
381
build() {
382
_resolve_options
383
384
echo "==> Options: variant=$GEO_VARIANT nerd=$NERD_PATCH hinting=$HINTING"
385
if [[ "$GEO_VARIANT" == static ]]; then
386
echo "==> Statics: weights='$STATIC_WEIGHTS' italic=$STATIC_ITALIC shrp=$STATIC_SHRP"
387
fi
388
if [[ "$HINTING" == true ]]; then
389
echo "==> ttfautohint: preset=$HINT_PRESET stem=$HINT_MODE" \
390
"range=$HINT_RANGE_MIN-$HINT_RANGE_MAX limit=$HINT_LIMIT x-height=$HINT_XHEIGHT" \
391
"x-snap-exc='${HINT_XSNAP_EXC-<default>}' gasp=$GASP_MODE integer-ppem=$HINT_INTEGER_PPEM" \
392
"ttfa-table=$HINT_TTFA_TABLE${HINT_FAMILY_SUFFIX:+ suffix='$HINT_FAMILY_SUFFIX'}"
393
fi
394
395
rm -rf "$srcdir/selected" "$srcdir/patched" "$srcdir/hinted"
396
mkdir -p "$srcdir/selected" "$srcdir/patched" "$srcdir/hinted"
397
398
# ── Step 1: select ──────────────────────────────────────────────────────────
399
if [[ "$GEO_VARIANT" == variable ]]; then
400
# A plain filename: brackets in file names upset some tools and globs.
401
install -m644 "$srcdir/GeologicaVariable.ttf" "$srcdir/selected/GeologicaVariable.ttf"
402
else
403
echo "==> Cutting static instances..."
404
local -a _italics=("0 0")
405
[[ "$STATIC_ITALIC" == true ]] && _italics+=("1 -12")
406
local _w _ic
407
for _w in $STATIC_WEIGHTS; do
408
for _ic in "${_italics[@]}"; do
409
printf '%s %s %s %s\n' "$_w" "$STATIC_SHRP" "${_ic% *}" "${_ic#* }"
410
done
411
done | parallel --will-cite -j"$(nproc)" --colsep ' ' \
412
python "$srcdir/make_statics.py" --wght {1} --shrp {2} --crsv {3} --slnt {4} \
413
-o "$srcdir/selected" "$srcdir/GeologicaVariable.ttf"
414
fi
415
local _count
416
_count=$(find "$srcdir/selected" -name '*.ttf' | wc -l)
417
echo "==> Selected $_count font file(s)."
418
if (( _count == 0 )); then
419
echo "Error: no fonts produced." >&2; exit 1
420
fi
421
422
local _hintsrc="$srcdir/selected"
423
424
# ── Step 2: Nerd Fonts patching ─────────────────────────────────────────────
425
# Patch first, hint afterwards: font-patcher does not keep hint bytecode, and
426
# ttfautohint then sees the final glyph set including the icons.
427
if [[ "$NERD_PATCH" == true ]]; then
428
echo "==> Patching with Nerd Fonts glyphs (this takes a while)..."
429
find "$srcdir/selected" -type f | sort | \
430
parallel --will-cite -j"$(nproc)" python "$srcdir/font-patcher/font-patcher" \
431
--variable-width-glyphs -q -c {} -out "$srcdir/patched" '>/dev/null'
432
_count=$(find "$srcdir/patched" -maxdepth 1 -name '*.ttf' | wc -l)
433
if (( _count == 0 )); then
434
echo "Error: font-patcher produced no output. See messages above." >&2; exit 1
435
fi
436
echo "==> Patched $_count font(s)."
437
_hintsrc="$srcdir/patched"
438
fi
439
440
if [[ "$HINTING" != true ]]; then
441
return 0
442
fi
443
444
# ── Step 3: ttfautohint ─────────────────────────────────────────────────────
445
# On the variable font ttfautohint keeps fvar/gvar/STAT/MVAR and all 18
446
# named instances, but its blue zones come from the default master (Thin).
447
# On a static it hints that exact instance, which is the point of the static
448
# build.
449
local -a _ta_opts=(
450
--hinting-range-min="$HINT_RANGE_MIN"
451
--hinting-range-max="$HINT_RANGE_MAX"
452
--hinting-limit="$HINT_LIMIT"
453
--increase-x-height="$HINT_XHEIGHT"
454
--stem-width-mode="$HINT_MODE"
455
--default-script="$HINT_DEFAULT_SCRIPT"
456
--ignore-restrictions
457
)
458
if [[ "$NERD_PATCH" == true ]]; then
459
# Icons would otherwise get Latin blue zones: scale them instead.
460
_ta_opts+=(--fallback-script=none --fallback-scaling)
461
else
462
_ta_opts+=(--fallback-script="$HINT_FALLBACK_SCRIPT")
463
fi
464
# Honour an explicit empty HINT_XSNAP_EXC (= no exceptions); omit if unset.
465
if [[ "${HINT_XSNAP_EXC+set}" == set ]]; then
466
_ta_opts+=(--x-height-snapping-exceptions="$HINT_XSNAP_EXC")
467
fi
468
if [[ "$HINT_WINCOMPAT" == true ]]; then _ta_opts+=(--windows-compatibility); fi
469
if [[ -n "${HINT_FAMILY_SUFFIX:-}" ]]; then _ta_opts+=(--family-suffix="$HINT_FAMILY_SUFFIX"); fi
470
if [[ "$HINT_TTFA_TABLE" == true ]]; then _ta_opts+=(--ttfa-table); else _ta_opts+=(--no-info); fi
471
472
# Serial on purpose: GNU parallel rebuilds the command line from its
473
# arguments, so a knob whose value contains a space (HINT_FAMILY_SUFFIX="
474
# Hinted", HINT_XSNAP_EXC="10-12 14") is split and ttfautohint prints its
475
# usage and fails. Hinting one font takes well under a second.
476
echo "==> Hinting with ttfautohint..."
477
local _f
478
for _f in "$_hintsrc"/*.ttf; do
479
ttfautohint "${_ta_opts[@]}" "$_f" "$srcdir/hinted/${_f##*/}"
480
done
481
482
if [[ "$HINT_INTEGER_PPEM" == true ]]; then
483
echo "==> Setting head.flags bit 3 (integer ppem)..."
484
python - "$srcdir/hinted/"*.ttf <<'PY'
485
import sys
486
from fontTools.ttLib import TTFont
487
for path in sys.argv[1:]:
488
font = TTFont(path)
489
font["head"].flags |= 1 << 3
490
font.save(path)
491
PY
492
fi
493
494
if [[ "$GASP_MODE" != keep ]]; then
495
echo "==> Applying gasp mode '$GASP_MODE'..."
496
python "$srcdir/set_gasp.py" "$GASP_MODE" "$srcdir/hinted/"*.ttf
497
fi
498
}
499
500
package() {
501
_resolve_options
502
503
local _out="$srcdir/selected"
504
if [[ "$HINTING" == true ]]; then
505
_out="$srcdir/hinted"
506
elif [[ "$NERD_PATCH" == true ]]; then
507
_out="$srcdir/patched"
508
fi
509
if ! compgen -G "$_out/*.ttf" >/dev/null; then
510
echo "Error: no fonts to install in $_out." >&2; exit 1
511
fi
512
local _fontdir="$pkgdir/usr/share/fonts/geologica"
513
install -Dm644 -t "$_fontdir" "$_out/"*.ttf
514
install -Dm644 "$srcdir/OFL.txt" "$pkgdir/usr/share/licenses/$pkgname/OFL.txt"
515
516
local _avail="$pkgdir/usr/share/fontconfig/conf.avail"
517
install -d "$_avail"
518
519
# Family names for the SHRP and CRSV axes. Variable build only: the statics
520
# have no axes, and with STATIC_SHRP=100 they are already "Geologica Sharp".
521
if [[ "$GEO_VARIANT" == variable ]]; then
522
install -Dm644 -t "$_avail" "$srcdir/79-geologica-families.conf"
523
fi
524
525
# Opt-in rendering tweak (see the header): bytecode hinting for the families
526
# this build installed. Shipped in conf.avail and deliberately not enabled.
527
# The family list is read from the fonts, so Nerd Fonts names and family
528
# suffixes are covered.
529
if [[ "$HINTING" == true ]]; then
530
local _fam _conf="$_avail/80-$pkgname.conf"
531
local _chromium_prgnames=(chrome chromium brave helium electron code slack signal-desktop vesktop)
532
local _prg _prgtests=""
533
for _prg in "${_chromium_prgnames[@]}"; do
534
_prgtests+=" <test qual=\"all\" name=\"prgname\" compare=\"not_eq\"><string>$_prg</string></test>"$'\n'
535
done
536
{
537
echo '<?xml version="1.0" encoding="UTF-8"?>'
538
echo '<!DOCTYPE fontconfig SYSTEM "urn:fontconfig:fonts.dtd">'
539
echo "<!-- Installed by $pkgname. Use the ttfautohint bytecode hints in these"
540
echo " fonts rather than FreeType's light autohinter. hintmedium, not"
541
echo " hintfull: same rendering in cairo/pango, and Blink keeps subpixel"
542
echo " positioning at hintmedium but drops it at hintfull."
543
echo " Chromium and Electron apps are excluded: at device scale 1 they use"
544
echo " linear advances only for hintslight/none, so hintmedium would round"
545
echo " every glyph advance to a whole pixel (uneven letter gaps)."
546
echo ""
547
echo " Geologica is a case where you may not want this file at all: its"
548
echo " x-height grows with weight, ttfautohint's blue zones are those of"
549
echo " the default (Thin) master, and FreeType's light autohinter - what"
550
echo " hintstyle hintslight already uses - measures better at 700-900."
551
echo " See README.md section 2. -->"
552
echo '<fontconfig>'
553
# One <match> per family: several <test>s in one <match> are ANDed, so
554
# the prgname tests exclude every listed app. qual="all" makes a test
555
# pass when prgname is unset. prgname is the executable's basename.
556
while IFS= read -r _fam; do
557
_fam="${_fam//&/&}"; _fam="${_fam//</<}"
558
cat <<EOF
559
<match target="font">
560
<test name="family" compare="eq"><string>$_fam</string></test>
561
${_prgtests%$'\n'}
562
<edit name="hinting" mode="assign"><bool>true</bool></edit>
563
<edit name="autohint" mode="assign"><bool>false</bool></edit>
564
<edit name="hintstyle" mode="assign"><const>hintmedium</const></edit>
565
</match>
566
EOF
567
done < <(python - "$_fontdir/"*.ttf <<'PY' | sort -u
568
import sys
569
from fontTools.ttLib import TTFont
570
for path in sys.argv[1:]:
571
name = TTFont(path, lazy=True)["name"]
572
print(name.getDebugName(16) or name.getDebugName(1))
573
PY
574
)
575
echo '</fontconfig>'
576
} > "$_conf"
577
chmod 644 "$_conf"
578
fi
579
580
install -Dm644 -t "$pkgdir/usr/share/doc/$pkgname" "$srcdir/README.md" "$srcdir/FEATURES.md"
581
}
582
Scan history
| Scanned at (UTC) | Severity | Rules |
|---|---|---|
| 2026-10-26 15:45:54 | Low | 1 |