136 lines
4.4 KiB
Python
136 lines
4.4 KiB
Python
"""Theming support for LaTeX builder."""
|
|
|
|
from __future__ import annotations
|
|
|
|
import configparser
|
|
from os import path
|
|
from typing import TYPE_CHECKING
|
|
|
|
from sphinx.errors import ThemeError
|
|
from sphinx.locale import __
|
|
from sphinx.util import logging
|
|
|
|
if TYPE_CHECKING:
|
|
from sphinx.application import Sphinx
|
|
from sphinx.config import Config
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
|
|
class Theme:
|
|
"""A set of LaTeX configurations."""
|
|
|
|
LATEX_ELEMENTS_KEYS = ['papersize', 'pointsize']
|
|
UPDATABLE_KEYS = ['papersize', 'pointsize']
|
|
|
|
def __init__(self, name: str) -> None:
|
|
self.name = name
|
|
self.docclass = name
|
|
self.wrapperclass = name
|
|
self.papersize = 'letterpaper'
|
|
self.pointsize = '10pt'
|
|
self.toplevel_sectioning = 'chapter'
|
|
|
|
def update(self, config: Config) -> None:
|
|
"""Override theme settings by user's configuration."""
|
|
for key in self.LATEX_ELEMENTS_KEYS:
|
|
if config.latex_elements.get(key):
|
|
value = config.latex_elements[key]
|
|
setattr(self, key, value)
|
|
|
|
for key in self.UPDATABLE_KEYS:
|
|
if key in config.latex_theme_options:
|
|
value = config.latex_theme_options[key]
|
|
setattr(self, key, value)
|
|
|
|
|
|
class BuiltInTheme(Theme):
|
|
"""A built-in LaTeX theme."""
|
|
|
|
def __init__(self, name: str, config: Config) -> None:
|
|
super().__init__(name)
|
|
|
|
if name == 'howto':
|
|
self.docclass = config.latex_docclass.get('howto', 'article')
|
|
else:
|
|
self.docclass = config.latex_docclass.get('manual', 'report')
|
|
|
|
if name in ('manual', 'howto'):
|
|
self.wrapperclass = 'sphinx' + name
|
|
else:
|
|
self.wrapperclass = name
|
|
|
|
# we assume LaTeX class provides \chapter command except in case
|
|
# of non-Japanese 'howto' case
|
|
if name == 'howto' and not self.docclass.startswith('j'):
|
|
self.toplevel_sectioning = 'section'
|
|
else:
|
|
self.toplevel_sectioning = 'chapter'
|
|
|
|
|
|
class UserTheme(Theme):
|
|
"""A user defined LaTeX theme."""
|
|
|
|
REQUIRED_CONFIG_KEYS = ['docclass', 'wrapperclass']
|
|
OPTIONAL_CONFIG_KEYS = ['papersize', 'pointsize', 'toplevel_sectioning']
|
|
|
|
def __init__(self, name: str, filename: str) -> None:
|
|
super().__init__(name)
|
|
self.config = configparser.RawConfigParser()
|
|
self.config.read(path.join(filename), encoding='utf-8')
|
|
|
|
for key in self.REQUIRED_CONFIG_KEYS:
|
|
try:
|
|
value = self.config.get('theme', key)
|
|
setattr(self, key, value)
|
|
except configparser.NoSectionError as exc:
|
|
raise ThemeError(__('%r doesn\'t have "theme" setting') %
|
|
filename) from exc
|
|
except configparser.NoOptionError as exc:
|
|
raise ThemeError(__('%r doesn\'t have "%s" setting') %
|
|
(filename, exc.args[0])) from exc
|
|
|
|
for key in self.OPTIONAL_CONFIG_KEYS:
|
|
try:
|
|
value = self.config.get('theme', key)
|
|
setattr(self, key, value)
|
|
except configparser.NoOptionError:
|
|
pass
|
|
|
|
|
|
class ThemeFactory:
|
|
"""A factory class for LaTeX Themes."""
|
|
|
|
def __init__(self, app: Sphinx) -> None:
|
|
self.themes: dict[str, Theme] = {}
|
|
self.theme_paths = [path.join(app.srcdir, p) for p in app.config.latex_theme_path]
|
|
self.config = app.config
|
|
self.load_builtin_themes(app.config)
|
|
|
|
def load_builtin_themes(self, config: Config) -> None:
|
|
"""Load built-in themes."""
|
|
self.themes['manual'] = BuiltInTheme('manual', config)
|
|
self.themes['howto'] = BuiltInTheme('howto', config)
|
|
|
|
def get(self, name: str) -> Theme:
|
|
"""Get a theme for given *name*."""
|
|
if name in self.themes:
|
|
theme = self.themes[name]
|
|
else:
|
|
theme = self.find_user_theme(name) or Theme(name)
|
|
|
|
theme.update(self.config)
|
|
return theme
|
|
|
|
def find_user_theme(self, name: str) -> Theme | None:
|
|
"""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
|