From 7765940f14201f03aed1038d644bd80289ad672f Mon Sep 17 00:00:00 2001
From: Adam Turner <9087854+aa-turner@users.noreply.github.com>
Date: Wed, 28 Sep 2022 15:17:46 +0100
Subject: [PATCH 01/20] Ensure non-builtin extensions use the proper version
---
sphinx/ext/autodoc/preserve_defaults.py | 3 ++-
sphinx/ext/autodoc/typehints.py | 3 ++-
sphinx/ext/autosectionlabel.py | 3 ++-
sphinx/ext/duration.py | 3 ++-
sphinx/ext/imgconverter.py | 3 ++-
sphinx/ext/napoleon/__init__.py | 6 +++---
6 files changed, 13 insertions(+), 8 deletions(-)
diff --git a/sphinx/ext/autodoc/preserve_defaults.py b/sphinx/ext/autodoc/preserve_defaults.py
index a12f42fcf..5ae3f3593 100644
--- a/sphinx/ext/autodoc/preserve_defaults.py
+++ b/sphinx/ext/autodoc/preserve_defaults.py
@@ -10,6 +10,7 @@ import sys
from inspect import Parameter
from typing import Any, Dict, List, Optional
+import sphinx
from sphinx.application import Sphinx
from sphinx.locale import __
from sphinx.pycode.ast import parse as ast_parse
@@ -121,6 +122,6 @@ def setup(app: Sphinx) -> Dict[str, Any]:
app.connect('autodoc-before-process-signature', update_defvalue)
return {
- 'version': '1.0',
+ 'version': sphinx.__display_version__,
'parallel_read_safe': True
}
diff --git a/sphinx/ext/autodoc/typehints.py b/sphinx/ext/autodoc/typehints.py
index 98c51e9e9..2acacfe94 100644
--- a/sphinx/ext/autodoc/typehints.py
+++ b/sphinx/ext/autodoc/typehints.py
@@ -7,6 +7,7 @@ from typing import Any, Dict, Iterable, Set, cast
from docutils import nodes
from docutils.nodes import Element
+import sphinx
from sphinx import addnodes
from sphinx.application import Sphinx
from sphinx.util import inspect, typing
@@ -206,7 +207,7 @@ def setup(app: Sphinx) -> Dict[str, Any]:
app.connect('object-description-transform', merge_typehints)
return {
- 'version': 'builtin',
+ 'version': sphinx.__display_version__,
'parallel_read_safe': True,
'parallel_write_safe': True,
}
diff --git a/sphinx/ext/autosectionlabel.py b/sphinx/ext/autosectionlabel.py
index ff2f695ec..ef9249e5b 100644
--- a/sphinx/ext/autosectionlabel.py
+++ b/sphinx/ext/autosectionlabel.py
@@ -5,6 +5,7 @@ from typing import Any, Dict, cast
from docutils import nodes
from docutils.nodes import Node
+import sphinx
from sphinx.application import Sphinx
from sphinx.domains.std import StandardDomain
from sphinx.locale import __
@@ -54,7 +55,7 @@ def setup(app: Sphinx) -> Dict[str, Any]:
app.connect('doctree-read', register_sections_as_label)
return {
- 'version': 'builtin',
+ 'version': sphinx.__display_version__,
'parallel_read_safe': True,
'parallel_write_safe': True,
}
diff --git a/sphinx/ext/duration.py b/sphinx/ext/duration.py
index 94fd9077a..13b92fc1a 100644
--- a/sphinx/ext/duration.py
+++ b/sphinx/ext/duration.py
@@ -7,6 +7,7 @@ from typing import Any, Dict, List, cast
from docutils import nodes
+import sphinx
from sphinx.application import Sphinx
from sphinx.domains import Domain
from sphinx.locale import __
@@ -81,7 +82,7 @@ def setup(app: Sphinx) -> Dict[str, Any]:
app.connect('build-finished', on_build_finished)
return {
- 'version': 'builtin',
+ 'version': sphinx.__display_version__,
'parallel_read_safe': True,
'parallel_write_safe': True,
}
diff --git a/sphinx/ext/imgconverter.py b/sphinx/ext/imgconverter.py
index 2a2260c17..599984c18 100644
--- a/sphinx/ext/imgconverter.py
+++ b/sphinx/ext/imgconverter.py
@@ -5,6 +5,7 @@ import sys
from subprocess import PIPE, CalledProcessError
from typing import Any, Dict
+import sphinx
from sphinx.application import Sphinx
from sphinx.errors import ExtensionError
from sphinx.locale import __
@@ -83,7 +84,7 @@ def setup(app: Sphinx) -> Dict[str, Any]:
app.add_config_value('image_converter_args', [], 'env')
return {
- 'version': 'builtin',
+ 'version': sphinx.__display_version__,
'parallel_read_safe': True,
'parallel_write_safe': True,
}
diff --git a/sphinx/ext/napoleon/__init__.py b/sphinx/ext/napoleon/__init__.py
index bcb12416e..06ca82779 100644
--- a/sphinx/ext/napoleon/__init__.py
+++ b/sphinx/ext/napoleon/__init__.py
@@ -2,7 +2,7 @@
from typing import Any, Dict, List
-from sphinx import __display_version__ as __version__
+import sphinx
from sphinx.application import Sphinx
from sphinx.ext.napoleon.docstring import GoogleDocstring, NumpyDocstring
from sphinx.util import inspect
@@ -310,7 +310,7 @@ def setup(app: Sphinx) -> Dict[str, Any]:
"""
if not isinstance(app, Sphinx):
# probably called by tests
- return {'version': __version__, 'parallel_read_safe': True}
+ return {'version': sphinx.__display_version__, 'parallel_read_safe': True}
_patch_python_domain()
@@ -320,7 +320,7 @@ def setup(app: Sphinx) -> Dict[str, Any]:
for name, (default, rebuild) in Config._config_values.items():
app.add_config_value(name, default, rebuild)
- return {'version': __version__, 'parallel_read_safe': True}
+ return {'version': sphinx.__display_version__, 'parallel_read_safe': True}
def _patch_python_domain() -> None:
From 0a91adb64d70ff9ab3bf59f5f2a57f22225f80e6 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Martin=20Li=C5=A1ka?=
Date: Sun, 2 Oct 2022 16:50:53 +0200
Subject: [PATCH 02/20] Extend cross referencing options with values (#10883)
This change means that text following `=`, `[=`, or ` ` is ignored when
searching for a corresponding option directive to an option cross reference
role. These are commonly used options, for example `--profile=path`,
`--profile[=path]` or `--profile path`.
Co-authored-by: Adam Turner <9087854+AA-Turner@users.noreply.github.com>
---
doc/usage/restructuredtext/domains.rst | 3 ++-
sphinx/domains/std.py | 15 +++++++++++----
tests/roots/test-root/objects.txt | 3 ++-
tests/test_build_html.py | 3 +++
4 files changed, 18 insertions(+), 6 deletions(-)
diff --git a/doc/usage/restructuredtext/domains.rst b/doc/usage/restructuredtext/domains.rst
index f2f07927d..cc0713b9f 100644
--- a/doc/usage/restructuredtext/domains.rst
+++ b/doc/usage/restructuredtext/domains.rst
@@ -1803,7 +1803,8 @@ There is a set of directives allowing documenting command-line programs:
.. versionchanged:: 5.3
- One can cross-reference including an option value: ``:option:`--module=foobar```.
+ One can cross-reference including an option value: ``:option:`--module=foobar```,
+ ,``:option:`--module[=foobar]``` or ``:option:`--module foobar```.
Use :confval:`option_emphasise_placeholders` for parsing of
"variable part" of a literal text (similarly to the :rst:role:`samp` role).
diff --git a/sphinx/domains/std.py b/sphinx/domains/std.py
index ef13e156e..6154a6ac1 100644
--- a/sphinx/domains/std.py
+++ b/sphinx/domains/std.py
@@ -943,10 +943,17 @@ class StandardDomain(Domain):
progname = node.get('std:program')
target = target.strip()
docname, labelid = self.progoptions.get((progname, target), ('', ''))
- # for :option:`-foo=bar` search for -foo option directive
- if not docname and '=' in target:
- target2 = target[:target.find('=')]
- docname, labelid = self.progoptions.get((progname, target2), ('', ''))
+ if not docname:
+ # Support also reference that contain an option value:
+ # * :option:`-foo=bar`
+ # * :option:`-foo[=bar]`
+ # * :option:`-foo bar`
+ for needle in {'=', '[=', ' '}:
+ if needle in target:
+ stem, _, _ = target.partition(needle)
+ docname, labelid = self.progoptions.get((progname, stem), ('', ''))
+ if docname:
+ break
if not docname:
commands = []
while ws_re.search(target):
diff --git a/tests/roots/test-root/objects.txt b/tests/roots/test-root/objects.txt
index fa9e475e5..170c026e7 100644
--- a/tests/roots/test-root/objects.txt
+++ b/tests/roots/test-root/objects.txt
@@ -214,7 +214,8 @@ Test repeated option directive.
My secret API.
-Reference the first option :option:`-mapi=secret`.
+Reference the first option :option:`-mapi=secret`, :option:`-mapi[=xxx]`
+or :option:`-mapi with_space`.
User markup
diff --git a/tests/test_build_html.py b/tests/test_build_html.py
index 453225e18..138f8a9c1 100644
--- a/tests/test_build_html.py
+++ b/tests/test_build_html.py
@@ -1771,6 +1771,9 @@ def test_option_reference_with_value(app, status, warning):
assert ('-mapi'
''
+ '-mapi[=xxx]
') in content
+ assert '-mapi with_space' in content
@pytest.mark.sphinx('html', testroot='theming')
From 3e29abf8de3f6f29aff18859b5eb88c8c79a1cbc Mon Sep 17 00:00:00 2001
From: Martin Patz <5219726+patzm@users.noreply.github.com>
Date: Tue, 4 Oct 2022 16:25:06 +0200
Subject: [PATCH 03/20] Add debug logging to `autosectionlabel` (#10881)
Co-authored-by: Martin Patz
---
CHANGES | 1 +
doc/usage/extensions/autosectionlabel.rst | 9 +++++++++
sphinx/ext/autosectionlabel.py | 3 +++
3 files changed, 13 insertions(+)
diff --git a/CHANGES b/CHANGES
index f425e20f0..e3c91d72e 100644
--- a/CHANGES
+++ b/CHANGES
@@ -15,6 +15,7 @@ Features added
* #10840: One can cross-reference including an option value: ``:option:`--module=foobar```.
Patch by Martin Liska.
+* #10881: autosectionlabel: Record the generated section label to the debug log.
Bugs fixed
----------
diff --git a/doc/usage/extensions/autosectionlabel.rst b/doc/usage/extensions/autosectionlabel.rst
index caaa5db26..b5b9b51a9 100644
--- a/doc/usage/extensions/autosectionlabel.rst
+++ b/doc/usage/extensions/autosectionlabel.rst
@@ -45,3 +45,12 @@ Configuration
example, when set 1 to ``autosectionlabel_maxdepth``, labels are generated
only for top level sections, and deeper sections are not labeled. It
defaults to ``None`` (disabled).
+
+
+Debugging
+---------
+
+The ``WARNING: undefined label`` indicates that your reference in
+:rst:role:`ref` is mis-spelled. Invoking :program:`sphinx-build` with ``-vv``
+(see :option:`-v`) will print all section names and the labels that have been
+generated for them. This output can help finding the right reference label.
diff --git a/sphinx/ext/autosectionlabel.py b/sphinx/ext/autosectionlabel.py
index ef9249e5b..7dc9ddaec 100644
--- a/sphinx/ext/autosectionlabel.py
+++ b/sphinx/ext/autosectionlabel.py
@@ -40,6 +40,9 @@ def register_sections_as_label(app: Sphinx, document: Node) -> None:
name = nodes.fully_normalize_name(ref_name)
sectname = clean_astext(title)
+ logger.debug(__('section "%s" gets labeled as "%s"'),
+ ref_name, name,
+ location=node, type='autosectionlabel', subtype=docname)
if name in domain.labels:
logger.warning(__('duplicate label %s, other instance in %s'),
name, app.env.doc2path(domain.labels[name][0]),
From ad5e17beb304e1ddc67accd22c5d1115eb28e9d3 Mon Sep 17 00:00:00 2001
From: Julien Schueller
Date: Wed, 5 Oct 2022 14:36:24 +0200
Subject: [PATCH 04/20] Fix `cleanup_tempdir` in imgmath (#10895)
---
sphinx/ext/imgmath.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/sphinx/ext/imgmath.py b/sphinx/ext/imgmath.py
index ddef58187..a7f1fc928 100644
--- a/sphinx/ext/imgmath.py
+++ b/sphinx/ext/imgmath.py
@@ -285,7 +285,7 @@ def cleanup_tempdir(app: Sphinx, exc: Exception) -> None:
if not hasattr(app.builder, '_imgmath_tempdir'):
return
try:
- shutil.rmtree(app.builder._mathpng_tempdir) # type: ignore
+ shutil.rmtree(app.builder._imgmath_tempdir) # type: ignore
except Exception:
pass
From 26fe10f0b7f064bca4b58e218083b0dcba274335 Mon Sep 17 00:00:00 2001
From: Adam Turner <9087854+AA-Turner@users.noreply.github.com>
Date: Wed, 5 Oct 2022 13:37:47 +0100
Subject: [PATCH 05/20] Fix disabling cross-references in pre-v3 C domain
(#10890)
---
sphinx/domains/c.py | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/sphinx/domains/c.py b/sphinx/domains/c.py
index e12eabfdc..fc96ac367 100644
--- a/sphinx/domains/c.py
+++ b/sphinx/domains/c.py
@@ -3676,7 +3676,8 @@ class CXRefRole(XRefRole):
return title, target
def run(self) -> Tuple[List[Node], List[system_message]]:
- if not self.env.config['c_allow_pre_v3']:
+ if not self.env.config['c_allow_pre_v3'] or self.disabled:
+ # workaround, remove entire method with c_allow_pre_v3 code
return super().run()
text = self.text.replace('\n', ' ')
From 3d25662550aba00d6e2e43d3ff76dce958079368 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Martin=20Li=C5=A1ka?=
Date: Wed, 5 Oct 2022 14:40:56 +0200
Subject: [PATCH 06/20] Update CHANGES for `:option:` reference format changes
(#10892)
---
CHANGES | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/CHANGES b/CHANGES
index e3c91d72e..fee65b939 100644
--- a/CHANGES
+++ b/CHANGES
@@ -13,7 +13,8 @@ Deprecated
Features added
--------------
-* #10840: One can cross-reference including an option value: ``:option:`--module=foobar```.
+* #10840: One can cross-reference including an option value like ``:option:`--module=foobar```,
+ ``:option:`--module[=foobar]``` or ``:option:`--module foobar```.
Patch by Martin Liska.
* #10881: autosectionlabel: Record the generated section label to the debug log.
From b1390c4191319e75d14ce3e6e73ef43c31d981b4 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Martin=20Li=C5=A1ka?=
Date: Wed, 5 Oct 2022 15:44:41 +0200
Subject: [PATCH 07/20] Fix detection for out of date files (#9360)
Co-authored-by: Adam Turner <9087854+AA-Turner@users.noreply.github.com>
---
CHANGES | 2 ++
sphinx/builders/__init__.py | 2 +-
tests/test_build_html.py | 2 --
tests/test_build_latex.py | 2 +-
tests/test_build_manpage.py | 2 +-
tests/test_build_texinfo.py | 2 +-
6 files changed, 6 insertions(+), 6 deletions(-)
diff --git a/CHANGES b/CHANGES
index fee65b939..4371efa40 100644
--- a/CHANGES
+++ b/CHANGES
@@ -17,6 +17,8 @@ Features added
``:option:`--module[=foobar]``` or ``:option:`--module foobar```.
Patch by Martin Liska.
* #10881: autosectionlabel: Record the generated section label to the debug log.
+* #9360: Fix caching for now-outdated files for some builders (e.g. manpage)
+ when there is no change in source files. Patch by Martin Liska.
Bugs fixed
----------
diff --git a/sphinx/builders/__init__.py b/sphinx/builders/__init__.py
index e70d1956c..6ca77c4bc 100644
--- a/sphinx/builders/__init__.py
+++ b/sphinx/builders/__init__.py
@@ -347,7 +347,7 @@ class Builder:
with progress_message(__('checking consistency')):
self.env.check_consistency()
else:
- if method == 'update' and not docnames:
+ if method == 'update' and (not docnames or docnames == ['__all__']):
logger.info(bold(__('no targets are out of date.')))
return
diff --git a/tests/test_build_html.py b/tests/test_build_html.py
index 138f8a9c1..0d19de4ca 100644
--- a/tests/test_build_html.py
+++ b/tests/test_build_html.py
@@ -633,7 +633,6 @@ def test_tocdepth(app, cached_etree_parse, fname, expect):
],
}))
@pytest.mark.sphinx('singlehtml', testroot='tocdepth')
-@pytest.mark.test_params(shared_result='test_build_html_tocdepth')
def test_tocdepth_singlehtml(app, cached_etree_parse, fname, expect):
app.build()
check_xpath(cached_etree_parse(app.outdir / fname), fname, *expect)
@@ -1138,7 +1137,6 @@ def test_numfig_with_secnum_depth(app, cached_etree_parse, fname, expect):
],
}))
@pytest.mark.sphinx('singlehtml', testroot='numfig', confoverrides={'numfig': True})
-@pytest.mark.test_params(shared_result='test_build_html_numfig_on')
def test_numfig_with_singlehtml(app, cached_etree_parse, fname, expect):
app.build()
check_xpath(cached_etree_parse(app.outdir / fname), fname, *expect)
diff --git a/tests/test_build_latex.py b/tests/test_build_latex.py
index 8f7a2e85a..efdbcb298 100644
--- a/tests/test_build_latex.py
+++ b/tests/test_build_latex.py
@@ -1604,7 +1604,7 @@ def test_latex_container(app, status, warning):
@pytest.mark.sphinx('latex', testroot='reST-code-role')
def test_latex_code_role(app):
- app.build()
+ app.build(force_all=True)
content = (app.outdir / 'python.tex').read_text()
common_content = (
diff --git a/tests/test_build_manpage.py b/tests/test_build_manpage.py
index 8509684d1..6c34e1771 100644
--- a/tests/test_build_manpage.py
+++ b/tests/test_build_manpage.py
@@ -42,7 +42,7 @@ def test_man_pages_empty_description(app, status, warning):
@pytest.mark.sphinx('man', testroot='basic',
confoverrides={'man_make_section_directory': True})
def test_man_make_section_directory(app, status, warning):
- app.build()
+ app.build(force_all=True)
assert (app.outdir / 'man1' / 'python.1').exists()
diff --git a/tests/test_build_texinfo.py b/tests/test_build_texinfo.py
index b33a7e01f..c60dd994b 100644
--- a/tests/test_build_texinfo.py
+++ b/tests/test_build_texinfo.py
@@ -60,7 +60,7 @@ def test_texinfo(app, status, warning):
@pytest.mark.sphinx('texinfo', testroot='markup-rubric')
def test_texinfo_rubric(app, status, warning):
- app.build()
+ app.build(force_all=True)
output = (app.outdir / 'python.texi').read_text(encoding='utf8')
assert '@heading This is a rubric' in output
From 6ed4bbba396eb410c4d00e5f9881aa621cec749a Mon Sep 17 00:00:00 2001
From: Anthony Sottile
Date: Wed, 5 Oct 2022 09:47:16 -0400
Subject: [PATCH 08/20] Don't display 'replaceable hardcoded link' when link
has a slash (#10137)
---
sphinx/ext/extlinks.py | 6 +++++-
.../index.rst | 2 ++
tests/test_ext_extlinks.py | 1 +
3 files changed, 8 insertions(+), 1 deletion(-)
diff --git a/sphinx/ext/extlinks.py b/sphinx/ext/extlinks.py
index c03cb78ba..3ee0735d4 100644
--- a/sphinx/ext/extlinks.py
+++ b/sphinx/ext/extlinks.py
@@ -72,7 +72,11 @@ class ExternalLinksChecker(SphinxPostTransform):
uri_pattern = re.compile(re.escape(base_uri).replace('%s', '(?P.+)'))
match = uri_pattern.match(uri)
- if match and match.groupdict().get('value'):
+ if (
+ match and
+ match.groupdict().get('value') and
+ '/' not in match.groupdict()['value']
+ ):
# build a replacement suggestion
msg = __('hardcoded link %r could be replaced by an extlink '
'(try using %r instead)')
diff --git a/tests/roots/test-ext-extlinks-hardcoded-urls-multiple-replacements/index.rst b/tests/roots/test-ext-extlinks-hardcoded-urls-multiple-replacements/index.rst
index c8b008ea2..162b3611a 100644
--- a/tests/roots/test-ext-extlinks-hardcoded-urls-multiple-replacements/index.rst
+++ b/tests/roots/test-ext-extlinks-hardcoded-urls-multiple-replacements/index.rst
@@ -17,6 +17,8 @@ https://github.com/octocat
`replaceable link`_
+`non replaceable link `_
+
.. hyperlinks
.. _replaceable link: https://github.com/octocat
diff --git a/tests/test_ext_extlinks.py b/tests/test_ext_extlinks.py
index 0e257364e..7634db688 100644
--- a/tests/test_ext_extlinks.py
+++ b/tests/test_ext_extlinks.py
@@ -28,6 +28,7 @@ def test_all_replacements_suggested_if_multiple_replacements_possible(app, warni
app.build()
warning_output = warning.getvalue()
# there should be six warnings for replaceable URLs, three pairs per link
+ assert warning_output.count("WARNING: hardcoded link") == 6
message = (
"index.rst:%d: WARNING: hardcoded link 'https://github.com/octocat' "
"could be replaced by an extlink (try using '%s' instead)"
From c51a88da8b7b40e8d8cbdb1fce85ca2346b2b59a Mon Sep 17 00:00:00 2001
From: Julien Schueller
Date: Sun, 9 Oct 2022 16:55:02 +0200
Subject: [PATCH 09/20] Fix performance regression for ``imgmath`` embedding
(#10888)
Co-authored-by: Adam Turner <9087854+aa-turner@users.noreply.github.com>
---
sphinx/ext/imgmath.py | 119 ++++++++++++++++++++----------------------
1 file changed, 58 insertions(+), 61 deletions(-)
diff --git a/sphinx/ext/imgmath.py b/sphinx/ext/imgmath.py
index a7f1fc928..f00567e19 100644
--- a/sphinx/ext/imgmath.py
+++ b/sphinx/ext/imgmath.py
@@ -1,7 +1,6 @@
"""Render math in HTML via dvipng or dvisvgm."""
import base64
-import posixpath
import re
import shutil
import subprocess
@@ -157,13 +156,10 @@ def convert_dvi_to_image(command: List[str], name: str) -> Tuple[str, str]:
raise MathExtError('%s exited with error' % name, exc.stderr, exc.stdout) from exc
-def convert_dvi_to_png(dvipath: str, builder: Builder) -> Tuple[str, Optional[int]]:
+def convert_dvi_to_png(dvipath: str, builder: Builder, out_path: str) -> Optional[int]:
"""Convert DVI file to PNG image."""
- tempdir = ensure_tempdir(builder)
- filename = path.join(tempdir, 'math.png')
-
name = 'dvipng'
- command = [builder.config.imgmath_dvipng, '-o', filename, '-T', 'tight', '-z9']
+ command = [builder.config.imgmath_dvipng, '-o', out_path, '-T', 'tight', '-z9']
command.extend(builder.config.imgmath_dvipng_args)
if builder.config.imgmath_use_preview:
command.append('--depth')
@@ -177,19 +173,16 @@ def convert_dvi_to_png(dvipath: str, builder: Builder) -> Tuple[str, Optional[in
matched = depth_re.match(line)
if matched:
depth = int(matched.group(1))
- write_png_depth(filename, depth)
+ write_png_depth(out_path, depth)
break
- return filename, depth
+ return depth
-def convert_dvi_to_svg(dvipath: str, builder: Builder) -> Tuple[str, Optional[int]]:
+def convert_dvi_to_svg(dvipath: str, builder: Builder, out_path: str) -> Optional[int]:
"""Convert DVI file to SVG image."""
- tempdir = ensure_tempdir(builder)
- filename = path.join(tempdir, 'math.svg')
-
name = 'dvisvgm'
- command = [builder.config.imgmath_dvisvgm, '-o', filename]
+ command = [builder.config.imgmath_dvisvgm, '-o', out_path]
command.extend(builder.config.imgmath_dvisvgm_args)
command.append(dvipath)
@@ -201,16 +194,16 @@ def convert_dvi_to_svg(dvipath: str, builder: Builder) -> Tuple[str, Optional[in
matched = depthsvg_re.match(line)
if matched:
depth = round(float(matched.group(1)) * 100 / 72.27) # assume 100ppi
- write_svg_depth(filename, depth)
+ write_svg_depth(out_path, depth)
break
- return filename, depth
+ return depth
def render_math(
self: HTMLTranslator,
math: str,
-) -> Tuple[Optional[str], Optional[int], Optional[str], Optional[str]]:
+) -> Tuple[Optional[str], Optional[int]]:
"""Render the LaTeX math expression *math* using latex and dvipng or
dvisvgm.
@@ -234,43 +227,43 @@ def render_math(
self.builder.config,
self.builder.confdir)
- filename = "%s.%s" % (sha1(latex.encode()).hexdigest(), image_format)
- relfn = posixpath.join(self.builder.imgpath, 'math', filename)
- outfn = path.join(self.builder.outdir, self.builder.imagedir, 'math', filename)
- if path.isfile(outfn):
+ filename = f"{sha1(latex.encode()).hexdigest()}.{image_format}"
+ generated_path = path.join(self.builder.outdir, self.builder.imagedir, 'math', filename)
+ ensuredir(path.dirname(generated_path))
+ if path.isfile(generated_path):
if image_format == 'png':
- depth = read_png_depth(outfn)
+ depth = read_png_depth(generated_path)
elif image_format == 'svg':
- depth = read_svg_depth(outfn)
- return relfn, depth, None, outfn
+ depth = read_svg_depth(generated_path)
+ return generated_path, depth
# if latex or dvipng (dvisvgm) has failed once, don't bother to try again
if hasattr(self.builder, '_imgmath_warned_latex') or \
hasattr(self.builder, '_imgmath_warned_image_translator'):
- return None, None, None, None
+ return None, None
# .tex -> .dvi
try:
dvipath = compile_math(latex, self.builder)
except InvokeError:
self.builder._imgmath_warned_latex = True # type: ignore
- return None, None, None, None
+ return None, None
# .dvi -> .png/.svg
try:
if image_format == 'png':
- imgpath, depth = convert_dvi_to_png(dvipath, self.builder)
+ depth = convert_dvi_to_png(dvipath, self.builder, generated_path)
elif image_format == 'svg':
- imgpath, depth = convert_dvi_to_svg(dvipath, self.builder)
+ depth = convert_dvi_to_svg(dvipath, self.builder, generated_path)
except InvokeError:
self.builder._imgmath_warned_image_translator = True # type: ignore
- return None, None, None, None
+ return None, None
- return relfn, depth, imgpath, outfn
+ return generated_path, depth
-def render_maths_to_base64(image_format: str, outfn: Optional[str]) -> str:
- with open(outfn, "rb") as f:
+def render_maths_to_base64(image_format: str, generated_path: Optional[str]) -> str:
+ with open(generated_path, "rb") as f:
encoded = base64.b64encode(f.read()).decode(encoding='utf-8')
if image_format == 'png':
return f'data:image/png;base64,{encoded}'
@@ -279,15 +272,23 @@ def render_maths_to_base64(image_format: str, outfn: Optional[str]) -> str:
raise MathExtError('imgmath_image_format must be either "png" or "svg"')
-def cleanup_tempdir(app: Sphinx, exc: Exception) -> None:
+def clean_up_files(app: Sphinx, exc: Exception) -> None:
if exc:
return
- if not hasattr(app.builder, '_imgmath_tempdir'):
- return
- try:
- shutil.rmtree(app.builder._imgmath_tempdir) # type: ignore
- except Exception:
- pass
+
+ if hasattr(app.builder, '_imgmath_tempdir'):
+ try:
+ shutil.rmtree(app.builder._imgmath_tempdir) # type: ignore
+ except Exception:
+ pass
+
+ if app.builder.config.imgmath_embed:
+ # in embed mode, the images are still generated in the math output dir
+ # to be shared across workers, but are not useful to the final document
+ try:
+ shutil.rmtree(path.join(app.builder.outdir, app.builder.imagedir, 'math'))
+ except Exception:
+ pass
def get_tooltip(self: HTMLTranslator, node: Element) -> str:
@@ -298,7 +299,7 @@ def get_tooltip(self: HTMLTranslator, node: Element) -> str:
def html_visit_math(self: HTMLTranslator, node: nodes.math) -> None:
try:
- fname, depth, imgpath, outfn = render_math(self, '$' + node.astext() + '$')
+ rendered_path, depth = render_math(self, '$' + node.astext() + '$')
except MathExtError as exc:
msg = str(exc)
sm = nodes.system_message(msg, type='WARNING', level=2,
@@ -306,20 +307,18 @@ def html_visit_math(self: HTMLTranslator, node: nodes.math) -> None:
sm.walkabout(self)
logger.warning(__('display latex %r: %s'), node.astext(), msg)
raise nodes.SkipNode from exc
- if self.builder.config.imgmath_embed:
- image_format = self.builder.config.imgmath_image_format.lower()
- img_src = render_maths_to_base64(image_format, imgpath)
- else:
- # Move generated image on tempdir to build dir
- if imgpath is not None:
- ensuredir(path.dirname(outfn))
- shutil.move(imgpath, outfn)
- img_src = fname
- if img_src is None:
+
+ if rendered_path is None:
# something failed -- use text-only as a bad substitute
self.body.append('%s' %
self.encode(node.astext()).strip())
else:
+ if self.builder.config.imgmath_embed:
+ image_format = self.builder.config.imgmath_image_format.lower()
+ img_src = render_maths_to_base64(image_format, rendered_path)
+ else:
+ relative_path = path.relpath(rendered_path, self.builder.outdir)
+ img_src = relative_path.replace(path.sep, '/')
c = f'
None
else:
latex = wrap_displaymath(node.astext(), None, False)
try:
- fname, depth, imgpath, outfn = render_math(self, latex)
+ rendered_path, depth = render_math(self, latex)
except MathExtError as exc:
msg = str(exc)
sm = nodes.system_message(msg, type='WARNING', level=2,
@@ -348,20 +347,18 @@ def html_visit_displaymath(self: HTMLTranslator, node: nodes.math_block) -> None
self.body.append('(%s)' % number)
self.add_permalink_ref(node, _('Permalink to this equation'))
self.body.append('')
- if self.builder.config.imgmath_embed:
- image_format = self.builder.config.imgmath_image_format.lower()
- img_src = render_maths_to_base64(image_format, imgpath)
- else:
- # Move generated image on tempdir to build dir
- if imgpath is not None:
- ensuredir(path.dirname(outfn))
- shutil.move(imgpath, outfn)
- img_src = fname
- if img_src is None:
+
+ if rendered_path is None:
# something failed -- use text-only as a bad substitute
self.body.append('%s
\n' %
self.encode(node.astext()).strip())
else:
+ if self.builder.config.imgmath_embed:
+ image_format = self.builder.config.imgmath_image_format.lower()
+ img_src = render_maths_to_base64(image_format, rendered_path)
+ else:
+ relative_path = path.relpath(rendered_path, self.builder.outdir)
+ img_src = relative_path.replace(path.sep, '/')
self.body.append(f'
\n')
raise nodes.SkipNode
@@ -386,5 +383,5 @@ def setup(app: Sphinx) -> Dict[str, Any]:
app.add_config_value('imgmath_add_tooltips', True, 'html')
app.add_config_value('imgmath_font_size', 12, 'html')
app.add_config_value('imgmath_embed', False, 'html', [bool])
- app.connect('build-finished', cleanup_tempdir)
+ app.connect('build-finished', clean_up_files)
return {'version': sphinx.__display_version__, 'parallel_read_safe': True}
From e7c088199206d3815670e2f92a1ff069ddabb0ab Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jean-Fran=C3=A7ois=20B?=
<2589111+jfbu@users.noreply.github.com>
Date: Wed, 12 Oct 2022 17:15:40 +0200
Subject: [PATCH 10/20] LaTeX: support for booktabs-style and zebra-striped
tables (#10759)
This is a combination of 2 + 28 + 7 + and some more commits...
* Cherry-pick: Add support for booktabs-style tables to LaTeX builder
* Cherry-pick: Add support for zebra-striped tables to LaTeX builder
Co-authored-by: Stefan Wiehler
Above work originally initiated by @sephalon (thanks!)
Development refactored and continued by @jfbu
* latex_table_style configuration, support booktabs, colorrows, borderless
Some details:
- Simplify a bit a conditional in the longtable template
This also puts the target for a longtable with a label but no caption
above the toprule for better hyperlinking (testing shows hyperlink
target can not end up alone at bottom of previous page).
- Extend allowed syntax for colour assignments via 'sphinxsetup'
- latex_table_style new configuration value and coloured rows
For the user interface I tried to look for inspiration in
https://docutils.sourceforge.io/docs/user/config.html#table-style
which mentions booktabs and borderless. They also mention
captionbelow which we can implement later, now that architecture
is here. They don't mention coloured rows.
- Test on our own document... looks fine!
- Work-around an incompatibility of \cline with row colours
- Reverse priority of classes to allow overruling booktabs by standard
after parsing source but before letting LaTeX writer act
- Closes #8220
Commit
https://github.com/sphinx-doc/sphinx/commit/bb859c669679baebd8cc8d10c99382478c0d1647
already improved a bit, this finishes it (as :rst:dir:`rst-class` was
actually not linking to anywhere).
- Let booktabs style defaults to *not* using \cmidrule. They actually
don't make much sense there, as all \hline's are removed.
- Add \sphinxnorowcolor which allows construct such as this one in
a tabularcolumns directive:
>{\columncolor{blue}\sphinxnorowcolor}
else LaTeX always overrides column colour by row colour
- Add TableMergeColorHeader, TableMergeColorOdd, TableMergeColorEven
so single-row merged cells can be styled especially
- Extend row colours to all header rows not only the first one
(all header rows will share same colour settings)
- Auto-adjust to a no '|'-colspec for optimal handling of merged cell
- Add \sphinxcolorblend
- Workaround LaTeX's \cline features and other grid tables matters
- Add \sphinxbuildwarning for important warnings
- Fix some white gaps in merged cells of tables with vlines and
colorrows
- Work around LaTeX's \cline serious deficiencies for complex grid
tables
This commit corrects \cline badly impacting vertical spacing and
making tables look even more cramped as they usually are in LaTeX
(although one sees it clearly only with \arrarrulewidth a bit more
than the LaTeX default of 0.4pt).
Most importantly this commit solves the problem that \cline's got
masked by colour panels from the row below.
- Update CHANGES for PR #10759
- Improve documentation of new latex_table_style regarding colours
---
CHANGES | 2 +
doc/conf.py | 3 +-
doc/extdev/deprecated.rst | 4 +-
doc/latex.rst | 70 +-
doc/usage/configuration.rst | 95 ++
doc/usage/restructuredtext/basics.rst | 17 +-
doc/usage/restructuredtext/directives.rst | 161 ++--
doc/usage/theming.rst | 2 +-
sphinx/builders/latex/__init__.py | 4 +
sphinx/templates/latex/latex.tex_t | 9 +
sphinx/templates/latex/longtable.tex_t | 64 +-
sphinx/templates/latex/tabular.tex_t | 35 +-
sphinx/templates/latex/tabulary.tex_t | 35 +-
sphinx/texinputs/sphinx.sty | 152 +++-
sphinx/texinputs/sphinxlatexstyleheadings.sty | 3 +-
sphinx/texinputs/sphinxlatextables.sty | 835 +++++++++++++++++-
sphinx/texinputs/sphinxpackagefootnote.sty | 3 +-
sphinx/writers/latex.py | 96 +-
tests/roots/test-latex-table/complex.rst | 23 +
.../expects/complex_spanning_cell.tex | 14 +-
.../test-latex-table/expects/gridtable.tex | 19 +-
.../expects/gridtable_with_tabularcolumn.tex | 73 ++
.../test-latex-table/expects/longtable.tex | 37 +-
.../expects/longtable_having_align.tex | 36 +-
.../expects/longtable_having_caption.tex | 36 +-
.../longtable_having_problematic_cell.tex | 36 +-
...ving_stub_columns_and_problematic_cell.tex | 34 +-
.../expects/longtable_having_verbatim.tex | 36 +-
.../expects/longtable_having_widths.tex | 37 +-
...ble_having_widths_and_problematic_cell.tex | 36 +-
.../expects/longtable_with_tabularcolumn.tex | 37 +-
.../test-latex-table/expects/simple_table.tex | 14 +-
.../expects/table_having_caption.tex | 14 +-
.../expects/table_having_problematic_cell.tex | 14 +-
...ving_stub_columns_and_problematic_cell.tex | 12 +-
...ving_threeparagraphs_cell_in_first_col.tex | 10 +-
.../expects/table_having_verbatim.tex | 14 +-
.../expects/table_having_widths.tex | 18 +-
...ble_having_widths_and_problematic_cell.tex | 14 +-
.../expects/tabular_having_widths.tex | 14 +-
.../expects/tabularcolumn.tex | 17 +-
.../expects/tabulary_having_widths.tex | 14 +-
tests/roots/test-latex-table/longtable.rst | 2 +-
tests/roots/test-latex-table/tabular.rst | 3 +-
tests/test_build_latex.py | 42 +-
45 files changed, 1822 insertions(+), 424 deletions(-)
create mode 100644 tests/roots/test-latex-table/expects/gridtable_with_tabularcolumn.tex
diff --git a/CHANGES b/CHANGES
index 4371efa40..19d58edb7 100644
--- a/CHANGES
+++ b/CHANGES
@@ -13,6 +13,8 @@ Deprecated
Features added
--------------
+* #10759: LaTeX: add :confval:`latex_table_style` and support the
+ ``'booktabs'``, ``'borderless'``, and ``'colorrows'`` styles.
* #10840: One can cross-reference including an option value like ``:option:`--module=foobar```,
``:option:`--module[=foobar]``` or ``:option:`--module foobar```.
Patch by Martin Liska.
diff --git a/doc/conf.py b/doc/conf.py
index eafa42a7d..fc7959d4d 100644
--- a/doc/conf.py
+++ b/doc/conf.py
@@ -78,7 +78,7 @@ latex_elements = {
{\begin{sphinxtheindex}\end{sphinxtheindex}}
''',
'sphinxsetup': """%
-VerbatimColor={RGB}{242,242,242},%
+VerbatimColor=black!5,% tests 5.2.0 extended syntax
VerbatimBorderColor={RGB}{32,32,32},%
pre_border-radius=3pt,%
pre_box-decoration-break=slice,%
@@ -86,6 +86,7 @@ pre_box-decoration-break=slice,%
}
latex_show_urls = 'footnote'
latex_use_xindy = True
+latex_table_style = ['booktabs', 'colorrows']
autodoc_member_order = 'groupwise'
autosummary_generate = False
diff --git a/doc/extdev/deprecated.rst b/doc/extdev/deprecated.rst
index 8c850ceb6..1692f2d4e 100644
--- a/doc/extdev/deprecated.rst
+++ b/doc/extdev/deprecated.rst
@@ -10,7 +10,7 @@ major versions (for more details, please see :ref:`deprecation-policy`).
The following is a list of deprecated interfaces.
-.. tabularcolumns:: |>{\raggedright}\Y{.4}|>{\centering}\Y{.1}|>{\centering}\Y{.12}|>{\raggedright\arraybackslash}\Y{.38}|
+.. tabularcolumns:: >{\raggedright}\Y{.4}>{\centering}\Y{.1}>{\sphinxcolorblend{!95!red}\centering\noindent\bfseries\color{red}}\Y{.12}>{\raggedright\arraybackslash}\Y{.38}
.. list-table:: deprecated APIs
:header-rows: 1
@@ -19,7 +19,7 @@ The following is a list of deprecated interfaces.
* - Target
- Deprecated
- - (will be) Removed
+ - Removed
- Alternatives
* - HTML 4 support
diff --git a/doc/latex.rst b/doc/latex.rst
index ae8c256cb..e467ac4e9 100644
--- a/doc/latex.rst
+++ b/doc/latex.rst
@@ -823,16 +823,31 @@ Do not use quotes to enclose values, whether numerical or strings.
definition of the continuation symbol was changed at 1.5 to accommodate
various font sizes (e.g. code-blocks can be in footnotes).
+.. note::
+
+ Values for colour keys must either:
+
+ - obey the syntax of the ``\definecolor`` LaTeX command, e.g. something
+ such as ``VerbatimColor={rgb}{0.2,0.3,0.5}`` or ``{RGB}{37,23,255}`` or
+ ``{gray}{0.75}`` or (only with package ``xcolor``) ``{HTML}{808080}`` or
+ ...
+
+ - or obey the syntax of the ``\colorlet`` command from package ``xcolor``
+ (which then must exist in the LaTeX installation),
+ e.g. ``VerbatimColor=red!10`` or ``red!50!green`` or ``-red!75`` or
+ ``MyPreviouslyDefinedColour`` or... Refer to xcolor_ documentation for
+ this syntax.
+
+ .. _xcolor: https://ctan.org/pkg/xcolor
+
+ .. versionchanged:: 5.2.0
+ Formerly only the ``\definecolor`` syntax was accepted.
+
``TitleColor``
The colour for titles (as configured via use of package "titlesec".)
Default: ``{rgb}{0.126,0.263,0.361}``
- .. warning::
-
- Colours set via ``'sphinxsetup'`` must obey the syntax of the
- argument of the ``color/xcolor`` packages ``\definecolor`` command.
-
``InnerLinkColor``
A colour passed to ``hyperref`` as value of ``linkcolor`` and
``citecolor``.
@@ -862,10 +877,47 @@ Do not use quotes to enclose values, whether numerical or strings.
.. versionadded:: 1.6.6
- .. note::
+.. _tablecolors:
- Starting with this colour, and for all others following, the
- names declared to "color" or "xcolor" are prefixed with "sphinx".
+``TableRowColorHeader``
+ Sets the background colour for (all) the header rows of tables.
+
+ It will have an effect only if either the :confval:`latex_table_style`
+ contains ``'colorrows'`` or if the table is assigned the ``colorrows``
+ class. It is ignored for tables with ``nocolorrows`` class.
+
+ As for the other ``'sphinxsetup'`` keys, it can also be set or modified
+ from a ``\sphinxsetup{...}`` LaTeX command inserted via the :dudir:`raw`
+ directive, or also from a LaTeX environment associated to a `container
+ class `_ and using such ``\sphinxsetup{...}``.
+
+ Default: ``{gray}{0.86}``
+
+ There is also ``TableMergeColorHeader``. If used, sets a specific colour
+ for merged single-row cells in the header.
+
+ .. versionadded:: 5.2.0
+
+``TableRowColorOdd``
+ Sets the background colour for odd rows in tables (the row count starts at
+ ``1`` at the first non-header row). Has an effect only if the
+ :confval:`latex_table_style` contains ``'colorrows'`` or for specific
+ tables assigned the ``colorrows`` class.
+
+ Default: ``{gray}{0.92}``
+
+ There is also ``TableMergeColorOdd``.
+
+ .. versionadded:: 5.2.0
+
+``TableRowColorEven``
+ Sets the background colour for even rows in tables.
+
+ Default ``{gray}{0.98}``
+
+ There is also ``TableMergeColorEven``.
+
+ .. versionadded:: 5.2.0
``verbatimsep``
The separation between code lines and the frame.
@@ -1425,6 +1477,8 @@ Miscellany
Formerly, use of *fncychap* with other styles than ``Bjarne`` was
dysfunctional.
+.. _latexcontainer:
+
- Docutils :dudir:`container` directives are supported in LaTeX output: to
let a container class with name ``foo`` influence the final PDF via LaTeX,
it is only needed to define in the preamble an environment
diff --git a/doc/usage/configuration.rst b/doc/usage/configuration.rst
index 906827ff3..b2196a9e5 100644
--- a/doc/usage/configuration.rst
+++ b/doc/usage/configuration.rst
@@ -2233,6 +2233,101 @@ These options influence LaTeX output.
.. versionadded:: 1.6
+.. confval:: latex_table_style
+
+ A list of styling classes (strings). Currently supported:
+
+ - ``'booktabs'``: no vertical lines, and only 2 or 3 horizontal lines (the
+ latter if there is a header), using the booktabs_ package.
+
+ - ``'borderless'``: no lines whatsoever.
+
+ - ``'colorrows'``: the table rows are rendered with alternating background
+ colours. The interface to customize them is via :ref:`dedicated keys
+ ` of :ref:`latexsphinxsetup`.
+
+ .. important::
+
+ With the ``'colorrows'`` style, the ``\rowcolors`` LaTeX command
+ becomes a no-op (this command has limitations and has never correctly
+ supported all types of tables Sphinx produces in LaTeX). Please
+ update your project to use instead
+ the :ref:`latex table color configuration ` keys.
+
+ Default: ``[]``
+
+ .. versionadded:: 5.2.0
+
+ If using ``'booktabs'`` or ``'borderless'`` it seems recommended to also
+ opt for ``'colorrows'``...
+
+ Each table can override the global style via ``:class:`` option, or
+ ``.. rst-class::`` for no-directive tables (cf. :ref:`table-directives`).
+ Currently recognized classes are ``booktabs``, ``borderless``,
+ ``standard``, ``colorrows``, ``nocolorrows``. The latter two can be
+ combined with any of the first three. The ``standard`` class produces
+ tables with both horizontal and vertical lines (as has been the default so
+ far with Sphinx).
+
+ A single-row multi-column merged cell will obey the row colour, if it is
+ set. See also ``TableMergeColor{Header,Odd,Even}`` in the
+ :ref:`latexsphinxsetup` section.
+
+ .. note::
+
+ - It is hard-coded in LaTeX that a single cell will obey the row colour
+ even if there is a column colour set via ``\columncolor`` from a
+ column specification (see :rst:dir:`tabularcolumns`). Sphinx provides
+ ``\sphinxnorowcolor`` which can be used like this:
+
+ .. code-block:: latex
+
+ >{\columncolor{blue}\sphinxnorowcolor}
+
+ in a table column specification.
+
+ - Sphinx also provides ``\sphinxcolorblend`` which however requires the
+ xcolor_ package. Here is an example:
+
+ .. code-block:: latex
+
+ >{\sphinxcolorblend{!95!red}}
+
+ It means that in this column, the row colours will be slightly tinted
+ by red; refer to xcolor_ documentation for more on the syntax of its
+ ``\blendcolors`` command (a ``\blendcolors`` in place of
+ ``\sphinxcolorblend`` would modify colours of the cell *contents*, not
+ of the cell *background colour panel*...). You can find an example of
+ usage in the :ref:`dev-deprecated-apis` section of this document in
+ PDF format.
+
+ .. hint::
+
+ If you want to use a special colour for the *contents* of the
+ cells of a given column use ``>{\noindent\color{}}``,
+ possibly in addition to the above.
+
+ - Multi-row merged cells, whether single column or multi-column
+ currently ignore any set column, row, or cell colour.
+
+ - It is possible for a simple cell to set a custom colour via the
+ :dudir:`raw` directive and the ``\cellcolor`` LaTeX command used
+ anywhere in the cell contents. This currently is without effect
+ in a merged cell, whatever its kind.
+
+ .. hint::
+
+ In a document not using ``'booktabs'`` globally, it is possible to style
+ an individual table via the ``booktabs`` class, but it will be necessary
+ to add ``r'\usepackage{booktabs}'`` to the LaTeX preamble.
+
+ On the other hand one can use ``colorrows`` class for individual tables
+ with no extra package (as Sphinx since 5.2.0 always loads colortbl_).
+
+ .. _booktabs: https://ctan.org/pkg/booktabs
+ .. _colortbl: https://ctan.org/pkg/colortbl
+ .. _xcolor: https://ctan.org/pkg/xcolor
+
.. confval:: latex_use_xindy
If ``True``, the PDF build from the LaTeX files created by Sphinx
diff --git a/doc/usage/restructuredtext/basics.rst b/doc/usage/restructuredtext/basics.rst
index c846dc145..824b59ee2 100644
--- a/doc/usage/restructuredtext/basics.rst
+++ b/doc/usage/restructuredtext/basics.rst
@@ -370,7 +370,15 @@ Docutils supports the following directives:
- :dudir:`include` (include reStructuredText from another file) -- in Sphinx,
when given an absolute include file path, this directive takes it as
relative to the source directory
- - :dudir:`class` (assign a class attribute to the next element) [1]_
+
+ .. _rstclass:
+
+ - :dudir:`class` (assign a class attribute to the next element)
+
+ .. note::
+
+ When the default domain contains a ``class`` directive, this directive
+ will be shadowed. Therefore, Sphinx re-exports it as ``rst-class``.
* HTML specifics:
@@ -621,10 +629,3 @@ There are some problems one commonly runs into while authoring reST documents:
* **No nested inline markup:** Something like ``*see :func:`foo`*`` is not
possible.
-
-
-.. rubric:: Footnotes
-
-.. [1] When the default domain contains a :rst:dir:`class` directive, this
- directive will be shadowed. Therefore, Sphinx re-exports it as
- :rst:dir:`rst-class`.
diff --git a/doc/usage/restructuredtext/directives.rst b/doc/usage/restructuredtext/directives.rst
index 4029b04e6..44e4b5ffe 100644
--- a/doc/usage/restructuredtext/directives.rst
+++ b/doc/usage/restructuredtext/directives.rst
@@ -371,8 +371,9 @@ units as well as normal text.
.. centered:: LICENSE AGREEMENT
.. deprecated:: 1.1
- This presentation-only directive is a legacy from older versions. Use a
- :rst:dir:`rst-class` directive instead and add an appropriate style.
+ This presentation-only directive is a legacy from older versions.
+ Use a :ref:`rst-class ` directive instead and add an
+ appropriate style.
.. rst:directive:: hlist
@@ -1045,114 +1046,78 @@ Use :ref:`reStructuredText tables `, i.e. either
The :dudir:`table` directive serves as optional wrapper of the *grid* and
*simple* syntaxes.
-They work fine in HTML output, however there are some gotchas when using tables
-in LaTeX: the column width is hard to determine correctly automatically. For
-this reason, the following directive exists:
+They work fine in HTML output, but rendering tables to LaTeX is complex.
+Check the :confval:`latex_table_style`.
+
+.. versionchanged:: 1.6
+ Merged cells (multi-row, multi-column, both) from grid tables containing
+ complex contents such as multiple paragraphs, blockquotes, lists, literal
+ blocks, will render correctly to LaTeX output.
.. rst:directive:: .. tabularcolumns:: column spec
- This directive gives a "column spec" for the next table occurring in the
- source file. The spec is the second argument to the LaTeX ``tabulary``
- package's environment (which Sphinx uses to translate tables). It can have
- values like ::
+ This directive influences only the LaTeX output for the next table in
+ source. The mandatory argument is a column specification (known as an
+ "alignment preamble" in LaTeX idiom). Please refer to a LaTeX
+ documentation, such as the `wiki page`_, for basics of such a column
+ specification.
- |l|l|l|
-
- which means three left-adjusted, nonbreaking columns. For columns with
- longer text that should automatically be broken, use either the standard
- ``p{width}`` construct, or tabulary's automatic specifiers:
-
- +-----+------------------------------------------+
- |``L``| flush left column with automatic width |
- +-----+------------------------------------------+
- |``R``| flush right column with automatic width |
- +-----+------------------------------------------+
- |``C``| centered column with automatic width |
- +-----+------------------------------------------+
- |``J``| justified column with automatic width |
- +-----+------------------------------------------+
-
- The automatic widths of the ``LRCJ`` columns are attributed by ``tabulary``
- in proportion to the observed shares in a first pass where the table cells
- are rendered at their natural "horizontal" widths.
-
- By default, Sphinx uses a table layout with ``J`` for every column.
+ .. _wiki page: https://en.wikibooks.org/wiki/LaTeX/Tables
.. versionadded:: 0.3
- .. versionchanged:: 1.6
- Merged cells may now contain multiple paragraphs and are much better
- handled, thanks to custom Sphinx LaTeX macros. This novel situation
- motivated the switch to ``J`` specifier and not ``L`` by default.
-
- .. hint::
-
- Sphinx actually uses ``T`` specifier having done ``\newcolumntype{T}{J}``.
- To revert to previous default, insert ``\newcolumntype{T}{L}`` in the
- LaTeX preamble (see :confval:`latex_elements`).
-
- A frequent issue with tabulary is that columns with little contents are
- "squeezed". The minimal column width is a tabulary parameter called
- ``\tymin``. You may set it globally in the LaTeX preamble via
- ``\setlength{\tymin}{40pt}`` for example.
-
- Else, use the :rst:dir:`tabularcolumns` directive with an explicit
- ``p{40pt}`` (for example) for that column. You may use also ``l``
- specifier but this makes the task of setting column widths more difficult
- if some merged cell intersects that column.
-
- .. warning::
-
- Tables with more than 30 rows are rendered using ``longtable``, not
- ``tabulary``, in order to allow pagebreaks. The ``L``, ``R``, ...
- specifiers do not work for these tables.
-
- Tables that contain list-like elements such as object descriptions,
- blockquotes or any kind of lists cannot be set out of the box with
- ``tabulary``. They are therefore set with the standard LaTeX ``tabular``
- (or ``longtable``) environment if you don't give a ``tabularcolumns``
- directive. If you do, the table will be set with ``tabulary`` but you
- must use the ``p{width}`` construct (or Sphinx's ``\X`` and ``\Y``
- specifiers described below) for the columns containing these elements.
-
- Literal blocks do not work with ``tabulary`` at all, so tables containing
- a literal block are always set with ``tabular``. The verbatim environment
- used for literal blocks only works in ``p{width}`` (and ``\X`` or ``\Y``)
- columns, hence Sphinx generates such column specs for tables containing
- literal blocks.
-
- Since Sphinx 1.5, the ``\X{a}{b}`` specifier is used (there *is* a backslash
- in the specifier letter). It is like ``p{width}`` with the width set to a
- fraction ``a/b`` of the current line width. You can use it in the
- :rst:dir:`tabularcolumns` (it is not a problem if some LaTeX macro is also
- called ``\X``.)
-
- It is *not* needed for ``b`` to be the total number of columns, nor for the
- sum of the fractions of the ``\X`` specifiers to add up to one. For example
- ``|\X{2}{5}|\X{1}{5}|\X{1}{5}|`` is legitimate and the table will occupy
- 80% of the line width, the first of its three columns having the same width
- as the sum of the next two.
-
- This is used by the ``:widths:`` option of the :dudir:`table` directive.
-
- Since Sphinx 1.6, there is also the ``\Y{f}`` specifier which admits a
- decimal argument, such has ``\Y{0.15}``: this would have the same effect as
- ``\X{3}{20}``.
-
- .. versionchanged:: 1.6
-
- Merged cells from complex grid tables (either multi-row, multi-column, or
- both) now allow blockquotes, lists, literal blocks, ... as do regular
- cells.
-
- Sphinx's merged cells interact well with ``p{width}``, ``\X{a}{b}``,
- ``\Y{f}`` and tabulary's columns.
-
.. note::
:rst:dir:`tabularcolumns` conflicts with ``:widths:`` option of table
directives. If both are specified, ``:widths:`` option will be ignored.
+ Sphinx will render tables with more than 30 rows with ``longtable``.
+ Besides the ``l``, ``r``, ``c`` and ``p{width}`` column specifiers, one can
+ also use ``\X{a}{b}`` (new in version 1.5) which configures the column
+ width to be a fraction ``a/b`` of the total line width and ``\Y{f}`` (new
+ in version 1.6) where ``f`` is a decimal: for example ``\Y{0.2}`` means that
+ the column will occupy ``0.2`` times the line width.
+
+ When this directive is used for a table with at most 30 rows, Sphinx will
+ render it with ``tabulary``. One can then use specific column types ``L``
+ (left), ``R`` (right), ``C`` (centered) and ``J`` (justified). They have
+ the effect of a ``p{width}`` (i.e. each cell is a LaTeX ``\parbox``) with
+ the specified internal text alignment and an automatically computed
+ ``width``.
+
+ .. warning::
+
+ - Cells that contain list-like elements such as object descriptions,
+ blockquotes or any kind of lists are not compatible with the ``LRCJ``
+ column types. The column type must then be some ``p{width}`` with an
+ explicit ``width`` (or ``\X{a}{b}`` or ``\Y{f}``).
+
+ - Literal blocks do not work with ``tabulary`` at all. Sphinx will
+ fall back to ``tabular`` or ``longtable`` environments and generate a
+ suitable column specification.
+
+In absence of the :rst:dir:`tabularcolumns` directive, and for a table with at
+most 30 rows and no problematic cells as described in the above warning,
+Sphinx uses ``tabulary`` and the ``J`` column-type for every column.
+
+.. versionchanged:: 1.6
+
+ Formerly, the ``L`` column-type was used (text is flushed-left). To revert
+ to this, include ``\newcolumntype{T}{L}`` in the LaTeX preamble, as in fact
+ Sphinx uses ``T`` and sets it by default to be an alias of ``J``.
+
+.. hint::
+
+ A frequent issue with ``tabulary`` is that columns with little contents
+ appear to be "squeezed". One can add to the LaTeX preamble for example
+ ``\setlength{\tymin}{40pt}`` to ensure a minimal column width of ``40pt``,
+ the ``tabulary`` default of ``10pt`` being too small.
+
+.. hint::
+
+ To force usage of the LaTeX ``longtable`` environment pass ``longtable`` as
+ a ``:class:`` option to :dudir:`table`, :dudir:`csv-table`, or
+ :dudir:`list-table`. Use :ref:`rst-class ` for other tables.
Math
----
diff --git a/doc/usage/theming.rst b/doc/usage/theming.rst
index 0e4b4a64d..c33c7d477 100644
--- a/doc/usage/theming.rst
+++ b/doc/usage/theming.rst
@@ -88,7 +88,7 @@ writing your own themes, refer to :doc:`/development/theming`.
Builtin themes
~~~~~~~~~~~~~~
-.. cssclass:: longtable
+.. cssclass:: longtable, standard
+--------------------+--------------------+
| **Theme overview** | |
diff --git a/sphinx/builders/latex/__init__.py b/sphinx/builders/latex/__init__.py
index d07093fd2..2979589db 100644
--- a/sphinx/builders/latex/__init__.py
+++ b/sphinx/builders/latex/__init__.py
@@ -170,6 +170,9 @@ class LaTeXBuilder(Builder):
self.context.update(self.config.latex_elements)
self.context['release'] = self.config.release
self.context['use_xindy'] = self.config.latex_use_xindy
+ self.context['booktabs'] = 'booktabs' in self.config.latex_table_style
+ self.context['borderless'] = 'borderless' in self.config.latex_table_style
+ self.context['colorrows'] = 'colorrows' in self.config.latex_table_style
if self.config.today:
self.context['date'] = self.config.today
@@ -524,6 +527,7 @@ def setup(app: Sphinx) -> Dict[str, Any]:
app.add_config_value('latex_show_pagerefs', False, False)
app.add_config_value('latex_elements', {}, False)
app.add_config_value('latex_additional_files', [], False)
+ app.add_config_value('latex_table_style', [], False, [list])
app.add_config_value('latex_theme', 'manual', False, [str])
app.add_config_value('latex_theme_options', {}, False)
app.add_config_value('latex_theme_path', [], False, [list])
diff --git a/sphinx/templates/latex/latex.tex_t b/sphinx/templates/latex/latex.tex_t
index 66408a4c4..deb030504 100644
--- a/sphinx/templates/latex/latex.tex_t
+++ b/sphinx/templates/latex/latex.tex_t
@@ -25,6 +25,15 @@
%% memoir class requires extra handling
\makeatletter\@ifclassloaded{memoir}
{\ifdefined\memhyperindexfalse\memhyperindexfalse\fi}{}\makeatother
+<% endif %>
+<% if booktabs -%>
+\PassOptionsToPackage{booktabs}{sphinx}
+<% endif -%>
+<% if borderless -%>
+\PassOptionsToPackage{borderless}{sphinx}
+<% endif -%>
+<% if colorrows -%>
+\PassOptionsToPackage{colorrows}{sphinx}
<% endif -%>
<%= passoptionstopackages %>
\PassOptionsToPackage{warn}{textcomp}
diff --git a/sphinx/templates/latex/longtable.tex_t b/sphinx/templates/latex/longtable.tex_t
index 8d4cd748c..f5cb522ce 100644
--- a/sphinx/templates/latex/longtable.tex_t
+++ b/sphinx/templates/latex/longtable.tex_t
@@ -1,4 +1,28 @@
-\begin{savenotes}\sphinxatlongtablestart\begin{longtable}
+\begin{savenotes}
+\sphinxatlongtablestart
+\sphinxthistablewithglobalstyle
+<% if 'booktabs' in table.styles -%>
+\sphinxthistablewithbooktabsstyle
+<% endif -%>
+<% if 'borderless' in table.styles -%>
+\sphinxthistablewithborderlessstyle
+<% endif -%>
+<% if 'standard' in table.styles -%>
+\sphinxthistablewithstandardstyle
+<% endif -%>
+<% if 'vlines' in table.styles -%>
+\sphinxthistablewithvlinesstyle
+<% endif -%>
+<% if 'novlines' in table.styles -%>
+\sphinxthistablewithnovlinesstyle
+<% endif -%>
+<% if 'colorrows' in table.styles -%>
+\sphinxthistablewithcolorrowsstyle
+<% endif -%>
+<% if 'nocolorrows' in table.styles -%>
+\sphinxthistablewithnocolorrowsstyle
+<% endif -%>
+\begin{longtable}
<%- if table.align in ('center', 'default') -%>
[c]
<%- elif table.align == 'left' -%>
@@ -10,25 +34,37 @@
<%- if table.caption -%>
\sphinxthelongtablecaptionisattop
\caption{<%= ''.join(table.caption) %>\strut}<%= labels %>\\*[\sphinxlongtablecapskipadjust]
-\hline
<% elif labels -%>
-\hline\noalign{\phantomsection<%= labels %>}%
-<% else -%>
-\hline
+\noalign{\phantomsection<%= labels %>}%
+<% endif -%>
+\sphinxtoprule
+<%= ''.join(table.header) -%>
+<%- if table.header -%>
+\sphinxmidrule
<% endif -%>
-<%= ''.join(table.header) %>
\endfirsthead
-\multicolumn{<%= table.colcount %>}{c}%
-{\makebox[0pt]{\sphinxtablecontinued{\tablename\ \thetable{} \textendash{} <%= _('continued from previous page') %>}}}\\
-\hline
-<%= ''.join(table.header) %>
+\multicolumn{<%= table.colcount %>}{c}{\sphinxnorowcolor
+ \makebox[0pt]{\sphinxtablecontinued{\tablename\ \thetable{} \textendash{} <%= _('continued from previous page') %>}}%
+}\\
+\sphinxtoprule
+<%= ''.join(table.header) -%>
+<%- if table.header -%>
+\sphinxmidrule
+<% endif -%>
\endhead
-\hline
-\multicolumn{<%= table.colcount %>}{r}{\makebox[0pt][r]{\sphinxtablecontinued{<%= _('continues on next page') %>}}}\\
+\sphinxbottomrule
+\multicolumn{<%= table.colcount %>}{r}{\sphinxnorowcolor
+ \makebox[0pt][r]{\sphinxtablecontinued{<%= _('continues on next page') %>}}%
+}\\
\endfoot
\endlastfoot
-<%= ''.join(table.body) %>
-\end{longtable}\sphinxatlongtableend\end{savenotes}
+\sphinxtableatstartofbodyhook
+<%= ''.join(table.body) -%>
+\sphinxbottomrule
+\end{longtable}
+\sphinxtableafterendhook
+\sphinxatlongtableend
+\end{savenotes}
diff --git a/sphinx/templates/latex/tabular.tex_t b/sphinx/templates/latex/tabular.tex_t
index a0db7faff..0a9310a5e 100644
--- a/sphinx/templates/latex/tabular.tex_t
+++ b/sphinx/templates/latex/tabular.tex_t
@@ -1,4 +1,26 @@
\begin{savenotes}\sphinxattablestart
+\sphinxthistablewithglobalstyle
+<% if 'booktabs' in table.styles -%>
+\sphinxthistablewithbooktabsstyle
+<% endif -%>
+<% if 'borderless' in table.styles -%>
+\sphinxthistablewithborderlessstyle
+<% endif -%>
+<% if 'standard' in table.styles -%>
+\sphinxthistablewithstandardstyle
+<% endif -%>
+<% if 'vlines' in table.styles -%>
+\sphinxthistablewithvlinesstyle
+<% endif -%>
+<% if 'novlines' in table.styles -%>
+\sphinxthistablewithnovlinesstyle
+<% endif -%>
+<% if 'colorrows' in table.styles -%>
+\sphinxthistablewithcolorrowsstyle
+<% endif -%>
+<% if 'nocolorrows' in table.styles -%>
+\sphinxthistablewithnocolorrowsstyle
+<% endif -%>
<% if table.align -%>
<%- if table.align in ('center', 'default') -%>
\centering
@@ -19,9 +41,14 @@
\phantomsection<%= labels %>\nobreak
<% endif -%>
\begin{tabular}[t]<%= table.get_colspec() -%>
-\hline
-<%= ''.join(table.header) %>
-<%=- ''.join(table.body) %>
+\sphinxtoprule
+<%= ''.join(table.header) -%>
+<%- if table.header -%>
+\sphinxmidrule
+<% endif -%>
+\sphinxtableatstartofbodyhook
+<%=- ''.join(table.body) -%>
+\sphinxbottomrule
\end{tabular}
-\par
+\sphinxtableafterendhook\par
\sphinxattableend\end{savenotes}
diff --git a/sphinx/templates/latex/tabulary.tex_t b/sphinx/templates/latex/tabulary.tex_t
index 3236b798a..6ebcec6d2 100644
--- a/sphinx/templates/latex/tabulary.tex_t
+++ b/sphinx/templates/latex/tabulary.tex_t
@@ -1,4 +1,26 @@
\begin{savenotes}\sphinxattablestart
+\sphinxthistablewithglobalstyle
+<% if 'booktabs' in table.styles -%>
+\sphinxthistablewithbooktabsstyle
+<% endif -%>
+<% if 'borderless' in table.styles -%>
+\sphinxthistablewithborderlessstyle
+<% endif -%>
+<% if 'standard' in table.styles -%>
+\sphinxthistablewithstandardstyle
+<% endif -%>
+<% if 'vlines' in table.styles -%>
+\sphinxthistablewithvlinesstyle
+<% endif -%>
+<% if 'novlines' in table.styles -%>
+\sphinxthistablewithnovlinesstyle
+<% endif -%>
+<% if 'colorrows' in table.styles -%>
+\sphinxthistablewithcolorrowsstyle
+<% endif -%>
+<% if 'nocolorrows' in table.styles -%>
+\sphinxthistablewithnocolorrowsstyle
+<% endif -%>
<% if table.align -%>
<%- if table.align in ('center', 'default') -%>
\centering
@@ -19,9 +41,14 @@
\phantomsection<%= labels %>\nobreak
<% endif -%>
\begin{tabulary}{\linewidth}[t]<%= table.get_colspec() -%>
-\hline
-<%= ''.join(table.header) %>
-<%=- ''.join(table.body) %>
+\sphinxtoprule
+<%= ''.join(table.header) -%>
+<%- if table.header -%>
+\sphinxmidrule
+<% endif -%>
+\sphinxtableatstartofbodyhook
+<%=- ''.join(table.body) -%>
+\sphinxbottomrule
\end{tabulary}
-\par
+\sphinxtableafterendhook\par
\sphinxattableend\end{savenotes}
diff --git a/sphinx/texinputs/sphinx.sty b/sphinx/texinputs/sphinx.sty
index 8e01c8ac2..748f8b977 100644
--- a/sphinx/texinputs/sphinx.sty
+++ b/sphinx/texinputs/sphinx.sty
@@ -6,7 +6,7 @@
%
\NeedsTeXFormat{LaTeX2e}[1995/12/01]
-\ProvidesPackage{sphinx}[2022/06/30 v5.1.0 LaTeX package (Sphinx markup)]
+\ProvidesPackage{sphinx}[2022/08/15 v5.2.0 LaTeX package (Sphinx markup)]
% provides \ltx@ifundefined
% (many packages load ltxcmds: graphicx does for pdftex and lualatex but
@@ -17,6 +17,7 @@
%% for deprecation warnings
\newcommand\sphinxdeprecationwarning[4]{% #1 the deprecated macro or name,
% #2 = when deprecated, #3 = when removed, #4 = additional info
+ {% limit scope of \spx@tempa, \AtEndDocument works even if nested.
\edef\spx@tempa{\detokenize{#1}}%
\ltx@ifundefined{sphinx_depr_\spx@tempa}{%
\global\expandafter\let\csname sphinx_depr_\spx@tempa\endcsname\spx@tempa
@@ -28,7 +29,43 @@
\@spaces- and removed at Sphinx #3.^^J
#4^^J****}}%
}{% warning already emitted (at end of latex log), don't repeat
- }}
+ }%
+ }% end of scope limiting group for \spx@tempa
+}
+%% important build warnings use an undefined reference to induce latexmk
+%% into complaining (once per warning) at very end of console output
+\newcommand\sphinxbuildwarning[1]{%
+ \ifcsname sphinx_emitted_#1\endcsname
+ \else
+ \global\expandafter\let\csname sphinx_emitted_#1\endcsname\@empty
+ \AtEndDocument{\hbox{% should the printing of text be made conditional on
+ % some boolean?
+ \bfseries\color{red}%
+ \@nameuse{sphinx_buildwarning_#1}%
+ % place an undefined reference deliberately
+ \let\nfss@text\@gobble % no ??
+ \ref{!!\@nameuse{sphinx_buildwarning_#1}}%
+ }}%
+ \fi
+}
+\@namedef{sphinx_buildwarning_coloursyntax}{%
+ The colours whose definition used xcolor syntax were set to white
+ as xcolor was not found; check the latex log warnings for details}
+\@namedef{sphinx_buildwarning_colorblend}{%
+ Command \string\sphinxcolorblend\space seen but ignored in tables
+ as xcolor was not found; check the latex log warnings for details}
+\@namedef{sphinx_buildwarning_nopict2e}{%
+ Some radii options for box corners used; they were ignored as pict2e
+ was not found}
+\@namedef{sphinx_buildwarning_badtitlesec}{%
+ Your system has titlesec version 2.10.1 which causes disappearance
+ of section numbers; check the latex log warning for details}
+\@namedef{sphinx_buildwarning_booktabs}{%
+ Some tables with booktabs class (check latex log) but booktabs
+ package not loaded; add its loading to the latex preamble}%
+\@namedef{sphinx_buildwarning_badfootnotes}{%
+ Footnote rendering may have had problems, due to extra package or
+ document class; check latex log for instructions}%
%% OPTION HANDLING
@@ -47,16 +84,62 @@
}{
\RequirePackage{color}
}
-% the \colorlet of xcolor (if at all loaded) is overkill for our use case
+
+% the \colorlet of xcolor (if at all loaded) is overkill for our internal use
\newcommand{\sphinxcolorlet}[2]
{\expandafter\let\csname\@backslashchar color@#1\expandafter\endcsname
\csname\@backslashchar color@#2\endcsname }
+% (5.2.0) allow colour options to use both the \definecolor and the \colorlet
+% syntaxes, for example VerbatimColor={gray}{0.9} or VerbatimColor=red!10
+% In the latter case we need the real \colorlet from xcolor package.
+\def\spx@defineorletcolor#1{%
+ \def\spx@definedcolor{{#1}}%
+ \futurelet\spx@token\spx@defineorlet}
+\def\spx@defineorlet{%
+ \ifx\spx@token\bgroup
+ \expandafter\spx@definecolor\else\expandafter\spx@colorlet\fi}
+\def\spx@colorlet#1\relax{\expandafter\colorlet\spx@definedcolor{#1}}
+\def\spx@definecolor{\expandafter\definecolor\spx@definedcolor}
+%
+\@ifpackageloaded{xcolor}%
+ {}%
+ {% xcolor not loaded because it was not found in the LaTeX installation
+\def\spx@colorlet#1\relax{%
+ \sphinxbuildwarning{coloursyntax}%
+ \PackageWarning{sphinx}{%
+Sorry, the #1 syntax requires package xcolor,\MessageBreak
+which was not found on your TeX/LaTeX installation.\MessageBreak
+\@spaces\expandafter\@firstofone\spx@definedcolor\MessageBreak
+will be set to white}%
+ \expandafter\definecolor\spx@definedcolor{rgb}{1,1,1}%
+ }% end of redefinition of \spx@colorlet
+ }% end of xcolor not found branch
+
% Handle options via "kvoptions" (later loaded by hyperref anyhow)
\RequirePackage{kvoptions}
\SetupKeyvalOptions{prefix=spx@opt@} % use \spx@opt@ prefix
+% Optional usage of booktabs package for tables
+\DeclareBoolOption[false]{booktabs}
+\DeclareBoolOption[false]{borderless}
+\DeclareBoolOption[true]{booktabscolorgaps}
+\DeclareVoidOption{booktabsnogaps}{%
+ \ifx\@nodocument\relax
+ % in body
+ \expandafter\@firstofone
+ \else
+ % in preamble, wait for at begin document
+ \expandafter\AtBeginDocument
+ \fi
+ {\ifdefined\abovetopsep % silently do nothing if booktabs not loaded
+ \abovetopsep\z@\belowrulesep\z@\aboverulesep\z@\belowbottomsep\z@
+ \fi
+ }%
+}
+% Coloured table rows
+\DeclareBoolOption[false]{colorrows}
% Sphinx legacy text layout: 1in margins on all four sides
\ifx\@jsc@uplatextrue\@undefined
\DeclareStringOption[1in]{hmargin}
@@ -143,20 +226,42 @@
% same problems as for dimensions: we want the key handler to use \definecolor.
% first, some colours with no prefix, for backwards compatibility
\newcommand*{\sphinxDeclareColorOption}[2]{%
+ % set the initial default; only \definecolor syntax for defaults!
\definecolor{#1}#2%
- \define@key{sphinx}{#1}{\definecolor{#1}##1}%
+ % set the key handler to accept both \definecolor and \colorlet syntax
+ \define@key{sphinx}{#1}{\spx@defineorletcolor{#1}##1\relax}%
}%
\sphinxDeclareColorOption{TitleColor}{{rgb}{0.126,0.263,0.361}}
\sphinxDeclareColorOption{InnerLinkColor}{{rgb}{0.208,0.374,0.486}}
\sphinxDeclareColorOption{OuterLinkColor}{{rgb}{0.216,0.439,0.388}}
\sphinxDeclareColorOption{VerbatimColor}{{rgb}{1,1,1}}
\sphinxDeclareColorOption{VerbatimBorderColor}{{rgb}{0,0,0}}
-% now the colours defined with "sphinx" prefix in their names
+% all other colours will be named with a "sphinx" prefix
\newcommand*{\sphinxDeclareSphinxColorOption}[2]{%
- % set the initial default
+ % set the initial default; only \definecolor syntax for defaults!
\definecolor{sphinx#1}#2%
- % set the key handler. The "value" ##1 must be acceptable by \definecolor.
- \define@key{sphinx}{#1}{\definecolor{sphinx#1}##1}%
+ % set the key handler to accept both \definecolor and \colorlet syntax
+ \define@key{sphinx}{#1}{\spx@defineorletcolor{sphinx#1}##1\relax}%
+}%
+% table row colors
+\sphinxDeclareSphinxColorOption{TableRowColorHeader}{{gray}{0.86}}
+\sphinxDeclareSphinxColorOption{TableRowColorOdd}{{gray}{0.92}}
+\sphinxDeclareSphinxColorOption{TableRowColorEven}{{gray}{0.98}}
+% if not set, the "Merge" colour will keep in sync with the "Row" colour
+\def\sphinxTableMergeColorHeader{sphinxTableRowColorHeader}
+\define@key{sphinx}{TableMergeColorHeader}{%
+ \spx@defineorletcolor{sphinxTableMergeColorHeader}#1\relax
+ \def\sphinxTableMergeColorHeader{sphinxTableMergeColorHeader}%
+}%
+\def\sphinxTableMergeColorOdd{sphinxTableRowColorOdd}
+\define@key{sphinx}{TableMergeColorOdd}{%
+ \spx@defineorletcolor{sphinxTableMergeColorOdd}#1\relax
+ \def\sphinxTableMergeColorOdd{sphinxTableMergeColorOdd}%
+}%
+\def\sphinxTableMergeColorEven{sphinxTableRowColorEven}
+\define@key{sphinx}{TableMergeColorEven}{%
+ \spx@defineorletcolor{sphinxTableMergeColorEven}#1\relax
+ \def\sphinxTableMergeColorEven{sphinxTableMergeColorEven}%
}%
% Default color chosen to be as in minted.sty LaTeX package!
\sphinxDeclareSphinxColorOption{VerbatimHighlightColor}{{rgb}{0.878,1,1}}
@@ -292,21 +397,21 @@
\newif\ifspx@pre@withbordercolor
\define@key{sphinx}{pre_border-TeXcolor}{%
\spx@pre@withbordercolortrue
- \definecolor{VerbatimBorderColor}#1% legacy colour name with no sphinx prefix
+ \spx@defineorletcolor{VerbatimBorderColor}#1\relax
}
\expandafter\let\expandafter\KV@sphinx@VerbatimBorderColor
\csname KV@sphinx@pre_border-TeXcolor\endcsname
\newif\ifspx@pre@withbackgroundcolor
\define@key{sphinx}{pre_background-TeXcolor}{%
\spx@pre@withbackgroundcolortrue
- \definecolor{VerbatimColor}#1% legacy colour name with no sphinx prefix
+ \spx@defineorletcolor{VerbatimColor}#1\relax
}
\expandafter\let\expandafter\KV@sphinx@VerbatimColor
\csname KV@sphinx@pre_background-TeXcolor\endcsname
\newif\ifspx@pre@withshadowcolor
\define@key{sphinx}{pre_box-shadow-TeXcolor}{%
\spx@pre@withshadowcolortrue
- \definecolor{sphinxVerbatimShadowColor}#1%
+ \spx@defineorletcolor{sphinxVerbatimShadowColor}#1\relax
}
\definecolor{sphinxVerbatimShadowColor}{rgb}{0,0,0}
% topics
@@ -412,17 +517,17 @@
\newif\ifspx@topic@withbordercolor
\define@key{sphinx}{div.topic_border-TeXcolor}{%
\spx@topic@withbordercolortrue
- \definecolor{sphinxTopicBorderColor}#1%
+ \spx@defineorletcolor{sphinxTopicBorderColor}#1\relax
}
\newif\ifspx@topic@withbackgroundcolor
\define@key{sphinx}{div.topic_background-TeXcolor}{%
\spx@topic@withbackgroundcolortrue
- \definecolor{sphinxTopicBackgroundColor}#1%
+ \spx@defineorletcolor{sphinxTopicBackgroundColor}#1\relax
}
\newif\ifspx@topic@withshadowcolor
\define@key{sphinx}{div.topic_box-shadow-TeXcolor}{%
\spx@topic@withshadowcolortrue
- \definecolor{sphinxTopicShadowColor}#1%
+ \spx@defineorletcolor{sphinxTopicShadowColor}#1\relax
}
% warning, caution, attention, danger, error
\def\spx@tempa#1{%
@@ -572,9 +677,12 @@
\definecolor{sphinx#4BorderColor}{rgb}{0,0,0}%
\definecolor{sphinx#4BgColor}{rgb}{1,1,1}%
\definecolor{sphinx#4ShadowColor}{rgb}{0,0,0}%
- \define@key{sphinx}{div.#4_border-TeXcolor}{#1\definecolor{sphinx#4BorderColor}##1}%
- \define@key{sphinx}{div.#4_background-TeXcolor}{#2\definecolor{sphinx#4BgColor}##1}%
- \define@key{sphinx}{div.#4_box-shadow-TeXcolor}{#3\definecolor{sphinx#4ShadowColor}##1}%
+ \define@key{sphinx}{div.#4_border-TeXcolor}%
+ {#1\spx@defineorletcolor{sphinx#4BorderColor}##1\relax}%
+ \define@key{sphinx}{div.#4_background-TeXcolor}%
+ {#2\spx@defineorletcolor{sphinx#4BgColor}##1\relax}%
+ \define@key{sphinx}{div.#4_box-shadow-TeXcolor}%
+ {#3\spx@defineorletcolor{sphinx#4ShadowColor}##1\relax}%
\expandafter\let\csname KV@sphinx@#4BorderColor\expandafter\endcsname
\csname KV@sphinx@div.#4_border-TeXcolor\endcsname
\expandafter\let\csname KV@sphinx@#4BgColor\expandafter\endcsname
@@ -594,6 +702,9 @@
\DisableKeyvalOption{sphinx}{numfigreset}
\DisableKeyvalOption{sphinx}{nonumfigreset}
\DisableKeyvalOption{sphinx}{mathnumfig}
+\DisableKeyvalOption{sphinx}{booktabs}
+\DisableKeyvalOption{sphinx}{borderless}
+\DisableKeyvalOption{sphinx}{rowcolors}
% FIXME: this is unrelated to an option, move this elsewhere
% To allow hyphenation of first word in narrow contexts; no option,
% customization to be done via 'preamble' key
@@ -693,11 +804,8 @@
{\PackageWarningNoLine{sphinx}{%
The package pict2e is required for rounded boxes.\MessageBreak
It does not seem to be available on your system.\MessageBreak
- Options for setting radii will thus be ignored}%
- \AtEndDocument{\PackageWarningNoLine{sphinx}{%
- I issued a warning which may have gotten lost in the\MessageBreak
- gigantic console output: pict2e.sty was not found,\MessageBreak
- and radii setting options have been ignored}}%
+ Options for setting radii have thus been ignored}%
+ \sphinxbuildwarning{nopict2e}%
\def\spx@boxes@fcolorbox@rounded{\spx@boxes@fcolorbox}%
}%
}%
diff --git a/sphinx/texinputs/sphinxlatexstyleheadings.sty b/sphinx/texinputs/sphinxlatexstyleheadings.sty
index fa9be82b4..a54980bc0 100644
--- a/sphinx/texinputs/sphinxlatexstyleheadings.sty
+++ b/sphinx/texinputs/sphinxlatexstyleheadings.sty
@@ -1,7 +1,7 @@
%% TITLES
%
% change this info string if making any custom modification
-\ProvidesFile{sphinxlatexstyleheadings.sty}[2021/01/27 headings]
+\ProvidesFile{sphinxlatexstyleheadings.sty}[2022/08/15 headings]
\RequirePackage[nobottomtitles*]{titlesec}
\@ifpackagelater{titlesec}{2016/03/15}%
@@ -25,6 +25,7 @@
******** and Sphinx could not patch it, perhaps because your local ...|^^J%
******** copy is already fixed without a changed release date. .......|^^J%
******** If not, you must update titlesec! ...........................|}}%
+ \sphinxbuildwarning{badtitlesec}%
\fi
}%
}{}
diff --git a/sphinx/texinputs/sphinxlatextables.sty b/sphinx/texinputs/sphinxlatextables.sty
index c3c1d6ad1..247ad7a76 100644
--- a/sphinx/texinputs/sphinxlatextables.sty
+++ b/sphinx/texinputs/sphinxlatextables.sty
@@ -1,7 +1,7 @@
%% TABLES (WITH SUPPORT FOR MERGED CELLS OF GENERAL CONTENTS)
%
% change this info string if making any custom modification
-\ProvidesFile{sphinxlatextables.sty}[2021/01/27 tables]%
+\ProvidesFile{sphinxlatextables.sty}[2022/08/15 tables]%
% Provides support for this output mark-up from Sphinx latex writer
% and table templates:
@@ -25,12 +25,31 @@
% - \sphinxtablestrut
% - \sphinxthecaptionisattop
% - \sphinxthelongtablecaptionisattop
+% - \sphinxhline
+% - \sphinxcline
+% - \sphinxvlinecrossing
+% - \sphinxfixclines
+% - \sphinxtoprule
+% - \sphinxmidrule
+% - \sphinxbottomrule
+% - \sphinxtableatstartofbodyhook
+% - \sphinxtableafterendhook
+% - \sphinxthistablewithglobalstyle
+% - \sphinxthistablewithbooktabsstyle
+% - \sphinxthistablewithborderlessstyle
+% - \sphinxthistablewithstandardstyle
+% - \sphinxthistablewithcolorrowsstyle
+% - \sphinxthistablewithnocolorrowsstyle
+% - \sphinxthistablewithvlinesstyle
+% - \sphinxthistablewithnovlinesstyle
%
% Executes \RequirePackage for:
%
% - tabulary
% - longtable
% - varwidth
+% - colortbl
+% - booktabs if 'booktabs' in latex_table_style
%
% Extends tabulary and longtable via patches and custom macros to support
% merged cells possibly containing code-blocks in complex tables
@@ -43,9 +62,13 @@
% X or S (Sphinx) may have meanings if some table package is loaded hence
% \X was chosen to avoid possibility of conflict
\newcolumntype{\X}[2]{p{\dimexpr
- (\linewidth-\arrayrulewidth)*#1/#2-\tw@\tabcolsep-\arrayrulewidth\relax}}
+ (\linewidth-\spx@arrayrulewidth)*#1/#2-\tw@\tabcolsep-\spx@arrayrulewidth\relax}}
\newcolumntype{\Y}[1]{p{\dimexpr
- #1\dimexpr\linewidth-\arrayrulewidth\relax-\tw@\tabcolsep-\arrayrulewidth\relax}}
+ #1\dimexpr\linewidth-\spx@arrayrulewidth\relax-\tw@\tabcolsep-\spx@arrayrulewidth\relax}}
+% \spx@arrayrulewidth is used internally and its meaning will be set according
+% to the table type; no extra user code should modify it. In particular any
+% \setlength{\spx@arrayrulewidth}{...} may break all of LaTeX... (really...)
+\def\spx@arrayrulewidth{\arrayrulewidth}% 5.2.0, to be adjusted by each table
% using here T (for Tabulary) feels less of a problem than the X could be
\newcolumntype{T}{J}%
% For tables allowing pagebreaks
@@ -167,6 +190,11 @@
\unexpanded\expandafter{\@vwid@setup}}%
}%
+% NOTA BENE: since the multicolumn and multirow code was written Sphinx
+% decided to prefix non public internal macros by \spx@ and in fact all
+% such macros here should now be prefixed by \spx@table@, but doing the
+% update is delayed to later. (written at 5.2.0)
+
%%%%%%%%%%%%%%%%%%%%%
% --- MULTICOLUMN ---
% standard LaTeX's \multicolumn
@@ -208,6 +236,16 @@
% \arrayrulewidth space for each column separation in its estimate of available
% width).
%
+% Update at 5.2.0: code uses \spx@arrayrulewidth which is kept in sync with the
+% table column specification (aka preamble):
+% - no | in preamble: \spx@arrayrulewidth -> \z@
+% - at least a | in the preamble: \spx@arrayrulewidth -> \arrayrulewidth
+% This is used for computation of merged cells widths. Mixed preambles using
+% at least a | but not using it for all columns (as can be obtained via the
+% tabularcolumns directive) may cause some merged cells contents to be slightly
+% shifted to the left as they assume merged columns are | separated where in
+% fact they perhaps are not.
+%
% TN. 1b: as Sphinx multicolumn uses neither \omit nor \span, it can not
% (easily) get rid of extra macros from >{...} or <{...} between columns. At
% least, it has been made compatible with colortbl's \columncolor.
@@ -229,7 +267,19 @@
% Sphinx generates no nested tables, and if some LaTeX macro uses internally a
% tabular this will not have a \sphinxstartmulticolumn within it!
%
-\def\sphinxstartmulticolumn{%
+% 5.2.0 adds a check for multirow as single-row multi-column will allow a row
+% colour but multi-row multi-column should not.
+% Attention that this assumes \sphinxstartmulticolumn is always followed
+% in latex mark-up either by \sphinxmultirow or \begin (from \begin{varwidth}).
+\def\sphinxstartmulticolumn#1#2{%
+ \ifx\sphinxmultirow#2%
+ \gdef\spx@table@hackCT@inmergedcell{\spx@table@hackCT@nocolor}%
+ \else
+ \global\let\spx@table@hackCT@inmergedcell\spx@@table@hackCT@inmergedcell
+ \fi
+ \sphinx@startmulticolumn{#1}#2%
+}%
+\def\sphinx@startmulticolumn{%
\ifx\equation$% $ tabulary's first pass
\expandafter\sphinx@TYI@start@multicolumn
\else % either not tabulary or tabulary's second pass
@@ -285,32 +335,21 @@
\else
% if in an l, r, c type column, try and hope for the best
\xdef\sphinx@tempb{\the\dimexpr(\ifx\TY@final\@undefined\linewidth\else
- \sphinx@TY@tablewidth\fi-\arrayrulewidth)/\sphinx@tempa
- -\tw@\tabcolsep-\arrayrulewidth\relax}%
+ \sphinx@TY@tablewidth\fi-\spx@arrayrulewidth)/\sphinx@tempa
+ -\tw@\tabcolsep-\spx@arrayrulewidth\relax}%
\fi
\noindent\kern\sphinx@tempb\relax
\xdef\sphinx@multiwidth
- {\the\dimexpr\sphinx@multiwidth+\sphinx@tempb+\tw@\tabcolsep+\arrayrulewidth}%
- % hack the \vline and the colortbl macros
- \sphinx@hack@vline\sphinx@hack@CT&\relax
+ {\the\dimexpr\sphinx@multiwidth+\sphinx@tempb+\tw@\tabcolsep+\spx@arrayrulewidth}%
+ \spx@table@hackCT@fixcolorpanel
+ % silence a | column separator in our merged cell
+ \spx@table@hackCT@inhibitvline
+ % prevent column colours to interfere with our multi-column but allow row
+ % colour (we can't obey a \cellcolor as it has not be seen yet at this stage)
+ \spx@table@hackCT@inmergedcell&\relax
% repeat
\expandafter\sphinx@multispan\expandafter{\the\numexpr#1-\@ne}%
}%
-% packages like colortbl add group levels, we need to "climb back up" to be
-% able to hack the \vline and also the colortbl inserted tokens. This creates
-% empty space whether or not the columns were | separated:
-\def\sphinx@hack@vline{\ifnum\currentgrouptype=6\relax
- \kern\arrayrulewidth\arrayrulewidth\z@\else\aftergroup\sphinx@hack@vline\fi}%
-\def\sphinx@hack@CT{\ifnum\currentgrouptype=6\relax
- \let\CT@setup\sphinx@CT@setup\else\aftergroup\sphinx@hack@CT\fi}%
-% It turns out \CT@row@color is not expanded contrarily to \CT@column@color
-% during LaTeX+colortbl preamble preparation, hence it would be possible for
-% \sphinx@CT@setup to discard only the column color and choose to obey or not
-% row color and cell color. It would even be possible to propagate cell color
-% to row color for the duration of the Sphinx multicolumn... the (provisional?)
-% choice has been made to cancel the colortbl colours for the multicolumn
-% duration.
-\def\sphinx@CT@setup #1\endgroup{\endgroup}% hack to remove colour commands
\def\sphinx@multispan@end#1{%
% first, trace back our steps horizontally
\noindent\kern-\dimexpr\sphinx@multiwidth\relax
@@ -320,11 +359,12 @@
\else
\xdef\sphinx@multiwidth{\the\dimexpr\sphinx@multiwidth+
(\ifx\TY@final\@undefined\linewidth\else
- \sphinx@TY@tablewidth\fi-\arrayrulewidth)/\sphinx@tempa
- -\tw@\tabcolsep-\arrayrulewidth\relax}%
+ \sphinx@TY@tablewidth\fi-\spx@arrayrulewidth)/\sphinx@tempa
+ -\tw@\tabcolsep-\spx@arrayrulewidth\relax}%
\fi
- % we need to remove colour set-up also for last cell of the multi-column
- \aftergroup\sphinx@hack@CT
+ % last cell of the multi-column
+ \aftergroup\spx@table@hackCT@fixcolorpanel
+ \aftergroup\spx@table@hackCT@inmergedcell
}%
\newcommand*\sphinxcolwidth[2]{%
% this dimension will always be used for varwidth, and serves as maximum
@@ -345,8 +385,8 @@
\linewidth
\else
% l, c, r columns. Do our best.
- \dimexpr(\linewidth-\arrayrulewidth)/#2-
- \tw@\tabcolsep-\arrayrulewidth\relax
+ \dimexpr(\linewidth-\spx@arrayrulewidth)/#2-
+ \tw@\tabcolsep-\spx@arrayrulewidth\relax
\fi
\else % in tabulary
\ifx\equation$%$% first pass
@@ -357,8 +397,8 @@
\linewidth % in a L, R, C, J column or a p, \X, \Y ...
\else
% we have hacked \TY@final to put in \sphinx@TY@tablewidth the table width
- \dimexpr(\sphinx@TY@tablewidth-\arrayrulewidth)/#2-
- \tw@\tabcolsep-\arrayrulewidth\relax
+ \dimexpr(\sphinx@TY@tablewidth-\spx@arrayrulewidth)/#2-
+ \tw@\tabcolsep-\spx@arrayrulewidth\relax
\fi
\fi
\fi
@@ -368,7 +408,125 @@
% \sphinxcolwidth will use this only inside LaTeX's standard \multicolumn
\def\sphinx@multiwidth #1#2{\dimexpr % #1 to gobble the \@gobble (!)
(\ifx\TY@final\@undefined\linewidth\else\sphinx@TY@tablewidth\fi
- -\arrayrulewidth)*#2-\tw@\tabcolsep-\arrayrulewidth\relax}%
+ -\spx@arrayrulewidth)*#2-\tw@\tabcolsep-\spx@arrayrulewidth\relax}%
+
+% \spx@table@hackCT@inhibitvline
+% packages like colortbl add group levels, we need to "climb back up" to be
+% able to hack the \vline and also the colortbl inserted tokens. The hack
+% sets the \arrayrulewidth to \z@ to inhibit a | separator at right end
+% of the cell, if present (our code does not use \omit so can not avoid the
+% \vline insertion, but setting its width to zero makes it do nothing).
+% Some subtlety with colour panels must be taken care of.
+\def\spx@table@hackCT@inhibitvline{\ifnum\currentgrouptype=6\relax
+ \kern\spx@arrayrulewidth % will be compensated by extra colour panel left overhang
+ \arrayrulewidth\z@% trick to inhibit the {\vrule width \arrayrulewidth}
+ \else\aftergroup\spx@table@hackCT@inhibitvline\fi}%
+
+% hacking around colour matters
+% Sphinx 1.6 comment:
+% It turns out \CT@row@color is not expanded contrarily to \CT@column@color
+% during LaTeX+colortbl preamble preparation, hence it would be possible for
+% \CT@setup to discard only the column color and choose to obey or not
+% row color and cell color. It would even be possible to propagate cell color
+% to row color for the duration of the Sphinx multicolumn... the (provisional?)
+% choice has been made to cancel the colortbl colours for the multicolumn
+% duration.
+% Sphinx 5.2.0 comment:
+% - colortbl has no mechanism to disable colour background in a given cell:
+% \cellcolor triggers one more \color, but has no possibility to revert
+% a previously emitted \color, only to override it via an additional \color
+% - prior to <5.2.0, Sphinx did not officially support colour in tables,
+% but it did have a mechanism to protect merged cells from being partly
+% covered by colour panels at various places. At 5.2.0 this mechanism
+% is relaxed a bit to allow row colour for a single-row merged cell.
+%
+% fixcolorpanel
+\def\spx@table@hackCT@fixcolorpanel{\ifnum\currentgrouptype=6\relax
+ \edef\spx@table@leftcolorpanelextra
+ % \edef as \arrayrulewidth will be set to \z@ next,
+ % hence also \spx@arrayrulewidth...
+ {\sphinxcolorpanelextraoverhang+\the\spx@arrayrulewidth}%
+ \else\aftergroup\spx@table@hackCT@fixcolorpanel\fi}%
+%
+% inmergedcell
+% \spx@table@hackCT@inmergedcell will be locally set to either this
+% \spx@@table@hackCT@inmergedcell or to \spx@table@hackCT@nocolor
+% "\let\spx@original@CT@setup\CT@setup" is done after loading colortbl
+\def\spx@@table@hackCT@inmergedcell{\ifnum\currentgrouptype=6\relax
+ \let\CT@setup\spx@CT@setup@inmergedcell
+ \else\aftergroup\spx@@table@hackCT@inmergedcell\fi
+}%
+\newif\ifspx@table@inmergedcell
+\def\spx@CT@setup@inmergedcell #1\endgroup{%
+ % - obey only row color and disable effect of \sphinxblendcolor
+ % - turn on the inmergedcell boolean to signal to \CT@row@color
+ \spx@original@CT@setup
+ \spx@table@inmergedcelltrue % needed by \CT@row@color
+ % deactivate effect of \sphinxcolorblend if it happened at all
+ \ifdefined\blendcolors\blendcolors{}\fi
+ \CT@row@color
+ \CT@do@color
+ \global\let\CT@cell@color\relax
+ \endgroup
+}%
+%
+% nocolor
+\def\spx@table@hackCT@nocolor{\ifnum\currentgrouptype=6\relax
+% sadly \CT@column@color is possibly already expanded so we can't
+% simply do \let\CT@column@color\relax etc...
+% admittedly we could perhaps hack \CT@color but well
+ \let\CT@setup\spx@CT@setup@nocolor
+ \else\aftergroup\spx@table@hackCT@nocolor\fi
+}
+\def\spx@CT@setup@nocolor#1\endgroup{%
+ \global\let\CT@cell@color\relax
+ % the above fix was added at 5.2.0
+ % formerly a \cellcolor added by a raw latex directive in the merged cell
+ % would have caused colour to apply to the *next* cell after the merged
+ % one; we don't support \cellcolor from merged cells contents anyhow.
+ \endgroup}
+%
+% norowcolor
+\def\spx@table@hackCT@norowcolor{%
+% a bit easier although merged cells complicate the matter as they do need
+% to keep the rowcolor; and we can't know yet if we are in a merged cell
+ \ifnum\currentgrouptype=6\relax
+ \ifx\CT@row@color\relax
+ \else
+ \let\spx@saved@CT@row@color\CT@row@color
+ \def\CT@row@color{%
+ \ifspx@table@inmergedcell\expandafter\spx@saved@CT@row@color\fi
+ }%
+ \fi
+ \else\aftergroup\spx@table@hackCT@norowcolor\fi
+}
+%
+% \sphinxcolorblend
+\def\spx@table@hackCT@colorblend{%
+ \ifnum\currentgrouptype=6\relax
+ \expandafter\blendcolors\spx@colorblendparam
+ % merged cells will do a \blendcolors{} to cancel the effet
+ % we can not know here yet if in merged cell as the boolean
+ % \ifspx@table@inmergedcell is not yet updated
+ \else
+ \aftergroup\spx@table@hackCT@colorblend
+ \fi
+}
+\def\sphinxcolorblend#1{\gdef\spx@colorblendparam{{#1}}\spx@table@hackCT@colorblend}
+% Either xcolor.sty exists on user system and has been loaded by sphinx.sty,
+% or it does not exist, so we can use \@ifpackageloaded without delaying.
+\@ifpackageloaded{xcolor}%
+ {}%
+ {\def\sphinxcolorblend#1{%
+\PackageWarning{sphinx}{This table uses \string\sphinxcolorblend\space
+ but xcolor is not in\MessageBreak
+ the TeX/LaTeX installation, the command will be\MessageBreak
+ ignored in this and the next tables}%
+ \global\let\sphinxcolorblend\@gobble
+ \sphinxbuildwarning{colorblend}%
+ }%
+ }
+
%%%%%%%%%%%%%%%%%%
% --- MULTIROW ---
@@ -390,9 +548,22 @@
% that the table rows have the needed height. The needed mark-up is done
% by LaTeX writer, which has its own id for the merged cells.
%
-% The colour issue is solved by clearing colour panels in all cells,
+% The colour issue is "solved" by clearing colour panels in all cells,
% whether or not the multirow is single-column or multi-column.
%
+% MEMO at 5.2.0: to allow a multirow cell in a single column to react to
+% \columncolor correctly, it seems only way is that the contents
+% are inserted by bottom cell (this is mentioned in multirow.sty doc, too).
+% Sphinx could at Python level "move" the contents to that cell. But the
+% mechanism used here via \sphinxtablestrut to enlarge rows to make room for
+% the contents if needed becomes more challenging yet, because \sphinxtablestrut
+% mark-up will be parsed by TeX *before* it sees the contents of the merged
+% cell.. So it seems the best way would be to actually store the contents into
+% some owned-by-Sphinx box storage which needs to be globally allocated to
+% that usage ; then we need multiple such boxes, say at least 5 to cover
+% 99% or use case. Or perhaps some trick with storing in a \vbox and recovering
+% via some \vsplit but this becomes complicated... perhaps in future.
+%
% In passing we obtain baseline alignements across rows (only if
% \arraystretch is 1, as LaTeX's does not obey \arraystretch in "p"
% multi-line contents, only first and last line...)
@@ -410,6 +581,15 @@
\setbox\z@\hbox\bgroup\aftergroup\sphinx@@multirow\strut
}%
\def\sphinx@@multirow {%
+% MEMO: we could check status of \CT@cell@color here, but unfortunately we
+% can't know the exact height which will be covered by the cells in total
+% (it may be more than our \box\z@ dimensions). We could use an \fcolorbox
+% wrapper on \box\z@ but this will not extend precisely to the bottom rule.
+%
+% Only solution if we want to obey a raw \cellcolor, or a \columncolor, seems
+% to delay unboxing the gathered contents as part of the bottom row with
+% a suitable vertical adjustment...
+%
% The contents, which is a varwidth environment, has been captured in
% \box0 (a \hbox).
% We have with \sphinx@cellid an assigned unique id. The goal is to give
@@ -475,7 +655,592 @@
\@width\z@
\endgroup
% we need this to avoid colour panels hiding bottom parts of multirow text
- \sphinx@hack@CT
+ \spx@table@hackCT@nocolor
}%
+%%%%%%%%%%%%%%%%%%
+% --- STYLING ---
+%
+
+%
+% Support for colour in table
+%
+% Core LaTeX package (very old, part of texlive-latex-base on Debian distr.)
+% providing \columncolor, \rowcolor, \cellcolor and \arrayrulecolor.
+\RequirePackage{colortbl}
+\let\spx@original@CT@setup\CT@setup
+
+% LaTeX's \cline has **strong** deficiencies
+% ******************************************
+% We work around them via an added \sphinxfixclines{number of columns} in the
+% table mark-up, and also extra mark-up \sphinxvlinecrossing{col no} for
+% crossings not contiguous to any cline. To fix the gap at left extremity of a
+% \cline, we redefine the core LaTeX \c@line because this avoids adjoining a
+% small square with potential PDF viewer anti-aliasing issues. We waited
+% after loading colortbl because it also redefines \c@line for it to obey the
+% colour set by \arrayrulecolor.
+% MEMO: booktabs package does *not* redefine \@cline so we are safe here.
+\def\@cline#1-#2\@nil{%
+ \omit
+ \@multicnt#1%
+ \advance\@multispan\m@ne
+ \ifnum\@multicnt=\@ne\@firstofone{&\omit}\fi
+ \@multicnt#2%
+ \advance\@multicnt-#1%
+ \advance\@multispan\@ne
+ {\CT@arc@
+% start of Sphinx modification
+ \ifnum#1>\@ne\kern-\spx@arrayrulewidth\fi% fix gap at join with vertical lines
+% end of Sphinx modification
+% Comments:
+%
+% If we had the information whether the previous column ended with a | or
+% not, we could decide what to do here. Alternatively the mark-up could
+% use either original \cline or the one modified as here depending on case.
+% One wonders why LaTeX does not provide itself the alternative as a
+% complement to \cline, to use on case by case basis.
+% Here we handle both at same time via using the \spx@arrayrulewidth which
+% will be \z@ if no | at all so will induce here nothing.
+%
+% As a result Sphinx basically supports well only tables having either all
+% columns |-separated, or no | at all, as it uses \spx@arrayrrulewidth in
+% all columns (here and in multicolumn code).
+%
+% We also considered a method not modifying \c@line but it requires too
+% much extra mark-up from Python LaTeX writer and/or extra LaTeX coding.
+% back to LaTeX+colortbl code
+ \leaders\hrule\@height\arrayrulewidth\hfill}%
+ \cr
+% the last one will need to be compensated, this is job of \sphinxclines
+ \noalign{\vskip-\arrayrulewidth}%
+}
+\def\spx@table@fixvlinejoin{%
+ {\CT@arc@ % this is the color command set up by \arrayrulecolor
+ \vrule\@height\arrayrulewidth
+% side remark: LaTeX has only a single \arrayrulewidth for all kinds
+% for cell borders in table, horizontal or vertical...
+ \@depth\z@
+ \@width\spx@arrayrulewidth
+ }%
+}
+% Sphinx LaTeX writer issues one such for each vertical line separating two
+% contiguous multirow cells; i.e. those crossings which can are not already
+% taken care of by our modified at left extremity \cline.
+% One could imagine a more \...crossingS (plural) receiving a comma delimited
+% list, which would simplify the mark-up but this would complexify both the
+% Python and the LaTeX coding.
+\def\sphinxtablevlinecrossing#1{%
+ \sphinxtabledecrementrownum
+ \omit
+ \@multispan{#1}%
+ \hfill
+ \spx@table@fixvlinejoin
+ \cr
+ \noalign{\vskip-\arrayrulewidth}%
+}
+% This "fixclines" is also needed if no \sphinxcline emitted and is useful
+% even in extreme case with no \sphinxvlinecrossing either, to give correct
+% height to multirow extending across all table width assuming other rows are
+% separated generally by an \hline, so as to keep coherent line spacing.
+%
+% It is designed to work ok even if no | separators are in the table (because
+% \spx@table@fixvlinejoin uses \spx@arrayrulewidth which is \z@ in that case).
+\def\sphinxtablefixclines#1{% #1 is the number of columns of the table
+ \sphinxtabledecrementrownum
+ \omit
+ \spx@table@fixvlinejoin% unneeded if first \cline started at column 1 but does
+ % not hurt; fills small gap at left-bordered table
+ \@multispan{#1}%
+ \hfill
+ \spx@table@fixvlinejoin% fill small gap at right-bordered table
+ \cr
+ % this final one does NO \vskip-\arrayrulewidth... that's the whole point
+}
+%%%% end of \cline workarounds
+
+%
+% - passing option "table" to xcolor also loads colortbl but we needed to
+% load color or xcolor prior to the handling of the options
+%
+% - the \rowcolors command from [table]{xcolor} has various problems:
+%
+% * it is rigid and does not out-of-the-box allow a more complex scheme
+% such as colorA+colorB+colorC+colorB+colorC+colorB+colorC... suitable to
+% distinguish a header row.
+%
+% * its code does not export the used colour, an information which we may
+% need for example to colourize the rule via \arrayrulecolor in the
+% appropriate manner, for example to colourize the booktabs induced vertical
+% whitespace to avoid gaps (if one wants to).
+%
+% * incompatibility with tabulary: the output depends on parity of total
+% number of rows!
+%
+% * problems with longtable: the caption will receive a background colour
+% panel, if we do not deactivate the \rowcolors action during definition of
+% the headers and footers; this requires extra mark-up. Besides if we
+% deactivate using \hiderowcolors during header and footer formation, the
+% parity of the body rows is shifted, \rownum is even, not odd, at first body
+% row. And setting \rownum at start of first body row is too late for
+% influencing the colour.
+%
+% * it has a global impact and must be reset at each table. We can not
+% issue it only once and it provides no public interface (without @) to
+% cancel its effect conveniently (\hiderowcolors can only be used from
+% *inside* a table.)
+%
+% * its core mechanism which increments the row count is triggered
+% if a \cline is encountered... so this offsets the alternating colours...
+% ... or not if there are two \cline's in the row...
+% (as we will use same mechanism we have to correct this increment).
+%
+% So we need our own code.
+
+% Provide \rownum and rownum LaTeX counter (code copied from colortbl v1.0f)
+\ltx@ifundefined{rownum}{%
+ \ltx@ifundefined{c@rownum}%
+ {\newcount\rownum\let\c@rownum\rownum}%
+ {\let\rownum\c@rownum}%
+ }%
+{\let\c@rownum\rownum}
+\providecommand\therownum{\arabic{rownum}}
+
+% extra overhang for color panels to avoid visual artifacts in pdf viewers
+% (particularly if borderless)
+\def\sphinxcolorpanelextraoverhang{0.1pt}
+\def\spx@table@leftcolorpanelextra {\sphinxcolorpanelextraoverhang}
+\def\spx@table@rightcolorpanelextra{\sphinxcolorpanelextraoverhang}
+% the macro to which \CT@row@color will be set for coloured rows, serves both
+% in header and body, the colours must have been defined at time of use
+\def\spx@table@CT@row@color{\ifspx@table@inmergedcell
+ \CT@color{sphinxTableMergeColor}%
+ \else
+ \CT@color{sphinxTableRowColor}%
+ \fi
+ \@tempdimb\dimexpr\col@sep+\spx@table@leftcolorpanelextra\relax
+ \@tempdimc\dimexpr\col@sep+\spx@table@rightcolorpanelextra\relax
+ }%
+% used by itself this will influence a single row if \CT@everycr is the
+% colortbl one, to influences all rows the \CT@everycr must be modified (see
+% below)
+\def\sphinxrowcolorON {\global\let\CT@row@color\spx@table@CT@row@color}%
+% this one turns off row colours until the next \sphinxrowcolorON
+\def\sphinxrowcolorOFF{\global\let\CT@row@color\relax}%
+% this one inhibits the row colour in one cell only (can be used as
+% >{\sphinxnorowcolor} for turning off row colours in a given column)
+\def\sphinxnorowcolor{\spx@table@hackCT@norowcolor}%
+
+% \sphinxtoprule (or rather \sphinxtabletoprulehook) will be modified by
+% the colorrows class to execute this one:
+\def\spx@table@@toprule@rowcolorON{%
+ \noalign{%
+ % Because of tabulary 2-pass system, the colour set-up at end of table
+ % would contaminate the header colours at start of table, so must reset
+ % them here. We want all header rows to obey same colours, so we don't
+ % use original \CT@everycr which sets \CT@row@color to \relax.
+ \global\CT@everycr{\the\everycr}%
+ \global\sphinxcolorlet{sphinxTableRowColor}{sphinxTableRowColorHeader}%
+ \global\sphinxcolorlet{sphinxTableMergeColor}{\sphinxTableMergeColorHeader}%
+ \sphinxrowcolorON
+ }%
+}%
+
+% \sphinxtableatstartofbodyhook will be modified by colorrows class to
+% execute this one; it starts the alternating colours and triggers increment
+% or \rownum count at each new row (the xcolor base method for \rowcolors)
+\def\spx@table@@startbodycolorrows{%
+ \noalign{%
+ \global\CT@everycr{% Nota Bene: in a longtable with \hline the \everycr is
+ % done two extra times! but 2 is even, so this is ok
+ \noalign{\global\advance\rownum\@ne % the xcolor \rowcolors base trick
+% MEMO: colortbl \CT@row@color is expanded *after* the cell contents have been
+% gathered and measured, so it can't be used to expose e.g. the colour to the
+% cell contents macro code. Of course if it is known how the colour is chosen
+% the procedure could be done from inside the cell. Simpler to expose the colour
+% in a public name sphinxTableRowColor at start of the row in this \noalign.
+ \sphinxSwitchCaseRowColor\rownum
+ }%
+ \the\everycr
+ }%
+ \global\rownum\@ne % is done from inside table so ok with tabulary two passes
+ \sphinxSwitchCaseRowColor\rownum % set up color for the first body row
+ \sphinxrowcolorON % has been done from \sphinxtoprule location but let's do
+ % it again in case \sphinxtabletoprulehook has been used
+ % to inhibit colours in the header rows
+ }% end of noalign contents
+}
+% set the colours according to row parity; a priori #1 is \rownum, but
+% the macro has been designed to be usable in user level added code
+\def\sphinxSwitchCaseRowColor#1{%
+ \ifodd#1\relax
+ \global\sphinxcolorlet{sphinxTableRowColor}{sphinxTableRowColorOdd}%
+ \global\sphinxcolorlet{sphinxTableMergeColor}{\sphinxTableMergeColorOdd}%
+ \else
+ \global\sphinxcolorlet{sphinxTableRowColor}{sphinxTableRowColorEven}%
+ \global\sphinxcolorlet{sphinxTableMergeColor}{\sphinxTableMergeColorEven}%
+ \fi
+}
+
+% each \cline or \cmidrule (booktabs) consumes one \cr, offsetting the \rownum
+% parity; so this macro serves to compensate and must be added to each such
+% \cline or \cmidrule (see below)
+\def\spx@table@@decrementrownum{\noalign{\global\advance\rownum\m@ne}}
+\let\sphinxtabledecrementrownum\@empty
+
+% \sphinxtableafterendhook will be modified by colorrows class to execute
+% this after the table
+\def\spx@table@resetcolortbl{%
+ \sphinxrowcolorOFF
+ \spx@table@reset@CTeverycr
+% this last bit is done in order for the \sphinxbottomrule from the "foot"
+% longtable template to be able to use same code as the \sphinxbottomrule
+% at end of table body; see \sphinxbooktabsspecialbottomrule code
+ \global\rownum\z@
+}
+\def\spx@table@reset@CTeverycr{%
+% we should probably be more cautious and not hard-code here the colortbl
+% set-up; so the macro is defined without @ to fac
+ \global\CT@everycr{\noalign{\global\let\CT@row@color\relax}\the\everycr}%
+}
+
+% At last the style macros \sphinxthistablewithstandardstyle etc...
+
+% They are executed before the table environments in a scope limiting
+% wrapper "savenotes" environment.
+%
+% 0) colour support is enacted via adding code to three hooks:
+% - \sphinxtabletoprulehook (implicit from \sphinxtoprule expansion)
+% - \sphinxtableatstartofbodyhook (explicit from table templates)
+% - \sphinxtableafterendhook (explicit from table templates)
+% additionally special adjustment must be made in \sphinxcline
+%
+\def\sphinxtoprule{\spx@toprule\sphinxtabletoprulehook}
+% \spx@toprule is what is defined by the standard, booktabs and borderless
+% styles.
+% The colorrows class will prepend \spx@table@toprule@rowcolorON into
+% \sphinxtabletoprulehook which a priori is \@empty but can contain user added
+% extra code, and is executed after \spx@toprule.
+\let\sphinxtabletoprulehook \@empty
+\let\sphinxtableatstartofbodyhook\@empty
+\let\sphinxtableafterendhook \@empty
+%
+% 1) we manage these three hooks in a way allowing a custom user extra wrapper
+% environment from a container class to use them as entry point for some
+% custom code. The container code is done first, prior to table templates.
+% So, the style macros will *prepend* the needed color-code to the existing
+% custom user code, so the custom user code can override them. The custom
+% user code should not redefine any of the 3 \sphinxtable...hook macros via a
+% \global\def, but their contents can use \gdef. In fact they probably need
+% to for the first two hooks which are executed from inside the table and
+% a priori need their code to be in a \noalign which limits scope.
+%
+% 2) the table templates and LaTeX writer code make it so that only
+% one of either
+% \sphinxthistablewithcolorrowsstyle,
+% or \sphinxthistablewithnocolorrowsstyle
+% will be inserted explicitly depending on local :class: for table.
+% The global 'colorrows' style in latex_table_style translates at bottom
+% of this file into code for inserting \sphinxthistablewithcolorrowsstyle
+% at end of \sphinxthistablewithglobalstyle. So it is impossible
+% to have first \sphinxthistablewithnocolorrowsstyle, then
+% \sphinxthistablewithcolorrowsstyle. Nevertheless we have written
+% the code so that in this case colorrows would indeed activate (except
+% if it was already executed before as it self-annihilates).
+
+% standard style
+\def\sphinxthistablewithstandardstyle{%
+ % Those two are produced by the latex writer
+ \def\sphinxhline {\hline}%
+ % \sphinxtabledecrementrownum is a no-op which is redefined by colorrows
+ % to correct the \rownum increment induced by \cline in colorrows regime
+ \def\sphinxcline {\sphinxtabledecrementrownum\cline}%
+ % LaTeX's \cline needs fixing
+ \let\sphinxvlinecrossing\sphinxtablevlinecrossing
+ \let\sphinxfixclines \sphinxtablefixclines
+ % Those three are inserted by the table templates
+ \def\spx@toprule {\hline}%
+ \def\sphinxmidrule {\hline}%
+ \def\sphinxbottomrule {\hline}%
+ % Do not tamper with this internal
+ \def\spx@arrayrulewidth{\arrayrulewidth}%
+}
+
+% booktabs style
+% The \@xcmidrule patch below will do beyond its main stuff
+% \sphinxadjustcmidrulebelowsep
+% Indeed the poor booktabs spacing with \cmidrule (if \sphinxbooktabscmidrule
+% defined below is overwritten to use it) is quite awful. Do
+% \let\sphinxadjustcmidrulebelowsep\empty
+% if you prefer booktabs defaults.
+\def\sphinxadjustcmidrulebelowsep{\belowrulesep=\aboverulesep}
+\AtBeginDocument{% patch booktabs to avoid extra vertical space from
+ % consecutive \sphinxcline, if defined to use \cmidrule
+ \ifdefined\@xcmidrule
+ \let\spx@original@@xcmidrule\@xcmidrule
+ \def\@xcmidrule{\sphinxadjustcmidrulebelowsep
+ % if we don't do that, two \sphinxcline in the same row
+ % will cause the second short rule to be shifted down
+ \ifx\@tempa\sphinxcline\let\@tempa\cmidrule\fi
+ \spx@original@@xcmidrule}%
+ \fi
+}
+% wrappers to allow customization, e.g. via a container class
+% the top, mid, bottom definitions are in fact overwritten (later, below)
+% byt more complex ones needed to handle booktabs+colorrows context
+\def\sphinxbooktabstoprule {\toprule}
+\def\sphinxbooktabsmidrule {\midrule}
+\def\sphinxbooktabsbottomrule{\bottomrule}
+%
+\let\sphinxbooktabscmidrule \@gobble % i.e. draw no short rules at all!
+% You can redefine this to use \cmidrule with various options, such
+% as \cmidrule(lr), but:
+% Attention, if you want this to use \cmidrule (or \cline) you must,
+% if the table uses row colours,
+% also include the \sphinxtabledecrementrownum token like e.g. this
+% \def\sphinxbooktabscmidrule{\sphinxtabledecrementrownum\cmidrule(lr)}
+% and it must be first due to internals of the \cmidrule usage of \futurelet.
+
+\def\sphinxthistablewithbooktabsstyle{%
+ \let\sphinxhline\@empty % there is no wrapper macro here so if you want to change that
+ % you will have to redefine \sphinxthistablewithbooktabsstyle
+ \def\sphinxcline {\sphinxbooktabscmidrule}% defaults to give \@gobble
+ \let\sphinxvlinecrossing\@gobble % no | in a booktabs-style table !
+ \let\sphinxfixclines \@gobble % should not be used with booktabs + \cmidrule
+ \def\spx@toprule {\sphinxbooktabstoprule}%
+ \def\sphinxmidrule {\sphinxbooktabsmidrule}%
+ \def\sphinxbottomrule{\sphinxbooktabsbottomrule}%
+ \def\spx@arrayrulewidth{\z@}%
+}
+\AtBeginDocument{\@ifpackageloaded{booktabs}%
+ {}%
+ {\def\sphinxthistablewithbooktabsstyle{%
+ \PackageWarning{sphinx}{%
+Add \string\usepackage{booktabs} to the preamble to allow\MessageBreak
+local use of booktabs table style}%
+ \sphinxbuildwarning{booktabs}%
+ \sphinxthistablewithstandardstyle
+ }}%
+}%
+
+% borderless style
+\def\sphinxthistablewithborderlessstyle{%
+ \let\sphinxhline \@empty
+ \let\sphinxcline \@gobble
+ \let\sphinxvlinecrossing\@gobble
+ \let\sphinxfixclines \@gobble
+ \let\spx@toprule \@empty
+ \let\sphinxmidrule \@empty
+ \let\sphinxbottomrule \@empty
+ \def\spx@arrayrulewidth{\z@}%
+}%
+
+% colorrows style
+%
+\let\sphinxifthistablewithcolorrowsTF\@secondoftwo
+\def\sphinxthistablewithcolorrowsstyle{%
+ \let\sphinxifthistablewithcolorrowsTF\@firstoftwo
+% this is defined to auto-silence itself (in the surrounding scope-limiting
+% environment) after one execution ("colorrows" can never follow "nocolorrows")
+ \let\sphinxthistablewithcolorrowsstyle\@empty
+%
+ \let\spx@table@toprule@rowcolorON \spx@table@@toprule@rowcolorON
+ \let\spx@table@startbodycolorrows \spx@table@@startbodycolorrows
+ \let\sphinxtabledecrementrownum \spx@table@@decrementrownum
+% Is it the best choice to "prepend" to existing code there?
+ \spx@prepend\spx@table@toprule@rowcolorON\to\sphinxtabletoprulehook
+ \spx@prepend\spx@table@startbodycolorrows\to\sphinxtableatstartofbodyhook
+%
+% this one is not set to \@empty by nocolorrows, because it looks harmless
+% to execute it always, as it simply resets to standard colortbl state after
+% the table; so we don't need an @@ version for this one
+ \spx@prepend\spx@table@resetcolortbl\to\sphinxtableafterendhook
+}
+\def\spx@prepend#1\to#2{% attention about using this only with #2 "storage macro"
+ \toks@{#1}%
+ \toks@\expandafter\expandafter\expandafter{\expandafter\the\expandafter\toks@#2}%
+ \edef#2{\the\toks@}%
+}%
+
+\def\sphinxthistablewithnocolorrowsstyle{%
+ \let\sphinxifthistablewithcolorrowsTF\@secondoftwo
+% rather than trying to remove the code added by 'colorrows' style, we
+% simply make it no-op, without even checking if really it was activated.
+ \let\spx@table@toprule@rowcolorON\@empty
+ \let\spx@table@startbodycolorrows\@empty
+ \let\sphinxtabledecrementrownum \@empty
+% we don't worry about \sphinxtableafterendhook as the \spx@table@resetcolortbl
+% done at end can not do harm; and we could also have not bothered with the
+% \sphinxtabledecrementrownum as its \rownum decrement, if active, is harmless
+% in non-colorrows context
+}
+
+% (not so easy) implementation of the booktabscolorgaps option. This option
+% defaults to true and is not officially documented, as already colorrows is
+% only opt-in, so it is there only as a "turn-off" switch, but if nobody
+% complains in next few months, it will probably be removed altogether at
+% 6.0.0. The reason it exists is because of longtable aspeces described
+% below.
+%
+% As it is used via \sphinxsetup booktabscolorgaps status is not known here
+% and may change locally. So it must be implemented via delayed or
+% conditional code.
+%
+% We do not know the order of execution of \sphinxthistablewithbooktabsstyle
+% versus \sphinxthistablewithcolorrows: if booktabs is global option it
+% will be executed first; but if colorrows is global option and not booktabs
+% then colorrows will be executed first via \sphinxthistablewithglobalstyle
+%
+% Modifying things from locations such as \sphinxtabletoprulehook which are
+% executed within the table is not convenient as it must use \global
+% but then we would have to undo this after the table.
+%
+% So what we do is to prepare booktabs specific macros to incorporate
+% a conditional to check the colorrows status. We must each time check
+% both if colorrows is activated and if colorgaps is. We do this via
+% macros without @ so they can be used easily in customization code.
+% When and if booktabscolorgaps option is removed, we can then replace
+% \sphinxifbooktabswithcolorgapsTF by \sphinxifthistablewithcolorrowsTF
+\def\sphinxifbooktabswithcolorgapsTF{%
+ \if1\ifspx@opt@booktabscolorgaps
+ \sphinxifthistablewithcolorrowsTF{1}{0}%
+ \else0\fi
+ \expandafter\@firstoftwo
+ \else\expandafter\@secondoftwo
+ \fi
+}
+% as this is done without "@" it can be relatively easily be overwritten
+% by user in customization code
+\def\sphinxbooktabstoprule{%
+ \sphinxifbooktabswithcolorgapsTF
+ {\sphinxbooktabsspecialtoprule}%
+ {\toprule}%
+}%
+\def\sphinxbooktabscolorgapsoverhang{0.1pt}% avoid pixel/rounding effects
+% auxiliary fork
+\long\def\spx@table@crazyfork
+ #1\endfirsthead\endhead\sphinxtableatstartofbodyhook#2#3\@nil{#2}
+% we fetch the next token to check if there is a header or not
+% this is a bit fragile as it relies on the table templates
+% and it assumes this token #1 is never braced...
+% let's make this \long in case #1 is \par (should not be)
+\long\def\sphinxbooktabsspecialtoprule\sphinxtabletoprulehook#1{%
+ \specialrule{\heavyrulewidth}{\abovetopsep}{\z@}%
+ % this macro contains colour init code (and defines sphinxTableRowColor)
+ \sphinxtabletoprulehook
+ % unfortunately colortbl provides no way to save/restore the
+ % \arrayrulecolor status, we have to code it ourselves
+ \noalign{\global\let\spx@@saved@CT@arc@\CT@arc@
+% \@declaredcolor is not \long. Although #1 can probably never be \par with
+% our templates, let's be cautious and not use the creazyfork inside the \color
+ \spx@table@crazyfork
+% this crazy code checks if #1 is one of \endfirsthead, \endhead or
+% \sphinxtableatstartofbodyhook, as criterion for table with no header
+ #1\endhead\sphinxtableatstartofbodyhook\@secondoftwo
+ \endfirsthead#1\sphinxtableatstartofbodyhook\@secondoftwo
+ \endfirsthead\endhead#1\@secondoftwo
+ \endfirsthead\endhead\sphinxtableatstartofbodyhook\@firstoftwo
+ \@nil
+ {\gdef\CT@arc@{\color{sphinxTableRowColor}}}%
+ {\gdef\CT@arc@{\color{sphinxTableRowColorOdd}}}%
+ }% end of \noalign
+ % \specialrule uses \noalign itself
+ \specialrule{\dimexpr\belowrulesep+\sphinxbooktabscolorgapsoverhang\relax}%
+ {\z@}{-\sphinxbooktabscolorgapsoverhang}%
+ \noalign{\global\let\CT@arc@\spx@@saved@CT@arc@}%
+ #1% let's not forget to re-insert this #1 in token stream
+ % fortunately longtable's \endfirsthead/\endhead are not delimiters but
+ % are really tokens awaiting expansion...
+}%
+\def\sphinxbooktabsmidrule{%
+ \sphinxifbooktabswithcolorgapsTF
+ {\sphinxbooktabsspecialmidrule}%
+ {\midrule}%
+}%
+\def\sphinxbooktabsspecialmidrule{%
+ \noalign{\global\let\spx@@saved@CT@arc@\CT@arc@
+ \gdef\CT@arc@{\color{sphinxTableRowColor}}% this is RowColorHeader
+ }%
+ \specialrule{\dimexpr\aboverulesep+\sphinxbooktabscolorgapsoverhang\relax\relax}%
+ {-\sphinxbooktabscolorgapsoverhang}{\z@}%
+ \noalign{\global\let\CT@arc@\spx@@saved@CT@arc@}%
+ \specialrule{\lightrulewidth}{\z@}{\z@}%
+ \noalign{\gdef\CT@arc@{\color{sphinxTableRowColorOdd}}}%
+ \specialrule{\dimexpr\belowrulesep+\sphinxbooktabscolorgapsoverhang\relax\relax}%
+ {\z@}{-\sphinxbooktabscolorgapsoverhang}%
+ \noalign{\global\let\CT@arc@\spx@@saved@CT@arc@}%
+}%
+\def\sphinxbooktabsbottomrule{%
+ \sphinxifbooktabswithcolorgapsTF
+ {\sphinxbooktabsspecialbottomrule}%
+ {\bottomrule}%
+}%
+% The colour here is already updated because of the \\ before so we must
+% execute again the colour selection code, but this is not too complicated.
+% What is annoying though is that \sphinxbottomrule in the longtable context
+% appears both in the "foot" part and after the last body row. For the first
+% occurrence the \rownum could be arbitrary if it had not been reset by each
+% table using it via the \sphinxtableafterendhook (see above). This avoids
+% having to modify the longtable template. But as \rownum is thus 0 in the
+% "foot", the \sphinxSwitchCaseRowColor has to know how to handle negative
+% inputs (in fact the -1 value), the Sphinx definition has no issue with that
+% but any redefinition must be aware of this constraint.
+\def\sphinxbooktabsspecialbottomrule{%
+ \noalign{\global\let\spx@@saved@CT@arc@\CT@arc@
+ \sphinxSwitchCaseRowColor{\numexpr\rownum-\@ne\relax}%
+ \gdef\CT@arc@{\color{sphinxTableRowColor}}%
+ }%
+ \specialrule{\dimexpr\aboverulesep+\sphinxbooktabscolorgapsoverhang\relax}%
+ {-\sphinxbooktabscolorgapsoverhang}{\z@}%
+ \noalign{\global\let\CT@arc@\spx@@saved@CT@arc@}%
+ \specialrule{\heavyrulewidth}{\z@}{\belowbottomsep}%
+}%
+%
+% MEMO: with longtable \sphinxtoprule, \sphinxmidrule and \sphinxbottomrule
+% are evaluated at time of constructing the headers and footers as boxes
+% (already typeset material and expanded macros; \sphinxbottomrule is also
+% evaluated at very end of table body, i.e. "normally"). So the used colour
+% to fill the booktabs gaps is decided during the headers and footers
+% construction by longtable. Actually they are expanded twice: in firsthead
+% then in head, respectively in foot and lastfoot. But in current design the
+% header row colours are fixed, not alternating, so there is at least no
+% coherence issue there.
+
+% The \spx@arrayrulewidth is used for some complex matters of merged
+% cells size computations.
+% tabularcolumns argument will override any global or local style and
+% trigger the appropriate adjustment of \spx@arrayrulewidth.
+% Notice that this will be bad if the table uses booktabs style
+% but anyhow table with booktabs should not use any | separator.
+\def\sphinxthistablewithvlinesstyle{%
+ \def\spx@arrayrulewidth{\arrayrulewidth}%
+ \let\sphinxvlinecrossing\sphinxtablevlinecrossing
+ \let\sphinxfixclines \sphinxtablefixclines
+}%
+\def\sphinxthistablewithnovlinesstyle{%
+ \def\spx@arrayrulewidth{\z@}%
+ \let\sphinxvlinecrossing\@gobble
+ % let's not bother to modify \sphinxfixclines, it works fine and is
+ % useful in standard style + no vline (only hlines and clines);
+ % besides, only one of vline or novline style macro is executed
+}%
+
+% default is the standard style
+\def\sphinxthistablewithglobalstyle{\sphinxthistablewithstandardstyle}
+
+\ifspx@opt@booktabs
+ \RequirePackage{booktabs}
+ \def\sphinxthistablewithglobalstyle{\sphinxthistablewithbooktabsstyle}
+\fi
+\ifspx@opt@borderless
+ \def\sphinxthistablewithglobalstyle{\sphinxthistablewithborderlessstyle}
+\fi
+% colorrows appends to the current globalstyle (standard, booktabs, or borderless)
+\ifspx@opt@colorrows % let the globalstyle trigger the colorrows style on top of it
+ \expandafter\def\expandafter\sphinxthistablewithglobalstyle\expandafter
+ {\sphinxthistablewithglobalstyle
+ \sphinxthistablewithcolorrowsstyle
+ }
+\fi
+
+
\endinput
diff --git a/sphinx/texinputs/sphinxpackagefootnote.sty b/sphinx/texinputs/sphinxpackagefootnote.sty
index 6a7884f83..39d8cfacf 100644
--- a/sphinx/texinputs/sphinxpackagefootnote.sty
+++ b/sphinx/texinputs/sphinxpackagefootnote.sty
@@ -1,6 +1,6 @@
\NeedsTeXFormat{LaTeX2e}
\ProvidesPackage{sphinxpackagefootnote}%
- [2022/02/12 v4.5.0 Sphinx custom footnotehyper package (Sphinx team)]
+ [2022/08/15 v5.2.0 Sphinx custom footnotehyper package (Sphinx team)]
%%
%% Package: sphinxpackagefootnote
%% Version: based on footnotehyper.sty 2021/02/04 v1.1d
@@ -338,6 +338,7 @@
}%
% slight reformulation for Sphinx
\def\FNH@bad@makefntext@alert{%
+ \sphinxbuildwarning{badfootnotes}%
\PackageWarningNoLine{sphinxpackagefootnote}%
{Footnotes will be sub-optimal, sorry. This is due to the document class or^^J
some package modifying macro \string\@makefntext.^^J
diff --git a/sphinx/writers/latex.py b/sphinx/writers/latex.py
index fbff3d906..0e2250c17 100644
--- a/sphinx/writers/latex.py
+++ b/sphinx/writers/latex.py
@@ -97,8 +97,24 @@ class Table:
self.body: List[str] = []
self.align = node.get('align', 'default')
self.classes: List[str] = node.get('classes', [])
+ self.styles: List[str] = []
+ if 'standard' in self.classes:
+ self.styles.append('standard')
+ elif 'borderless' in self.classes:
+ self.styles.append('borderless')
+ elif 'booktabs' in self.classes:
+ self.styles.append('booktabs')
+ if 'nocolorrows' in self.classes:
+ self.styles.append('nocolorrows')
+ elif 'colorrows' in self.classes:
+ self.styles.append('colorrows')
self.colcount = 0
self.colspec: str = None
+ self.colsep: str = None
+ if 'booktabs' in self.styles or 'borderless' in self.styles:
+ self.colsep = ''
+ elif 'standard' in self.styles:
+ self.colsep = '|'
self.colwidths: List[int] = []
self.has_problematic = False
self.has_oldproblematic = False
@@ -143,23 +159,30 @@ class Table:
This is what LaTeX calls the 'preamble argument' of the used table environment.
- .. note:: the ``\\X`` and ``T`` column type specifiers are defined in ``sphinx.sty``.
+ .. note::
+
+ The ``\\X`` and ``T`` column type specifiers are defined in
+ ``sphinxlatextables.sty``.
"""
if self.colspec:
return self.colspec
- elif self.colwidths and 'colwidths-given' in self.classes:
+
+ _colsep = self.colsep
+ if self.colwidths and 'colwidths-given' in self.classes:
total = sum(self.colwidths)
colspecs = [r'\X{%d}{%d}' % (width, total) for width in self.colwidths]
- return '{|%s|}' % '|'.join(colspecs) + CR
+ return '{%s%s%s}' % (_colsep, _colsep.join(colspecs), _colsep) + CR
elif self.has_problematic:
- return r'{|*{%d}{\X{1}{%d}|}}' % (self.colcount, self.colcount) + CR
+ return r'{%s*{%d}{\X{1}{%d}%s}}' % (_colsep, self.colcount,
+ self.colcount, _colsep) + CR
elif self.get_table_type() == 'tabulary':
# sphinx.sty sets T to be J by default.
- return '{|' + ('T|' * self.colcount) + '}' + CR
+ return '{' + _colsep + (('T' + _colsep) * self.colcount) + '}' + CR
elif self.has_oldproblematic:
- return r'{|*{%d}{\X{1}{%d}|}}' % (self.colcount, self.colcount) + CR
+ return r'{%s*{%d}{\X{1}{%d}%s}}' % (_colsep, self.colcount,
+ self.colcount, _colsep) + CR
else:
- return '{|' + ('l|' * self.colcount) + '}' + CR
+ return '{' + _colsep + (('l' + _colsep) * self.colcount) + '}' + CR
def add_cell(self, height: int, width: int) -> None:
"""Adds a new cell to a table.
@@ -857,8 +880,19 @@ class LaTeXTranslator(SphinxTranslator):
(self.curfilestack[-1], node.line or ''))
self.tables.append(Table(node))
+ if self.table.colsep is None:
+ self.table.colsep = '' if (
+ 'booktabs' in self.builder.config.latex_table_style or
+ 'borderless' in self.builder.config.latex_table_style
+ ) else '|'
if self.next_table_colspec:
self.table.colspec = '{%s}' % self.next_table_colspec + CR
+ if '|' in self.table.colspec:
+ self.table.styles.append('vlines')
+ self.table.colsep = '|'
+ else:
+ self.table.styles.append('novlines')
+ self.table.colsep = ''
if 'colwidths-given' in node.get('classes', []):
logger.info(__('both tabularcolumns and :widths: option are given. '
':widths: is ignored.'), location=node)
@@ -896,6 +930,8 @@ class LaTeXTranslator(SphinxTranslator):
self.pushbody(self.table.header)
def depart_thead(self, node: Element) -> None:
+ if self.body and self.body[-1] == r'\sphinxhline':
+ self.body.pop()
self.popbody()
def visit_tbody(self, node: Element) -> None:
@@ -903,11 +939,13 @@ class LaTeXTranslator(SphinxTranslator):
self.pushbody(self.table.body)
def depart_tbody(self, node: Element) -> None:
+ if self.body and self.body[-1] == r'\sphinxhline':
+ self.body.pop()
self.popbody()
def visit_row(self, node: Element) -> None:
self.table.col = 0
-
+ _colsep = self.table.colsep
# fill columns if the row starts with the bottom of multirow cell
while True:
cell = self.table.cell(self.table.row, self.table.col)
@@ -921,24 +959,35 @@ class LaTeXTranslator(SphinxTranslator):
# insert suitable strut for equalizing row heights in given multirow
self.body.append(r'\sphinxtablestrut{%d}' % cell.cell_id)
else: # use \multicolumn for wide multirow cell
- self.body.append(r'\multicolumn{%d}{|l|}{\sphinxtablestrut{%d}}' %
- (cell.width, cell.cell_id))
+ self.body.append(r'\multicolumn{%d}{%sl%s}{\sphinxtablestrut{%d}}' %
+ (cell.width, _colsep, _colsep, cell.cell_id))
def depart_row(self, node: Element) -> None:
self.body.append(r'\\' + CR)
cells = [self.table.cell(self.table.row, i) for i in range(self.table.colcount)]
underlined = [cell.row + cell.height == self.table.row + 1 for cell in cells]
if all(underlined):
- self.body.append(r'\hline')
+ self.body.append(r'\sphinxhline')
else:
i = 0
underlined.extend([False]) # sentinel
- while i < len(underlined):
- if underlined[i] is True:
- j = underlined[i:].index(False)
- self.body.append(r'\cline{%d-%d}' % (i + 1, i + j))
- i += j
+ if underlined[0] is False:
+ i = 1
+ while i < self.table.colcount and underlined[i] is False:
+ if cells[i - 1].cell_id != cells[i].cell_id:
+ self.body.append(r'\sphinxvlinecrossing{%d}' % i)
+ i += 1
+ while i < self.table.colcount:
+ # each time here underlined[i] is True
+ j = underlined[i:].index(False)
+ self.body.append(r'\sphinxcline{%d-%d}' % (i + 1, i + j))
+ i += j
i += 1
+ while i < self.table.colcount and underlined[i] is False:
+ if cells[i - 1].cell_id != cells[i].cell_id:
+ self.body.append(r'\sphinxvlinecrossing{%d}' % i)
+ i += 1
+ self.body.append(r'\sphinxfixclines{%d}' % self.table.colcount)
self.table.row += 1
def visit_entry(self, node: Element) -> None:
@@ -947,12 +996,14 @@ class LaTeXTranslator(SphinxTranslator):
self.table.add_cell(node.get('morerows', 0) + 1, node.get('morecols', 0) + 1)
cell = self.table.cell()
context = ''
+ _colsep = self.table.colsep
if cell.width > 1:
if self.config.latex_use_latex_multicolumn:
if self.table.col == 0:
- self.body.append(r'\multicolumn{%d}{|l|}{%%' % cell.width + CR)
+ self.body.append(r'\multicolumn{%d}{%sl%s}{%%' %
+ (cell.width, _colsep, _colsep) + CR)
else:
- self.body.append(r'\multicolumn{%d}{l|}{%%' % cell.width + CR)
+ self.body.append(r'\multicolumn{%d}{l%s}{%%' % (cell.width, _colsep) + CR)
context = '}%' + CR
else:
self.body.append(r'\sphinxstartmulticolumn{%d}%%' % cell.width + CR)
@@ -992,6 +1043,7 @@ class LaTeXTranslator(SphinxTranslator):
cell = self.table.cell()
self.table.col += cell.width
+ _colsep = self.table.colsep
# fill columns if next ones are a bottom of wide-multirow cell
while True:
@@ -999,16 +1051,16 @@ class LaTeXTranslator(SphinxTranslator):
if nextcell is None: # not a bottom of multirow cell
break
else: # a bottom part of multirow cell
- self.table.col += nextcell.width
self.body.append('&')
if nextcell.width == 1:
# insert suitable strut for equalizing row heights in multirow
# they also serve to clear colour panels which would hide the text
self.body.append(r'\sphinxtablestrut{%d}' % nextcell.cell_id)
else:
- # use \multicolumn for wide multirow cell
- self.body.append(r'\multicolumn{%d}{l|}{\sphinxtablestrut{%d}}' %
- (nextcell.width, nextcell.cell_id))
+ # use \multicolumn for not first row of wide multirow cell
+ self.body.append(r'\multicolumn{%d}{l%s}{\sphinxtablestrut{%d}}' %
+ (nextcell.width, _colsep, nextcell.cell_id))
+ self.table.col += nextcell.width
def visit_acks(self, node: Element) -> None:
# this is a list in the source, but should be rendered as a
diff --git a/tests/roots/test-latex-table/complex.rst b/tests/roots/test-latex-table/complex.rst
index fa84f8266..d648ff194 100644
--- a/tests/roots/test-latex-table/complex.rst
+++ b/tests/roots/test-latex-table/complex.rst
@@ -4,6 +4,27 @@ complex tables
grid table
----------
+.. rst-class:: nocolorrows
+
++---------+---------+---------+
+| header1 | header2 | header3 |
++=========+=========+=========+
+| cell1-1 | cell1-2 | cell1-3 |
++---------+ +---------+
+| cell2-1 | | cell2-3 |
++ +---------+---------+
+| | cell3-2-par1 |
++---------+ |
+| cell4-1 | cell3-2-par2 |
++---------+---------+---------+
+| cell5-1 |
++---------+---------+---------+
+
+grid table with tabularcolumns having no vline
+----------------------------------------------
+
+.. tabularcolumns:: TTT
+
+---------+---------+---------+
| header1 | header2 | header3 |
+=========+=========+=========+
@@ -26,6 +47,8 @@ table having ...
* consecutive multirow at top of row (1-1 and 1-2)
* consecutive multirow at end of row (1-4 and 1-5)
+.. rst-class:: standard
+
+-----------+-----------+-----------+-----------+-----------+
| | | cell1-3 | | |
| | +-----------+ | cell1-5 |
diff --git a/tests/roots/test-latex-table/expects/complex_spanning_cell.tex b/tests/roots/test-latex-table/expects/complex_spanning_cell.tex
index 966f39a95..d2d618942 100644
--- a/tests/roots/test-latex-table/expects/complex_spanning_cell.tex
+++ b/tests/roots/test-latex-table/expects/complex_spanning_cell.tex
@@ -14,10 +14,12 @@ consecutive multirow at end of row (1\sphinxhyphen{}4 and 1\sphinxhyphen{}5)
\begin{savenotes}\sphinxattablestart
+\sphinxthistablewithglobalstyle
+\sphinxthistablewithstandardstyle
\centering
\begin{tabulary}{\linewidth}[t]{|T|T|T|T|T|}
-\hline
-\sphinxmultirow{3}{1}{%
+\sphinxtoprule
+\sphinxtableatstartofbodyhook\sphinxmultirow{3}{1}{%
\begin{varwidth}[t]{\sphinxcolwidth{1}{5}}
\sphinxAtStartPar
cell1\sphinxhyphen{}1
@@ -49,7 +51,7 @@ cell1\sphinxhyphen{}5
\vskip-\baselineskip\vbox{\hbox{\strut}}\end{varwidth}%
}%
\\
-\cline{3-3}\sphinxtablestrut{1}&\sphinxtablestrut{2}&\sphinxmultirow{2}{6}{%
+\sphinxvlinecrossing{1}\sphinxcline{3-3}\sphinxvlinecrossing{4}\sphinxfixclines{5}\sphinxtablestrut{1}&\sphinxtablestrut{2}&\sphinxmultirow{2}{6}{%
\begin{varwidth}[t]{\sphinxcolwidth{1}{5}}
\sphinxAtStartPar
cell2\sphinxhyphen{}3
@@ -57,11 +59,11 @@ cell2\sphinxhyphen{}3
\vskip-\baselineskip\vbox{\hbox{\strut}}\end{varwidth}%
}%
&\sphinxtablestrut{4}&\sphinxtablestrut{5}\\
-\cline{5-5}\sphinxtablestrut{1}&\sphinxtablestrut{2}&\sphinxtablestrut{6}&\sphinxtablestrut{4}&
+\sphinxvlinecrossing{1}\sphinxvlinecrossing{2}\sphinxvlinecrossing{3}\sphinxcline{5-5}\sphinxfixclines{5}\sphinxtablestrut{1}&\sphinxtablestrut{2}&\sphinxtablestrut{6}&\sphinxtablestrut{4}&
\sphinxAtStartPar
cell3\sphinxhyphen{}5
\\
-\hline
+\sphinxbottomrule
\end{tabulary}
-\par
+\sphinxtableafterendhook\par
\sphinxattableend\end{savenotes}
diff --git a/tests/roots/test-latex-table/expects/gridtable.tex b/tests/roots/test-latex-table/expects/gridtable.tex
index a71c9e202..407abe7f2 100644
--- a/tests/roots/test-latex-table/expects/gridtable.tex
+++ b/tests/roots/test-latex-table/expects/gridtable.tex
@@ -1,9 +1,11 @@
\label{\detokenize{complex:grid-table}}
\begin{savenotes}\sphinxattablestart
+\sphinxthistablewithglobalstyle
+\sphinxthistablewithnocolorrowsstyle
\centering
\begin{tabulary}{\linewidth}[t]{|T|T|T|}
-\hline
+\sphinxtoprule
\sphinxstyletheadfamily
\sphinxAtStartPar
header1
@@ -14,7 +16,8 @@ header2
\sphinxAtStartPar
header3
\\
-\hline
+\sphinxmidrule
+\sphinxtableatstartofbodyhook
\sphinxAtStartPar
cell1\sphinxhyphen{}1
&\sphinxmultirow{2}{5}{%
@@ -28,7 +31,7 @@ cell1\sphinxhyphen{}2
\sphinxAtStartPar
cell1\sphinxhyphen{}3
\\
-\cline{1-1}\cline{3-3}\sphinxmultirow{2}{7}{%
+\sphinxcline{1-1}\sphinxcline{3-3}\sphinxfixclines{3}\sphinxmultirow{2}{7}{%
\begin{varwidth}[t]{\sphinxcolwidth{1}{3}}
\sphinxAtStartPar
cell2\sphinxhyphen{}1
@@ -39,7 +42,7 @@ cell2\sphinxhyphen{}1
\sphinxAtStartPar
cell2\sphinxhyphen{}3
\\
-\cline{2-3}\sphinxtablestrut{7}&\sphinxstartmulticolumn{2}%
+\sphinxcline{2-3}\sphinxfixclines{3}\sphinxtablestrut{7}&\sphinxstartmulticolumn{2}%
\sphinxmultirow{2}{9}{%
\begin{varwidth}[t]{\sphinxcolwidth{2}{3}}
\sphinxAtStartPar
@@ -52,11 +55,11 @@ cell3\sphinxhyphen{}2\sphinxhyphen{}par2
}%
\sphinxstopmulticolumn
\\
-\cline{1-1}
+\sphinxcline{1-1}\sphinxfixclines{3}
\sphinxAtStartPar
cell4\sphinxhyphen{}1
&\multicolumn{2}{l|}{\sphinxtablestrut{9}}\\
-\hline\sphinxstartmulticolumn{3}%
+\sphinxhline\sphinxstartmulticolumn{3}%
\begin{varwidth}[t]{\sphinxcolwidth{3}{3}}
\sphinxAtStartPar
cell5\sphinxhyphen{}1
@@ -64,7 +67,7 @@ cell5\sphinxhyphen{}1
\vskip-\baselineskip\vbox{\hbox{\strut}}\end{varwidth}%
\sphinxstopmulticolumn
\\
-\hline
+\sphinxbottomrule
\end{tabulary}
-\par
+\sphinxtableafterendhook\par
\sphinxattableend\end{savenotes}
diff --git a/tests/roots/test-latex-table/expects/gridtable_with_tabularcolumn.tex b/tests/roots/test-latex-table/expects/gridtable_with_tabularcolumn.tex
new file mode 100644
index 000000000..c77b99041
--- /dev/null
+++ b/tests/roots/test-latex-table/expects/gridtable_with_tabularcolumn.tex
@@ -0,0 +1,73 @@
+\label{\detokenize{complex:grid-table-with-tabularcolumns-having-no-vline}}
+
+\begin{savenotes}\sphinxattablestart
+\sphinxthistablewithglobalstyle
+\sphinxthistablewithnovlinesstyle
+\centering
+\begin{tabulary}{\linewidth}[t]{TTT}
+\sphinxtoprule
+\sphinxstyletheadfamily
+\sphinxAtStartPar
+header1
+&\sphinxstyletheadfamily
+\sphinxAtStartPar
+header2
+&\sphinxstyletheadfamily
+\sphinxAtStartPar
+header3
+\\
+\sphinxmidrule
+\sphinxtableatstartofbodyhook
+\sphinxAtStartPar
+cell1\sphinxhyphen{}1
+&\sphinxmultirow{2}{5}{%
+\begin{varwidth}[t]{\sphinxcolwidth{1}{3}}
+\sphinxAtStartPar
+cell1\sphinxhyphen{}2
+\par
+\vskip-\baselineskip\vbox{\hbox{\strut}}\end{varwidth}%
+}%
+&
+\sphinxAtStartPar
+cell1\sphinxhyphen{}3
+\\
+\sphinxcline{1-1}\sphinxcline{3-3}\sphinxfixclines{3}\sphinxmultirow{2}{7}{%
+\begin{varwidth}[t]{\sphinxcolwidth{1}{3}}
+\sphinxAtStartPar
+cell2\sphinxhyphen{}1
+\par
+\vskip-\baselineskip\vbox{\hbox{\strut}}\end{varwidth}%
+}%
+&\sphinxtablestrut{5}&
+\sphinxAtStartPar
+cell2\sphinxhyphen{}3
+\\
+\sphinxcline{2-3}\sphinxfixclines{3}\sphinxtablestrut{7}&\sphinxstartmulticolumn{2}%
+\sphinxmultirow{2}{9}{%
+\begin{varwidth}[t]{\sphinxcolwidth{2}{3}}
+\sphinxAtStartPar
+cell3\sphinxhyphen{}2\sphinxhyphen{}par1
+
+\sphinxAtStartPar
+cell3\sphinxhyphen{}2\sphinxhyphen{}par2
+\par
+\vskip-\baselineskip\vbox{\hbox{\strut}}\end{varwidth}%
+}%
+\sphinxstopmulticolumn
+\\
+\sphinxcline{1-1}\sphinxfixclines{3}
+\sphinxAtStartPar
+cell4\sphinxhyphen{}1
+&\multicolumn{2}{l}{\sphinxtablestrut{9}}\\
+\sphinxhline\sphinxstartmulticolumn{3}%
+\begin{varwidth}[t]{\sphinxcolwidth{3}{3}}
+\sphinxAtStartPar
+cell5\sphinxhyphen{}1
+\par
+\vskip-\baselineskip\vbox{\hbox{\strut}}\end{varwidth}%
+\sphinxstopmulticolumn
+\\
+\sphinxbottomrule
+\end{tabulary}
+\sphinxtableafterendhook\par
+\sphinxattableend\end{savenotes}
diff --git a/tests/roots/test-latex-table/expects/longtable.tex b/tests/roots/test-latex-table/expects/longtable.tex
index e2138ad58..754d10249 100644
--- a/tests/roots/test-latex-table/expects/longtable.tex
+++ b/tests/roots/test-latex-table/expects/longtable.tex
@@ -1,7 +1,11 @@
\label{\detokenize{longtable:longtable}}
-\begin{savenotes}\sphinxatlongtablestart\begin{longtable}[c]{|l|l|}
-\hline
+\begin{savenotes}
+\sphinxatlongtablestart
+\sphinxthistablewithglobalstyle
+\sphinxthistablewithborderlessstyle
+\begin{longtable}[c]{ll}
+\sphinxtoprule
\sphinxstyletheadfamily
\sphinxAtStartPar
header1
@@ -9,12 +13,13 @@ header1
\sphinxAtStartPar
header2
\\
-\hline
+\sphinxmidrule
\endfirsthead
-\multicolumn{2}{c}%
-{\makebox[0pt]{\sphinxtablecontinued{\tablename\ \thetable{} \textendash{} continued from previous page}}}\\
-\hline
+\multicolumn{2}{c}{\sphinxnorowcolor
+ \makebox[0pt]{\sphinxtablecontinued{\tablename\ \thetable{} \textendash{} continued from previous page}}%
+}\\
+\sphinxtoprule
\sphinxstyletheadfamily
\sphinxAtStartPar
header1
@@ -22,14 +27,17 @@ header1
\sphinxAtStartPar
header2
\\
-\hline
+\sphinxmidrule
\endhead
-\hline
-\multicolumn{2}{r}{\makebox[0pt][r]{\sphinxtablecontinued{continues on next page}}}\\
+\sphinxbottomrule
+\multicolumn{2}{r}{\sphinxnorowcolor
+ \makebox[0pt][r]{\sphinxtablecontinued{continues on next page}}%
+}\\
\endfoot
\endlastfoot
+\sphinxtableatstartofbodyhook
\sphinxAtStartPar
cell1\sphinxhyphen{}1
@@ -37,19 +45,22 @@ cell1\sphinxhyphen{}1
\sphinxAtStartPar
cell1\sphinxhyphen{}2
\\
-\hline
+\sphinxhline
\sphinxAtStartPar
cell2\sphinxhyphen{}1
&
\sphinxAtStartPar
cell2\sphinxhyphen{}2
\\
-\hline
+\sphinxhline
\sphinxAtStartPar
cell3\sphinxhyphen{}1
&
\sphinxAtStartPar
cell3\sphinxhyphen{}2
\\
-\hline
-\end{longtable}\sphinxatlongtableend\end{savenotes}
+\sphinxbottomrule
+\end{longtable}
+\sphinxtableafterendhook
+\sphinxatlongtableend
+\end{savenotes}
diff --git a/tests/roots/test-latex-table/expects/longtable_having_align.tex b/tests/roots/test-latex-table/expects/longtable_having_align.tex
index 764ef55f3..dcf8f83a1 100644
--- a/tests/roots/test-latex-table/expects/longtable_having_align.tex
+++ b/tests/roots/test-latex-table/expects/longtable_having_align.tex
@@ -1,7 +1,10 @@
\label{\detokenize{longtable:longtable-having-align-option}}
-\begin{savenotes}\sphinxatlongtablestart\begin{longtable}[r]{|l|l|}
-\hline
+\begin{savenotes}
+\sphinxatlongtablestart
+\sphinxthistablewithglobalstyle
+\begin{longtable}[r]{|l|l|}
+\sphinxtoprule
\sphinxstyletheadfamily
\sphinxAtStartPar
header1
@@ -9,12 +12,13 @@ header1
\sphinxAtStartPar
header2
\\
-\hline
+\sphinxmidrule
\endfirsthead
-\multicolumn{2}{c}%
-{\makebox[0pt]{\sphinxtablecontinued{\tablename\ \thetable{} \textendash{} continued from previous page}}}\\
-\hline
+\multicolumn{2}{c}{\sphinxnorowcolor
+ \makebox[0pt]{\sphinxtablecontinued{\tablename\ \thetable{} \textendash{} continued from previous page}}%
+}\\
+\sphinxtoprule
\sphinxstyletheadfamily
\sphinxAtStartPar
header1
@@ -22,14 +26,17 @@ header1
\sphinxAtStartPar
header2
\\
-\hline
+\sphinxmidrule
\endhead
-\hline
-\multicolumn{2}{r}{\makebox[0pt][r]{\sphinxtablecontinued{continues on next page}}}\\
+\sphinxbottomrule
+\multicolumn{2}{r}{\sphinxnorowcolor
+ \makebox[0pt][r]{\sphinxtablecontinued{continues on next page}}%
+}\\
\endfoot
\endlastfoot
+\sphinxtableatstartofbodyhook
\sphinxAtStartPar
cell1\sphinxhyphen{}1
@@ -37,19 +44,22 @@ cell1\sphinxhyphen{}1
\sphinxAtStartPar
cell1\sphinxhyphen{}2
\\
-\hline
+\sphinxhline
\sphinxAtStartPar
cell2\sphinxhyphen{}1
&
\sphinxAtStartPar
cell2\sphinxhyphen{}2
\\
-\hline
+\sphinxhline
\sphinxAtStartPar
cell3\sphinxhyphen{}1
&
\sphinxAtStartPar
cell3\sphinxhyphen{}2
\\
-\hline
-\end{longtable}\sphinxatlongtableend\end{savenotes}
+\sphinxbottomrule
+\end{longtable}
+\sphinxtableafterendhook
+\sphinxatlongtableend
+\end{savenotes}
diff --git a/tests/roots/test-latex-table/expects/longtable_having_caption.tex b/tests/roots/test-latex-table/expects/longtable_having_caption.tex
index 0ca5506be..dd2a87fa6 100644
--- a/tests/roots/test-latex-table/expects/longtable_having_caption.tex
+++ b/tests/roots/test-latex-table/expects/longtable_having_caption.tex
@@ -1,9 +1,12 @@
\label{\detokenize{longtable:longtable-having-caption}}
-\begin{savenotes}\sphinxatlongtablestart\begin{longtable}[c]{|l|l|}
+\begin{savenotes}
+\sphinxatlongtablestart
+\sphinxthistablewithglobalstyle
+\begin{longtable}[c]{|l|l|}
\sphinxthelongtablecaptionisattop
\caption{caption for longtable\strut}\label{\detokenize{longtable:id1}}\\*[\sphinxlongtablecapskipadjust]
-\hline
+\sphinxtoprule
\sphinxstyletheadfamily
\sphinxAtStartPar
header1
@@ -11,12 +14,13 @@ header1
\sphinxAtStartPar
header2
\\
-\hline
+\sphinxmidrule
\endfirsthead
-\multicolumn{2}{c}%
-{\makebox[0pt]{\sphinxtablecontinued{\tablename\ \thetable{} \textendash{} continued from previous page}}}\\
-\hline
+\multicolumn{2}{c}{\sphinxnorowcolor
+ \makebox[0pt]{\sphinxtablecontinued{\tablename\ \thetable{} \textendash{} continued from previous page}}%
+}\\
+\sphinxtoprule
\sphinxstyletheadfamily
\sphinxAtStartPar
header1
@@ -24,14 +28,17 @@ header1
\sphinxAtStartPar
header2
\\
-\hline
+\sphinxmidrule
\endhead
-\hline
-\multicolumn{2}{r}{\makebox[0pt][r]{\sphinxtablecontinued{continues on next page}}}\\
+\sphinxbottomrule
+\multicolumn{2}{r}{\sphinxnorowcolor
+ \makebox[0pt][r]{\sphinxtablecontinued{continues on next page}}%
+}\\
\endfoot
\endlastfoot
+\sphinxtableatstartofbodyhook
\sphinxAtStartPar
cell1\sphinxhyphen{}1
@@ -39,19 +46,22 @@ cell1\sphinxhyphen{}1
\sphinxAtStartPar
cell1\sphinxhyphen{}2
\\
-\hline
+\sphinxhline
\sphinxAtStartPar
cell2\sphinxhyphen{}1
&
\sphinxAtStartPar
cell2\sphinxhyphen{}2
\\
-\hline
+\sphinxhline
\sphinxAtStartPar
cell3\sphinxhyphen{}1
&
\sphinxAtStartPar
cell3\sphinxhyphen{}2
\\
-\hline
-\end{longtable}\sphinxatlongtableend\end{savenotes}
+\sphinxbottomrule
+\end{longtable}
+\sphinxtableafterendhook
+\sphinxatlongtableend
+\end{savenotes}
diff --git a/tests/roots/test-latex-table/expects/longtable_having_problematic_cell.tex b/tests/roots/test-latex-table/expects/longtable_having_problematic_cell.tex
index 9551a0a3b..8258612f5 100644
--- a/tests/roots/test-latex-table/expects/longtable_having_problematic_cell.tex
+++ b/tests/roots/test-latex-table/expects/longtable_having_problematic_cell.tex
@@ -1,7 +1,10 @@
\label{\detokenize{longtable:longtable-having-problematic-cell}}
-\begin{savenotes}\sphinxatlongtablestart\begin{longtable}[c]{|*{2}{\X{1}{2}|}}
-\hline
+\begin{savenotes}
+\sphinxatlongtablestart
+\sphinxthistablewithglobalstyle
+\begin{longtable}[c]{|*{2}{\X{1}{2}|}}
+\sphinxtoprule
\sphinxstyletheadfamily
\sphinxAtStartPar
header1
@@ -9,12 +12,13 @@ header1
\sphinxAtStartPar
header2
\\
-\hline
+\sphinxmidrule
\endfirsthead
-\multicolumn{2}{c}%
-{\makebox[0pt]{\sphinxtablecontinued{\tablename\ \thetable{} \textendash{} continued from previous page}}}\\
-\hline
+\multicolumn{2}{c}{\sphinxnorowcolor
+ \makebox[0pt]{\sphinxtablecontinued{\tablename\ \thetable{} \textendash{} continued from previous page}}%
+}\\
+\sphinxtoprule
\sphinxstyletheadfamily
\sphinxAtStartPar
header1
@@ -22,14 +26,17 @@ header1
\sphinxAtStartPar
header2
\\
-\hline
+\sphinxmidrule
\endhead
-\hline
-\multicolumn{2}{r}{\makebox[0pt][r]{\sphinxtablecontinued{continues on next page}}}\\
+\sphinxbottomrule
+\multicolumn{2}{r}{\sphinxnorowcolor
+ \makebox[0pt][r]{\sphinxtablecontinued{continues on next page}}%
+}\\
\endfoot
\endlastfoot
+\sphinxtableatstartofbodyhook
\begin{itemize}
\item {}
\sphinxAtStartPar
@@ -44,19 +51,22 @@ item2
\sphinxAtStartPar
cell1\sphinxhyphen{}2
\\
-\hline
+\sphinxhline
\sphinxAtStartPar
cell2\sphinxhyphen{}1
&
\sphinxAtStartPar
cell2\sphinxhyphen{}2
\\
-\hline
+\sphinxhline
\sphinxAtStartPar
cell3\sphinxhyphen{}1
&
\sphinxAtStartPar
cell3\sphinxhyphen{}2
\\
-\hline
-\end{longtable}\sphinxatlongtableend\end{savenotes}
+\sphinxbottomrule
+\end{longtable}
+\sphinxtableafterendhook
+\sphinxatlongtableend
+\end{savenotes}
diff --git a/tests/roots/test-latex-table/expects/longtable_having_stub_columns_and_problematic_cell.tex b/tests/roots/test-latex-table/expects/longtable_having_stub_columns_and_problematic_cell.tex
index e54f8acec..44114ce78 100644
--- a/tests/roots/test-latex-table/expects/longtable_having_stub_columns_and_problematic_cell.tex
+++ b/tests/roots/test-latex-table/expects/longtable_having_stub_columns_and_problematic_cell.tex
@@ -1,7 +1,10 @@
\label{\detokenize{longtable:longtable-having-both-stub-columns-and-problematic-cell}}
-\begin{savenotes}\sphinxatlongtablestart\begin{longtable}[c]{|*{3}{\X{1}{3}|}}
-\hline
+\begin{savenotes}
+\sphinxatlongtablestart
+\sphinxthistablewithglobalstyle
+\begin{longtable}[c]{|*{3}{\X{1}{3}|}}
+\sphinxtoprule
\sphinxstyletheadfamily
\sphinxAtStartPar
header1
@@ -12,12 +15,13 @@ header2
\sphinxAtStartPar
header3
\\
-\hline
+\sphinxmidrule
\endfirsthead
-\multicolumn{3}{c}%
-{\makebox[0pt]{\sphinxtablecontinued{\tablename\ \thetable{} \textendash{} continued from previous page}}}\\
-\hline
+\multicolumn{3}{c}{\sphinxnorowcolor
+ \makebox[0pt]{\sphinxtablecontinued{\tablename\ \thetable{} \textendash{} continued from previous page}}%
+}\\
+\sphinxtoprule
\sphinxstyletheadfamily
\sphinxAtStartPar
header1
@@ -28,14 +32,17 @@ header2
\sphinxAtStartPar
header3
\\
-\hline
+\sphinxmidrule
\endhead
-\hline
-\multicolumn{3}{r}{\makebox[0pt][r]{\sphinxtablecontinued{continues on next page}}}\\
+\sphinxbottomrule
+\multicolumn{3}{r}{\sphinxnorowcolor
+ \makebox[0pt][r]{\sphinxtablecontinued{continues on next page}}%
+}\\
\endfoot
\endlastfoot
+\sphinxtableatstartofbodyhook
\sphinxstyletheadfamily \begin{itemize}
\item {}
\sphinxAtStartPar
@@ -53,7 +60,7 @@ instub1\sphinxhyphen{}2
\sphinxAtStartPar
notinstub1\sphinxhyphen{}3
\\
-\hline\sphinxstyletheadfamily
+\sphinxhline\sphinxstyletheadfamily
\sphinxAtStartPar
cell2\sphinxhyphen{}1
&\sphinxstyletheadfamily
@@ -63,5 +70,8 @@ cell2\sphinxhyphen{}2
\sphinxAtStartPar
cell2\sphinxhyphen{}3
\\
-\hline
-\end{longtable}\sphinxatlongtableend\end{savenotes}
+\sphinxbottomrule
+\end{longtable}
+\sphinxtableafterendhook
+\sphinxatlongtableend
+\end{savenotes}
diff --git a/tests/roots/test-latex-table/expects/longtable_having_verbatim.tex b/tests/roots/test-latex-table/expects/longtable_having_verbatim.tex
index a0e7ecfcd..fc78914a9 100644
--- a/tests/roots/test-latex-table/expects/longtable_having_verbatim.tex
+++ b/tests/roots/test-latex-table/expects/longtable_having_verbatim.tex
@@ -1,7 +1,10 @@
\label{\detokenize{longtable:longtable-having-verbatim}}
-\begin{savenotes}\sphinxatlongtablestart\begin{longtable}[c]{|*{2}{\X{1}{2}|}}
-\hline
+\begin{savenotes}
+\sphinxatlongtablestart
+\sphinxthistablewithglobalstyle
+\begin{longtable}[c]{|*{2}{\X{1}{2}|}}
+\sphinxtoprule
\sphinxstyletheadfamily
\sphinxAtStartPar
header1
@@ -9,12 +12,13 @@ header1
\sphinxAtStartPar
header2
\\
-\hline
+\sphinxmidrule
\endfirsthead
-\multicolumn{2}{c}%
-{\makebox[0pt]{\sphinxtablecontinued{\tablename\ \thetable{} \textendash{} continued from previous page}}}\\
-\hline
+\multicolumn{2}{c}{\sphinxnorowcolor
+ \makebox[0pt]{\sphinxtablecontinued{\tablename\ \thetable{} \textendash{} continued from previous page}}%
+}\\
+\sphinxtoprule
\sphinxstyletheadfamily
\sphinxAtStartPar
header1
@@ -22,14 +26,17 @@ header1
\sphinxAtStartPar
header2
\\
-\hline
+\sphinxmidrule
\endhead
-\hline
-\multicolumn{2}{r}{\makebox[0pt][r]{\sphinxtablecontinued{continues on next page}}}\\
+\sphinxbottomrule
+\multicolumn{2}{r}{\sphinxnorowcolor
+ \makebox[0pt][r]{\sphinxtablecontinued{continues on next page}}%
+}\\
\endfoot
\endlastfoot
+\sphinxtableatstartofbodyhook
\begin{sphinxVerbatimintable}[commandchars=\\\{\}]
\PYG{n}{hello} \PYG{n}{world}
@@ -38,19 +45,22 @@ header2
\sphinxAtStartPar
cell1\sphinxhyphen{}2
\\
-\hline
+\sphinxhline
\sphinxAtStartPar
cell2\sphinxhyphen{}1
&
\sphinxAtStartPar
cell2\sphinxhyphen{}2
\\
-\hline
+\sphinxhline
\sphinxAtStartPar
cell3\sphinxhyphen{}1
&
\sphinxAtStartPar
cell3\sphinxhyphen{}2
\\
-\hline
-\end{longtable}\sphinxatlongtableend\end{savenotes}
+\sphinxbottomrule
+\end{longtable}
+\sphinxtableafterendhook
+\sphinxatlongtableend
+\end{savenotes}
diff --git a/tests/roots/test-latex-table/expects/longtable_having_widths.tex b/tests/roots/test-latex-table/expects/longtable_having_widths.tex
index cdd0e7a2b..5bf1507a7 100644
--- a/tests/roots/test-latex-table/expects/longtable_having_widths.tex
+++ b/tests/roots/test-latex-table/expects/longtable_having_widths.tex
@@ -1,7 +1,11 @@
\label{\detokenize{longtable:longtable-having-widths-option}}
-\begin{savenotes}\sphinxatlongtablestart\begin{longtable}[c]{|\X{30}{100}|\X{70}{100}|}
-\hline\noalign{\phantomsection\label{\detokenize{longtable:namedlongtable}}\label{\detokenize{longtable:mylongtable}}}%
+\begin{savenotes}
+\sphinxatlongtablestart
+\sphinxthistablewithglobalstyle
+\begin{longtable}[c]{|\X{30}{100}|\X{70}{100}|}
+\noalign{\phantomsection\label{\detokenize{longtable:namedlongtable}}\label{\detokenize{longtable:mylongtable}}}%
+\sphinxtoprule
\sphinxstyletheadfamily
\sphinxAtStartPar
header1
@@ -9,12 +13,13 @@ header1
\sphinxAtStartPar
header2
\\
-\hline
+\sphinxmidrule
\endfirsthead
-\multicolumn{2}{c}%
-{\makebox[0pt]{\sphinxtablecontinued{\tablename\ \thetable{} \textendash{} continued from previous page}}}\\
-\hline
+\multicolumn{2}{c}{\sphinxnorowcolor
+ \makebox[0pt]{\sphinxtablecontinued{\tablename\ \thetable{} \textendash{} continued from previous page}}%
+}\\
+\sphinxtoprule
\sphinxstyletheadfamily
\sphinxAtStartPar
header1
@@ -22,14 +27,17 @@ header1
\sphinxAtStartPar
header2
\\
-\hline
+\sphinxmidrule
\endhead
-\hline
-\multicolumn{2}{r}{\makebox[0pt][r]{\sphinxtablecontinued{continues on next page}}}\\
+\sphinxbottomrule
+\multicolumn{2}{r}{\sphinxnorowcolor
+ \makebox[0pt][r]{\sphinxtablecontinued{continues on next page}}%
+}\\
\endfoot
\endlastfoot
+\sphinxtableatstartofbodyhook
\sphinxAtStartPar
cell1\sphinxhyphen{}1
@@ -37,22 +45,25 @@ cell1\sphinxhyphen{}1
\sphinxAtStartPar
cell1\sphinxhyphen{}2
\\
-\hline
+\sphinxhline
\sphinxAtStartPar
cell2\sphinxhyphen{}1
&
\sphinxAtStartPar
cell2\sphinxhyphen{}2
\\
-\hline
+\sphinxhline
\sphinxAtStartPar
cell3\sphinxhyphen{}1
&
\sphinxAtStartPar
cell3\sphinxhyphen{}2
\\
-\hline
-\end{longtable}\sphinxatlongtableend\end{savenotes}
+\sphinxbottomrule
+\end{longtable}
+\sphinxtableafterendhook
+\sphinxatlongtableend
+\end{savenotes}
\sphinxAtStartPar
See {\hyperref[\detokenize{longtable:mylongtable}]{\sphinxcrossref{mylongtable}}}, same as {\hyperref[\detokenize{longtable:namedlongtable}]{\sphinxcrossref{\DUrole{std,std-ref}{this one}}}}.
diff --git a/tests/roots/test-latex-table/expects/longtable_having_widths_and_problematic_cell.tex b/tests/roots/test-latex-table/expects/longtable_having_widths_and_problematic_cell.tex
index ea868ffe4..bf9192009 100644
--- a/tests/roots/test-latex-table/expects/longtable_having_widths_and_problematic_cell.tex
+++ b/tests/roots/test-latex-table/expects/longtable_having_widths_and_problematic_cell.tex
@@ -1,7 +1,10 @@
\label{\detokenize{longtable:longtable-having-both-widths-and-problematic-cell}}
-\begin{savenotes}\sphinxatlongtablestart\begin{longtable}[c]{|\X{30}{100}|\X{70}{100}|}
-\hline
+\begin{savenotes}
+\sphinxatlongtablestart
+\sphinxthistablewithglobalstyle
+\begin{longtable}[c]{|\X{30}{100}|\X{70}{100}|}
+\sphinxtoprule
\sphinxstyletheadfamily
\sphinxAtStartPar
header1
@@ -9,12 +12,13 @@ header1
\sphinxAtStartPar
header2
\\
-\hline
+\sphinxmidrule
\endfirsthead
-\multicolumn{2}{c}%
-{\makebox[0pt]{\sphinxtablecontinued{\tablename\ \thetable{} \textendash{} continued from previous page}}}\\
-\hline
+\multicolumn{2}{c}{\sphinxnorowcolor
+ \makebox[0pt]{\sphinxtablecontinued{\tablename\ \thetable{} \textendash{} continued from previous page}}%
+}\\
+\sphinxtoprule
\sphinxstyletheadfamily
\sphinxAtStartPar
header1
@@ -22,14 +26,17 @@ header1
\sphinxAtStartPar
header2
\\
-\hline
+\sphinxmidrule
\endhead
-\hline
-\multicolumn{2}{r}{\makebox[0pt][r]{\sphinxtablecontinued{continues on next page}}}\\
+\sphinxbottomrule
+\multicolumn{2}{r}{\sphinxnorowcolor
+ \makebox[0pt][r]{\sphinxtablecontinued{continues on next page}}%
+}\\
\endfoot
\endlastfoot
+\sphinxtableatstartofbodyhook
\begin{itemize}
\item {}
\sphinxAtStartPar
@@ -44,19 +51,22 @@ item2
\sphinxAtStartPar
cell1\sphinxhyphen{}2
\\
-\hline
+\sphinxhline
\sphinxAtStartPar
cell2\sphinxhyphen{}1
&
\sphinxAtStartPar
cell2\sphinxhyphen{}2
\\
-\hline
+\sphinxhline
\sphinxAtStartPar
cell3\sphinxhyphen{}1
&
\sphinxAtStartPar
cell3\sphinxhyphen{}2
\\
-\hline
-\end{longtable}\sphinxatlongtableend\end{savenotes}
+\sphinxbottomrule
+\end{longtable}
+\sphinxtableafterendhook
+\sphinxatlongtableend
+\end{savenotes}
diff --git a/tests/roots/test-latex-table/expects/longtable_with_tabularcolumn.tex b/tests/roots/test-latex-table/expects/longtable_with_tabularcolumn.tex
index 426086de5..6383d9d56 100644
--- a/tests/roots/test-latex-table/expects/longtable_with_tabularcolumn.tex
+++ b/tests/roots/test-latex-table/expects/longtable_with_tabularcolumn.tex
@@ -1,7 +1,11 @@
\label{\detokenize{longtable:longtable-with-tabularcolumn}}
-\begin{savenotes}\sphinxatlongtablestart\begin{longtable}[c]{|c|c|}
-\hline
+\begin{savenotes}
+\sphinxatlongtablestart
+\sphinxthistablewithglobalstyle
+\sphinxthistablewithvlinesstyle
+\begin{longtable}[c]{|c|c|}
+\sphinxtoprule
\sphinxstyletheadfamily
\sphinxAtStartPar
header1
@@ -9,12 +13,13 @@ header1
\sphinxAtStartPar
header2
\\
-\hline
+\sphinxmidrule
\endfirsthead
-\multicolumn{2}{c}%
-{\makebox[0pt]{\sphinxtablecontinued{\tablename\ \thetable{} \textendash{} continued from previous page}}}\\
-\hline
+\multicolumn{2}{c}{\sphinxnorowcolor
+ \makebox[0pt]{\sphinxtablecontinued{\tablename\ \thetable{} \textendash{} continued from previous page}}%
+}\\
+\sphinxtoprule
\sphinxstyletheadfamily
\sphinxAtStartPar
header1
@@ -22,14 +27,17 @@ header1
\sphinxAtStartPar
header2
\\
-\hline
+\sphinxmidrule
\endhead
-\hline
-\multicolumn{2}{r}{\makebox[0pt][r]{\sphinxtablecontinued{continues on next page}}}\\
+\sphinxbottomrule
+\multicolumn{2}{r}{\sphinxnorowcolor
+ \makebox[0pt][r]{\sphinxtablecontinued{continues on next page}}%
+}\\
\endfoot
\endlastfoot
+\sphinxtableatstartofbodyhook
\sphinxAtStartPar
cell1\sphinxhyphen{}1
@@ -37,19 +45,22 @@ cell1\sphinxhyphen{}1
\sphinxAtStartPar
cell1\sphinxhyphen{}2
\\
-\hline
+\sphinxhline
\sphinxAtStartPar
cell2\sphinxhyphen{}1
&
\sphinxAtStartPar
cell2\sphinxhyphen{}2
\\
-\hline
+\sphinxhline
\sphinxAtStartPar
cell3\sphinxhyphen{}1
&
\sphinxAtStartPar
cell3\sphinxhyphen{}2
\\
-\hline
-\end{longtable}\sphinxatlongtableend\end{savenotes}
+\sphinxbottomrule
+\end{longtable}
+\sphinxtableafterendhook
+\sphinxatlongtableend
+\end{savenotes}
diff --git a/tests/roots/test-latex-table/expects/simple_table.tex b/tests/roots/test-latex-table/expects/simple_table.tex
index a06bfb1cf..7bd85c737 100644
--- a/tests/roots/test-latex-table/expects/simple_table.tex
+++ b/tests/roots/test-latex-table/expects/simple_table.tex
@@ -1,9 +1,10 @@
\label{\detokenize{tabular:simple-table}}
\begin{savenotes}\sphinxattablestart
+\sphinxthistablewithglobalstyle
\centering
\begin{tabulary}{\linewidth}[t]{|T|T|}
-\hline
+\sphinxtoprule
\sphinxstyletheadfamily
\sphinxAtStartPar
header1
@@ -11,28 +12,29 @@ header1
\sphinxAtStartPar
header2
\\
-\hline
+\sphinxmidrule
+\sphinxtableatstartofbodyhook
\sphinxAtStartPar
cell1\sphinxhyphen{}1
&
\sphinxAtStartPar
cell1\sphinxhyphen{}2
\\
-\hline
+\sphinxhline
\sphinxAtStartPar
cell2\sphinxhyphen{}1
&
\sphinxAtStartPar
cell2\sphinxhyphen{}2
\\
-\hline
+\sphinxhline
\sphinxAtStartPar
cell3\sphinxhyphen{}1
&
\sphinxAtStartPar
cell3\sphinxhyphen{}2
\\
-\hline
+\sphinxbottomrule
\end{tabulary}
-\par
+\sphinxtableafterendhook\par
\sphinxattableend\end{savenotes}
diff --git a/tests/roots/test-latex-table/expects/table_having_caption.tex b/tests/roots/test-latex-table/expects/table_having_caption.tex
index 33a5f1d8f..f2ce55360 100644
--- a/tests/roots/test-latex-table/expects/table_having_caption.tex
+++ b/tests/roots/test-latex-table/expects/table_having_caption.tex
@@ -1,13 +1,14 @@
\label{\detokenize{tabular:table-having-caption}}
\begin{savenotes}\sphinxattablestart
+\sphinxthistablewithglobalstyle
\centering
\sphinxcapstartof{table}
\sphinxthecaptionisattop
\sphinxcaption{caption for table}\label{\detokenize{tabular:id1}}
\sphinxaftertopcaption
\begin{tabulary}{\linewidth}[t]{|T|T|}
-\hline
+\sphinxtoprule
\sphinxstyletheadfamily
\sphinxAtStartPar
header1
@@ -15,28 +16,29 @@ header1
\sphinxAtStartPar
header2
\\
-\hline
+\sphinxmidrule
+\sphinxtableatstartofbodyhook
\sphinxAtStartPar
cell1\sphinxhyphen{}1
&
\sphinxAtStartPar
cell1\sphinxhyphen{}2
\\
-\hline
+\sphinxhline
\sphinxAtStartPar
cell2\sphinxhyphen{}1
&
\sphinxAtStartPar
cell2\sphinxhyphen{}2
\\
-\hline
+\sphinxhline
\sphinxAtStartPar
cell3\sphinxhyphen{}1
&
\sphinxAtStartPar
cell3\sphinxhyphen{}2
\\
-\hline
+\sphinxbottomrule
\end{tabulary}
-\par
+\sphinxtableafterendhook\par
\sphinxattableend\end{savenotes}
diff --git a/tests/roots/test-latex-table/expects/table_having_problematic_cell.tex b/tests/roots/test-latex-table/expects/table_having_problematic_cell.tex
index c5c57e2f7..7d7ad4b71 100644
--- a/tests/roots/test-latex-table/expects/table_having_problematic_cell.tex
+++ b/tests/roots/test-latex-table/expects/table_having_problematic_cell.tex
@@ -1,9 +1,10 @@
\label{\detokenize{tabular:table-having-problematic-cell}}
\begin{savenotes}\sphinxattablestart
+\sphinxthistablewithglobalstyle
\centering
\begin{tabular}[t]{|*{2}{\X{1}{2}|}}
-\hline
+\sphinxtoprule
\sphinxstyletheadfamily
\sphinxAtStartPar
header1
@@ -11,7 +12,8 @@ header1
\sphinxAtStartPar
header2
\\
-\hline\begin{itemize}
+\sphinxmidrule
+\sphinxtableatstartofbodyhook\begin{itemize}
\item {}
\sphinxAtStartPar
item1
@@ -25,21 +27,21 @@ item2
\sphinxAtStartPar
cell1\sphinxhyphen{}2
\\
-\hline
+\sphinxhline
\sphinxAtStartPar
cell2\sphinxhyphen{}1
&
\sphinxAtStartPar
cell2\sphinxhyphen{}2
\\
-\hline
+\sphinxhline
\sphinxAtStartPar
cell3\sphinxhyphen{}1
&
\sphinxAtStartPar
cell3\sphinxhyphen{}2
\\
-\hline
+\sphinxbottomrule
\end{tabular}
-\par
+\sphinxtableafterendhook\par
\sphinxattableend\end{savenotes}
diff --git a/tests/roots/test-latex-table/expects/table_having_stub_columns_and_problematic_cell.tex b/tests/roots/test-latex-table/expects/table_having_stub_columns_and_problematic_cell.tex
index 13c48a213..fbd797a1b 100644
--- a/tests/roots/test-latex-table/expects/table_having_stub_columns_and_problematic_cell.tex
+++ b/tests/roots/test-latex-table/expects/table_having_stub_columns_and_problematic_cell.tex
@@ -1,9 +1,10 @@
\label{\detokenize{tabular:table-having-both-stub-columns-and-problematic-cell}}
\begin{savenotes}\sphinxattablestart
+\sphinxthistablewithglobalstyle
\centering
\begin{tabular}[t]{|*{3}{\X{1}{3}|}}
-\hline
+\sphinxtoprule
\sphinxstyletheadfamily
\sphinxAtStartPar
header1
@@ -14,7 +15,8 @@ header2
\sphinxAtStartPar
header3
\\
-\hline\sphinxstyletheadfamily \begin{itemize}
+\sphinxmidrule
+\sphinxtableatstartofbodyhook\sphinxstyletheadfamily \begin{itemize}
\item {}
\sphinxAtStartPar
instub1\sphinxhyphen{}1a
@@ -31,7 +33,7 @@ instub1\sphinxhyphen{}2
\sphinxAtStartPar
notinstub1\sphinxhyphen{}3
\\
-\hline\sphinxstyletheadfamily
+\sphinxhline\sphinxstyletheadfamily
\sphinxAtStartPar
cell2\sphinxhyphen{}1
&\sphinxstyletheadfamily
@@ -41,7 +43,7 @@ cell2\sphinxhyphen{}2
\sphinxAtStartPar
cell2\sphinxhyphen{}3
\\
-\hline
+\sphinxbottomrule
\end{tabular}
-\par
+\sphinxtableafterendhook\par
\sphinxattableend\end{savenotes}
diff --git a/tests/roots/test-latex-table/expects/table_having_threeparagraphs_cell_in_first_col.tex b/tests/roots/test-latex-table/expects/table_having_threeparagraphs_cell_in_first_col.tex
index c1a440558..9acd9a86d 100644
--- a/tests/roots/test-latex-table/expects/table_having_threeparagraphs_cell_in_first_col.tex
+++ b/tests/roots/test-latex-table/expects/table_having_threeparagraphs_cell_in_first_col.tex
@@ -1,14 +1,16 @@
\label{\detokenize{tabular:table-with-cell-in-first-column-having-three-paragraphs}}
\begin{savenotes}\sphinxattablestart
+\sphinxthistablewithglobalstyle
\centering
\begin{tabulary}{\linewidth}[t]{|T|}
-\hline
+\sphinxtoprule
\sphinxstyletheadfamily
\sphinxAtStartPar
header1
\\
-\hline
+\sphinxmidrule
+\sphinxtableatstartofbodyhook
\sphinxAtStartPar
cell1\sphinxhyphen{}1\sphinxhyphen{}par1
@@ -18,7 +20,7 @@ cell1\sphinxhyphen{}1\sphinxhyphen{}par2
\sphinxAtStartPar
cell1\sphinxhyphen{}1\sphinxhyphen{}par3
\\
-\hline
+\sphinxbottomrule
\end{tabulary}
-\par
+\sphinxtableafterendhook\par
\sphinxattableend\end{savenotes}
diff --git a/tests/roots/test-latex-table/expects/table_having_verbatim.tex b/tests/roots/test-latex-table/expects/table_having_verbatim.tex
index 23faac55e..a002de586 100644
--- a/tests/roots/test-latex-table/expects/table_having_verbatim.tex
+++ b/tests/roots/test-latex-table/expects/table_having_verbatim.tex
@@ -1,9 +1,10 @@
\label{\detokenize{tabular:table-having-verbatim}}
\begin{savenotes}\sphinxattablestart
+\sphinxthistablewithglobalstyle
\centering
\begin{tabular}[t]{|*{2}{\X{1}{2}|}}
-\hline
+\sphinxtoprule
\sphinxstyletheadfamily
\sphinxAtStartPar
header1
@@ -11,7 +12,8 @@ header1
\sphinxAtStartPar
header2
\\
-\hline
+\sphinxmidrule
+\sphinxtableatstartofbodyhook
\begin{sphinxVerbatimintable}[commandchars=\\\{\}]
\PYG{n}{hello} \PYG{n}{world}
\end{sphinxVerbatimintable}
@@ -19,21 +21,21 @@ header2
\sphinxAtStartPar
cell1\sphinxhyphen{}2
\\
-\hline
+\sphinxhline
\sphinxAtStartPar
cell2\sphinxhyphen{}1
&
\sphinxAtStartPar
cell2\sphinxhyphen{}2
\\
-\hline
+\sphinxhline
\sphinxAtStartPar
cell3\sphinxhyphen{}1
&
\sphinxAtStartPar
cell3\sphinxhyphen{}2
\\
-\hline
+\sphinxbottomrule
\end{tabular}
-\par
+\sphinxtableafterendhook\par
\sphinxattableend\end{savenotes}
diff --git a/tests/roots/test-latex-table/expects/table_having_widths.tex b/tests/roots/test-latex-table/expects/table_having_widths.tex
index d01a40576..fe5f4c44d 100644
--- a/tests/roots/test-latex-table/expects/table_having_widths.tex
+++ b/tests/roots/test-latex-table/expects/table_having_widths.tex
@@ -1,10 +1,13 @@
\label{\detokenize{tabular:table-having-widths-option}}
\begin{savenotes}\sphinxattablestart
+\sphinxthistablewithglobalstyle
+\sphinxthistablewithbooktabsstyle
+\sphinxthistablewithcolorrowsstyle
\centering
\phantomsection\label{\detokenize{tabular:namedtabular}}\label{\detokenize{tabular:mytabular}}\nobreak
-\begin{tabular}[t]{|\X{30}{100}|\X{70}{100}|}
-\hline
+\begin{tabular}[t]{\X{30}{100}\X{70}{100}}
+\sphinxtoprule
\sphinxstyletheadfamily
\sphinxAtStartPar
header1
@@ -12,30 +15,31 @@ header1
\sphinxAtStartPar
header2
\\
-\hline
+\sphinxmidrule
+\sphinxtableatstartofbodyhook
\sphinxAtStartPar
cell1\sphinxhyphen{}1
&
\sphinxAtStartPar
cell1\sphinxhyphen{}2
\\
-\hline
+\sphinxhline
\sphinxAtStartPar
cell2\sphinxhyphen{}1
&
\sphinxAtStartPar
cell2\sphinxhyphen{}2
\\
-\hline
+\sphinxhline
\sphinxAtStartPar
cell3\sphinxhyphen{}1
&
\sphinxAtStartPar
cell3\sphinxhyphen{}2
\\
-\hline
+\sphinxbottomrule
\end{tabular}
-\par
+\sphinxtableafterendhook\par
\sphinxattableend\end{savenotes}
\sphinxAtStartPar
diff --git a/tests/roots/test-latex-table/expects/table_having_widths_and_problematic_cell.tex b/tests/roots/test-latex-table/expects/table_having_widths_and_problematic_cell.tex
index ca6b697e5..1baf92c1a 100644
--- a/tests/roots/test-latex-table/expects/table_having_widths_and_problematic_cell.tex
+++ b/tests/roots/test-latex-table/expects/table_having_widths_and_problematic_cell.tex
@@ -1,9 +1,10 @@
\label{\detokenize{tabular:table-having-both-widths-and-problematic-cell}}
\begin{savenotes}\sphinxattablestart
+\sphinxthistablewithglobalstyle
\centering
\begin{tabular}[t]{|\X{30}{100}|\X{70}{100}|}
-\hline
+\sphinxtoprule
\sphinxstyletheadfamily
\sphinxAtStartPar
header1
@@ -11,7 +12,8 @@ header1
\sphinxAtStartPar
header2
\\
-\hline\begin{itemize}
+\sphinxmidrule
+\sphinxtableatstartofbodyhook\begin{itemize}
\item {}
\sphinxAtStartPar
item1
@@ -25,21 +27,21 @@ item2
\sphinxAtStartPar
cell1\sphinxhyphen{}2
\\
-\hline
+\sphinxhline
\sphinxAtStartPar
cell2\sphinxhyphen{}1
&
\sphinxAtStartPar
cell2\sphinxhyphen{}2
\\
-\hline
+\sphinxhline
\sphinxAtStartPar
cell3\sphinxhyphen{}1
&
\sphinxAtStartPar
cell3\sphinxhyphen{}2
\\
-\hline
+\sphinxbottomrule
\end{tabular}
-\par
+\sphinxtableafterendhook\par
\sphinxattableend\end{savenotes}
diff --git a/tests/roots/test-latex-table/expects/tabular_having_widths.tex b/tests/roots/test-latex-table/expects/tabular_having_widths.tex
index 596ba4868..15321d693 100644
--- a/tests/roots/test-latex-table/expects/tabular_having_widths.tex
+++ b/tests/roots/test-latex-table/expects/tabular_having_widths.tex
@@ -1,9 +1,10 @@
\label{\detokenize{tabular:table-having-align-option-tabular}}
\begin{savenotes}\sphinxattablestart
+\sphinxthistablewithglobalstyle
\raggedright
\begin{tabular}[t]{|\X{30}{100}|\X{70}{100}|}
-\hline
+\sphinxtoprule
\sphinxstyletheadfamily
\sphinxAtStartPar
header1
@@ -11,28 +12,29 @@ header1
\sphinxAtStartPar
header2
\\
-\hline
+\sphinxmidrule
+\sphinxtableatstartofbodyhook
\sphinxAtStartPar
cell1\sphinxhyphen{}1
&
\sphinxAtStartPar
cell1\sphinxhyphen{}2
\\
-\hline
+\sphinxhline
\sphinxAtStartPar
cell2\sphinxhyphen{}1
&
\sphinxAtStartPar
cell2\sphinxhyphen{}2
\\
-\hline
+\sphinxhline
\sphinxAtStartPar
cell3\sphinxhyphen{}1
&
\sphinxAtStartPar
cell3\sphinxhyphen{}2
\\
-\hline
+\sphinxbottomrule
\end{tabular}
-\par
+\sphinxtableafterendhook\par
\sphinxattableend\end{savenotes}
diff --git a/tests/roots/test-latex-table/expects/tabularcolumn.tex b/tests/roots/test-latex-table/expects/tabularcolumn.tex
index c020e0cb4..fcb01be3f 100644
--- a/tests/roots/test-latex-table/expects/tabularcolumn.tex
+++ b/tests/roots/test-latex-table/expects/tabularcolumn.tex
@@ -1,9 +1,11 @@
\label{\detokenize{tabular:table-with-tabularcolumn}}
\begin{savenotes}\sphinxattablestart
+\sphinxthistablewithglobalstyle
+\sphinxthistablewithnovlinesstyle
\centering
-\begin{tabulary}{\linewidth}[t]{|c|c|}
-\hline
+\begin{tabulary}{\linewidth}[t]{cc}
+\sphinxtoprule
\sphinxstyletheadfamily
\sphinxAtStartPar
header1
@@ -11,28 +13,29 @@ header1
\sphinxAtStartPar
header2
\\
-\hline
+\sphinxmidrule
+\sphinxtableatstartofbodyhook
\sphinxAtStartPar
cell1\sphinxhyphen{}1
&
\sphinxAtStartPar
cell1\sphinxhyphen{}2
\\
-\hline
+\sphinxhline
\sphinxAtStartPar
cell2\sphinxhyphen{}1
&
\sphinxAtStartPar
cell2\sphinxhyphen{}2
\\
-\hline
+\sphinxhline
\sphinxAtStartPar
cell3\sphinxhyphen{}1
&
\sphinxAtStartPar
cell3\sphinxhyphen{}2
\\
-\hline
+\sphinxbottomrule
\end{tabulary}
-\par
+\sphinxtableafterendhook\par
\sphinxattableend\end{savenotes}
diff --git a/tests/roots/test-latex-table/expects/tabulary_having_widths.tex b/tests/roots/test-latex-table/expects/tabulary_having_widths.tex
index 0b42fb0cf..246341630 100644
--- a/tests/roots/test-latex-table/expects/tabulary_having_widths.tex
+++ b/tests/roots/test-latex-table/expects/tabulary_having_widths.tex
@@ -1,9 +1,10 @@
\label{\detokenize{tabular:table-having-align-option-tabulary}}
\begin{savenotes}\sphinxattablestart
+\sphinxthistablewithglobalstyle
\raggedleft
\begin{tabulary}{\linewidth}[t]{|T|T|}
-\hline
+\sphinxtoprule
\sphinxstyletheadfamily
\sphinxAtStartPar
header1
@@ -11,28 +12,29 @@ header1
\sphinxAtStartPar
header2
\\
-\hline
+\sphinxmidrule
+\sphinxtableatstartofbodyhook
\sphinxAtStartPar
cell1\sphinxhyphen{}1
&
\sphinxAtStartPar
cell1\sphinxhyphen{}2
\\
-\hline
+\sphinxhline
\sphinxAtStartPar
cell2\sphinxhyphen{}1
&
\sphinxAtStartPar
cell2\sphinxhyphen{}2
\\
-\hline
+\sphinxhline
\sphinxAtStartPar
cell3\sphinxhyphen{}1
&
\sphinxAtStartPar
cell3\sphinxhyphen{}2
\\
-\hline
+\sphinxbottomrule
\end{tabulary}
-\par
+\sphinxtableafterendhook\par
\sphinxattableend\end{savenotes}
diff --git a/tests/roots/test-latex-table/longtable.rst b/tests/roots/test-latex-table/longtable.rst
index bace9d4d2..da6fa5c5c 100644
--- a/tests/roots/test-latex-table/longtable.rst
+++ b/tests/roots/test-latex-table/longtable.rst
@@ -5,7 +5,7 @@ longtable
---------
.. table::
- :class: longtable
+ :class: longtable, borderless
======= =======
header1 header2
diff --git a/tests/roots/test-latex-table/tabular.rst b/tests/roots/test-latex-table/tabular.rst
index 7f0909540..15db823a0 100644
--- a/tests/roots/test-latex-table/tabular.rst
+++ b/tests/roots/test-latex-table/tabular.rst
@@ -20,6 +20,7 @@ table having :widths: option
.. table::
:widths: 30,70
:name: namedtabular
+ :class: booktabs, colorrows
======= =======
header1 header2
@@ -63,7 +64,7 @@ table having :align: option (tabular)
table with tabularcolumn
------------------------
-.. tabularcolumns:: |c|c|
+.. tabularcolumns:: cc
======= =======
header1 header2
diff --git a/tests/test_build_latex.py b/tests/test_build_latex.py
index efdbcb298..ae141a4f2 100644
--- a/tests/test_build_latex.py
+++ b/tests/test_build_latex.py
@@ -22,7 +22,8 @@ LATEX_ENGINES = ['pdflatex', 'lualatex', 'xelatex']
DOCCLASSES = ['howto', 'manual']
STYLEFILES = ['article.cls', 'fancyhdr.sty', 'titlesec.sty', 'amsmath.sty',
'framed.sty', 'color.sty', 'fancyvrb.sty',
- 'fncychap.sty', 'geometry.sty', 'kvoptions.sty', 'hyperref.sty']
+ 'fncychap.sty', 'geometry.sty', 'kvoptions.sty', 'hyperref.sty',
+ 'booktabs.sty']
LATEX_WARNINGS = ENV_WARNINGS + """\
%(root)s/index.rst:\\d+: WARNING: unknown option: '&option'
@@ -90,6 +91,10 @@ def skip_if_stylefiles_notfound(testfunc):
def test_build_latex_doc(app, status, warning, engine, docclass):
app.config.latex_engine = engine
app.config.latex_documents = [app.config.latex_documents[0][:4] + (docclass,)]
+ if engine == 'xelatex':
+ app.config.latex_table_style = ['booktabs']
+ elif engine == 'lualatex':
+ app.config.latex_table_style = ['colorrows']
app.builder.init()
LaTeXTranslator.ignore_missing_images = True
@@ -723,7 +728,8 @@ def test_footnote(app, status, warning):
assert '\\sphinxcite{footnote:bar}' in result
assert ('\\bibitem[bar]{footnote:bar}\n\\sphinxAtStartPar\ncite\n') in result
assert '\\sphinxcaption{Table caption \\sphinxfootnotemark[4]' in result
- assert ('\\hline%\n\\begin{footnotetext}[4]\\sphinxAtStartFootnote\n'
+ assert ('\\sphinxmidrule\n\\sphinxtableatstartofbodyhook%\n'
+ '\\begin{footnotetext}[4]\\sphinxAtStartFootnote\n'
'footnote in table caption\n%\n\\end{footnotetext}\\ignorespaces %\n'
'\\begin{footnotetext}[5]\\sphinxAtStartFootnote\n'
'footnote in table header\n%\n\\end{footnotetext}\\ignorespaces '
@@ -731,9 +737,9 @@ def test_footnote(app, status, warning):
'VIDIOC\\_CROPCAP\n&\n\\sphinxAtStartPar\n') in result
assert ('Information about VIDIOC\\_CROPCAP %\n'
'\\begin{footnote}[6]\\sphinxAtStartFootnote\n'
- 'footnote in table not in header\n%\n\\end{footnote}\n\\\\\n\\hline\n'
- '\\end{tabulary}\n'
- '\\par\n\\sphinxattableend\\end{savenotes}\n') in result
+ 'footnote in table not in header\n%\n\\end{footnote}\n\\\\\n'
+ '\\sphinxbottomrule\n\\end{tabulary}\n'
+ '\\sphinxtableafterendhook\\par\n\\sphinxattableend\\end{savenotes}\n') in result
@pytest.mark.sphinx('latex', testroot='footnotes')
@@ -761,7 +767,8 @@ def test_reference_in_caption_and_codeblock_in_footnote(app, status, warning):
'caption of normal table}\\label{\\detokenize{index:id36}}') in result
assert ('\\caption{footnote \\sphinxfootnotemark[10] '
'in caption \\sphinxfootnotemark[11] of longtable\\strut}') in result
- assert ('\\endlastfoot\n%\n\\begin{footnotetext}[10]\\sphinxAtStartFootnote\n'
+ assert ('\\endlastfoot\n\\sphinxtableatstartofbodyhook\n%\n'
+ '\\begin{footnotetext}[10]\\sphinxAtStartFootnote\n'
'Foot note in longtable\n%\n\\end{footnotetext}\\ignorespaces %\n'
'\\begin{footnotetext}[11]\\sphinxAtStartFootnote\n'
'Second footnote in caption of longtable\n') in result
@@ -1309,12 +1316,35 @@ def test_latex_table_complex_tables(app, status, warning):
expected = get_expected('gridtable')
assert actual == expected
+ # grid table with tabularcolumns
+ # MEMO: filename should end with tabularcolumns but tabularcolumn has been
+ # used in existing other cases
+ actual = tables['grid table with tabularcolumns having no vline']
+ expected = get_expected('gridtable_with_tabularcolumn')
+ assert actual == expected
+
# complex spanning cell
actual = tables['complex spanning cell']
expected = get_expected('complex_spanning_cell')
assert actual == expected
+@pytest.mark.sphinx('latex', testroot='latex-table',
+ confoverrides={'latex_table_style': ['booktabs', 'colorrows']})
+def test_latex_table_with_booktabs_and_colorrows(app, status, warning):
+ app.builder.build_all()
+ result = (app.outdir / 'python.tex').read_text(encoding='utf8')
+ assert r'\PassOptionsToPackage{booktabs}{sphinx}' in result
+ assert r'\PassOptionsToPackage{colorrows}{sphinx}' in result
+ # tabularcolumns
+ assert r'\begin{longtable}[c]{|c|c|}' in result
+ # class: standard
+ assert r'\begin{tabulary}{\linewidth}[t]{|T|T|T|T|T|}' in result
+ assert r'\begin{longtable}[c]{ll}' in result
+ assert r'\begin{tabular}[t]{*{2}{\X{1}{2}}}' in result
+ assert r'\begin{tabular}[t]{\X{30}{100}\X{70}{100}}' in result
+
+
@pytest.mark.sphinx('latex', testroot='latex-table',
confoverrides={'templates_path': ['_mytemplates/latex']})
def test_latex_table_custom_template_caseA(app, status, warning):
From 3deb5275be13bb6d3da5d11f55e557199fd6bd6b Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jean-Fran=C3=A7ois=20B?=
<2589111+jfbu@users.noreply.github.com>
Date: Wed, 12 Oct 2022 17:23:21 +0200
Subject: [PATCH 11/20] Update CHANGES for PR #10759
---
CHANGES | 1 +
1 file changed, 1 insertion(+)
diff --git a/CHANGES b/CHANGES
index 19d58edb7..211fb63d5 100644
--- a/CHANGES
+++ b/CHANGES
@@ -15,6 +15,7 @@ Features added
* #10759: LaTeX: add :confval:`latex_table_style` and support the
``'booktabs'``, ``'borderless'``, and ``'colorrows'`` styles.
+ (thanks to Stefan Wiehler for initial pull requests #6666, #6671)
* #10840: One can cross-reference including an option value like ``:option:`--module=foobar```,
``:option:`--module[=foobar]``` or ``:option:`--module foobar```.
Patch by Martin Liska.
From b5ba1ede2ab62ea566b5ab944a4ccd9b1c2eecdd Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jean-Fran=C3=A7ois=20B?=
<2589111+jfbu@users.noreply.github.com>
Date: Wed, 12 Oct 2022 17:41:39 +0200
Subject: [PATCH 12/20] s/5.2.0/5.3.0 in various code comments as #10759 got
merged in 5.3.0
---
doc/conf.py | 2 +-
doc/latex.rst | 8 ++++----
doc/usage/configuration.rst | 4 ++--
sphinx/texinputs/sphinx.sty | 4 ++--
sphinx/texinputs/sphinxlatextables.sty | 18 +++++++++---------
sphinx/texinputs/sphinxpackagefootnote.sty | 2 +-
6 files changed, 19 insertions(+), 19 deletions(-)
diff --git a/doc/conf.py b/doc/conf.py
index fc7959d4d..ec845bec8 100644
--- a/doc/conf.py
+++ b/doc/conf.py
@@ -78,7 +78,7 @@ latex_elements = {
{\begin{sphinxtheindex}\end{sphinxtheindex}}
''',
'sphinxsetup': """%
-VerbatimColor=black!5,% tests 5.2.0 extended syntax
+VerbatimColor=black!5,% tests 5.3.0 extended syntax
VerbatimBorderColor={RGB}{32,32,32},%
pre_border-radius=3pt,%
pre_box-decoration-break=slice,%
diff --git a/doc/latex.rst b/doc/latex.rst
index e467ac4e9..e8fcb95f8 100644
--- a/doc/latex.rst
+++ b/doc/latex.rst
@@ -840,7 +840,7 @@ Do not use quotes to enclose values, whether numerical or strings.
.. _xcolor: https://ctan.org/pkg/xcolor
- .. versionchanged:: 5.2.0
+ .. versionchanged:: 5.3.0
Formerly only the ``\definecolor`` syntax was accepted.
``TitleColor``
@@ -896,7 +896,7 @@ Do not use quotes to enclose values, whether numerical or strings.
There is also ``TableMergeColorHeader``. If used, sets a specific colour
for merged single-row cells in the header.
- .. versionadded:: 5.2.0
+ .. versionadded:: 5.3.0
``TableRowColorOdd``
Sets the background colour for odd rows in tables (the row count starts at
@@ -908,7 +908,7 @@ Do not use quotes to enclose values, whether numerical or strings.
There is also ``TableMergeColorOdd``.
- .. versionadded:: 5.2.0
+ .. versionadded:: 5.3.0
``TableRowColorEven``
Sets the background colour for even rows in tables.
@@ -917,7 +917,7 @@ Do not use quotes to enclose values, whether numerical or strings.
There is also ``TableMergeColorEven``.
- .. versionadded:: 5.2.0
+ .. versionadded:: 5.3.0
``verbatimsep``
The separation between code lines and the frame.
diff --git a/doc/usage/configuration.rst b/doc/usage/configuration.rst
index b2196a9e5..790c18e5a 100644
--- a/doc/usage/configuration.rst
+++ b/doc/usage/configuration.rst
@@ -2256,7 +2256,7 @@ These options influence LaTeX output.
Default: ``[]``
- .. versionadded:: 5.2.0
+ .. versionadded:: 5.3.0
If using ``'booktabs'`` or ``'borderless'`` it seems recommended to also
opt for ``'colorrows'``...
@@ -2322,7 +2322,7 @@ These options influence LaTeX output.
to add ``r'\usepackage{booktabs}'`` to the LaTeX preamble.
On the other hand one can use ``colorrows`` class for individual tables
- with no extra package (as Sphinx since 5.2.0 always loads colortbl_).
+ with no extra package (as Sphinx since 5.3.0 always loads colortbl_).
.. _booktabs: https://ctan.org/pkg/booktabs
.. _colortbl: https://ctan.org/pkg/colortbl
diff --git a/sphinx/texinputs/sphinx.sty b/sphinx/texinputs/sphinx.sty
index 748f8b977..0ac55cc49 100644
--- a/sphinx/texinputs/sphinx.sty
+++ b/sphinx/texinputs/sphinx.sty
@@ -6,7 +6,7 @@
%
\NeedsTeXFormat{LaTeX2e}[1995/12/01]
-\ProvidesPackage{sphinx}[2022/08/15 v5.2.0 LaTeX package (Sphinx markup)]
+\ProvidesPackage{sphinx}[2022/08/15 v5.3.0 LaTeX package (Sphinx markup)]
% provides \ltx@ifundefined
% (many packages load ltxcmds: graphicx does for pdftex and lualatex but
@@ -90,7 +90,7 @@
{\expandafter\let\csname\@backslashchar color@#1\expandafter\endcsname
\csname\@backslashchar color@#2\endcsname }
-% (5.2.0) allow colour options to use both the \definecolor and the \colorlet
+% (5.3.0) allow colour options to use both the \definecolor and the \colorlet
% syntaxes, for example VerbatimColor={gray}{0.9} or VerbatimColor=red!10
% In the latter case we need the real \colorlet from xcolor package.
\def\spx@defineorletcolor#1{%
diff --git a/sphinx/texinputs/sphinxlatextables.sty b/sphinx/texinputs/sphinxlatextables.sty
index 247ad7a76..fee7b16bc 100644
--- a/sphinx/texinputs/sphinxlatextables.sty
+++ b/sphinx/texinputs/sphinxlatextables.sty
@@ -68,7 +68,7 @@
% \spx@arrayrulewidth is used internally and its meaning will be set according
% to the table type; no extra user code should modify it. In particular any
% \setlength{\spx@arrayrulewidth}{...} may break all of LaTeX... (really...)
-\def\spx@arrayrulewidth{\arrayrulewidth}% 5.2.0, to be adjusted by each table
+\def\spx@arrayrulewidth{\arrayrulewidth}% 5.3.0, to be adjusted by each table
% using here T (for Tabulary) feels less of a problem than the X could be
\newcolumntype{T}{J}%
% For tables allowing pagebreaks
@@ -193,7 +193,7 @@
% NOTA BENE: since the multicolumn and multirow code was written Sphinx
% decided to prefix non public internal macros by \spx@ and in fact all
% such macros here should now be prefixed by \spx@table@, but doing the
-% update is delayed to later. (written at 5.2.0)
+% update is delayed to later. (written at 5.3.0)
%%%%%%%%%%%%%%%%%%%%%
% --- MULTICOLUMN ---
@@ -236,7 +236,7 @@
% \arrayrulewidth space for each column separation in its estimate of available
% width).
%
-% Update at 5.2.0: code uses \spx@arrayrulewidth which is kept in sync with the
+% Update at 5.3.0: code uses \spx@arrayrulewidth which is kept in sync with the
% table column specification (aka preamble):
% - no | in preamble: \spx@arrayrulewidth -> \z@
% - at least a | in the preamble: \spx@arrayrulewidth -> \arrayrulewidth
@@ -267,7 +267,7 @@
% Sphinx generates no nested tables, and if some LaTeX macro uses internally a
% tabular this will not have a \sphinxstartmulticolumn within it!
%
-% 5.2.0 adds a check for multirow as single-row multi-column will allow a row
+% 5.3.0 adds a check for multirow as single-row multi-column will allow a row
% colour but multi-row multi-column should not.
% Attention that this assumes \sphinxstartmulticolumn is always followed
% in latex mark-up either by \sphinxmultirow or \begin (from \begin{varwidth}).
@@ -431,13 +431,13 @@
% to row color for the duration of the Sphinx multicolumn... the (provisional?)
% choice has been made to cancel the colortbl colours for the multicolumn
% duration.
-% Sphinx 5.2.0 comment:
+% Sphinx 5.3.0 comment:
% - colortbl has no mechanism to disable colour background in a given cell:
% \cellcolor triggers one more \color, but has no possibility to revert
% a previously emitted \color, only to override it via an additional \color
-% - prior to <5.2.0, Sphinx did not officially support colour in tables,
+% - prior to 5.3.0, Sphinx did not officially support colour in tables,
% but it did have a mechanism to protect merged cells from being partly
-% covered by colour panels at various places. At 5.2.0 this mechanism
+% covered by colour panels at various places. At 5.3.0 this mechanism
% is relaxed a bit to allow row colour for a single-row merged cell.
%
% fixcolorpanel
@@ -480,7 +480,7 @@
}
\def\spx@CT@setup@nocolor#1\endgroup{%
\global\let\CT@cell@color\relax
- % the above fix was added at 5.2.0
+ % the above fix was added at 5.3.0
% formerly a \cellcolor added by a raw latex directive in the merged cell
% would have caused colour to apply to the *next* cell after the merged
% one; we don't support \cellcolor from merged cells contents anyhow.
@@ -551,7 +551,7 @@
% The colour issue is "solved" by clearing colour panels in all cells,
% whether or not the multirow is single-column or multi-column.
%
-% MEMO at 5.2.0: to allow a multirow cell in a single column to react to
+% MEMO at 5.3.0: to allow a multirow cell in a single column to react to
% \columncolor correctly, it seems only way is that the contents
% are inserted by bottom cell (this is mentioned in multirow.sty doc, too).
% Sphinx could at Python level "move" the contents to that cell. But the
diff --git a/sphinx/texinputs/sphinxpackagefootnote.sty b/sphinx/texinputs/sphinxpackagefootnote.sty
index 39d8cfacf..55901234d 100644
--- a/sphinx/texinputs/sphinxpackagefootnote.sty
+++ b/sphinx/texinputs/sphinxpackagefootnote.sty
@@ -1,6 +1,6 @@
\NeedsTeXFormat{LaTeX2e}
\ProvidesPackage{sphinxpackagefootnote}%
- [2022/08/15 v5.2.0 Sphinx custom footnotehyper package (Sphinx team)]
+ [2022/08/15 v5.3.0 Sphinx custom footnotehyper package (Sphinx team)]
%%
%% Package: sphinxpackagefootnote
%% Version: based on footnotehyper.sty 2021/02/04 v1.1d
From cef1a030cd892cd9f0f4931e39a96331947f695b Mon Sep 17 00:00:00 2001
From: Adam Turner <9087854+AA-Turner@users.noreply.github.com>
Date: Wed, 12 Oct 2022 23:01:55 +0100
Subject: [PATCH 13/20] Update image for LaTeX job (#10916)
---
.github/workflows/latex.yml | 9 +++++----
tox.ini | 1 +
2 files changed, 6 insertions(+), 4 deletions(-)
diff --git a/.github/workflows/latex.yml b/.github/workflows/latex.yml
index 12e39f54f..e5d9930c6 100644
--- a/.github/workflows/latex.yml
+++ b/.github/workflows/latex.yml
@@ -6,15 +6,16 @@ permissions:
contents: read
jobs:
- build:
+ test:
runs-on: ubuntu-18.04
- name: Python 3.8
+ name: Test on LaTeX image
container:
- image: sphinxdoc/docker-ci
+ image: ghcr.io/sphinx-doc/sphinx-ci
env:
DO_EPUBCHECK: 1
- PATH: /python3.8/bin/:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
steps:
+ - name: Alias python3 to python
+ run: ln -s /usr/bin/python3 /usr/bin/python
- uses: actions/checkout@v3
- name: Check Python version
run: python --version
diff --git a/tox.ini b/tox.ini
index dfae569cf..834e8e78a 100644
--- a/tox.ini
+++ b/tox.ini
@@ -13,6 +13,7 @@ passenv =
PERL
PERL5LIB
PYTEST_ADDOPTS
+ DO_EPUBCHECK
EPUBCHECK_PATH
TERM
description =
From e008e162005fa51a04b61f4271d4e766cf7c671f Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Martin=20Li=C5=A1ka?=
Date: Thu, 13 Oct 2022 14:16:17 +0200
Subject: [PATCH 14/20] Revert "Fix detection for out of date files (#9360)"
(#10908)
This reverts commit b1390c4191319e75d14ce3e6e73ef43c31d981b4.
The change is reverted because some builders don't have
fine-grained support for outdated docs:
https://github.com/sphinx-doc/sphinx/issues/10903#issuecomment-1273199352
---
CHANGES | 2 --
sphinx/builders/__init__.py | 2 +-
tests/test_build_html.py | 2 ++
tests/test_build_latex.py | 2 +-
tests/test_build_manpage.py | 2 +-
tests/test_build_texinfo.py | 2 +-
6 files changed, 6 insertions(+), 6 deletions(-)
diff --git a/CHANGES b/CHANGES
index 211fb63d5..9fe27cdee 100644
--- a/CHANGES
+++ b/CHANGES
@@ -20,8 +20,6 @@ Features added
``:option:`--module[=foobar]``` or ``:option:`--module foobar```.
Patch by Martin Liska.
* #10881: autosectionlabel: Record the generated section label to the debug log.
-* #9360: Fix caching for now-outdated files for some builders (e.g. manpage)
- when there is no change in source files. Patch by Martin Liska.
Bugs fixed
----------
diff --git a/sphinx/builders/__init__.py b/sphinx/builders/__init__.py
index 6ca77c4bc..e70d1956c 100644
--- a/sphinx/builders/__init__.py
+++ b/sphinx/builders/__init__.py
@@ -347,7 +347,7 @@ class Builder:
with progress_message(__('checking consistency')):
self.env.check_consistency()
else:
- if method == 'update' and (not docnames or docnames == ['__all__']):
+ if method == 'update' and not docnames:
logger.info(bold(__('no targets are out of date.')))
return
diff --git a/tests/test_build_html.py b/tests/test_build_html.py
index 0d19de4ca..138f8a9c1 100644
--- a/tests/test_build_html.py
+++ b/tests/test_build_html.py
@@ -633,6 +633,7 @@ def test_tocdepth(app, cached_etree_parse, fname, expect):
],
}))
@pytest.mark.sphinx('singlehtml', testroot='tocdepth')
+@pytest.mark.test_params(shared_result='test_build_html_tocdepth')
def test_tocdepth_singlehtml(app, cached_etree_parse, fname, expect):
app.build()
check_xpath(cached_etree_parse(app.outdir / fname), fname, *expect)
@@ -1137,6 +1138,7 @@ def test_numfig_with_secnum_depth(app, cached_etree_parse, fname, expect):
],
}))
@pytest.mark.sphinx('singlehtml', testroot='numfig', confoverrides={'numfig': True})
+@pytest.mark.test_params(shared_result='test_build_html_numfig_on')
def test_numfig_with_singlehtml(app, cached_etree_parse, fname, expect):
app.build()
check_xpath(cached_etree_parse(app.outdir / fname), fname, *expect)
diff --git a/tests/test_build_latex.py b/tests/test_build_latex.py
index ae141a4f2..db0e67fc4 100644
--- a/tests/test_build_latex.py
+++ b/tests/test_build_latex.py
@@ -1634,7 +1634,7 @@ def test_latex_container(app, status, warning):
@pytest.mark.sphinx('latex', testroot='reST-code-role')
def test_latex_code_role(app):
- app.build(force_all=True)
+ app.build()
content = (app.outdir / 'python.tex').read_text()
common_content = (
diff --git a/tests/test_build_manpage.py b/tests/test_build_manpage.py
index 6c34e1771..8509684d1 100644
--- a/tests/test_build_manpage.py
+++ b/tests/test_build_manpage.py
@@ -42,7 +42,7 @@ def test_man_pages_empty_description(app, status, warning):
@pytest.mark.sphinx('man', testroot='basic',
confoverrides={'man_make_section_directory': True})
def test_man_make_section_directory(app, status, warning):
- app.build(force_all=True)
+ app.build()
assert (app.outdir / 'man1' / 'python.1').exists()
diff --git a/tests/test_build_texinfo.py b/tests/test_build_texinfo.py
index c60dd994b..b33a7e01f 100644
--- a/tests/test_build_texinfo.py
+++ b/tests/test_build_texinfo.py
@@ -60,7 +60,7 @@ def test_texinfo(app, status, warning):
@pytest.mark.sphinx('texinfo', testroot='markup-rubric')
def test_texinfo_rubric(app, status, warning):
- app.build(force_all=True)
+ app.build()
output = (app.outdir / 'python.texi').read_text(encoding='utf8')
assert '@heading This is a rubric' in output
From fa6d42597f2c1259ccdd9166763657bd9c2a316e Mon Sep 17 00:00:00 2001
From: Eric Wieser
Date: Thu, 13 Oct 2022 17:37:07 +0100
Subject: [PATCH 15/20] URI-escape image filenames (#10268)
Without this change, local images with `#` in their name result in incorrect URLs
There is already a similar call to `urllib.parse.quote` for file downloads, suggesting this is a sensible approach.
Co-authored-by: Adam Turner <9087854+AA-Turner@users.noreply.github.com>
Co-authored-by: Takeshi KOMIYA
---
CHANGES | 1 +
sphinx/builders/_epub_base.py | 3 ++-
sphinx/writers/html.py | 2 +-
sphinx/writers/html5.py | 2 +-
sphinx/writers/latex.py | 11 +++++++----
tests/roots/test-image-escape/conf.py | 0
tests/roots/test-image-escape/img_#1.png | Bin 0 -> 66247 bytes
tests/roots/test-image-escape/index.rst | 5 +++++
tests/test_build_html.py | 9 +++++++++
tests/test_build_latex.py | 4 ++--
10 files changed, 28 insertions(+), 9 deletions(-)
create mode 100644 tests/roots/test-image-escape/conf.py
create mode 100644 tests/roots/test-image-escape/img_#1.png
create mode 100644 tests/roots/test-image-escape/index.rst
diff --git a/CHANGES b/CHANGES
index 9fe27cdee..4df27fbbf 100644
--- a/CHANGES
+++ b/CHANGES
@@ -20,6 +20,7 @@ Features added
``:option:`--module[=foobar]``` or ``:option:`--module foobar```.
Patch by Martin Liska.
* #10881: autosectionlabel: Record the generated section label to the debug log.
+* #10268: Correctly URI-escape image filenames.
Bugs fixed
----------
diff --git a/sphinx/builders/_epub_base.py b/sphinx/builders/_epub_base.py
index 2f3917411..422fd448f 100644
--- a/sphinx/builders/_epub_base.py
+++ b/sphinx/builders/_epub_base.py
@@ -5,6 +5,7 @@ import os
import re
from os import path
from typing import Any, Dict, List, NamedTuple, Optional, Set, Tuple
+from urllib.parse import quote
from zipfile import ZIP_DEFLATED, ZIP_STORED, ZipFile
from docutils import nodes
@@ -524,7 +525,7 @@ class EpubBuilder(StandaloneHTMLBuilder):
type='epub', subtype='unknown_project_files')
continue
filename = filename.replace(os.sep, '/')
- item = ManifestItem(html.escape(filename),
+ item = ManifestItem(html.escape(quote(filename)),
html.escape(self.make_id(filename)),
html.escape(self.media_types[ext]))
metadata['manifest_items'].append(item)
diff --git a/sphinx/writers/html.py b/sphinx/writers/html.py
index 48183204d..751b2f352 100644
--- a/sphinx/writers/html.py
+++ b/sphinx/writers/html.py
@@ -620,7 +620,7 @@ class HTMLTranslator(SphinxTranslator, BaseTranslator):
# rewrite the URI if the environment knows about it
if olduri in self.builder.images:
node['uri'] = posixpath.join(self.builder.imgpath,
- self.builder.images[olduri])
+ urllib.parse.quote(self.builder.images[olduri]))
if 'scale' in node:
# Try to figure out image height and width. Docutils does that too,
diff --git a/sphinx/writers/html5.py b/sphinx/writers/html5.py
index 1a0b6f28e..344ae7f16 100644
--- a/sphinx/writers/html5.py
+++ b/sphinx/writers/html5.py
@@ -567,7 +567,7 @@ class HTML5Translator(SphinxTranslator, BaseTranslator):
# rewrite the URI if the environment knows about it
if olduri in self.builder.images:
node['uri'] = posixpath.join(self.builder.imgpath,
- self.builder.images[olduri])
+ urllib.parse.quote(self.builder.images[olduri]))
if 'scale' in node:
# Try to figure out image height and width. Docutils does that too,
diff --git a/sphinx/writers/latex.py b/sphinx/writers/latex.py
index 0e2250c17..846d365d1 100644
--- a/sphinx/writers/latex.py
+++ b/sphinx/writers/latex.py
@@ -1319,14 +1319,17 @@ class LaTeXTranslator(SphinxTranslator):
if include_graphics_options:
options = '[%s]' % ','.join(include_graphics_options)
base, ext = path.splitext(uri)
+
if self.in_title and base:
# Lowercase tokens forcely because some fncychap themes capitalize
# the options of \sphinxincludegraphics unexpectedly (ex. WIDTH=...).
- self.body.append(r'\lowercase{\sphinxincludegraphics%s}{{%s}%s}' %
- (options, base, ext))
+ cmd = r'\lowercase{\sphinxincludegraphics%s}{{%s}%s}' % (options, base, ext)
else:
- self.body.append(r'\sphinxincludegraphics%s{{%s}%s}' %
- (options, base, ext))
+ cmd = r'\sphinxincludegraphics%s{{%s}%s}' % (options, base, ext)
+ # escape filepath for includegraphics, https://tex.stackexchange.com/a/202714/41112
+ if '#' in base:
+ cmd = r'{\catcode`\#=12' + cmd + '}'
+ self.body.append(cmd)
self.body.extend(post)
def depart_image(self, node: Element) -> None:
diff --git a/tests/roots/test-image-escape/conf.py b/tests/roots/test-image-escape/conf.py
new file mode 100644
index 000000000..e69de29bb
diff --git a/tests/roots/test-image-escape/img_#1.png b/tests/roots/test-image-escape/img_#1.png
new file mode 100644
index 0000000000000000000000000000000000000000..a97e86d66af1ad396be5ece749bfe35b452aefec
GIT binary patch
literal 66247
zcmV(#K;*xPP)EaK&8zz5KX4BJqb#PZA>p^8SAiHclk?q>2$Rp^n7#Wi;q^-xRyG
z$HR;EhrZqF`p3s#)>T+Q!a;}G_`<|D{V^vX$!u1xa@&_tn*_6aqHs
zO1AUTi2eR)dweTqvM-6__s`q!pV#_JoKO^K6GnTrX;A1Ov}caL=3FjM%xsBUI5Q*m
z-0$!2C8Rb-TB#T$%|3T&2Ks>1)B-R?sH$kphO|8qfDIBeZlJ4f-AxK027P$ifEr)e
z78#8#v(|_x8gouaj)vE+K9RIme|_vRvjA1OyFE;SqR2`DB$UzB}zW(s@udMxY
zd3t$z=5*H$(LDc|`3MYhat8!vQY2vH9G1vXEWnfdIoD}ng5-I4AcBZ8D~t5DS^yU$
z=}3S@CZhUVsLEsq;C$g(FiTOYI@Cq;k#4v5G#d|>GKxT{3a)j_l|5rlVLTU+;GpVxzU>5yu=bmAN#;5#C+JbYZE
zO)5-!Ma12)QLi+wLXj*us$$a7}*QFVlGK#v?PrD&LixUi(#9^Go4iWg-{KISya
zYp5|7bwo85%AJ#xRIhBufC>;%OC$0RgO4cA6HrYx;gKC6d`_E)+=my?10R&EV-%Zt
z`}zNXWH=xzZ>uZ|_eIwY8mpGEXd6QgBpnjP?`#r5s%wPxRtF5=qyEk!dVG|
zM?uh>-(2*_fC7Ys9(7726|_<%`?BoE5ui-0(!kNpRW@CpX<3AISWO8j9U|PF5bxulvY
zQOCk}#hQRA#Dr5iremuM!GoHW2jnrpeWc3*CO7ih;b8X??oc?)7#Rv*;WZ`@)D6Qa
zB56US5tzkFiV}Ctg9(mWu^8!^gp~Y7ScN&xCMaFb14Fhj1ZDx6k}#$AI1aOsKKF;j
z7676kJ`Q0LAvZg2pTw-Htu-+jmUOvX*7x_n#yBE&K#=M{#eH)NCX@#I4cxJ0AP*Q0
z2~Yc#vQFy9%yv+cLrx6Aj39pONytXSgUZY-szLj>5u>)Iy#>^)(Yd0+_49*SbXEVn
zs><8jM}%*$e;;FyaeMpopQvA3@IoDe!$-I~!VuKfTkFCyS&_|%tu+L+VcrZ0o-Z$z
z6rpMZPR*DSWn&;E0^ob_fUg9=fyf=EdjTO2N#-omqu~b5h#pWL*L}YhO({uW{w?~T
z9b**ls85ZdDgTq!?k-dhPkMT`gx3jJ90(YN*&hl*0uFemzJ+6yXC{*(J?I2fNn0H&
zusWcb*UJW^V=2Z+>Sv=nFnl4$i+V00`%nKNmkTXx^adcy_PlP-fHK?z$O2duiN`Sl
zNLuIRS(gj9)jf)rRJ$VcSBxws`%tBO0pfrY;g>vGO(?V&6xIVqK
z?fEuR*eyU>KaR>El7%$(VKc=<_EI4S)MYy-EUjx_kX4jttVKds0≀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