#!/usr/bin/env python3 # :Author: Günter Milde # :Revision: $Revision: 9943 $ # :Date: $Date: 2024-09-29 11:31:39 +0200 (So, 29. Sep 2024) $ # :Copyright: © 2010 Günter Milde. # :License: Released under the terms of the `2-Clause BSD license`_, in short: # # Copying and distribution of this file, with or without modification, # are permitted in any medium without royalty provided the copyright # notice and this notice are preserved. # This file is offered as-is, without any warranty. # # .. _2-Clause BSD license: https://opensource.org/licenses/BSD-2-Clause """ XeLaTeX document tree Writer. A variant of Docutils' standard 'latex2e' writer producing LaTeX output suited for processing with the Unicode-aware TeX engines LuaTeX and XeTeX. """ __docformat__ = 'reStructuredText' from docutils import frontend from docutils.writers import latex2e from docutils.writers.latex2e import PreambleCmds class Writer(latex2e.Writer): """A writer for Unicode-aware LaTeX variants (XeTeX, LuaTeX)""" supported = ('latex', 'tex', 'xetex', 'xelatex', 'luatex', 'lualatex') """Formats this writer supports.""" default_template = 'xelatex.tex' default_preamble = """\ % Linux Libertine (free, wide coverage, not only for Linux) \\setmainfont{Linux Libertine O} \\setsansfont{Linux Biolinum O} \\setmonofont[HyphenChar=None,Scale=MatchLowercase]{DejaVu Sans Mono}""" config_section = 'xetex writer' config_section_dependencies = ('writers', 'latex writers') # use a copy of the parent spec with some modifications: settings_spec = frontend.filter_settings_spec( latex2e.Writer.settings_spec, # removed settings 'font_encoding', # changed settings: template=('Template file. Default: "%s".' % default_template, ['--template'], {'default': default_template, 'metavar': ''}), latex_preamble=('Customization by LaTeX code in the preamble. ' 'Default: select "Linux Libertine" fonts.', ['--latex-preamble'], {'default': default_preamble}), ) def __init__(self) -> None: latex2e.Writer.__init__(self) self.settings_defaults.update({'fontencoding': ''}) # use default (TU) self.translator_class = XeLaTeXTranslator class Babel(latex2e.Babel): """Language specifics for XeTeX. Use `polyglossia` instead of `babel` and adapt settings. """ language_codes = latex2e.Babel.language_codes.copy() # Additionally supported or differently named languages: language_codes.update({ # code Polyglossia-name comment 'cop': 'coptic', 'de': 'german', # new spelling (de_1996) 'de-1901': 'ogerman', # old spelling 'dv': 'divehi', # Maldivian 'dsb': 'lsorbian', 'el-polyton': 'polygreek', 'fa': 'farsi', 'grc': 'ancientgreek', 'ko': 'korean', 'hsb': 'usorbian', 'sh-Cyrl': 'serbian', # Serbo-Croatian, Cyrillic script 'sh-Latn': 'croatian', # Serbo-Croatian, Latin script 'sq': 'albanian', 'sr': 'serbian', # Cyrillic script (sr-Cyrl) 'th': 'thai', 'vi': 'vietnamese', # zh-Latn: ??? # Chinese Pinyin }) # normalize (downcase) keys language_codes = {k.lower(): v for k, v in language_codes.items()} # Languages without Polyglossia support: for key in ('af', # 'afrikaans', 'de-AT', # 'naustrian', 'de-AT-1901', # 'austrian', # TODO: use variant=... for English variants 'en-CA', # 'canadian', 'en-GB', # 'british', 'en-NZ', # 'newzealand', 'en-US', # 'american', 'fr-CA', # 'canadien', 'grc-ibycus', # 'ibycus', (Greek Ibycus encoding) 'sr-Latn', # 'serbian script=latin' ): del language_codes[key.lower()] def __init__(self, language_code, reporter) -> None: self.language_code = language_code self.reporter = reporter self.language = self.language_name(language_code) self.otherlanguages = {} self.warn_msg = 'Language "%s" not supported by Polyglossia.' self.quote_index = 0 self.quotes = ('"', '"') # language dependent configuration: # double quotes are "active" in some languages (e.g. German). self.literal_double_quote = '"' # TODO: use \textquotedbl ? def __call__(self): setup = [r'\usepackage{polyglossia}', r'\setdefaultlanguage{%s}' % self.language] if self.otherlanguages: setup.append(r'\setotherlanguages{%s}' % ','.join(sorted(self.otherlanguages.keys()))) return '\n'.join(setup) class XeLaTeXTranslator(latex2e.LaTeXTranslator): """ Generate code for LaTeX using Unicode fonts (XeLaTex or LuaLaTeX). See the docstring of docutils.writers._html_base.HTMLTranslator for notes on and examples of safe subclassing. """ def __init__(self, document) -> None: self.is_xetex = True # typeset with XeTeX or LuaTeX engine latex2e.LaTeXTranslator.__init__(self, document, Babel) if self.latex_encoding == 'utf8': self.requirements.pop('_inputenc', None) else: self.requirements['_inputenc'] = (r'\XeTeXinputencoding %s ' % self.latex_encoding) def to_latex_length(self, length_str: str, node=None) -> str: """Convert "measure" `length_str` to LaTeX length specification. XeTeX does not know the length unit px. Use ``\\pdfpxdimen``, the macro holding the value of 1 px in pdfTeX. This way, configuring works the same for pdftex and xetex. """ length_str = super().to_latex_length(length_str, node) if length_str.endswith('px'): if not self.fallback_stylesheet: self.fallbacks['_providelength'] = PreambleCmds.providelength self.fallbacks['px'] = '\n\\DUprovidelength{\\pdfpxdimen}{1bp}' return length_str.replace('px', '\\pdfpxdimen') return length_str