blob: e6df1e68cdb544441f91f1450d42e495ab8dbe8b [file] [log] [blame] [edit]
#!/bin/bash
trap "exit" INT
dirname=`dirname "$0"`
# Parse options
output_dir=
font_file=
face_index=0
hb_info=hb-info
hb_vector=hb-vector
hb_svg_compare=$dirname/hb-svg-compare
tolerance=0
vector_precision=
precision=
font_size=upem
features=
variations=
face_loader=ot
font_funcs1=ot
font_funcs2=fontations
clear1=false
clear2=false
unicodes=
glyphs=
quiet=false
help=false
max_procs=
while test $# -gt 0; do
case "$1" in
-o|--output-dir)
shift
output_dir=$1
shift
;;
--font-file)
shift
font_file=$1
shift
;;
--face-index)
shift
face_index=$1
shift
;;
--hb-vector)
shift
hb_vector=$1
shift
;;
--hb-info)
shift
hb_info=$1
shift
;;
--hb-svg-compare)
shift
hb_svg_compare=$1
shift
;;
--tolerance)
shift
tolerance=$1
shift
;;
--precision)
shift
precision=$1
shift
;;
--max-procs)
shift
max_procs=$1
shift
;;
--font-size)
shift
font_size=$1
shift
;;
--features)
shift
features=$1
shift
;;
--variations)
shift
variations=$1
shift
;;
--unicodes)
shift
unicodes=$1
shift
;;
--face-loader)
shift
face_loader=$1
shift
;;
--font-funcs1)
shift
font_funcs1=$1
shift
;;
--font-funcs2)
shift
font_funcs2=$1
shift
;;
--clear1)
shift
clear1=true
;;
--clear2)
shift
clear2=true
;;
--quiet)
quiet=true
shift
;;
--help)
help=true
shift
;;
*)
if test "x$font_file" == x; then
font_file=$1
shift
else
glyphs="$glyphs $1"
shift
fi
;;
esac
done
if $help; then
cmd=`basename "$0"`
echo "Usage: $cmd [OPTIONS] FONTFILE [GLYPH...]"
echo "Render a font with two font backends and compare the results."
echo
echo "Options:"
echo " -o, --output-dir DIR: Output in DIR"
echo " --font-file FONTFILE: Font file to render"
echo " --hb-vector HB_VECTOR: Path to hb-vector; default $hb_vector"
echo " --hb-info HB_INFO: Path to hb-info; default $hb_info"
echo " --hb-svg-compare HB_SVG_COMPARE: Path to hb-svg-compare; default $hb_svg_compare"
echo " --tolerance TOLERANCE: Tolerance for SVG comparison; default $tolerance"
echo " --precision N: hb-vector precision override; default 5"
echo " --max-procs N: Max parallel processes for render/compare; default auto"
echo " --font-size SIZE: Font size; default $font_size"
echo " --features FEATURES: Font features; default none"
echo " --variations VARIATIONS: Font variations; default none"
echo " --face-loader: Face loader; default $face_loader"
echo " --font-funcs1 font_funcs: First font-funcs; default $font_funcs1"
echo " --font-funcs2 font_funcs: Second font-funcs; default $font_funcs2"
echo " --clear1: Clear first font backend output if exists"
echo " --clear2: Clear second font backend output if exists"
echo " --unicodes CODES: Unicodes to render"
echo " --quiet: Quiet mode"
echo " --help: Print help"
exit 0
fi
get_num_jobs() {
n=`getconf _NPROCESSORS_ONLN 2>/dev/null || true`
test "x$n" == x && n=`nproc 2>/dev/null || true`
test "x$n" == x && n=`sysctl -n hw.ncpu 2>/dev/null || true`
case "$n" in
''|*[!0-9]*) n=1 ;;
esac
test "$n" -lt 1 && n=1
echo "$n"
}
if test "x$max_procs" != x; then
echo "$max_procs" | grep -Eq '^[0-9]+$' || {
echo "Invalid max-procs '$max_procs'" >&2
exit 2
}
test "$max_procs" -gt 0 || {
echo "Invalid max-procs '$max_procs'" >&2
exit 2
}
fi
if test "x$font_file" == x; then
echo "No font file specified." >&2
exit 2
fi
if ! which "$hb_vector" 2>/dev/null >/dev/null; then
echo "'$hb_vector' not found" >&2
exit 2
fi
if ! which "$hb_info" 2>/dev/null >/dev/null; then
echo "'$hb_info' not found" >&2
exit 2
fi
if ! test -f "$font_file"; then
echo "Font file '$font_file' not found" >&2
exit 2
fi
# Sanity check Unicode values
if test "x$unicodes" != x; then
echo "$unicodes" |
tr ' ' '\n' |
grep -v '^U[+][0-9a-fA-F]\+$' &&
{
echo "Invalid Unicode values" >&2
exit 2
}
fi
$quiet || echo "Comparing '$font_file' with '$font_funcs1' and '$font_funcs2' font backends. " >&2
if test "x$output_dir" == x; then
output_dir=`mktemp -d`
echo "Output in '$output_dir'" >&2
fi
mkdir -p "$output_dir" || exit 1
if test "x$precision" != x; then
echo "$precision" | grep -Eq '^[0-9]+$' || {
echo "Invalid precision '$precision'" >&2
exit 2
}
vector_precision="$precision"
else
vector_precision=5
fi
$quiet || echo "Using precision=$vector_precision for tolerance=$tolerance." >&2
# Populate glyphs file
glyphs_file="$output_dir/glyphs"
> "$glyphs_file"
echo "$unicodes" |
tr ' ' '\n' |
sed 's/^U+//' |
grep . |
while read unicode; do
echo "uni$unicode"
done >> "$glyphs_file"
echo "$glyphs" |
tr ' ' '\n' |
grep . >> "$glyphs_file"
if test "x$unicodes" == x -a "x$glyphs" == x; then
$quiet || echo "No unicodes or glyphs specified. Comparing all glyphs in the font." >&2
num_glyphs=`$hb_info --quiet --show-glyph-count "$font_file"`
$quiet || echo "Font has $num_glyphs glyphs." >&2
seq 0 $((num_glyphs - 1)) |
sed 's/^/gid/' >> "$glyphs_file"
fi
num_glyphs=`wc -l < "$glyphs_file" | tr -d '[:space:]'`
if test "$num_glyphs" -lt 1; then
echo "No glyphs to compare." >&2
exit 2
fi
base_jobs=`get_num_jobs`
if test "x$max_procs" != x; then
if test "$base_jobs" -gt "$max_procs"; then
base_jobs="$max_procs"
fi
fi
compare_jobs="$base_jobs"
if test "$compare_jobs" -gt "$num_glyphs"; then
compare_jobs="$num_glyphs"
fi
$quiet || test "$base_jobs" -le 1 || echo "Using up to $base_jobs shards." >&2
flavor=
if test "$features" != ""; then
flavor="$flavor.features=$features"
fi
if test "$variations" != ""; then
flavor="$flavor.variations=$variations"
fi
# Render with both font backends
if $clear1; then
funcs_prefix="$output_dir/$font_funcs1$flavor"
$quiet || echo "Clearing '$funcs_prefix'... " >&2
rm -rf "$funcs_prefix"
fi
if $clear2; then
funcs_prefix="$output_dir/$font_funcs2$flavor"
$quiet || echo "Clearing '$funcs_prefix'... " >&2
rm -rf "$funcs_prefix"
fi
for font_funcs in "$font_funcs1" "$font_funcs2"; do
test "x$font_funcs" == x && continue
$quiet || echo "Rendering with font backend '$font_funcs'..." >&2
funcs_prefix="$output_dir/$font_funcs$flavor"
mkdir -p "$funcs_prefix"
render_cmds="$output_dir/render-cmds.$font_funcs$flavor"
> "$render_cmds"
count=0
while read glyph; do
dir="$funcs_prefix"
svg="$dir/$glyph.svg"
if test -f "$svg"; then
continue
fi
count=$((count + 1))
if test $((count % 100)) == 0; then
$quiet || echo -n . >&2
fi
printf '%s;%s;%s;%s;%s;%s;%s;%s;%s;%s;%s\n' \
"--font-file=$font_file" \
"--face-index=$face_index" \
"--glyphs" \
"--face-loader=$face_loader" \
"--font-funcs=$font_funcs" \
"--features=$features" \
"--variations=$variations" \
"--font-size=$font_size" \
"--precision=$vector_precision" \
"--output-file=$svg" \
"$glyph" >> "$render_cmds"
done < "$glyphs_file"
render_cmd_count=`wc -l < "$render_cmds" | tr -d '[:space:]'`
render_jobs="$base_jobs"
if test "$render_jobs" -gt "$render_cmd_count"; then
render_jobs="$render_cmd_count"
fi
if test "$render_cmd_count" -gt 0; then
render_shard_dir="$output_dir/render-shards.$font_funcs$flavor"
rm -rf "$render_shard_dir"
mkdir -p "$render_shard_dir" || exit 1
split -d -n l/"$render_jobs" "$render_cmds" "$render_shard_dir/cmds." || exit 1
pids=
for shard in "$render_shard_dir"/cmds.*; do
"$hb_vector" --batch < "$shard" > "$shard.out" &
pids="$pids $!"
done
for pid in $pids; do
wait "$pid" || exit 1
done
cat "$render_shard_dir"/cmds.*.out | grep -v '^success$' || true
fi
if test "$count" -ge 100; then
$quiet || echo >&2
fi
done
test "x$font_funcs1" == x || test "x$font_funcs2" == x && exit 0
diff="$output_dir/diff$flavor"
rm -f "$diff"
> "$diff"
test "x$flavor" == x || ln -f -s "diff$flavor" "$output_dir/diff"
$quiet || echo "Comparing SVGs into '$diff'..." >&2
funcs1_prefix="$output_dir/$font_funcs1$flavor"
funcs2_prefix="$output_dir/$font_funcs2$flavor"
pairs="$output_dir/pairs$flavor"
> "$pairs"
count=0
while read glyph; do
count=$((count + 1))
if test $((count % 100)) == 0; then
$quiet || echo -n . >&2
fi
svg1="$funcs1_prefix/$glyph.svg"
svg2="$funcs2_prefix/$glyph.svg"
if ! test -f "$svg1" || ! test -f "$svg2"; then
echo -e "\n$glyph not rendered." >&2
exit 1
fi
echo -e "$svg1\t$svg2" >> "$pairs"
done < "$glyphs_file"
if test "$count" -ge 100; then
$quiet || echo >&2
fi
raw_diff="$output_dir/diff.raw$flavor"
shard_dir="$output_dir/compare-shards$flavor"
rm -rf "$shard_dir"
mkdir -p "$shard_dir" || exit 1
split -d -n l/"$compare_jobs" "$pairs" "$shard_dir/pairs." || exit 1
pids=
for shard in "$shard_dir"/pairs.*; do
"$hb_svg_compare" "$tolerance" < "$shard" > "$shard.out" &
pids="$pids $!"
done
for pid in $pids; do
wait "$pid" || exit 1
done
cat "$shard_dir"/pairs.*.out > "$raw_diff" || exit 1
# Sort diff by error
python3 - "$raw_diff" "$diff" <<'PY' || exit 1
import math
import sys
src, dst = sys.argv[1], sys.argv[2]
rows = []
with open(src, encoding='utf-8') as f:
for idx, line in enumerate(f):
line = line.rstrip('\n')
first = line.split('\t', 1)[0]
try:
value = float(first)
key = (0, value, idx) if math.isfinite(value) else (1, float('inf'), idx)
except ValueError:
key = (1, float('inf'), idx)
rows.append((key, line))
rows.sort(key=lambda row: row[0])
with open(dst, 'w', encoding='utf-8') as f:
for _, line in rows:
f.write(line + '\n')
PY
if ! $quiet; then
while IFS= read -r line; do
echo "$line"
done < "$diff"
fi
# Count number of differences
num_diffs=`cat "$diff" | wc -l`
$quiet || echo -e "\nFound $num_diffs differences in $num_glyphs glyphs." >&2