projects/linux·blob master · refs

184 lines · 5.8 KiB

1#!/usr/bin/env python3
2# SPDX-License-Identifier: GPL-2.0-only
3# Copyright (C) Akira Yokosawa, 2024
4#
5# Ported to Python by (c) Mauro Carvalho Chehab, 2025
6
7"""
8Detect problematic Noto CJK variable fonts
9==========================================
10
11For ``make pdfdocs``, reports of build errors of translations.pdf started
12arriving early 2024 [1]_ [2]_. It turned out that Fedora and openSUSE
13tumbleweed have started deploying variable-font [3]_ format of "Noto CJK"
14fonts [4]_ [5]_. For PDF, a LaTeX package named xeCJK is used for CJK
15(Chinese, Japanese, Korean) pages. xeCJK requires XeLaTeX/XeTeX, which
16does not (and likely never will) understand variable fonts for historical
17reasons.
18
19The build error happens even when both of variable- and non-variable-format
20fonts are found on the build system. To make matters worse, Fedora enlists
21variable "Noto CJK" fonts in the requirements of langpacks-ja, -ko, -zh_CN,
22-zh_TW, etc. Hence developers who have interest in CJK pages are more
23likely to encounter the build errors.
24
25This script is invoked from the error path of "make pdfdocs" and emits
26suggestions if variable-font files of "Noto CJK" fonts are in the list of
27fonts accessible from XeTeX.
28
29.. [1] https://lore.kernel.org/r/[email protected]/
30.. [2] https://lore.kernel.org/r/[email protected]/
31.. [3] https://en.wikipedia.org/wiki/Variable_font
32.. [4] https://fedoraproject.org/wiki/Changes/Noto_CJK_Variable_Fonts
33.. [5] https://build.opensuse.org/request/show/1157217
34
35Workarounds for building translations.pdf
36-----------------------------------------
37
38* Denylist "variable font" Noto CJK fonts.
39
40 - Create $HOME/deny-vf/fontconfig/fonts.conf from template below, with
41 tweaks if necessary. Remove leading "".
42
43 - Path of fontconfig/fonts.conf can be overridden by setting an env
44 variable FONTS_CONF_DENY_VF.
45
46 * Template::
47
48 <?xml version="1.0"?>
49 <!DOCTYPE fontconfig SYSTEM "urn:fontconfig:fonts.dtd">
50 <fontconfig>
51 <!--
52 Ignore variable-font glob (not to break xetex)
53 -->
54 <selectfont>
55 <rejectfont>
56 <!--
57 for Fedora
58 -->
59 <glob>/usr/share/fonts/google-noto-*-cjk-vf-fonts</glob>
60 <!--
61 for openSUSE tumbleweed
62 -->
63 <glob>/usr/share/fonts/truetype/Noto*CJK*-VF.otf</glob>
64 </rejectfont>
65 </selectfont>
66 </fontconfig>
67
68 The denylisting is activated for "make pdfdocs".
69
70* For skipping CJK pages in PDF
71
72 - Uninstall texlive-xecjk.
73 Denylisting is not needed in this case.
74
75* For printing CJK pages in PDF
76
77 - Need non-variable "Noto CJK" fonts.
78
79 * Fedora
80
81 - google-noto-sans-cjk-fonts
82 - google-noto-serif-cjk-fonts
83
84 * openSUSE tumbleweed
85
86 - Non-variable "Noto CJK" fonts are not available as distro packages
87 as of April, 2024. Fetch a set of font files from upstream Noto
88 CJK Font released at:
89
90 https://github.com/notofonts/noto-cjk/tree/main/Sans#super-otc
91
92 and at:
93
94 https://github.com/notofonts/noto-cjk/tree/main/Serif#super-otc
95
96 then uncompress and deploy them.
97 - Remember to update fontconfig cache by running fc-cache.
98
99.. caution::
100 Uninstalling "variable font" packages can be dangerous.
101 They might be depended upon by other packages important for your work.
102 Denylisting should be less invasive, as it is effective only while
103 XeLaTeX runs in "make pdfdocs".
104"""
105
106import os
107import re
108import subprocess
109import textwrap
110import sys
111
112class LatexFontChecker:
113 """
114 Detect problems with CJK variable fonts that affect PDF builds for
115 translations.
116 """
117
118 def __init__(self, deny_vf=None):
119 if not deny_vf:
120 deny_vf = os.environ.get('FONTS_CONF_DENY_VF', "~/deny-vf")
121
122 self.environ = os.environ.copy()
123 self.environ['XDG_CONFIG_HOME'] = os.path.expanduser(deny_vf)
124
125 self.re_cjk = re.compile(r"([^:]+):\s*Noto\s+(Sans|Sans Mono|Serif) CJK")
126
127 def description(self):
128 """
129 Returns module description.
130 """
131 return __doc__
132
133 def get_noto_cjk_vf_fonts(self):
134 """
135 Get Noto CJK fonts.
136 """
137
138 cjk_fonts = set()
139 cmd = ["fc-list", ":", "file", "family", "variable"]
140 try:
141 result = subprocess.run(cmd,stdout=subprocess.PIPE,
142 stderr=subprocess.PIPE,
143 universal_newlines=True,
144 env=self.environ,
145 check=True)
146
147 except subprocess.CalledProcessError as exc:
148 sys.exit(f"Error running fc-list: {repr(exc)}")
149
150 for line in result.stdout.splitlines():
151 if 'variable=True' not in line:
152 continue
153
154 match = self.re_cjk.search(line)
155 if match:
156 cjk_fonts.add(match.group(1))
157
158 return sorted(cjk_fonts)
159
160 def check(self):
161 """
162 Check for problems with CJK fonts.
163 """
164
165 fonts = textwrap.indent("\n".join(self.get_noto_cjk_vf_fonts()), " ")
166 if not fonts:
167 return None
168
169 rel_file = os.path.relpath(__file__, os.getcwd())
170
171 msg = "=" * 77 + "\n"
172 msg += 'XeTeX is confused by "variable font" files listed below:\n'
173 msg += fonts + "\n"
174 msg += textwrap.dedent(f"""
175 For CJK pages in PDF, they need to be hidden from XeTeX by denylisting.
176 Or, CJK pages can be skipped by uninstalling texlive-xecjk.
177
178 For more info on denylisting, other options, and variable font, run:
179
180 tools/docs/check-variable-fonts.py -h
181 """)
182 msg += "=" * 77
183
184 return msg