aboutsummaryrefslogtreecommitdiffstats
path: root/build/merge-font-noto.sh
blob: 603f6dba9d3eab3210db4898752c70399a0e28be (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
#!/usr/bin/env bash

# SPDX-FileCopyrightText: 2021 Nextcloud GmbH and Nextcloud contributors
# SPDX-License-Identifier: AGPL-3.0-or-later

# Helper script to merge several Noto fonts in a single TTF file.
#
# The "Noto Sans" font (https://www.google.com/get/noto) only includes a subset
# of all the available glyphs in the Noto fonts. This scripts uses
# "merge_noto.py" from the Noto Tools package to add other scripts, like Arabic,
# Devanagari or Hebrew.
#
# "merge_noto.py" originally merges the fonts by region. However it was adjusted
# to merge "all" the fonts in a single file, like done by "merge_fonts.py". The
# reason to use "merge_noto.py" instead of "merge_fonts.py" is that
# "merge_noto.py" merges regular and bold fonts, which are both needed in
# Nextcloud. "merge_fonts.py" only merges regular fonts, and adjusting it to
# handle bold fonts too would have been more work than adjusting
# "merge_noto.py".
#
# Please note that, due to technical limitations of the TTF format (a single
# file can not have more than 65535 glyphs) the merged file does not include any
# Chinese, Japanese or Korean glyph (the Noto CJK files already use all the
# slots). In fact, it seems that it can not include either all the glyphs from
# all the non CJK Noto fonts, so it merges only those predefined in the
# "merge_fonts.py" script (as it is a larger set than the original one in
# "merge_noto.py").
#
# Also please note that merging the fonts is a slow process and it can take a
# while (from minutes to hours, depending on the system).
#
# To perform its job, the script requires the "docker" command to be available.
#
# The Docker Command Line Interface (the "docker" command) requires special
# permissions to talk to the Docker daemon, and those permissions are typically
# available only to the root user. Please see the Docker documentation to find
# out how to give access to a regular user to the Docker daemon:
# https://docs.docker.com/engine/installation/linux/linux-postinstall/
#
# Note, however, that being able to communicate with the Docker daemon is the
# same as being able to get root privileges for the system. Therefore, you must
# give access to the Docker daemon (and thus run this script as) ONLY to trusted
# and secure users:
# https://docs.docker.com/engine/security/security/#docker-daemon-attack-surface

# Stops the container started by this script.
function cleanUp() {
	# Disable (yes, "+" disables) exiting immediately on errors to ensure that
	# all the cleanup commands are executed (well, no errors should occur during
	# the cleanup anyway, but just in case).
	set +o errexit

	echo "Cleaning up"
	docker rm --volumes --force $DOCKER_CONTAINER_ID
}

# Exit immediately on errors.
set -o errexit

# Execute cleanUp when the script exits, either normally or due to an error.
trap cleanUp EXIT

# Ensure working directory is script directory, as some actions (like copying
# the patches to the container) expect that.
cd "$(dirname $0)"

# python:3.9 can not be used, as one of the requeriments of Noto Tools
# (pyclipper) fails to build.
#
# The container exits immediately if no command is given, so a Bash session
# is created to prevent that.
DOCKER_CONTAINER_ID=`docker run --rm --detach --interactive --tty python:3.8-slim bash`

# Install required dependencies.
docker exec $DOCKER_CONTAINER_ID apt-get update
docker exec $DOCKER_CONTAINER_ID apt-get install -y git gcc g++ libjpeg-dev zlib1g-dev wget

# Install Noto Tools in the container.
docker exec --workdir /tmp $DOCKER_CONTAINER_ID git clone https://github.com/googlefonts/nototools
docker exec --workdir /tmp/nototools $DOCKER_CONTAINER_ID git checkout 76b29f8f8f9b
docker exec --workdir /tmp/nototools $DOCKER_CONTAINER_ID pip install --requirement requirements.txt
docker exec --workdir /tmp/nototools $DOCKER_CONTAINER_ID pip install --editable .

# As Noto Tools were installed as "editable" the scripts can be patched after
# installation.
docker cp merge-font-noto-fix-merging-v20201206-phase3-76b29f8f8f9b.patch $DOCKER_CONTAINER_ID:/tmp/nototools/merge-font-noto-fix-merging-v20201206-phase3-76b29f8f8f9b.patch
docker exec --workdir /tmp/nototools --interactive $DOCKER_CONTAINER_ID patch --strip 1 < merge-font-noto-fix-merging-v20201206-phase3-76b29f8f8f9b.patch

# Get Noto fonts.
#
# Phase 2 Noto fonts use 2048 units per em, while phase 3 Noto fonts use 1000*.
# Currently the fonts in the released package** (apparently from 2017-10-25) are
# a mix of both, but fonts with different units per em can not be merged***.
# However, the fonts in the Git repository, although not released yet, are all
# using 1000 units per em already, so those are the ones merged.
#
# *https://github.com/googlefonts/noto-fonts/issues/908#issuecomment-298687906.
# **https://noto-website-2.storage.googleapis.com/pkgs/Noto-unhinted.zip
# ***https://fonttools.readthedocs.io/en/latest/merge.html
docker exec --workdir /tmp $DOCKER_CONTAINER_ID wget https://github.com/googlefonts/noto-fonts/archive/v20201206-phase3.tar.gz
docker exec --workdir /tmp $DOCKER_CONTAINER_ID tar -xzf v20201206-phase3.tar.gz

# noto-fonts in Git and snapshots of Git (like the package used) have a
# subdirectory for each font, but "merge_noto.py" expects to find all the fonts
# in a single directory, so the structure needs to be "flattened".
#
# Hinted fonts* adapt better to being rendered in different sizes. The full
# package in https://www.google.com/get/noto/ includes only unhinted fonts
# (according to its name**, I have not actually verified the fonts themselves),
# while the individual fonts listed below in the page are a mix of hinted and
# unhinted fonts. However, the Git directory has hinted versions of all fonts,
# so those are the ones merged (maybe there is a good reason not to merge hinted
# fonts, but seems to work :-P).
#
# *https://en.wikipedia.org/wiki/Font_hinting
# **https://noto-website-2.storage.googleapis.com/pkgs/Noto-unhinted.zip
docker exec --workdir /tmp $DOCKER_CONTAINER_ID mkdir --parent individual/hinted
docker exec --workdir /tmp $DOCKER_CONTAINER_ID find noto-fonts-20201206-phase3/hinted/ttf -iname "NotoSans*Regular.ttf" -exec mv {} individual/hinted/ \;
docker exec --workdir /tmp $DOCKER_CONTAINER_ID find noto-fonts-20201206-phase3/hinted/ttf -iname "NotoSans*Bold.ttf" -exec mv {} individual/hinted/ \;

# Merge the fonts.
docker exec --workdir /tmp $DOCKER_CONTAINER_ID mkdir --parent combined/hinted
docker exec --workdir /tmp $DOCKER_CONTAINER_ID merge_noto.py

# Copy resulting files.
#
# Noto fonts, as well as the merged files, are licensed under the SIL Open Font
# License: https://scripts.sil.org/OFL
docker cp $DOCKER_CONTAINER_ID:/tmp/combined/hinted/NotoSans-Regular.ttf ../core/fonts/NotoSans-Regular.ttf
docker cp $DOCKER_CONTAINER_ID:/tmp/combined/hinted/NotoSans-Bold.ttf ../core/fonts/NotoSans-Bold.ttf