summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTakeshi KOMIYA <i.tkomiya@gmail.com>2017-02-04 17:06:53 +0900
committerTakeshi KOMIYA <i.tkomiya@gmail.com>2017-02-04 20:17:49 +0900
commit976fc326b9f91e9d16bb29a76b9daf07c6afc92e (patch)
treea9ad9d943a2c10e81b10fe2702d518689624b23a
parent7edcec66c9f79c9ba47e54a3e1a381b4e2599327 (diff)
downloadsphinx-git-976fc326b9f91e9d16bb29a76b9daf07c6afc92e.tar.gz
latex: Use templates to render tables
-rw-r--r--sphinx/templates/latex/longtable.tex_t21
-rw-r--r--sphinx/templates/latex/tabular.tex_t12
-rw-r--r--sphinx/templates/latex/tabulary.tex_t12
-rw-r--r--sphinx/util/template.py9
-rw-r--r--sphinx/writers/latex.py163
-rw-r--r--tests/test_build_latex.py37
6 files changed, 120 insertions, 134 deletions
diff --git a/sphinx/templates/latex/longtable.tex_t b/sphinx/templates/latex/longtable.tex_t
new file mode 100644
index 000000000..694483ce2
--- /dev/null
+++ b/sphinx/templates/latex/longtable.tex_t
@@ -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}
diff --git a/sphinx/templates/latex/tabular.tex_t b/sphinx/templates/latex/tabular.tex_t
new file mode 100644
index 000000000..7448d6c1f
--- /dev/null
+++ b/sphinx/templates/latex/tabular.tex_t
@@ -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 -%>
diff --git a/sphinx/templates/latex/tabulary.tex_t b/sphinx/templates/latex/tabulary.tex_t
new file mode 100644
index 000000000..959eadcbd
--- /dev/null
+++ b/sphinx/templates/latex/tabulary.tex_t
@@ -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 -%>
diff --git a/sphinx/util/template.py b/sphinx/util/template.py
index 01d365994..f6db8034b 100644
--- a/sphinx/util/template.py
+++ b/sphinx/util/template.py
@@ -43,8 +43,10 @@ class FileRenderer(BaseRenderer):
class SphinxRenderer(FileRenderer):
- def __init__(self):
- super(SphinxRenderer, self).__init__(os.path.join(package_dir, 'templates'))
+ def __init__(self, template_path=None):
+ if template_path is None:
+ template_path = os.path.join(package_dir, 'templates')
+ super(SphinxRenderer, self).__init__(template_path)
@classmethod
def render_from_file(cls, filename, context):
@@ -53,7 +55,8 @@ class SphinxRenderer(FileRenderer):
class LaTeXRenderer(SphinxRenderer):
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
# tagging of jinja2 is not good for LaTeX sources.
diff --git a/sphinx/writers/latex.py b/sphinx/writers/latex.py
index 0799e9237..ab716e86c 100644
--- a/sphinx/writers/latex.py
+++ b/sphinx/writers/latex.py
@@ -46,7 +46,6 @@ BEGIN_DOC = r'''
'''
-DEFAULT_TEMPLATE = 'latex/content.tex_t'
URI_SCHEMES = ('mailto:', 'http:', 'https:', 'ftp:')
SECNUMDEPTH = 3
@@ -316,19 +315,49 @@ class ShowUrlsTransform(object):
class Table(object):
- def __init__(self):
- # type: () -> None
- self.classes = []
+ def __init__(self, node):
+ # type: (nodes.table) -> None
+ self.header = [] # type: List[unicode]
+ self.body = [] # type: List[unicode]
+ self.classes = node.get('classes', []) # type: List[unicode]
self.col = 0
self.colcount = 0
- self.colspec = None # type: unicode
- self.colwidths = [] # type: List[int]
+ self.colspec = None # type: unicode
+ self.colwidths = [] # type: List[int]
self.rowcount = 0
- self.had_head = False
self.has_problematic = False
self.has_verbatim = False
- self.caption = None # type: List[unicode]
- self.longtable = False
+ self.caption = None # type: List[unicode]
+
+ 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):
@@ -606,7 +635,7 @@ class LaTeXTranslator(nodes.NodeVisitor):
if path.exists(template_path):
return LaTeXRenderer().render(template_path, self.elements)
else:
- return LaTeXRenderer().render(DEFAULT_TEMPLATE, self.elements)
+ return LaTeXRenderer().render('content.tex_t', self.elements)
def hypertarget(self, id, withdoc=True, anchor=True):
# type: (unicode, bool, bool) -> unicode
@@ -1166,95 +1195,29 @@ class LaTeXTranslator(nodes.NodeVisitor):
raise UnsupportedError(
'%s:%s: nested tables are not yet implemented.' %
(self.curfilestack[-1], node.line or ''))
- self.table = Table()
- self.table.classes = node['classes']
- self.table.longtable = 'longtable' in node['classes']
- self.tablebody = [] # type: List[unicode]
- self.tableheaders = [] # type: List[unicode]
- # Redirect body output until table is finished.
- self.pushbody(self.tablebody)
+ self.table = Table(node)
+ if self.next_table_colspec:
+ self.table.colspec = '{%s}\n' % self.next_table_colspec
+ self.next_table_colspec = None
self.restrict_footnote(node)
def depart_table(self, node):
# type: (nodes.Node) -> None
- if self.table.rowcount > 30:
- self.table.longtable = True
- self.popbody()
- 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']:
- self.body.append(self.hypertarget(node['ids'][0], anchor=False))
- if self.table.longtable:
- self.body.append('\n\\begin{longtable}')
- endmacro = '\\end{longtable}\n\n'
- elif self.table.has_verbatim:
- self.body.append('\n\\noindent\\begin{tabular}')
- endmacro = '\\end{tabular}\n\n'
- elif self.table.colspec:
- 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')
+ labels = '' # type: unicode
+ for labelid in self.pop_hyperlink_ids('table'):
+ labels += self.hypertarget(labelid, anchor=False)
+ if node['ids']:
+ labels += self.hypertarget(node['ids'][0], anchor=False)
+
+ table_type = self.table.get_table_type()
+ table = LaTeXRenderer().render(table_type + '.tex_t',
+ dict(table=self.table, labels=labels))
+ self.body.append("\n\n")
+ self.body.append(table)
+ self.body.append("\n\n")
+
self.unrestrict_footnote(node)
self.table = None
- self.tablebody = None
def visit_colspec(self, node):
# type: (nodes.Node) -> None
@@ -1276,25 +1239,19 @@ class LaTeXTranslator(nodes.NodeVisitor):
def visit_thead(self, node):
# type: (nodes.Node) -> None
- self.table.had_head = True
- 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
+ self.pushbody(self.table.header) # Redirect head output until header is finished.
def depart_thead(self, node):
# type: (nodes.Node) -> None
- pass
+ self.popbody()
def visit_tbody(self, node):
# type: (nodes.Node) -> None
- if not self.table.had_head:
- self.visit_thead(node)
- self.body = self.tablebody
+ self.pushbody(self.table.body) # Redirect body output until table is finished.
def depart_tbody(self, node):
# type: (nodes.Node) -> None
+ self.popbody()
self.remember_multirow = {}
self.remember_multirowcol = {}
diff --git a/tests/test_build_latex.py b/tests/test_build_latex.py
index a7fa95168..534270e45 100644
--- a/tests/test_build_latex.py
+++ b/tests/test_build_latex.py
@@ -841,10 +841,8 @@ def test_latex_table(app, status, warning):
# table having :widths: option
table = tables['table having :widths: option']
- assert ('\\noindent\\begin{tabulary}{\\linewidth}{'
- '|p{\\dimexpr(\\linewidth-\\arrayrulewidth)*30/100-2\\tabcolsep-\\arrayrulewidth\\relax}'
- '|p{\\dimexpr(\\linewidth-\\arrayrulewidth)*70/100-2\\tabcolsep-\\arrayrulewidth\\relax}|}'
- in table)
+ assert ('\\noindent\\begin{tabulary}{\\linewidth}'
+ '{|\\X{30}{100}|\\X{70}{100}|}' in table)
assert ('\\hline\n'
'\\sphinxstylethead{\\relax \nheader1\n\\unskip}\\relax &'
'\\sphinxstylethead{\\relax \nheader2\n\\unskip}\\relax' in table)
@@ -869,22 +867,15 @@ def test_latex_table(app, status, warning):
# table having verbatim
table = tables['table having verbatim']
- assert ('\\noindent\\begin{tabular}{|*{2}{'
- 'p{\\dimexpr(\\linewidth-\\arrayrulewidth)/2-2\\tabcolsep-\\arrayrulewidth\\relax}|}}\n'
- '\\hline' in table)
+ assert ('\\noindent\\begin{tabular}{|*{2}{\\X{1}{2}|}}\n\\hline' in table)
# table having problematic cell
table = tables['table having problematic cell']
- assert ('\\noindent\\begin{tabular}{|*{2}{'
- 'p{\\dimexpr(\\linewidth-\\arrayrulewidth)/2-2\\tabcolsep-\\arrayrulewidth\\relax}|}}\n'
- '\\hline' in table)
+ assert ('\\noindent\\begin{tabular}{|*{2}{\\X{1}{2}|}}\n\\hline' in table)
# table having both :widths: and problematic cell
table = tables['table having both :widths: and problematic cell']
- assert ('\\noindent\\begin{tabular}{'
- '|p{\\dimexpr(\\linewidth-\\arrayrulewidth)*30/100-2\\tabcolsep-\\arrayrulewidth\\relax}'
- '|p{\\dimexpr(\\linewidth-\\arrayrulewidth)*70/100-2\\tabcolsep-\\arrayrulewidth\\relax}|}'
- in table)
+ assert ('\\noindent\\begin{tabular}{|\\X{30}{100}|\\X{70}{100}|}' in table)
# longtable
table = tables['longtable']
@@ -909,10 +900,7 @@ def test_latex_table(app, status, warning):
# longtable having :widths: option
table = tables['longtable having :widths: option']
- assert ('\\begin{longtable}{'
- '|p{\\dimexpr(\\linewidth-\\arrayrulewidth)*30/100-2\\tabcolsep-\\arrayrulewidth\\relax}'
- '|p{\\dimexpr(\\linewidth-\\arrayrulewidth)*70/100-2\\tabcolsep-\\arrayrulewidth\\relax}|}'
- in table)
+ assert ('\\begin{longtable}{|\\X{30}{100}|\\X{70}{100}|}' in table)
# longtable having caption
table = tables['longtable having caption']
@@ -921,19 +909,12 @@ def test_latex_table(app, status, warning):
# longtable having verbatim
table = tables['longtable having verbatim']
- assert ('\\begin{longtable}{|*{2}{'
- 'p{\\dimexpr(\\linewidth-\\arrayrulewidth)/2-2\\tabcolsep-\\arrayrulewidth\\relax}|}}\n'
- '\\hline' in table)
+ assert ('\\begin{longtable}{|*{2}{\\X{1}{2}|}}\n\\hline' in table)
# longtable having problematic cell
table = tables['longtable having problematic cell']
- assert ('\\begin{longtable}{|*{2}{'
- 'p{\\dimexpr(\\linewidth-\\arrayrulewidth)/2-2\\tabcolsep-\\arrayrulewidth\\relax}|}}\n'
- '\\hline' in table)
+ assert ('\\begin{longtable}{|*{2}{\\X{1}{2}|}}\n\\hline' in table)
# longtable having both :widths: and problematic cell
table = tables['longtable having both :widths: and problematic cell']
- assert ('\\begin{longtable}{'
- '|p{\\dimexpr(\\linewidth-\\arrayrulewidth)*30/100-2\\tabcolsep-\\arrayrulewidth\\relax}'
- '|p{\\dimexpr(\\linewidth-\\arrayrulewidth)*70/100-2\\tabcolsep-\\arrayrulewidth\\relax}|}'
- in table)
+ assert ('\\begin{longtable}{|\\X{30}{100}|\\X{70}{100}|}' in table)