mirror of
https://github.com/sphinx-doc/sphinx.git
synced 2025-02-25 18:55:22 -06:00
latex: Use templates to render tables
This commit is contained in:
parent
7edcec66c9
commit
976fc326b9
21
sphinx/templates/latex/longtable.tex_t
Normal file
21
sphinx/templates/latex/longtable.tex_t
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
\begin{longtable}<%= table.get_colspec() %>
|
||||||
|
<%- if table.caption -%>
|
||||||
|
\caption{<%= ''.join(table.caption) %>}<%= labels %>\\
|
||||||
|
<% endif -%>
|
||||||
|
\hline
|
||||||
|
<%= ''.join(table.header) -%>
|
||||||
|
\endfirsthead
|
||||||
|
|
||||||
|
\multicolumn{<%= table.colcount %>}{c}%
|
||||||
|
{{\tablecontinued{\tablename\ \thetable{} -- <%= _('continued from previous page') %>}}} \\
|
||||||
|
\hline
|
||||||
|
<%= ''.join(table.header) -%>
|
||||||
|
\endhead
|
||||||
|
|
||||||
|
\hline \multicolumn{<%= table.colcount %>}{|r|}{{\tablecontinued{<%= _('Continued on next page') %>}}} \\ \hline
|
||||||
|
\endfoot
|
||||||
|
|
||||||
|
\endlastfoot
|
||||||
|
|
||||||
|
<%= ''.join(table.body) -%>
|
||||||
|
\end{longtable}
|
12
sphinx/templates/latex/tabular.tex_t
Normal file
12
sphinx/templates/latex/tabular.tex_t
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
<%- if table.caption -%>
|
||||||
|
\begin{threeparttable}
|
||||||
|
\capstart\caption{<%= ''.join(table.caption) %>}<%= labels %>
|
||||||
|
<%- endif %>
|
||||||
|
\noindent\begin{tabular}<%= table.get_colspec() -%>
|
||||||
|
\hline
|
||||||
|
<%= ''.join(table.header) -%>
|
||||||
|
<%= ''.join(table.body) -%>
|
||||||
|
\end{tabular}
|
||||||
|
<%- if table.caption -%>
|
||||||
|
\end{threeparttable}
|
||||||
|
<%- endif -%>
|
12
sphinx/templates/latex/tabulary.tex_t
Normal file
12
sphinx/templates/latex/tabulary.tex_t
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
<%- if table.caption -%>
|
||||||
|
\begin{threeparttable}
|
||||||
|
\capstart\caption{<%= ''.join(table.caption) %>}<%= labels %>
|
||||||
|
<%- endif %>
|
||||||
|
\noindent\begin{tabulary}{\linewidth}<%= table.get_colspec() -%>
|
||||||
|
\hline
|
||||||
|
<%= ''.join(table.header) -%>
|
||||||
|
<%= ''.join(table.body) -%>
|
||||||
|
\end{tabulary}
|
||||||
|
<%- if table.caption -%>
|
||||||
|
\end{threeparttable}
|
||||||
|
<%- endif -%>
|
@ -43,8 +43,10 @@ class FileRenderer(BaseRenderer):
|
|||||||
|
|
||||||
|
|
||||||
class SphinxRenderer(FileRenderer):
|
class SphinxRenderer(FileRenderer):
|
||||||
def __init__(self):
|
def __init__(self, template_path=None):
|
||||||
super(SphinxRenderer, self).__init__(os.path.join(package_dir, 'templates'))
|
if template_path is None:
|
||||||
|
template_path = os.path.join(package_dir, 'templates')
|
||||||
|
super(SphinxRenderer, self).__init__(template_path)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def render_from_file(cls, filename, context):
|
def render_from_file(cls, filename, context):
|
||||||
@ -53,7 +55,8 @@ class SphinxRenderer(FileRenderer):
|
|||||||
|
|
||||||
class LaTeXRenderer(SphinxRenderer):
|
class LaTeXRenderer(SphinxRenderer):
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
super(LaTeXRenderer, self).__init__()
|
template_path = os.path.join(package_dir, 'templates', 'latex')
|
||||||
|
super(LaTeXRenderer, self).__init__(template_path)
|
||||||
|
|
||||||
# use JSP/eRuby like tagging instead because curly bracket; the default
|
# use JSP/eRuby like tagging instead because curly bracket; the default
|
||||||
# tagging of jinja2 is not good for LaTeX sources.
|
# tagging of jinja2 is not good for LaTeX sources.
|
||||||
|
@ -46,7 +46,6 @@ BEGIN_DOC = r'''
|
|||||||
'''
|
'''
|
||||||
|
|
||||||
|
|
||||||
DEFAULT_TEMPLATE = 'latex/content.tex_t'
|
|
||||||
URI_SCHEMES = ('mailto:', 'http:', 'https:', 'ftp:')
|
URI_SCHEMES = ('mailto:', 'http:', 'https:', 'ftp:')
|
||||||
SECNUMDEPTH = 3
|
SECNUMDEPTH = 3
|
||||||
|
|
||||||
@ -316,19 +315,49 @@ class ShowUrlsTransform(object):
|
|||||||
|
|
||||||
|
|
||||||
class Table(object):
|
class Table(object):
|
||||||
def __init__(self):
|
def __init__(self, node):
|
||||||
# type: () -> None
|
# type: (nodes.table) -> None
|
||||||
self.classes = []
|
self.header = [] # type: List[unicode]
|
||||||
|
self.body = [] # type: List[unicode]
|
||||||
|
self.classes = node.get('classes', []) # type: List[unicode]
|
||||||
self.col = 0
|
self.col = 0
|
||||||
self.colcount = 0
|
self.colcount = 0
|
||||||
self.colspec = None # type: unicode
|
self.colspec = None # type: unicode
|
||||||
self.colwidths = [] # type: List[int]
|
self.colwidths = [] # type: List[int]
|
||||||
self.rowcount = 0
|
self.rowcount = 0
|
||||||
self.had_head = False
|
|
||||||
self.has_problematic = False
|
self.has_problematic = False
|
||||||
self.has_verbatim = False
|
self.has_verbatim = False
|
||||||
self.caption = None # type: List[unicode]
|
self.caption = None # type: List[unicode]
|
||||||
self.longtable = False
|
|
||||||
|
def is_longtable(self):
|
||||||
|
# type: () -> bool
|
||||||
|
return self.rowcount > 30 or 'longtable' in self.classes
|
||||||
|
|
||||||
|
def get_table_type(self):
|
||||||
|
# type: () -> unicode
|
||||||
|
if self.is_longtable():
|
||||||
|
return 'longtable'
|
||||||
|
elif self.has_verbatim:
|
||||||
|
return 'tabular'
|
||||||
|
elif self.has_problematic and not self.colspec:
|
||||||
|
return 'tabular'
|
||||||
|
else:
|
||||||
|
return 'tabulary'
|
||||||
|
|
||||||
|
def get_colspec(self):
|
||||||
|
# type: () -> unicode
|
||||||
|
if self.colspec:
|
||||||
|
return self.colspec
|
||||||
|
elif self.colwidths and 'colwidths-given' in self.classes:
|
||||||
|
total = sum(self.colwidths)
|
||||||
|
colspecs = ['\\X{%d}{%d}' % (width, total) for width in self.colwidths]
|
||||||
|
return '{|%s|}\n' % '|'.join(colspecs)
|
||||||
|
elif self.has_problematic:
|
||||||
|
return '{|*{%d}{\\X{1}{%d}|}}\n' % (self.colcount, self.colcount)
|
||||||
|
elif self.is_longtable():
|
||||||
|
return '{|' + ('l|' * self.colcount) + '}\n'
|
||||||
|
else:
|
||||||
|
return '{|' + ('L|' * self.colcount) + '}\n'
|
||||||
|
|
||||||
|
|
||||||
def escape_abbr(text):
|
def escape_abbr(text):
|
||||||
@ -606,7 +635,7 @@ class LaTeXTranslator(nodes.NodeVisitor):
|
|||||||
if path.exists(template_path):
|
if path.exists(template_path):
|
||||||
return LaTeXRenderer().render(template_path, self.elements)
|
return LaTeXRenderer().render(template_path, self.elements)
|
||||||
else:
|
else:
|
||||||
return LaTeXRenderer().render(DEFAULT_TEMPLATE, self.elements)
|
return LaTeXRenderer().render('content.tex_t', self.elements)
|
||||||
|
|
||||||
def hypertarget(self, id, withdoc=True, anchor=True):
|
def hypertarget(self, id, withdoc=True, anchor=True):
|
||||||
# type: (unicode, bool, bool) -> unicode
|
# type: (unicode, bool, bool) -> unicode
|
||||||
@ -1166,95 +1195,29 @@ class LaTeXTranslator(nodes.NodeVisitor):
|
|||||||
raise UnsupportedError(
|
raise UnsupportedError(
|
||||||
'%s:%s: nested tables are not yet implemented.' %
|
'%s:%s: nested tables are not yet implemented.' %
|
||||||
(self.curfilestack[-1], node.line or ''))
|
(self.curfilestack[-1], node.line or ''))
|
||||||
self.table = Table()
|
self.table = Table(node)
|
||||||
self.table.classes = node['classes']
|
if self.next_table_colspec:
|
||||||
self.table.longtable = 'longtable' in node['classes']
|
self.table.colspec = '{%s}\n' % self.next_table_colspec
|
||||||
self.tablebody = [] # type: List[unicode]
|
self.next_table_colspec = None
|
||||||
self.tableheaders = [] # type: List[unicode]
|
|
||||||
# Redirect body output until table is finished.
|
|
||||||
self.pushbody(self.tablebody)
|
|
||||||
self.restrict_footnote(node)
|
self.restrict_footnote(node)
|
||||||
|
|
||||||
def depart_table(self, node):
|
def depart_table(self, node):
|
||||||
# type: (nodes.Node) -> None
|
# type: (nodes.Node) -> None
|
||||||
if self.table.rowcount > 30:
|
labels = '' # type: unicode
|
||||||
self.table.longtable = True
|
for labelid in self.pop_hyperlink_ids('table'):
|
||||||
self.popbody()
|
labels += self.hypertarget(labelid, anchor=False)
|
||||||
if not self.table.longtable and self.table.caption is not None:
|
|
||||||
self.body.append('\n\n\\begin{threeparttable}\n'
|
|
||||||
'\\capstart\\caption{')
|
|
||||||
for caption in self.table.caption:
|
|
||||||
self.body.append(caption)
|
|
||||||
self.body.append('}')
|
|
||||||
for id in self.pop_hyperlink_ids('table'):
|
|
||||||
self.body.append(self.hypertarget(id, anchor=False))
|
|
||||||
if node['ids']:
|
if node['ids']:
|
||||||
self.body.append(self.hypertarget(node['ids'][0], anchor=False))
|
labels += self.hypertarget(node['ids'][0], anchor=False)
|
||||||
if self.table.longtable:
|
|
||||||
self.body.append('\n\\begin{longtable}')
|
table_type = self.table.get_table_type()
|
||||||
endmacro = '\\end{longtable}\n\n'
|
table = LaTeXRenderer().render(table_type + '.tex_t',
|
||||||
elif self.table.has_verbatim:
|
dict(table=self.table, labels=labels))
|
||||||
self.body.append('\n\\noindent\\begin{tabular}')
|
self.body.append("\n\n")
|
||||||
endmacro = '\\end{tabular}\n\n'
|
self.body.append(table)
|
||||||
elif self.table.colspec:
|
self.body.append("\n\n")
|
||||||
self.body.append('\n\\noindent\\begin{tabulary}{\\linewidth}')
|
|
||||||
endmacro = '\\end{tabulary}\n\n'
|
|
||||||
elif self.table.has_problematic or self.table.colwidths:
|
|
||||||
self.body.append('\n\\noindent\\begin{tabular}')
|
|
||||||
endmacro = '\\end{tabular}\n\n'
|
|
||||||
else:
|
|
||||||
self.body.append('\n\\noindent\\begin{tabulary}{\\linewidth}')
|
|
||||||
endmacro = '\\end{tabulary}\n\n'
|
|
||||||
if self.table.colspec:
|
|
||||||
self.body.append(self.table.colspec)
|
|
||||||
elif self.table.colwidths and 'colwidths-given' in self.table.classes:
|
|
||||||
total = sum(self.table.colwidths)
|
|
||||||
colspec = ['\\X{%d}{%d}' % (width, total)
|
|
||||||
for width in self.table.colwidths]
|
|
||||||
self.body.append('{|%s|}\n' % '|'.join(colspec))
|
|
||||||
elif self.table.has_problematic:
|
|
||||||
colspec = ('*{%d}{\\X{1}{%d}|}' %
|
|
||||||
(self.table.colcount, self.table.colcount))
|
|
||||||
self.body.append('{|' + colspec + '}\n')
|
|
||||||
elif self.table.longtable:
|
|
||||||
self.body.append('{|' + ('l|' * self.table.colcount) + '}\n')
|
|
||||||
else:
|
|
||||||
self.body.append('{|' + ('L|' * self.table.colcount) + '}\n')
|
|
||||||
if self.table.longtable and self.table.caption is not None:
|
|
||||||
self.body.append(u'\\caption{')
|
|
||||||
for caption in self.table.caption:
|
|
||||||
self.body.append(caption)
|
|
||||||
self.body.append('}')
|
|
||||||
for id in self.pop_hyperlink_ids('table'):
|
|
||||||
self.body.append(self.hypertarget(id, anchor=False))
|
|
||||||
if node['ids']:
|
|
||||||
self.body.append(self.hypertarget(node['ids'][0], anchor=False))
|
|
||||||
self.body.append(u'\\\\\n')
|
|
||||||
if self.table.longtable:
|
|
||||||
self.body.append('\\hline\n')
|
|
||||||
self.body.extend(self.tableheaders)
|
|
||||||
self.body.append('\\endfirsthead\n\n')
|
|
||||||
self.body.append('\\multicolumn{%s}{c}%%\n' % self.table.colcount)
|
|
||||||
self.body.append(r'{{\tablecontinued{\tablename\ \thetable{} -- %s}}} \\'
|
|
||||||
% _('continued from previous page'))
|
|
||||||
self.body.append('\n\\hline\n')
|
|
||||||
self.body.extend(self.tableheaders)
|
|
||||||
self.body.append('\\endhead\n\n')
|
|
||||||
self.body.append(r'\hline \multicolumn{%s}{|r|}{{\tablecontinued{%s}}} \\ \hline'
|
|
||||||
% (self.table.colcount,
|
|
||||||
_('Continued on next page')))
|
|
||||||
self.body.append('\n\\endfoot\n\n')
|
|
||||||
self.body.append('\\endlastfoot\n\n')
|
|
||||||
else:
|
|
||||||
self.body.append('\\hline\n')
|
|
||||||
self.body.extend(self.tableheaders)
|
|
||||||
self.body.extend(self.tablebody)
|
|
||||||
self.body.append(endmacro)
|
|
||||||
if not self.table.longtable and self.table.caption is not None:
|
|
||||||
self.body.append('\\end{threeparttable}\n\n')
|
|
||||||
self.unrestrict_footnote(node)
|
self.unrestrict_footnote(node)
|
||||||
self.table = None
|
self.table = None
|
||||||
self.tablebody = None
|
|
||||||
|
|
||||||
def visit_colspec(self, node):
|
def visit_colspec(self, node):
|
||||||
# type: (nodes.Node) -> None
|
# type: (nodes.Node) -> None
|
||||||
@ -1276,25 +1239,19 @@ class LaTeXTranslator(nodes.NodeVisitor):
|
|||||||
|
|
||||||
def visit_thead(self, node):
|
def visit_thead(self, node):
|
||||||
# type: (nodes.Node) -> None
|
# type: (nodes.Node) -> None
|
||||||
self.table.had_head = True
|
self.pushbody(self.table.header) # Redirect head output until header is finished.
|
||||||
if self.next_table_colspec:
|
|
||||||
self.table.colspec = '{%s}\n' % self.next_table_colspec
|
|
||||||
self.next_table_colspec = None
|
|
||||||
# Redirect head output until header is finished. see visit_tbody.
|
|
||||||
self.body = self.tableheaders
|
|
||||||
|
|
||||||
def depart_thead(self, node):
|
def depart_thead(self, node):
|
||||||
# type: (nodes.Node) -> None
|
# type: (nodes.Node) -> None
|
||||||
pass
|
self.popbody()
|
||||||
|
|
||||||
def visit_tbody(self, node):
|
def visit_tbody(self, node):
|
||||||
# type: (nodes.Node) -> None
|
# type: (nodes.Node) -> None
|
||||||
if not self.table.had_head:
|
self.pushbody(self.table.body) # Redirect body output until table is finished.
|
||||||
self.visit_thead(node)
|
|
||||||
self.body = self.tablebody
|
|
||||||
|
|
||||||
def depart_tbody(self, node):
|
def depart_tbody(self, node):
|
||||||
# type: (nodes.Node) -> None
|
# type: (nodes.Node) -> None
|
||||||
|
self.popbody()
|
||||||
self.remember_multirow = {}
|
self.remember_multirow = {}
|
||||||
self.remember_multirowcol = {}
|
self.remember_multirowcol = {}
|
||||||
|
|
||||||
|
@ -841,10 +841,8 @@ def test_latex_table(app, status, warning):
|
|||||||
|
|
||||||
# table having :widths: option
|
# table having :widths: option
|
||||||
table = tables['table having :widths: option']
|
table = tables['table having :widths: option']
|
||||||
assert ('\\noindent\\begin{tabulary}{\\linewidth}{'
|
assert ('\\noindent\\begin{tabulary}{\\linewidth}'
|
||||||
'|p{\\dimexpr(\\linewidth-\\arrayrulewidth)*30/100-2\\tabcolsep-\\arrayrulewidth\\relax}'
|
'{|\\X{30}{100}|\\X{70}{100}|}' in table)
|
||||||
'|p{\\dimexpr(\\linewidth-\\arrayrulewidth)*70/100-2\\tabcolsep-\\arrayrulewidth\\relax}|}'
|
|
||||||
in table)
|
|
||||||
assert ('\\hline\n'
|
assert ('\\hline\n'
|
||||||
'\\sphinxstylethead{\\relax \nheader1\n\\unskip}\\relax &'
|
'\\sphinxstylethead{\\relax \nheader1\n\\unskip}\\relax &'
|
||||||
'\\sphinxstylethead{\\relax \nheader2\n\\unskip}\\relax' in table)
|
'\\sphinxstylethead{\\relax \nheader2\n\\unskip}\\relax' in table)
|
||||||
@ -869,22 +867,15 @@ def test_latex_table(app, status, warning):
|
|||||||
|
|
||||||
# table having verbatim
|
# table having verbatim
|
||||||
table = tables['table having verbatim']
|
table = tables['table having verbatim']
|
||||||
assert ('\\noindent\\begin{tabular}{|*{2}{'
|
assert ('\\noindent\\begin{tabular}{|*{2}{\\X{1}{2}|}}\n\\hline' in table)
|
||||||
'p{\\dimexpr(\\linewidth-\\arrayrulewidth)/2-2\\tabcolsep-\\arrayrulewidth\\relax}|}}\n'
|
|
||||||
'\\hline' in table)
|
|
||||||
|
|
||||||
# table having problematic cell
|
# table having problematic cell
|
||||||
table = tables['table having problematic cell']
|
table = tables['table having problematic cell']
|
||||||
assert ('\\noindent\\begin{tabular}{|*{2}{'
|
assert ('\\noindent\\begin{tabular}{|*{2}{\\X{1}{2}|}}\n\\hline' in table)
|
||||||
'p{\\dimexpr(\\linewidth-\\arrayrulewidth)/2-2\\tabcolsep-\\arrayrulewidth\\relax}|}}\n'
|
|
||||||
'\\hline' in table)
|
|
||||||
|
|
||||||
# table having both :widths: and problematic cell
|
# table having both :widths: and problematic cell
|
||||||
table = tables['table having both :widths: and problematic cell']
|
table = tables['table having both :widths: and problematic cell']
|
||||||
assert ('\\noindent\\begin{tabular}{'
|
assert ('\\noindent\\begin{tabular}{|\\X{30}{100}|\\X{70}{100}|}' in table)
|
||||||
'|p{\\dimexpr(\\linewidth-\\arrayrulewidth)*30/100-2\\tabcolsep-\\arrayrulewidth\\relax}'
|
|
||||||
'|p{\\dimexpr(\\linewidth-\\arrayrulewidth)*70/100-2\\tabcolsep-\\arrayrulewidth\\relax}|}'
|
|
||||||
in table)
|
|
||||||
|
|
||||||
# longtable
|
# longtable
|
||||||
table = tables['longtable']
|
table = tables['longtable']
|
||||||
@ -909,10 +900,7 @@ def test_latex_table(app, status, warning):
|
|||||||
|
|
||||||
# longtable having :widths: option
|
# longtable having :widths: option
|
||||||
table = tables['longtable having :widths: option']
|
table = tables['longtable having :widths: option']
|
||||||
assert ('\\begin{longtable}{'
|
assert ('\\begin{longtable}{|\\X{30}{100}|\\X{70}{100}|}' in table)
|
||||||
'|p{\\dimexpr(\\linewidth-\\arrayrulewidth)*30/100-2\\tabcolsep-\\arrayrulewidth\\relax}'
|
|
||||||
'|p{\\dimexpr(\\linewidth-\\arrayrulewidth)*70/100-2\\tabcolsep-\\arrayrulewidth\\relax}|}'
|
|
||||||
in table)
|
|
||||||
|
|
||||||
# longtable having caption
|
# longtable having caption
|
||||||
table = tables['longtable having caption']
|
table = tables['longtable having caption']
|
||||||
@ -921,19 +909,12 @@ def test_latex_table(app, status, warning):
|
|||||||
|
|
||||||
# longtable having verbatim
|
# longtable having verbatim
|
||||||
table = tables['longtable having verbatim']
|
table = tables['longtable having verbatim']
|
||||||
assert ('\\begin{longtable}{|*{2}{'
|
assert ('\\begin{longtable}{|*{2}{\\X{1}{2}|}}\n\\hline' in table)
|
||||||
'p{\\dimexpr(\\linewidth-\\arrayrulewidth)/2-2\\tabcolsep-\\arrayrulewidth\\relax}|}}\n'
|
|
||||||
'\\hline' in table)
|
|
||||||
|
|
||||||
# longtable having problematic cell
|
# longtable having problematic cell
|
||||||
table = tables['longtable having problematic cell']
|
table = tables['longtable having problematic cell']
|
||||||
assert ('\\begin{longtable}{|*{2}{'
|
assert ('\\begin{longtable}{|*{2}{\\X{1}{2}|}}\n\\hline' in table)
|
||||||
'p{\\dimexpr(\\linewidth-\\arrayrulewidth)/2-2\\tabcolsep-\\arrayrulewidth\\relax}|}}\n'
|
|
||||||
'\\hline' in table)
|
|
||||||
|
|
||||||
# longtable having both :widths: and problematic cell
|
# longtable having both :widths: and problematic cell
|
||||||
table = tables['longtable having both :widths: and problematic cell']
|
table = tables['longtable having both :widths: and problematic cell']
|
||||||
assert ('\\begin{longtable}{'
|
assert ('\\begin{longtable}{|\\X{30}{100}|\\X{70}{100}|}' in table)
|
||||||
'|p{\\dimexpr(\\linewidth-\\arrayrulewidth)*30/100-2\\tabcolsep-\\arrayrulewidth\\relax}'
|
|
||||||
'|p{\\dimexpr(\\linewidth-\\arrayrulewidth)*70/100-2\\tabcolsep-\\arrayrulewidth\\relax}|}'
|
|
||||||
in table)
|
|
||||||
|
Loading…
Reference in New Issue
Block a user