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