xref: /linux/tools/lib/python/kdoc/latex_fonts.py (revision 72c395024dac5e215136cbff793455f065603b06)
14515ffdfSMauro Carvalho Chehab#!/usr/bin/env python3
24515ffdfSMauro Carvalho Chehab# SPDX-License-Identifier: GPL-2.0-only
34515ffdfSMauro Carvalho Chehab# Copyright (C) Akira Yokosawa, 2024
44515ffdfSMauro Carvalho Chehab#
54515ffdfSMauro Carvalho Chehab# Ported to Python by (c) Mauro Carvalho Chehab, 2025
64515ffdfSMauro Carvalho Chehab
74515ffdfSMauro Carvalho Chehab"""
8*4d7f6319SMauro Carvalho ChehabDetect problematic Noto CJK variable fonts
9*4d7f6319SMauro Carvalho Chehab==========================================
104515ffdfSMauro Carvalho Chehab
11*4d7f6319SMauro Carvalho ChehabFor ``make pdfdocs``, reports of build errors of translations.pdf started
12*4d7f6319SMauro Carvalho Chehabarriving early 2024 [1]_ [2]_.  It turned out that Fedora and openSUSE
13*4d7f6319SMauro Carvalho Chehabtumbleweed have started deploying variable-font [3]_ format of "Noto CJK"
14*4d7f6319SMauro Carvalho Chehabfonts [4]_ [5]_.  For PDF, a LaTeX package named xeCJK is used for CJK
154515ffdfSMauro Carvalho Chehab(Chinese, Japanese, Korean) pages.  xeCJK requires XeLaTeX/XeTeX, which
164515ffdfSMauro Carvalho Chehabdoes not (and likely never will) understand variable fonts for historical
174515ffdfSMauro Carvalho Chehabreasons.
184515ffdfSMauro Carvalho Chehab
194515ffdfSMauro Carvalho ChehabThe build error happens even when both of variable- and non-variable-format
204515ffdfSMauro Carvalho Chehabfonts are found on the build system.  To make matters worse, Fedora enlists
214515ffdfSMauro Carvalho Chehabvariable "Noto CJK" fonts in the requirements of langpacks-ja, -ko, -zh_CN,
224515ffdfSMauro Carvalho Chehab-zh_TW, etc.  Hence developers who have interest in CJK pages are more
234515ffdfSMauro Carvalho Chehablikely to encounter the build errors.
244515ffdfSMauro Carvalho Chehab
254515ffdfSMauro Carvalho ChehabThis script is invoked from the error path of "make pdfdocs" and emits
264515ffdfSMauro Carvalho Chehabsuggestions if variable-font files of "Noto CJK" fonts are in the list of
274515ffdfSMauro Carvalho Chehabfonts accessible from XeTeX.
284515ffdfSMauro Carvalho Chehab
29*4d7f6319SMauro Carvalho Chehab.. [1] https://lore.kernel.org/r/8734tqsrt7.fsf@meer.lwn.net/
30*4d7f6319SMauro Carvalho Chehab.. [2] https://lore.kernel.org/r/1708585803.600323099@f111.i.mail.ru/
31*4d7f6319SMauro Carvalho Chehab.. [3] https://en.wikipedia.org/wiki/Variable_font
32*4d7f6319SMauro Carvalho Chehab.. [4] https://fedoraproject.org/wiki/Changes/Noto_CJK_Variable_Fonts
33*4d7f6319SMauro Carvalho Chehab.. [5] https://build.opensuse.org/request/show/1157217
344515ffdfSMauro Carvalho Chehab
354515ffdfSMauro Carvalho ChehabWorkarounds for building translations.pdf
36*4d7f6319SMauro Carvalho Chehab-----------------------------------------
374515ffdfSMauro Carvalho Chehab
384515ffdfSMauro Carvalho Chehab* Denylist "variable font" Noto CJK fonts.
39*4d7f6319SMauro Carvalho Chehab
404515ffdfSMauro Carvalho Chehab  - Create $HOME/deny-vf/fontconfig/fonts.conf from template below, with
414515ffdfSMauro Carvalho Chehab    tweaks if necessary.  Remove leading "".
42*4d7f6319SMauro Carvalho Chehab
434515ffdfSMauro Carvalho Chehab  - Path of fontconfig/fonts.conf can be overridden by setting an env
444515ffdfSMauro Carvalho Chehab    variable FONTS_CONF_DENY_VF.
454515ffdfSMauro Carvalho Chehab
46*4d7f6319SMauro Carvalho Chehab    * Template::
47*4d7f6319SMauro Carvalho Chehab
484515ffdfSMauro Carvalho Chehab        <?xml version="1.0"?>
494515ffdfSMauro Carvalho Chehab        <!DOCTYPE fontconfig SYSTEM "urn:fontconfig:fonts.dtd">
504515ffdfSMauro Carvalho Chehab        <fontconfig>
514515ffdfSMauro Carvalho Chehab        <!--
524515ffdfSMauro Carvalho Chehab        Ignore variable-font glob (not to break xetex)
534515ffdfSMauro Carvalho Chehab        -->
544515ffdfSMauro Carvalho Chehab            <selectfont>
554515ffdfSMauro Carvalho Chehab                <rejectfont>
564515ffdfSMauro Carvalho Chehab                    <!--
574515ffdfSMauro Carvalho Chehab                        for Fedora
584515ffdfSMauro Carvalho Chehab                    -->
594515ffdfSMauro Carvalho Chehab                    <glob>/usr/share/fonts/google-noto-*-cjk-vf-fonts</glob>
604515ffdfSMauro Carvalho Chehab                    <!--
614515ffdfSMauro Carvalho Chehab                        for openSUSE tumbleweed
624515ffdfSMauro Carvalho Chehab                    -->
634515ffdfSMauro Carvalho Chehab                    <glob>/usr/share/fonts/truetype/Noto*CJK*-VF.otf</glob>
644515ffdfSMauro Carvalho Chehab                </rejectfont>
654515ffdfSMauro Carvalho Chehab            </selectfont>
664515ffdfSMauro Carvalho Chehab        </fontconfig>
674515ffdfSMauro Carvalho Chehab
684515ffdfSMauro Carvalho Chehab    The denylisting is activated for "make pdfdocs".
694515ffdfSMauro Carvalho Chehab
704515ffdfSMauro Carvalho Chehab* For skipping CJK pages in PDF
71*4d7f6319SMauro Carvalho Chehab
724515ffdfSMauro Carvalho Chehab  - Uninstall texlive-xecjk.
734515ffdfSMauro Carvalho Chehab    Denylisting is not needed in this case.
744515ffdfSMauro Carvalho Chehab
754515ffdfSMauro Carvalho Chehab* For printing CJK pages in PDF
76*4d7f6319SMauro Carvalho Chehab
774515ffdfSMauro Carvalho Chehab  - Need non-variable "Noto CJK" fonts.
78*4d7f6319SMauro Carvalho Chehab
794515ffdfSMauro Carvalho Chehab    * Fedora
80*4d7f6319SMauro Carvalho Chehab
814515ffdfSMauro Carvalho Chehab      - google-noto-sans-cjk-fonts
824515ffdfSMauro Carvalho Chehab      - google-noto-serif-cjk-fonts
83*4d7f6319SMauro Carvalho Chehab
844515ffdfSMauro Carvalho Chehab    * openSUSE tumbleweed
85*4d7f6319SMauro Carvalho Chehab
864515ffdfSMauro Carvalho Chehab      - Non-variable "Noto CJK" fonts are not available as distro packages
874515ffdfSMauro Carvalho Chehab        as of April, 2024.  Fetch a set of font files from upstream Noto
884515ffdfSMauro Carvalho Chehab        CJK Font released at:
89*4d7f6319SMauro Carvalho Chehab
904515ffdfSMauro Carvalho Chehab          https://github.com/notofonts/noto-cjk/tree/main/Sans#super-otc
91*4d7f6319SMauro Carvalho Chehab
924515ffdfSMauro Carvalho Chehab        and at:
93*4d7f6319SMauro Carvalho Chehab
944515ffdfSMauro Carvalho Chehab          https://github.com/notofonts/noto-cjk/tree/main/Serif#super-otc
95*4d7f6319SMauro Carvalho Chehab
96*4d7f6319SMauro Carvalho Chehab        then uncompress and deploy them.
974515ffdfSMauro Carvalho Chehab      - Remember to update fontconfig cache by running fc-cache.
984515ffdfSMauro Carvalho Chehab
99*4d7f6319SMauro Carvalho Chehab.. caution::
1004515ffdfSMauro Carvalho Chehab    Uninstalling "variable font" packages can be dangerous.
1014515ffdfSMauro Carvalho Chehab    They might be depended upon by other packages important for your work.
1024515ffdfSMauro Carvalho Chehab    Denylisting should be less invasive, as it is effective only while
1034515ffdfSMauro Carvalho Chehab    XeLaTeX runs in "make pdfdocs".
1044515ffdfSMauro Carvalho Chehab"""
1054515ffdfSMauro Carvalho Chehab
1064515ffdfSMauro Carvalho Chehabimport os
1074515ffdfSMauro Carvalho Chehabimport re
1084515ffdfSMauro Carvalho Chehabimport subprocess
1094515ffdfSMauro Carvalho Chehabimport textwrap
1104515ffdfSMauro Carvalho Chehabimport sys
1114515ffdfSMauro Carvalho Chehab
1124515ffdfSMauro Carvalho Chehabclass LatexFontChecker:
1134515ffdfSMauro Carvalho Chehab    """
1144515ffdfSMauro Carvalho Chehab    Detect problems with CJK variable fonts that affect PDF builds for
1154515ffdfSMauro Carvalho Chehab    translations.
1164515ffdfSMauro Carvalho Chehab    """
1174515ffdfSMauro Carvalho Chehab
11892ea342fSMauro Carvalho Chehab    def __init__(self, deny_vf=None):
11992ea342fSMauro Carvalho Chehab        if not deny_vf:
1204515ffdfSMauro Carvalho Chehab            deny_vf = os.environ.get('FONTS_CONF_DENY_VF', "~/deny-vf")
1214515ffdfSMauro Carvalho Chehab
1224515ffdfSMauro Carvalho Chehab        self.environ = os.environ.copy()
1234515ffdfSMauro Carvalho Chehab        self.environ['XDG_CONFIG_HOME'] = os.path.expanduser(deny_vf)
1244515ffdfSMauro Carvalho Chehab
1254515ffdfSMauro Carvalho Chehab        self.re_cjk = re.compile(r"([^:]+):\s*Noto\s+(Sans|Sans Mono|Serif) CJK")
1264515ffdfSMauro Carvalho Chehab
12792ea342fSMauro Carvalho Chehab    def description(self):
128*4d7f6319SMauro Carvalho Chehab        """
129*4d7f6319SMauro Carvalho Chehab        Returns module description.
130*4d7f6319SMauro Carvalho Chehab        """
13192ea342fSMauro Carvalho Chehab        return __doc__
13292ea342fSMauro Carvalho Chehab
1334515ffdfSMauro Carvalho Chehab    def get_noto_cjk_vf_fonts(self):
134*4d7f6319SMauro Carvalho Chehab        """
135*4d7f6319SMauro Carvalho Chehab        Get Noto CJK fonts.
136*4d7f6319SMauro Carvalho Chehab        """
1374515ffdfSMauro Carvalho Chehab
1384515ffdfSMauro Carvalho Chehab        cjk_fonts = set()
1394515ffdfSMauro Carvalho Chehab        cmd = ["fc-list", ":", "file", "family", "variable"]
1404515ffdfSMauro Carvalho Chehab        try:
1414515ffdfSMauro Carvalho Chehab            result = subprocess.run(cmd,stdout=subprocess.PIPE,
1424515ffdfSMauro Carvalho Chehab                                    stderr=subprocess.PIPE,
1434515ffdfSMauro Carvalho Chehab                                    universal_newlines=True,
1444515ffdfSMauro Carvalho Chehab                                    env=self.environ,
1454515ffdfSMauro Carvalho Chehab                                    check=True)
1464515ffdfSMauro Carvalho Chehab
1474515ffdfSMauro Carvalho Chehab        except subprocess.CalledProcessError as exc:
1484515ffdfSMauro Carvalho Chehab            sys.exit(f"Error running fc-list: {repr(exc)}")
1494515ffdfSMauro Carvalho Chehab
1504515ffdfSMauro Carvalho Chehab        for line in result.stdout.splitlines():
1514515ffdfSMauro Carvalho Chehab            if 'variable=True' not in line:
1524515ffdfSMauro Carvalho Chehab                continue
1534515ffdfSMauro Carvalho Chehab
1544515ffdfSMauro Carvalho Chehab            match = self.re_cjk.search(line)
1554515ffdfSMauro Carvalho Chehab            if match:
1564515ffdfSMauro Carvalho Chehab                cjk_fonts.add(match.group(1))
1574515ffdfSMauro Carvalho Chehab
1584515ffdfSMauro Carvalho Chehab        return sorted(cjk_fonts)
1594515ffdfSMauro Carvalho Chehab
1604515ffdfSMauro Carvalho Chehab    def check(self):
161*4d7f6319SMauro Carvalho Chehab        """
162*4d7f6319SMauro Carvalho Chehab        Check for problems with CJK fonts.
163*4d7f6319SMauro Carvalho Chehab        """
1644515ffdfSMauro Carvalho Chehab
1654515ffdfSMauro Carvalho Chehab        fonts = textwrap.indent("\n".join(self.get_noto_cjk_vf_fonts()), "    ")
1664515ffdfSMauro Carvalho Chehab        if not fonts:
1674515ffdfSMauro Carvalho Chehab            return None
1684515ffdfSMauro Carvalho Chehab
1694515ffdfSMauro Carvalho Chehab        rel_file = os.path.relpath(__file__, os.getcwd())
1704515ffdfSMauro Carvalho Chehab
1714515ffdfSMauro Carvalho Chehab        msg = "=" * 77 + "\n"
1724515ffdfSMauro Carvalho Chehab        msg += 'XeTeX is confused by "variable font" files listed below:\n'
1734515ffdfSMauro Carvalho Chehab        msg += fonts + "\n"
1744515ffdfSMauro Carvalho Chehab        msg += textwrap.dedent(f"""
1754515ffdfSMauro Carvalho Chehab                For CJK pages in PDF, they need to be hidden from XeTeX by denylisting.
1764515ffdfSMauro Carvalho Chehab                Or, CJK pages can be skipped by uninstalling texlive-xecjk.
1774515ffdfSMauro Carvalho Chehab
17892ea342fSMauro Carvalho Chehab                For more info on denylisting, other options, and variable font, run:
17992ea342fSMauro Carvalho Chehab
18092ea342fSMauro Carvalho Chehab                    tools/docs/check-variable-fonts.py -h
1814515ffdfSMauro Carvalho Chehab            """)
1824515ffdfSMauro Carvalho Chehab        msg += "=" * 77
1834515ffdfSMauro Carvalho Chehab
1844515ffdfSMauro Carvalho Chehab        return msg
185