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