ttf-geologica-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-geologica-hinted
3_geover=1.010
4_geocommit=685f38d7c9e86b0c8530204c97ddcaf6558dd17b
5_nfver=3.5.1
6pkgver="${_geover}"
7pkgrel=2
8pkgdesc='Geologica, a humanist-geometric sans with Sharp/Cursive/Slant/Weight axes, re-hinted with ttfautohint for FreeType (optionally static / Nerd Fonts patched)'
9arch=('any')
10url='https://github.com/googlefonts/geologica'
11license=('OFL-1.1')
12makedepends=('python' 'python-fonttools' 'fontforge' 'parallel' 'ttfautohint')
13options=(!strip)
14provides=('ttf-geologica')
15conflicts=('ttf-geologica')
16source=(
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)
27noextract=("font-patcher-${_nfver}.zip")
28sha256sums=('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
370prepare() {
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
381build() {
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'
485import sys
486from fontTools.ttLib import TTFont
487for path in sys.argv[1:]:
488 font = TTFont(path)
489 font["head"].flags |= 1 << 3
490 font.save(path)
491PY
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
500package() {
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//&/&amp;}"; _fam="${_fam//</&lt;}"
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>
566EOF
567 done < <(python - "$_fontdir/"*.ttf <<'PY' | sort -u
568import sys
569from fontTools.ttLib import TTFont
570for path in sys.argv[1:]:
571 name = TTFont(path, lazy=True)["name"]
572 print(name.getDebugName(16) or name.getDebugName(1))
573PY
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)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