You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

265 lines
8.9 KiB
Bash

#!/usr/bin/env bash -e
#
# Copyright 2020 Nathan L. Conrad
#
# This program is free software: you can redistribute it and/or modify it under
# the terms of the GNU General Public License as published by the Free Software
# Foundation, either version 3 of the License, or (at your option) any later
# version.
#
# This program is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
# details.
#
# You should have received a copy of the GNU General Public License along with
# this program. If not, see http://www.gnu.org/licenses.
# Resolve the project directory before altering the arguments
prj_dir=$(cd "$(dirname "$0")" && pwd)
# Parse arguments
dirty=0
while [[ $# -gt 0 ]]
do
# If the argument is a single hyphen followed by multiple single-character
# options, pop the first option from the argument and leave the rest for
# the next iteration. Otherwise, pop the entire argument.
arg=$1
if (( ${#arg} > 2 )) && [[ $arg =~ ^-[^-]*$ ]]
then
set -- "-${arg:2}" "${@:2}"
arg=${arg::2}
else
shift
fi
# Handle the argument
case $arg in
-h|--help)
IFS= read -rd '' usage << __eof__ || true
Usage: build [-hd]
Downloads and patches Inconsolata with Nerd Font glyphs
Optional arguments:
-h, --help Show this help message and exit
-d, --dirty Allow cached source archives instead of forcing clean downloads
__eof__
echo -n "$usage"
exit 0
;;
-d|--dirty)
dirty=1
;;
*)
echo "Invalid argument '$arg'. Use -h for help." >&2
exit 1
esac
done
# If output files exist, delete them
rm -rf "$prj_dir/"{*.ttf,*.css}
# Initialize globals used by handle-trap
signals='INT TERM'
unset tmp_dir
# Usage: handle-trap [exit_code]
#
# Deletes temporary files upon script exit or signal
#
# Positional arguments:
# exit_code Optional exit code for which to trigger a script exit
function handle-trap {
trap - EXIT $signals
rm -rf "$tmp_dir"
if [[ $1 ]]
then
exit "$1"
fi
}
# Set the trap and create a temporary build directory
trap handle-trap EXIT
trap 'handle-trap 1' $signals
tmp_dir=$(mktemp -d)
# Usage: prep-src account_name repo_name commit
#
# Downloads (if necessary) and extracts a GitHub source archive
#
# Positional arguments:
# account_name GitHub organization or user name
# repo_name Git repository name
# commit Git commit hash
function prep-src {
local archive_basename=$2_$3.tar.gz
local archive=$prj_dir/$archive_basename
if (( dirty )) && [[ -f $archive ]]
then
echo "Extracting cached $archive_basename..."
else
local url=https://github.com/$1/$2/tarball/$3
echo "Downloading $url..."
local dl_archive=$tmp_dir/$archive_basename
curl -Lo "$dl_archive" "$url"
echo "Caching $archive_basename..."
rm -rf "$archive"
cp "$dl_archive" "$prj_dir/"
echo "Extracting $archive_basename..."
fi
local src_dir=$tmp_dir/$2
mkdir "$src_dir"
tar -xC "$src_dir" -f "$archive" --strip-components 1
}
# Download (if necessary) and extract source archives
inconsolata_repo_name=Inconsolata
inconsolata_commit=d7269b53386323faad00cc7eebb3d99d26d103ff
prep-src googlefonts "$inconsolata_repo_name" "$inconsolata_commit"
nf_repo_name=nerd-fonts
nf_commit=c41890f82b4d346cbfffff25ae29d1e145690d13
prep-src ryanoasis "$nf_repo_name" "$nf_commit"
# The Nerd Font patcher likes to use the OS/2 Windows ascent and descent rather
# than the HHea or OS/2 typographic values, and unfortunately, Inconsolata has
# much larger Windows values. Without the '-l' or '--adjust-line-height'
# option, the Nerd Font patcher will use the Windows values to size the Nerd
# Font glyphs resulting in glyphs that are too tall and misaligned when the
# font is rendered using the HHea or typographic values. With the '-l' or
# '--adjust-line-height' option, the Nerd Font patcher will also override the
# HHea with the Windows values. Even though this fixes the Powerline glyph
# alignment when rendering using the HHea values, it results in a line height
# that, in my opinion, is much too tall for the font. Adjust the ascent and
# descent to vertically center the 'E' glyph of the semi-expanded TTF while
# maintaining a reasonable line height. Apply the same adjustments to the
# semi-expanded extra-bold TTF.
src_dir="$tmp_dir/$inconsolata_repo_name/fonts/ttf"
vert_aligned_dir="$tmp_dir/vert-aligned"
mkdir "$vert_aligned_dir"
python3 << __eof__
import fontforge
from pathlib import Path
# Calculate the ascent which vertically centers the semi-expanded 'E' glyph
# while maintaining a reasonable line height
style = 'SemiExpanded'
glyph = 'E'
descent = -256
line_gap = 0
file_basename = f'Inconsolata-{style}.ttf'
print(f'Using {repr(glyph)} glyph from {file_basename} for vertical '
'alignment...')
font = fontforge.open(str(Path(f'$src_dir/{file_basename}')))
ascent = round(font[glyph].boundingBox()[3]) - descent
if (ascent - descent) % 2:
descent += 1
print(f' OS/2 Win OS/2 typo HHea Aligned\n Ascent '
f'{font.os2_winascent:>4} {font.os2_typoascent:>4} '
f'{font.hhea_ascent:>4} {ascent:>4}\n Descent '
f'{-font.os2_windescent:>4} {font.os2_typodescent:>4} '
f'{font.hhea_descent:>4} {descent:>4}\n Line gap '
f'{font.os2_typolinegap:>4} {font.hhea_linegap:>4} {line_gap:>4}')
font.close()
# Vertically align the semi-expanded and semi-expanded extra-bold TTFs
styles = style, 'SemiExpandedExtraBold'
flags = 'opentype', 'PfEd-comments'
for style in styles:
file_basename = f'Inconsolata-{style}.ttf'
print(f'Vertically aligning {file_basename}...')
font = fontforge.open(str(Path(f'$src_dir/{file_basename}')))
font.os2_winascent = ascent
font.os2_windescent = -descent
font.os2_typoascent = ascent
font.os2_typodescent = descent
font.os2_typolinegap = line_gap
font.hhea_ascent = ascent
font.hhea_descent = descent
font.hhea_linegap = line_gap
font.generate(str(Path(f'$vert_aligned_dir/{file_basename}')), flags=flags)
font.close()
__eof__
# Patch with Nerd Font glyphs
nf_patched_dir="$tmp_dir/nf-patched"
mkdir "$nf_patched_dir"
for file in "$vert_aligned_dir/"*
do
file_basename="$(basename "$file")"
echo "Patching $file_basename with Nerd Font glyphs..."
python3 "$tmp_dir/$nf_repo_name/font-patcher" -c --progressbars \
-out "$nf_patched_dir" "$file"
done
# Rename family and recategorize styles
python3 << __eof__
import fontforge
from pathlib import Path
family = 'Iconsolata'
styles = {'Semi Expanded': 'Regular', 'Semi Expanded ExtraBold': 'Bold'}
weights = {'Regular': 'Book', 'Bold': 'Bold'}
os2_width = 5
os2_weights = {'Regular': 400, 'Bold': 700}
lang = 'English (US)'
id_suffix = '${inconsolata_commit::6}${nf_commit::6}'
flags = 'opentype', 'PfEd-comments'
for src_style, style in styles.items():
postscript_name = f'{family}-{style}'
file_basename = f'{postscript_name}.ttf'
print(f'Generating {file_basename}...')
src_file_basename = f'Inconsolata {src_style} Nerd Font Complete.ttf'
font = fontforge.open(str(Path(f'$nf_patched_dir/{src_file_basename}')))
font.familyname = family
font.fullname = f'{family} {style}'
font.fontname = postscript_name
font.weight = weights[style]
font.os2_width = os2_width
font.os2_weight = os2_weights[style]
font.appendSFNTName(lang, 'SubFamily', None)
font.appendSFNTName(lang, 'UniqueID', f'{postscript_name}_{id_suffix}')
font.appendSFNTName(lang, 'Preferred Styles', None)
font.appendSFNTName(lang, 'Preferred Family', None)
font.appendSFNTName(lang, 'Compatible Full', None)
sfnt_names = {name: value for prop_lang, name, value in font.sfnt_names
if prop_lang == lang}
print(f' Weight is {repr(font.weight)} (PostScript) and '
f'{font.os2_weight} (OS/2)\n Subfamily is '
f"{repr(sfnt_names['SubFamily'])}\n Unique ID is "
f"{repr(sfnt_names['UniqueID'])}")
font.generate(str(Path(f'$prj_dir/{file_basename}')), flags=flags)
font.close()
__eof__
# Generate CSS
css_file_basename=Iconsolata.css
echo "Generating $css_file_basename..."
IFS= read -rd '' snippet << __eof__ || true
@font-face {
font-family: 'Iconsolata';
src: url(data:font/ttf;base64,
__eof__
css_file=$tmp_dir/$css_file_basename
echo -n "${snippet%?}" > "$css_file"
base64 -w0 "$prj_dir/Iconsolata-Regular.ttf" >> "$css_file"
IFS= read -rd '' snippet << __eof__ || true
) format('truetype');
}
@font-face {
font-family: 'Iconsolata';
font-weight: bold;
src: url(data:font/ttf;base64,
__eof__
echo -n "${snippet%?}" >> "$css_file"
base64 -w0 "$prj_dir/Iconsolata-Bold.ttf" >> "$css_file"
cat << __eof__ >> "$css_file"
) format('truetype');
}
__eof__
cp "$css_file" "$prj_dir/"
# Indicate success
echo 'Done'