Skip to content

Commit

Permalink
Add support for booktabs-style tables to LaTeX builder
Browse files Browse the repository at this point in the history
Render tables without vertical rules and horizontal rules of varying
thickness (with additional space above and below) using the booktabs
package.

This is a rebase of original commit which was on master branch prior to
2.1 release.  In this rebase the \RequirePackage{booktabs} has been
removed from sphinx.sty, as it has to be conditional on latex_booktabs
(or whatever its name will be) configuration option, and will be
repositioned in a subsequent commit.
  • Loading branch information
Stefan Wiehler authored and jfbu committed Aug 6, 2022
1 parent 59056aa commit 53f8840
Show file tree
Hide file tree
Showing 6 changed files with 89 additions and 11 deletions.
8 changes: 8 additions & 0 deletions doc/usage/configuration.rst
Expand Up @@ -2241,6 +2241,14 @@ These options influence LaTeX output.

.. versionadded:: 1.8

.. confval:: latex_booktabs

If ``True``, render tables without vertical rules and horizontal rules of
varying thickness (with additional space above and below) using the
``booktabs`` package.

.. versionadded:: 2.1

.. confval:: latex_elements

.. versionadded:: 0.5
Expand Down
1 change: 1 addition & 0 deletions sphinx/builders/latex/__init__.py
Expand Up @@ -517,6 +517,7 @@ def setup(app: Sphinx) -> Dict[str, Any]:
app.add_config_value('latex_appendices', [], None)
app.add_config_value('latex_use_latex_multicolumn', False, None)
app.add_config_value('latex_use_xindy', default_latex_use_xindy, None, [bool])
app.add_config_value('latex_booktabs', False, None)
app.add_config_value('latex_toplevel_sectioning', None, None,
ENUM(None, 'part', 'chapter', 'section'))
app.add_config_value('latex_domain_indices', True, None, [list])
Expand Down
26 changes: 26 additions & 0 deletions sphinx/templates/latex/longtable.tex_t
Expand Up @@ -10,25 +10,51 @@
<%- if table.caption -%>
\sphinxthelongtablecaptionisattop
\caption{<%= ''.join(table.caption) %>\strut}<%= labels %>\\*[\sphinxlongtablecapskipadjust]
<%- if table.booktabs -%>
\toprule
<% else -%>
\hline
<% endif -%>
<% elif labels -%>
<%- if table.booktabs -%>
\toprule\noalign{\phantomsection<%= labels %>}%
<% else -%>
\hline\noalign{\phantomsection<%= labels %>}%
<% endif -%>
<% else -%>
<%- if table.booktabs -%>
\toprule
<% else -%>
\hline
<% endif -%>
<% endif -%>
<%= ''.join(table.header) %>
\endfirsthead

\multicolumn{<%= table.colcount %>}{c}%
{\makebox[0pt]{\sphinxtablecontinued{\tablename\ \thetable{} \textendash{} <%= _('continued from previous page') %>}}}\\
<%- if table.booktabs -%>
\toprule
<% else -%>
\hline
<% endif -%>
<%= ''.join(table.header) %>
<%- if table.header and table.booktabs -%>
\midrule
<% endif -%>
\endhead

<%- if table.booktabs -%>
\bottomrule
<% else -%>
\hline
<% endif -%>
\multicolumn{<%= table.colcount %>}{r}{\makebox[0pt][r]{\sphinxtablecontinued{<%= _('continues on next page') %>}}}\\
\endfoot

