1
0

merge-font-noto.sh 6.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130
  1. #!/usr/bin/env bash
  2. # SPDX-FileCopyrightText: 2021 Nextcloud GmbH and Nextcloud contributors
  3. # SPDX-License-Identifier: AGPL-3.0-or-later
  4. # Helper script to merge several Noto fonts in a single TTF file.
  5. #
  6. # The "Noto Sans" font (https://www.google.com/get/noto) only includes a subset
  7. # of all the available glyphs in the Noto fonts. This scripts uses
  8. # "merge_noto.py" from the Noto Tools package to add other scripts, like Arabic,
  9. # Devanagari or Hebrew.
  10. #
  11. # "merge_noto.py" originally merges the fonts by region. However it was adjusted
  12. # to merge "all" the fonts in a single file, like done by "merge_fonts.py". The
  13. # reason to use "merge_noto.py" instead of "merge_fonts.py" is that
  14. # "merge_noto.py" merges regular and bold fonts, which are both needed in
  15. # Nextcloud. "merge_fonts.py" only merges regular fonts, and adjusting it to
  16. # handle bold fonts too would have been more work than adjusting
  17. # "merge_noto.py".
  18. #
  19. # Please note that, due to technical limitations of the TTF format (a single
  20. # file can not have more than 65535 glyphs) the merged file does not include any
  21. # Chinese, Japanese or Korean glyph (the Noto CJK files already use all the
  22. # slots). In fact, it seems that it can not include either all the glyphs from
  23. # all the non CJK Noto fonts, so it merges only those predefined in the
  24. # "merge_fonts.py" script (as it is a larger set than the original one in
  25. # "merge_noto.py").
  26. #
  27. # Also please note that merging the fonts is a slow process and it can take a
  28. # while (from minutes to hours, depending on the system).
  29. #
  30. # To perform its job, the script requires the "docker" command to be available.
  31. #
  32. # The Docker Command Line Interface (the "docker" command) requires special
  33. # permissions to talk to the Docker daemon, and those permissions are typically
  34. # available only to the root user. Please see the Docker documentation to find
  35. # out how to give access to a regular user to the Docker daemon:
  36. # https://docs.docker.com/engine/installation/linux/linux-postinstall/
  37. #
  38. # Note, however, that being able to communicate with the Docker daemon is the
  39. # same as being able to get root privileges for the system. Therefore, you must
  40. # give access to the Docker daemon (and thus run this script as) ONLY to trusted
  41. # and secure users:
  42. # https://docs.docker.com/engine/security/security/#docker-daemon-attack-surface
  43. # Stops the container started by this script.
  44. function cleanUp() {
  45. # Disable (yes, "+" disables) exiting immediately on errors to ensure that
  46. # all the cleanup commands are executed (well, no errors should occur during
  47. # the cleanup anyway, but just in case).
  48. set +o errexit
  49. echo "Cleaning up"
  50. docker rm --volumes --force $DOCKER_CONTAINER_ID
  51. }
  52. # Exit immediately on errors.
  53. set -o errexit
  54. # Execute cleanUp when the script exits, either normally or due to an error.
  55. trap cleanUp EXIT
  56. # Ensure working directory is script directory, as some actions (like copying
  57. # the patches to the container) expect that.
  58. cd "$(dirname $0)"
  59. # python:3.9 can not be used, as one of the requeriments of Noto Tools
  60. # (pyclipper) fails to build.
  61. #
  62. # The container exits immediately if no command is given, so a Bash session
  63. # is created to prevent that.
  64. DOCKER_CONTAINER_ID=`docker run --rm --detach --interactive --tty python:3.8-slim bash`
  65. # Install required dependencies.
  66. docker exec $DOCKER_CONTAINER_ID apt-get update
  67. docker exec $DOCKER_CONTAINER_ID apt-get install -y git gcc g++ libjpeg-dev zlib1g-dev wget
  68. # Install Noto Tools in the container.
  69. docker exec --workdir /tmp $DOCKER_CONTAINER_ID git clone https://github.com/googlefonts/nototools
  70. docker exec --workdir /tmp/nototools $DOCKER_CONTAINER_ID git checkout 76b29f8f8f9b
  71. docker exec --workdir /tmp/nototools $DOCKER_CONTAINER_ID pip install --requirement requirements.txt
  72. docker exec --workdir /tmp/nototools $DOCKER_CONTAINER_ID pip install --editable .
  73. # As Noto Tools were installed as "editable" the scripts can be patched after
  74. # installation.
  75. 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
  76. docker exec --workdir /tmp/nototools --interactive $DOCKER_CONTAINER_ID patch --strip 1 < merge-font-noto-fix-merging-v20201206-phase3-76b29f8f8f9b.patch
  77. # Get Noto fonts.
  78. #
  79. # Phase 2 Noto fonts use 2048 units per em, while phase 3 Noto fonts use 1000*.
  80. # Currently the fonts in the released package** (apparently from 2017-10-25) are
  81. # a mix of both, but fonts with different units per em can not be merged***.
  82. # However, the fonts in the Git repository, although not released yet, are all
  83. # using 1000 units per em already, so those are the ones merged.
  84. #
  85. # *https://github.com/googlefonts/noto-fonts/issues/908#issuecomment-298687906.
  86. # **https://noto-website-2.storage.googleapis.com/pkgs/Noto-unhinted.zip
  87. # ***https://fonttools.readthedocs.io/en/latest/merge.html
  88. docker exec --workdir /tmp $DOCKER_CONTAINER_ID wget https://github.com/googlefonts/noto-fonts/archive/v20201206-phase3.tar.gz
  89. docker exec --workdir /tmp $DOCKER_CONTAINER_ID tar -xzf v20201206-phase3.tar.gz
  90. # noto-fonts in Git and snapshots of Git (like the package used) have a
  91. # subdirectory for each font, but "merge_noto.py" expects to find all the fonts
  92. # in a single directory, so the structure needs to be "flattened".
  93. #
  94. # Hinted fonts* adapt better to being rendered in different sizes. The full
  95. # package in https://www.google.com/get/noto/ includes only unhinted fonts
  96. # (according to its name**, I have not actually verified the fonts themselves),
  97. # while the individual fonts listed below in the page are a mix of hinted and
  98. # unhinted fonts. However, the Git directory has hinted versions of all fonts,
  99. # so those are the ones merged (maybe there is a good reason not to merge hinted
  100. # fonts, but seems to work :-P).
  101. #
  102. # *https://en.wikipedia.org/wiki/Font_hinting
  103. # **https://noto-website-2.storage.googleapis.com/pkgs/Noto-unhinted.zip
  104. docker exec --workdir /tmp $DOCKER_CONTAINER_ID mkdir --parent individual/hinted
  105. docker exec --workdir /tmp $DOCKER_CONTAINER_ID find noto-fonts-20201206-phase3/hinted/ttf -iname "NotoSans*Regular.ttf" -exec mv {} individual/hinted/ \;
  106. docker exec --workdir /tmp $DOCKER_CONTAINER_ID find noto-fonts-20201206-phase3/hinted/ttf -iname "NotoSans*Bold.ttf" -exec mv {} individual/hinted/ \;
  107. # Merge the fonts.
  108. docker exec --workdir /tmp $DOCKER_CONTAINER_ID mkdir --parent combined/hinted
  109. docker exec --workdir /tmp $DOCKER_CONTAINER_ID merge_noto.py
  110. # Copy resulting files.
  111. #
  112. # Noto fonts, as well as the merged files, are licensed under the SIL Open Font
  113. # License: https://scripts.sil.org/OFL
  114. docker cp $DOCKER_CONTAINER_ID:/tmp/combined/hinted/NotoSans-Regular.ttf ../core/fonts/NotoSans-Regular.ttf
  115. docker cp $DOCKER_CONTAINER_ID:/tmp/combined/hinted/NotoSans-Bold.ttf ../core/fonts/NotoSans-Bold.ttf