diff --git a/CHANGES b/CHANGES index 37e6d893c7..e2600b06a6 100644 --- a/CHANGES +++ b/CHANGES @@ -19,6 +19,7 @@ Features added -------------- * LaTeX: Make the ``toplevel_sectioning`` setting optional in LaTeX theme +* LaTeX: Allow to override papersize and pointsize from LaTeX themes * #7410: Allow to suppress "circular toctree references detected" warnings using :confval:`suppress_warnings` * C, added scope control directives, :rst:dir:`c:namespace`, diff --git a/sphinx/builders/latex/__init__.py b/sphinx/builders/latex/__init__.py index 1837085202..04b8a3a0d0 100644 --- a/sphinx/builders/latex/__init__.py +++ b/sphinx/builders/latex/__init__.py @@ -314,6 +314,8 @@ class LaTeXBuilder(Builder): self.context['title'] = title self.context['author'] = author self.context['docclass'] = theme.docclass + self.context['papersize'] = theme.papersize + self.context['pointsize'] = theme.pointsize self.context['wrapperclass'] = theme.wrapperclass def assemble_doctree(self, indexfile: str, toctree_only: bool, appendices: List[str]) -> nodes.document: # NOQA diff --git a/sphinx/builders/latex/constants.py b/sphinx/builders/latex/constants.py index 9a89036bf3..7146079ff4 100644 --- a/sphinx/builders/latex/constants.py +++ b/sphinx/builders/latex/constants.py @@ -69,8 +69,8 @@ LUALATEX_DEFAULT_FONTPKG = XELATEX_DEFAULT_FONTPKG DEFAULT_SETTINGS = { 'latex_engine': 'pdflatex', - 'papersize': 'letterpaper', - 'pointsize': '10pt', + 'papersize': '', + 'pointsize': '', 'pxunit': '.75bp', 'classoptions': '', 'extraclassoptions': '', diff --git a/sphinx/builders/latex/theming.py b/sphinx/builders/latex/theming.py index d638639aa0..1a4f483603 100644 --- a/sphinx/builders/latex/theming.py +++ b/sphinx/builders/latex/theming.py @@ -24,50 +24,53 @@ logger = logging.getLogger(__name__) class Theme: """A set of LaTeX configurations.""" + LATEX_ELEMENTS_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) + class BuiltInTheme(Theme): """A built-in LaTeX theme.""" def __init__(self, name: str, config: Config) -> None: - # Note: Don't call supermethod here. - self.name = name - self.latex_docclass = config.latex_docclass # type: Dict[str, str] + super().__init__(name) - @property - def docclass(self) -> str: # type: ignore - if self.name == 'howto': - return self.latex_docclass.get('howto', 'article') + if name == 'howto': + self.docclass = config.latex_docclass.get('howto', 'article') else: - return self.latex_docclass.get('manual', 'report') + self.docclass = config.latex_docclass.get('manual', 'report') - @property - def wrapperclass(self) -> str: # type: ignore - if self.name in ('manual', 'howto'): - return 'sphinx' + self.name + if name in ('manual', 'howto'): + self.wrapperclass = 'sphinx' + name else: - return self.name + self.wrapperclass = name - @property - def toplevel_sectioning(self) -> str: # type: ignore # we assume LaTeX class provides \chapter command except in case # of non-Japanese 'howto' case - if self.name == 'howto' and not self.docclass.startswith('j'): - return 'section' + if name == 'howto' and not self.docclass.startswith('j'): + self.toplevel_sectioning = 'section' else: - return 'chapter' + self.toplevel_sectioning = 'chapter' class UserTheme(Theme): """A user defined LaTeX theme.""" REQUIRED_CONFIG_KEYS = ['docclass', 'wrapperclass'] - OPTIONAL_CONFIG_KEYS = ['toplevel_sectioning'] + OPTIONAL_CONFIG_KEYS = ['papersize', 'pointsize', 'toplevel_sectioning'] def __init__(self, name: str, filename: str) -> None: super().__init__(name) @@ -97,6 +100,7 @@ class ThemeFactory: 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.config = app.config self.load_builtin_themes(app.config) def load_builtin_themes(self, config: Config) -> None: @@ -107,13 +111,14 @@ class ThemeFactory: def get(self, name: str) -> Theme: """Get a theme for given *name*.""" if name in self.themes: - return self.themes[name] + theme = self.themes[name] else: theme = self.find_user_theme(name) - if theme: - return theme - else: - return Theme(name) + if not theme: + theme = Theme(name) + + theme.update(self.config) + return theme def find_user_theme(self, name: str) -> Theme: """Find a theme named as *name* from latex_theme_path.""" diff --git a/tests/roots/test-latex-theme/theme/custom/theme.conf b/tests/roots/test-latex-theme/theme/custom/theme.conf index 8961fac75f..ad8df262f6 100644 --- a/tests/roots/test-latex-theme/theme/custom/theme.conf +++ b/tests/roots/test-latex-theme/theme/custom/theme.conf @@ -1,4 +1,6 @@ [theme] docclass = book wrapperclass = sphinxbook +papersize = a4paper +pointsize = 12pt toplevel_sectioning = chapter diff --git a/tests/test_build_latex.py b/tests/test_build_latex.py index 61020b8610..ba70debc23 100644 --- a/tests/test_build_latex.py +++ b/tests/test_build_latex.py @@ -221,7 +221,18 @@ def test_latex_theme(app, status, warning): 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 + assert r'\documentclass[a4paper,12pt,english]{sphinxbook}' in result + + +@pytest.mark.sphinx('latex', testroot='latex-theme', + confoverrides={'latex_elements': {'papersize': 'b5paper', + 'pointsize': '9pt'}}) +def test_latex_theme_papersize(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[b5paper,9pt,english]{sphinxbook}' in result @pytest.mark.sphinx('latex', testroot='basic', confoverrides={'language': 'zh'})