mirror of
https://github.com/sphinx-doc/sphinx.git
synced 2025-02-25 18:55:22 -06:00
Support LaTeX Theming; a set of document class settings (refs: #6672)
This commit is contained in:
parent
4ab4100cc5
commit
a13ec4f41c
2
CHANGES
2
CHANGES
@ -42,6 +42,7 @@ Deprecated
|
|||||||
* ``sphinx.testing.path.Path.text()``
|
* ``sphinx.testing.path.Path.text()``
|
||||||
* ``sphinx.testing.path.Path.bytes()``
|
* ``sphinx.testing.path.Path.bytes()``
|
||||||
* ``sphinx.util.inspect.getargspec()``
|
* ``sphinx.util.inspect.getargspec()``
|
||||||
|
* ``sphinx.writers.latex.LaTeXWriter.format_docclass()``
|
||||||
|
|
||||||
Features added
|
Features added
|
||||||
--------------
|
--------------
|
||||||
@ -68,6 +69,7 @@ Features added
|
|||||||
``no-scaled-link`` class
|
``no-scaled-link`` class
|
||||||
* #7144: Add CSS class indicating its domain for each desc node
|
* #7144: Add CSS class indicating its domain for each desc node
|
||||||
* #7211: latex: Use babel for Chinese document when using XeLaTeX
|
* #7211: latex: Use babel for Chinese document when using XeLaTeX
|
||||||
|
* #6672: LaTeX: Support LaTeX Theming (experimental)
|
||||||
* #7005: LaTeX: Add LaTeX styling macro for :rst:role:`kbd` role
|
* #7005: LaTeX: Add LaTeX styling macro for :rst:role:`kbd` role
|
||||||
* #7220: genindex: Show "main" index entries at first
|
* #7220: genindex: Show "main" index entries at first
|
||||||
* #7103: linkcheck: writes all links to ``output.json``
|
* #7103: linkcheck: writes all links to ``output.json``
|
||||||
|
@ -61,6 +61,11 @@ The following is a list of deprecated interfaces.
|
|||||||
- 5.0
|
- 5.0
|
||||||
- ``inspect.getargspec()``
|
- ``inspect.getargspec()``
|
||||||
|
|
||||||
|
* - ``sphinx.writers.latex.LaTeXWriter.format_docclass()``
|
||||||
|
- 3.0
|
||||||
|
- 5.0
|
||||||
|
- LaTeX Themes
|
||||||
|
|
||||||
* - ``decode`` argument of ``sphinx.pycode.ModuleAnalyzer()``
|
* - ``decode`` argument of ``sphinx.pycode.ModuleAnalyzer()``
|
||||||
- 2.4
|
- 2.4
|
||||||
- 4.0
|
- 4.0
|
||||||
|
@ -21,6 +21,7 @@ from sphinx import package_dir, addnodes, highlighting
|
|||||||
from sphinx.application import Sphinx
|
from sphinx.application import Sphinx
|
||||||
from sphinx.builders import Builder
|
from sphinx.builders import Builder
|
||||||
from sphinx.builders.latex.constants import ADDITIONAL_SETTINGS, DEFAULT_SETTINGS
|
from sphinx.builders.latex.constants import ADDITIONAL_SETTINGS, DEFAULT_SETTINGS
|
||||||
|
from sphinx.builders.latex.theming import Theme, ThemeFactory
|
||||||
from sphinx.builders.latex.util import ExtBabel
|
from sphinx.builders.latex.util import ExtBabel
|
||||||
from sphinx.config import Config, ENUM
|
from sphinx.config import Config, ENUM
|
||||||
from sphinx.deprecation import RemovedInSphinx40Warning
|
from sphinx.deprecation import RemovedInSphinx40Warning
|
||||||
@ -126,6 +127,7 @@ class LaTeXBuilder(Builder):
|
|||||||
self.context = {} # type: Dict[str, Any]
|
self.context = {} # type: Dict[str, Any]
|
||||||
self.docnames = [] # type: Iterable[str]
|
self.docnames = [] # type: Iterable[str]
|
||||||
self.document_data = [] # type: List[Tuple[str, str, str, str, str, bool]]
|
self.document_data = [] # type: List[Tuple[str, str, str, str, str, bool]]
|
||||||
|
self.themes = ThemeFactory(self.app)
|
||||||
self.usepackages = self.app.registry.latex_packages
|
self.usepackages = self.app.registry.latex_packages
|
||||||
texescape.init()
|
texescape.init()
|
||||||
|
|
||||||
@ -227,7 +229,8 @@ class LaTeXBuilder(Builder):
|
|||||||
self.write_stylesheet()
|
self.write_stylesheet()
|
||||||
|
|
||||||
for entry in self.document_data:
|
for entry in self.document_data:
|
||||||
docname, targetname, title, author, docclass = entry[:5]
|
docname, targetname, title, author, themename = entry[:5]
|
||||||
|
theme = self.themes.get(themename)
|
||||||
toctree_only = False
|
toctree_only = False
|
||||||
if len(entry) > 5:
|
if len(entry) > 5:
|
||||||
toctree_only = entry[5]
|
toctree_only = entry[5]
|
||||||
@ -243,21 +246,22 @@ class LaTeXBuilder(Builder):
|
|||||||
|
|
||||||
doctree = self.assemble_doctree(
|
doctree = self.assemble_doctree(
|
||||||
docname, toctree_only,
|
docname, toctree_only,
|
||||||
appendices=(self.config.latex_appendices if docclass != 'howto' else []))
|
appendices=(self.config.latex_appendices if theme.name != 'howto' else []))
|
||||||
doctree['docclass'] = docclass
|
doctree['docclass'] = theme.docclass
|
||||||
doctree['contentsname'] = self.get_contentsname(docname)
|
doctree['contentsname'] = self.get_contentsname(docname)
|
||||||
doctree['tocdepth'] = tocdepth
|
doctree['tocdepth'] = tocdepth
|
||||||
self.post_process_images(doctree)
|
self.post_process_images(doctree)
|
||||||
self.update_doc_context(title, author)
|
self.update_doc_context(title, author, theme)
|
||||||
|
|
||||||
with progress_message(__("writing")):
|
with progress_message(__("writing")):
|
||||||
docsettings._author = author
|
docsettings._author = author
|
||||||
docsettings._title = title
|
docsettings._title = title
|
||||||
docsettings._contentsname = doctree['contentsname']
|
docsettings._contentsname = doctree['contentsname']
|
||||||
docsettings._docname = docname
|
docsettings._docname = docname
|
||||||
docsettings._docclass = docclass
|
docsettings._docclass = theme.name
|
||||||
|
|
||||||
doctree.settings = docsettings
|
doctree.settings = docsettings
|
||||||
|
docwriter.theme = theme
|
||||||
docwriter.write(doctree, destination)
|
docwriter.write(doctree, destination)
|
||||||
|
|
||||||
def get_contentsname(self, indexfile: str) -> str:
|
def get_contentsname(self, indexfile: str) -> str:
|
||||||
@ -270,9 +274,11 @@ class LaTeXBuilder(Builder):
|
|||||||
|
|
||||||
return contentsname
|
return contentsname
|
||||||
|
|
||||||
def update_doc_context(self, title: str, author: str) -> None:
|
def update_doc_context(self, title: str, author: str, theme: Theme) -> None:
|
||||||
self.context['title'] = title
|
self.context['title'] = title
|
||||||
self.context['author'] = author
|
self.context['author'] = author
|
||||||
|
self.context['docclass'] = theme.docclass
|
||||||
|
self.context['wrapperclass'] = theme.wrapperclass
|
||||||
|
|
||||||
def assemble_doctree(self, indexfile: str, toctree_only: bool, appendices: List[str]) -> nodes.document: # NOQA
|
def assemble_doctree(self, indexfile: str, toctree_only: bool, appendices: List[str]) -> nodes.document: # NOQA
|
||||||
self.docnames = set([indexfile] + appendices)
|
self.docnames = set([indexfile] + appendices)
|
||||||
|
76
sphinx/builders/latex/theming.py
Normal file
76
sphinx/builders/latex/theming.py
Normal file
@ -0,0 +1,76 @@
|
|||||||
|
"""
|
||||||
|
sphinx.builders.latex.theming
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
Theming support for LaTeX builder.
|
||||||
|
|
||||||
|
:copyright: Copyright 2007-2020 by the Sphinx team, see AUTHORS.
|
||||||
|
:license: BSD, see LICENSE for details.
|
||||||
|
"""
|
||||||
|
|
||||||
|
from typing import Dict
|
||||||
|
|
||||||
|
from sphinx.application import Sphinx
|
||||||
|
from sphinx.config import Config
|
||||||
|
|
||||||
|
|
||||||
|
class Theme:
|
||||||
|
"""A set of LaTeX configurations."""
|
||||||
|
|
||||||
|
def __init__(self, name: str) -> None:
|
||||||
|
self.name = name
|
||||||
|
self.docclass = name
|
||||||
|
self.wrapperclass = name
|
||||||
|
self.toplevel_sectioning = 'chapter'
|
||||||
|
|
||||||
|
|
||||||
|
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]
|
||||||
|
|
||||||
|
@property
|
||||||
|
def docclass(self) -> str: # type: ignore
|
||||||
|
if self.name == 'howto':
|
||||||
|
return self.latex_docclass.get('howto', 'article')
|
||||||
|
else:
|
||||||
|
return self.latex_docclass.get('manual', 'report')
|
||||||
|
|
||||||
|
@property
|
||||||
|
def wrapperclass(self) -> str: # type: ignore
|
||||||
|
if self.name in ('manual', 'howto'):
|
||||||
|
return 'sphinx' + self.name
|
||||||
|
else:
|
||||||
|
return self.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'
|
||||||
|
else:
|
||||||
|
return 'chapter'
|
||||||
|
|
||||||
|
|
||||||
|
class ThemeFactory:
|
||||||
|
"""A factory class for LaTeX Themes."""
|
||||||
|
|
||||||
|
def __init__(self, app: Sphinx) -> None:
|
||||||
|
self.themes = {} # type: Dict[str, Theme]
|
||||||
|
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 not in self.themes:
|
||||||
|
return Theme(name)
|
||||||
|
else:
|
||||||
|
return self.themes[name]
|
@ -23,7 +23,9 @@ from docutils.nodes import Element, Node, Text
|
|||||||
|
|
||||||
from sphinx import addnodes
|
from sphinx import addnodes
|
||||||
from sphinx import highlighting
|
from sphinx import highlighting
|
||||||
from sphinx.deprecation import RemovedInSphinx40Warning, deprecated_alias
|
from sphinx.deprecation import (
|
||||||
|
RemovedInSphinx40Warning, RemovedInSphinx50Warning, deprecated_alias
|
||||||
|
)
|
||||||
from sphinx.domains import IndexEntry
|
from sphinx.domains import IndexEntry
|
||||||
from sphinx.domains.std import StandardDomain
|
from sphinx.domains.std import StandardDomain
|
||||||
from sphinx.errors import SphinxError
|
from sphinx.errors import SphinxError
|
||||||
@ -43,6 +45,7 @@ except ImportError:
|
|||||||
if False:
|
if False:
|
||||||
# For type annotation
|
# For type annotation
|
||||||
from sphinx.builders.latex import LaTeXBuilder
|
from sphinx.builders.latex import LaTeXBuilder
|
||||||
|
from sphinx.builders.latex.theming import Theme
|
||||||
|
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
@ -93,9 +96,16 @@ class LaTeXWriter(writers.Writer):
|
|||||||
def __init__(self, builder: "LaTeXBuilder") -> None:
|
def __init__(self, builder: "LaTeXBuilder") -> None:
|
||||||
super().__init__()
|
super().__init__()
|
||||||
self.builder = builder
|
self.builder = builder
|
||||||
|
self.theme = None # type: Theme
|
||||||
|
|
||||||
def translate(self) -> None:
|
def translate(self) -> None:
|
||||||
visitor = self.builder.create_translator(self.document, self.builder)
|
try:
|
||||||
|
visitor = self.builder.create_translator(self.document, self.builder, self.theme)
|
||||||
|
except TypeError:
|
||||||
|
warnings.warn('LaTeXTranslator now takes 3rd argument; "theme".',
|
||||||
|
RemovedInSphinx50Warning)
|
||||||
|
visitor = self.builder.create_translator(self.document, self.builder)
|
||||||
|
|
||||||
self.document.walkabout(visitor)
|
self.document.walkabout(visitor)
|
||||||
self.output = cast(LaTeXTranslator, visitor).astext()
|
self.output = cast(LaTeXTranslator, visitor).astext()
|
||||||
|
|
||||||
@ -281,9 +291,15 @@ class LaTeXTranslator(SphinxTranslator):
|
|||||||
# sphinx specific document classes
|
# sphinx specific document classes
|
||||||
docclasses = ('howto', 'manual')
|
docclasses = ('howto', 'manual')
|
||||||
|
|
||||||
def __init__(self, document: nodes.document, builder: "LaTeXBuilder") -> None:
|
def __init__(self, document: nodes.document, builder: "LaTeXBuilder",
|
||||||
|
theme: "Theme" = None) -> None:
|
||||||
super().__init__(document, builder)
|
super().__init__(document, builder)
|
||||||
self.body = [] # type: List[str]
|
self.body = [] # type: List[str]
|
||||||
|
self.theme = theme
|
||||||
|
|
||||||
|
if theme is None:
|
||||||
|
warnings.warn('LaTeXTranslator now takes 3rd argument; "theme".',
|
||||||
|
RemovedInSphinx50Warning)
|
||||||
|
|
||||||
# flags
|
# flags
|
||||||
self.in_title = 0
|
self.in_title = 0
|
||||||
@ -306,21 +322,32 @@ class LaTeXTranslator(SphinxTranslator):
|
|||||||
# sort out some elements
|
# sort out some elements
|
||||||
self.elements = self.builder.context.copy()
|
self.elements = self.builder.context.copy()
|
||||||
|
|
||||||
# but some have other interface in config file
|
# initial section names
|
||||||
self.elements['wrapperclass'] = self.format_docclass(document.get('docclass'))
|
|
||||||
|
|
||||||
# we assume LaTeX class provides \chapter command except in case
|
|
||||||
# of non-Japanese 'howto' case
|
|
||||||
self.sectionnames = LATEXSECTIONNAMES[:]
|
self.sectionnames = LATEXSECTIONNAMES[:]
|
||||||
if document.get('docclass') == 'howto':
|
|
||||||
docclass = self.config.latex_docclass.get('howto', 'article')
|
if self.theme:
|
||||||
if docclass[0] == 'j': # Japanese class...
|
# new style: control sectioning via theme's setting
|
||||||
pass
|
#
|
||||||
else:
|
# .. note:: template variables(elements) are already assigned in builder
|
||||||
|
docclass = self.theme.docclass
|
||||||
|
if self.theme.toplevel_sectioning == 'section':
|
||||||
self.sectionnames.remove('chapter')
|
self.sectionnames.remove('chapter')
|
||||||
else:
|
else:
|
||||||
docclass = self.config.latex_docclass.get('manual', 'report')
|
# old style: sectioning control is hard-coded
|
||||||
self.elements['docclass'] = docclass
|
# but some have other interface in config file
|
||||||
|
self.elements['wrapperclass'] = self.format_docclass(self.settings.docclass)
|
||||||
|
|
||||||
|
# we assume LaTeX class provides \chapter command except in case
|
||||||
|
# of non-Japanese 'howto' case
|
||||||
|
if document.get('docclass') == 'howto':
|
||||||
|
docclass = self.config.latex_docclass.get('howto', 'article')
|
||||||
|
if docclass[0] == 'j': # Japanese class...
|
||||||
|
pass
|
||||||
|
else:
|
||||||
|
self.sectionnames.remove('chapter')
|
||||||
|
else:
|
||||||
|
docclass = self.config.latex_docclass.get('manual', 'report')
|
||||||
|
self.elements['docclass'] = docclass
|
||||||
|
|
||||||
# determine top section level
|
# determine top section level
|
||||||
self.top_sectionlevel = 1
|
self.top_sectionlevel = 1
|
||||||
@ -472,6 +499,8 @@ class LaTeXTranslator(SphinxTranslator):
|
|||||||
def format_docclass(self, docclass: str) -> str:
|
def format_docclass(self, docclass: str) -> str:
|
||||||
""" prepends prefix to sphinx document classes
|
""" prepends prefix to sphinx document classes
|
||||||
"""
|
"""
|
||||||
|
warnings.warn('LaTeXWriter.format_docclass() is deprecated.',
|
||||||
|
RemovedInSphinx50Warning, stacklevel=2)
|
||||||
if docclass in self.docclasses:
|
if docclass in self.docclasses:
|
||||||
docclass = 'sphinx' + docclass
|
docclass = 'sphinx' + docclass
|
||||||
return docclass
|
return docclass
|
||||||
|
Loading…
Reference in New Issue
Block a user