\endlastfoot
<%= ''.join(table.body) %>
<%- if table.booktabs -%>
\bottomrule
<% endif -%>
\end{longtable}\sphinxatlongtableend\end{savenotes}
10 changes: 10 additions & 0 deletions sphinx/templates/latex/tabular.tex_t
Expand Up @@ -19,9 +19,19 @@
\phantomsection<%= labels %>\nobreak
<% endif -%>
\begin{tabular}[t]<%= table.get_colspec() -%>
<%- if table.booktabs -%>
\toprule
<%- else -%>
\hline
<%- endif -%>
<%= ''.join(table.header) %>
<%- if table.header and table.booktabs -%>
\midrule
<%- endif -%>
<%=- ''.join(table.body) %>
<%- if table.booktabs -%>
\bottomrule
<%- endif -%>
\end{tabular}
\par
\sphinxattableend\end{savenotes}
10 changes: 10 additions & 0 deletions sphinx/templates/latex/tabulary.tex_t
Expand Up @@ -19,9 +19,19 @@
\phantomsection<%= labels %>\nobreak
<% endif -%>
\begin{tabulary}{\linewidth}[t]<%= table.get_colspec() -%>
<%- if table.booktabs -%>
\toprule
<%- else -%>
\hline
<%- endif -%>
<%= ''.join(table.header) %>
<%- if table.header and table.booktabs -%>
\midrule
<%- endif -%>
<%=- ''.join(table.body) %>
<%- if table.booktabs -%>
\bottomrule
<%- endif -%>
\end{tabulary}
\par
\sphinxattableend\end{savenotes}
45 changes: 34 additions & 11 deletions sphinx/writers/latex.py
Expand Up @@ -150,16 +150,21 @@ def get_colspec(self) -> str:
elif 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}' % (self.colsep, self.colsep.join(colspecs),
self.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}}' % (self.colsep, self.colcount,
self.colcount, self.colsep) + CR
elif self.get_table_type() == 'tabulary':
# sphinx.sty sets T to be J by default.
return '{|' + ('T|' * self.colcount) + '}' + CR
return '{' + self.colsep + (('T' + self.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}}' % (self.colsep, self.colcount,
self.colcount, self.colsep) + CR
else:
return '{|' + ('l|' * self.colcount) + '}' + CR
return '{' + self.colsep + (('l' + self.colsep) * self.colcount) + \
'}' + CR

def add_cell(self, height: int, width: int) -> None:
"""Adds a new cell to a table.
Expand Down Expand Up @@ -863,6 +868,12 @@ def visit_table(self, node: Element) -> None:
logger.info(__('both tabularcolumns and :widths: option are given. '
':widths: is ignored.'), location=node)
self.next_table_colspec = None
if self.builder.config.latex_booktabs:
self.table.booktabs = True
self.table.colsep = ''
else:
self.table.booktabs = False
self.table.colsep = '|'

def depart_table(self, node: Element) -> None:
labels = self.hypertarget_to(node)
Expand Down Expand Up @@ -921,11 +932,16 @@ def visit_row(self, node: Element) -> None:
# 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}'
r'{\sphinxtablestrut{%d}}' %
(cell.width, self.table.colsep,
self.table.colsep, cell.cell_id))

def depart_row(self, node: Element) -> None:
self.body.append(r'\\' + CR)
if self.table.booktabs:
self.table.row += 1
return
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):
Expand All @@ -950,9 +966,14 @@ def visit_entry(self, node: Element) -> None:
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,
self.table.colsep,
self.table.colsep)) + CR
else:
self.body.append(r'\multicolumn{%d}{l|}{%%' % cell.width + CR)
self.body.append(r'\multicolumn{%d}{l%s}{%%' %
(cell.width,
self.table.colsep)) + CR
context = '}%' + CR
else:
self.body.append(r'\sphinxstartmulticolumn{%d}%%' % cell.width + CR)
Expand Down Expand Up @@ -1007,8 +1028,10 @@ def depart_entry(self, node: Element) -> None:
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))
self.body.append(r'\multicolumn{%d}{l%s}'
r'{\sphinxtablestrut{%d}}' %
(nextcell.width, self.table.colsep,
nextcell.cell_id))

def visit_acks(self, node: Element) -> None:
# this is a list in the source, but should be rendered as a
Expand Down

0 comments on commit 53f8840

Please sign in to comment.