Support user defined LaTeX themes

This commit is contained in:
Takeshi KOMIYA 2019-12-28 19:36:55 +09:00
parent a13ec4f41c
commit cdbefb600f
7 changed files with 97 additions and 13 deletions

View File

@ -1904,7 +1904,7 @@ These options influence LaTeX output.
This value determines how to group the document tree into LaTeX source files.
It must be a list of tuples ``(startdocname, targetname, title, author,
documentclass, toctree_only)``, where the items are:
theme, toctree_only)``, where the items are:
*startdocname*
String that specifies the :term:`document name` of the LaTeX file's master
@ -1926,13 +1926,8 @@ These options influence LaTeX output.
applies. Use ``\\and`` to separate multiple authors, as in:
``'John \\and Sarah'`` (backslashes must be Python-escaped to reach LaTeX).
*documentclass*
Normally, one of ``'manual'`` or ``'howto'`` (provided by Sphinx and based
on ``'report'``, resp. ``'article'``; Japanese documents use ``'jsbook'``,
resp. ``'jreport'``.) "howto" (non-Japanese) documents will not get
appendices. Also they have a simpler title page. Other document classes
can be given. Independently of the document class, the "sphinx" package is
always loaded in order to define Sphinx's custom LaTeX commands.
*theme*
LaTeX theme. See :confval:`latex_theme`.
*toctree_only*
Must be ``True`` or ``False``. If true, the *startdoc* document itself is
@ -2087,6 +2082,33 @@ These options influence LaTeX output.
This overrides the files which is provided from Sphinx such as
``sphinx.sty``.
.. confval:: latex_theme
The "theme" that the LaTeX output should use. It is a collection of settings
for LaTeX output (ex. document class, top level sectioning unit and so on).
As a built-in LaTeX themes, ``manual`` and ``howto`` are bundled.
``manual``
A LaTeX theme for writing a manual. It imports the ``report`` document
class (Japanese documents use ``jsbook``).
``howto``
A LaTeX theme for writing an article. It imports the ``article`` document
class (Japanese documents use ``jreport`` rather). :confval:`latex_appendices`
is available only for this theme.
It defaults to ``'manual'``.
.. versionadded:: 3.0
.. confval:: latex_theme_path
A list of paths that contain custom LaTeX themes as subdirectories. Relative
paths are taken as relative to the configuration directory.
.. versionadded:: 3.0
.. _text-options:

View File

@ -493,7 +493,7 @@ def default_latex_documents(config: Config) -> List[Tuple[str, str, str, str, st
make_filename_from_project(config.project) + '.tex',
texescape.escape_abbr(project),
texescape.escape_abbr(author),
'manual')]
config.latex_theme)]
def setup(app: Sphinx) -> Dict[str, Any]:
@ -516,6 +516,8 @@ def setup(app: Sphinx) -> Dict[str, Any]:
app.add_config_value('latex_show_pagerefs', False, None)
app.add_config_value('latex_elements', {}, None)
app.add_config_value('latex_additional_files', [], None)
app.add_config_value('latex_theme', 'manual', None, [str])
app.add_config_value('latex_theme_path', [], None, [list])
app.add_config_value('latex_docclass', default_latex_docclass, None)

View File

@ -8,10 +8,17 @@
:license: BSD, see LICENSE for details.
"""
import configparser
from os import path
from typing import Dict
from sphinx.application import Sphinx
from sphinx.config import Config
from sphinx.errors import ThemeError
from sphinx.locale import __
from sphinx.util import logging
logger = logging.getLogger(__name__)
class Theme:
@ -56,11 +63,30 @@ class BuiltInTheme(Theme):
return 'chapter'
class UserTheme(Theme):
"""A user defined LaTeX theme."""
def __init__(self, name: str, filename: str) -> None:
self.name = name
self.config = configparser.RawConfigParser()
self.config.read(path.join(filename))
try:
self.docclass = self.config.get('theme', 'docclass')
self.wrapperclass = self.config.get('theme', 'wrapperclass')
self.toplevel_sectioning = self.config.get('theme', 'toplevel_sectioning')
except configparser.NoSectionError:
raise ThemeError(__('%r doesn\'t have "theme" setting') % filename)
except configparser.NoOptionError as exc:
raise ThemeError(__('%r doesn\'t have "%s" setting') % (filename, exc.args[0]))
class ThemeFactory:
"""A factory class for LaTeX Themes."""
def __init__(self, app: Sphinx) -> None:
self.themes = {} # type: Dict[str, Theme]
self.theme_paths = [path.join(app.srcdir, p) for p in app.config.latex_theme_path]
self.load_builtin_themes(app.config)
def load_builtin_themes(self, config: Config) -> None:
@ -70,7 +96,23 @@ class ThemeFactory:
def get(self, name: str) -> Theme:
"""Get a theme for given *name*."""
if name not in self.themes:
return Theme(name)
else:
if name in self.themes:
return self.themes[name]
else:
theme = self.find_user_theme(name)
if theme:
return theme
else:
return Theme(name)
def find_user_theme(self, name: str) -> Theme:
"""Find a theme named as *name* from latex_theme_path."""
for theme_path in self.theme_paths:
config_path = path.join(theme_path, name, 'theme.conf')
if path.isfile(config_path):
try:
return UserTheme(name, config_path)
except ThemeError as exc:
logger.warning(exc)
return None

View File

@ -0,0 +1,2 @@
latex_theme = 'custom'
latex_theme_path = ['theme']

View File

@ -0,0 +1,2 @@
latex_theme
===========

View File

@ -0,0 +1,4 @@
[theme]
docclass = book
wrapperclass = sphinxbook
toplevel_sectioning = chapter

View File

@ -20,7 +20,7 @@ from test_build_html import ENV_WARNINGS
from sphinx.builders.latex import default_latex_documents
from sphinx.config import Config
from sphinx.errors import SphinxError
from sphinx.errors import SphinxError, ThemeError
from sphinx.testing.util import strip_escseq
from sphinx.util import docutils
from sphinx.util.osutil import cd, ensuredir
@ -215,6 +215,15 @@ def test_latex_basic_howto_ja(app, status, warning):
assert r'\documentclass[letterpaper,10pt,dvipdfmx]{sphinxhowto}' in result
@pytest.mark.sphinx('latex', testroot='latex-theme')
def test_latex_theme(app, status, warning):
app.builder.build_all()
result = (app.outdir / 'python.tex').read_text(encoding='utf8')
print(result)
assert r'\def\sphinxdocclass{book}' in result
assert r'\documentclass[letterpaper,10pt,english]{sphinxbook}' in result
@pytest.mark.sphinx('latex', testroot='basic', confoverrides={'language': 'zh'})
def test_latex_additional_settings_for_language_code(app, status, warning):
app.builder.build_all()
@ -1465,6 +1474,7 @@ def test_default_latex_documents():
'author': "Wolfgang Schäuble & G'Beckstein."})
config.init_values()
config.add('latex_engine', None, True, None)
config.add('latex_theme', 'manual', True, None)
expected = [('index', 'stasi.tex', 'STASI™ Documentation',
r"Wolfgang Schäuble \& G\textquotesingle{}Beckstein.\@{}", 'manual')]
assert default_latex_documents(config) == expected