mirror of
https://github.com/sphinx-doc/sphinx.git
synced 2025-02-25 18:55:22 -06:00
latex: Refactor spanning cells
This commit is contained in:
parent
029e9908c0
commit
8b2c92d54f
@ -15,6 +15,7 @@
|
||||
import re
|
||||
import sys
|
||||
from os import path
|
||||
from collections import defaultdict
|
||||
|
||||
from six import itervalues, text_type
|
||||
from docutils import nodes, writers
|
||||
@ -321,17 +322,19 @@ class Table(object):
|
||||
self.body = [] # type: List[unicode]
|
||||
self.classes = node.get('classes', []) # type: List[unicode]
|
||||
self.col = 0
|
||||
self.row = 0
|
||||
self.colcount = 0
|
||||
self.colspec = None # type: unicode
|
||||
self.colwidths = [] # type: List[int]
|
||||
self.rowcount = 0
|
||||
self.has_problematic = False
|
||||
self.has_verbatim = False
|
||||
self.caption = None # type: List[unicode]
|
||||
self.cells = defaultdict(int) # type: Dict[Tuple[int, int], int]
|
||||
self.cell_id = 0
|
||||
|
||||
def is_longtable(self):
|
||||
# type: () -> bool
|
||||
return self.rowcount > 30 or 'longtable' in self.classes
|
||||
return self.row > 30 or 'longtable' in self.classes
|
||||
|
||||
def get_table_type(self):
|
||||
# type: () -> unicode
|
||||
@ -361,6 +364,52 @@ class Table(object):
|
||||
else:
|
||||
return '{|' + ('l|' * self.colcount) + '}\n'
|
||||
|
||||
def add_cell(self, height, width):
|
||||
self.cell_id += 1
|
||||
for col in range(width):
|
||||
for row in range(height):
|
||||
assert self.cells[(self.row + row, self.col + col)] == 0
|
||||
self.cells[(self.row + row, self.col + col)] = self.cell_id
|
||||
|
||||
def cell(self, row=None, col=None):
|
||||
try:
|
||||
if row is None:
|
||||
row = self.row
|
||||
if col is None:
|
||||
col = self.col
|
||||
return TableCell(self, row, col)
|
||||
except IndexError:
|
||||
return None
|
||||
|
||||
|
||||
class TableCell(object):
|
||||
def __init__(self, table, row, col):
|
||||
if table.cells[(row, col)] == 0:
|
||||
raise IndexError
|
||||
|
||||
self.table = table
|
||||
self.cell_id = table.cells[(row, col)]
|
||||
for n in range(row + 1):
|
||||
if table.cells[(row - n, col)] == self.cell_id:
|
||||
self.row = row - n
|
||||
for n in range(col + 1):
|
||||
if table.cells[(row, col - n)] == self.cell_id:
|
||||
self.col = col - n
|
||||
|
||||
@property
|
||||
def width(self):
|
||||
width = 0
|
||||
while self.table.cells[(self.row, self.col + width)] == self.cell_id:
|
||||
width += 1
|
||||
return width
|
||||
|
||||
@property
|
||||
def height(self):
|
||||
height = 0
|
||||
while self.table.cells[(self.row + height, self.col)] == self.cell_id:
|
||||
height += 1
|
||||
return height
|
||||
|
||||
|
||||
def escape_abbr(text):
|
||||
# type: (unicode) -> unicode
|
||||
@ -417,8 +466,6 @@ class LaTeXTranslator(nodes.NodeVisitor):
|
||||
self.in_parsed_literal = 0
|
||||
self.compact_list = 0
|
||||
self.first_param = 0
|
||||
self.remember_multirow = {} # type: Dict[int, int]
|
||||
self.remember_multirowcol = {} # type: Dict[int, int]
|
||||
|
||||
# determine top section level
|
||||
if builder.config.latex_toplevel_sectioning:
|
||||
@ -1254,72 +1301,53 @@ class LaTeXTranslator(nodes.NodeVisitor):
|
||||
def depart_tbody(self, node):
|
||||
# type: (nodes.Node) -> None
|
||||
self.popbody()
|
||||
self.remember_multirow = {}
|
||||
self.remember_multirowcol = {}
|
||||
|
||||
def visit_row(self, node):
|
||||
# type: (nodes.Node) -> None
|
||||
self.table.col = 0
|
||||
for key, value in self.remember_multirow.items():
|
||||
if not value and key in self.remember_multirowcol:
|
||||
del self.remember_multirowcol[key]
|
||||
|
||||
# fill column if first one is a wide-multirow
|
||||
cell = self.table.cell(self.table.row, 0)
|
||||
if cell and cell.row != self.table.row: # bottom part of multirow cell
|
||||
self.table.col += cell.width
|
||||
if cell.width > 1: # use \multicolumn for wide multirow cell
|
||||
self.body.append('\\multicolumn{%d}{|l|}{}\\relax ' % cell.width)
|
||||
|
||||
def depart_row(self, node):
|
||||
# type: (nodes.Node) -> None
|
||||
self.body.append('\\\\\n')
|
||||
if any(self.remember_multirow.values()):
|
||||
linestart = 1
|
||||
col = self.table.colcount
|
||||
for col in range(1, self.table.col + 1):
|
||||
if self.remember_multirow.get(col):
|
||||
if linestart != col:
|
||||
linerange = str(linestart) + '-' + str(col - 1)
|
||||
self.body.append('\\cline{' + linerange + '}')
|
||||
linestart = col + 1
|
||||
if self.remember_multirowcol.get(col, 0):
|
||||
linestart += self.remember_multirowcol[col]
|
||||
if linestart <= col:
|
||||
linerange = str(linestart) + '-' + str(col)
|
||||
self.body.append('\\cline{' + linerange + '}')
|
||||
else:
|
||||
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('\\hline')
|
||||
self.table.rowcount += 1
|
||||
else:
|
||||
i = 0
|
||||
underlined.extend([False]) # sentinel
|
||||
while i < len(underlined):
|
||||
if underlined[i] is True:
|
||||
j = underlined[i:].index(False)
|
||||
self.body.append('\\cline{%d-%d}' % (i + 1, i + j))
|
||||
i += j
|
||||
i += 1
|
||||
self.table.row += 1
|
||||
|
||||
def visit_entry(self, node):
|
||||
# type: (nodes.Node) -> None
|
||||
if self.table.col == 0:
|
||||
while self.remember_multirow.get(self.table.col + 1, 0):
|
||||
self.table.col += 1
|
||||
self.remember_multirow[self.table.col] -= 1
|
||||
if self.remember_multirowcol.get(self.table.col, 0):
|
||||
extracols = self.remember_multirowcol[self.table.col]
|
||||
self.body.append('\\multicolumn{')
|
||||
self.body.append(str(extracols + 1))
|
||||
self.body.append('}{|l|}{}\\relax ')
|
||||
self.table.col += extracols
|
||||
self.body.append('&')
|
||||
else:
|
||||
if self.table.col > 0:
|
||||
self.body.append('&')
|
||||
self.table.col += 1
|
||||
self.table.add_cell(node.get('morerows', 0) + 1, node.get('morecols', 0) + 1)
|
||||
cell = self.table.cell()
|
||||
context = ''
|
||||
if 'morecols' in node:
|
||||
self.body.append('\\multicolumn{')
|
||||
self.body.append(str(node.get('morecols') + 1))
|
||||
if self.table.col == 1:
|
||||
self.body.append('}{|l|}{\\relax ')
|
||||
if cell.width > 1:
|
||||
self.body.append('\\multicolumn{%d}' % cell.width)
|
||||
if self.table.col == 0:
|
||||
self.body.append('{|l|}{\\relax ')
|
||||
else:
|
||||
self.body.append('}{l|}{\\relax ')
|
||||
self.body.append('{l|}{\\relax ')
|
||||
context += '\\unskip}\\relax '
|
||||
if 'morerows' in node:
|
||||
self.body.append('\\multirow{')
|
||||
self.body.append(str(node.get('morerows') + 1))
|
||||
self.body.append('}{*}{\\relax ')
|
||||
if cell.height > 1:
|
||||
self.body.append('\\multirow{%d}{*}{\\relax ' % cell.height)
|
||||
context += '\\unskip}\\relax '
|
||||
self.remember_multirow[self.table.col] = node.get('morerows')
|
||||
if 'morecols' in node:
|
||||
if 'morerows' in node:
|
||||
self.remember_multirowcol[self.table.col] = node.get('morecols')
|
||||
self.table.col += node.get('morecols')
|
||||
if (('morecols' in node or 'morerows' in node) and
|
||||
(len(node) > 2 or len(node.astext().split('\n')) > 2)):
|
||||
self.in_merged_cell = 1
|
||||
@ -1333,16 +1361,6 @@ class LaTeXTranslator(nodes.NodeVisitor):
|
||||
else:
|
||||
self.body.append('\\sphinxstylethead{\\relax ')
|
||||
context += '\\unskip}\\relax '
|
||||
while self.remember_multirow.get(self.table.col + 1, 0):
|
||||
self.table.col += 1
|
||||
self.remember_multirow[self.table.col] -= 1
|
||||
context += '&'
|
||||
if self.remember_multirowcol.get(self.table.col, 0):
|
||||
extracols = self.remember_multirowcol[self.table.col]
|
||||
context += '\\multicolumn{'
|
||||
context += str(extracols + 1)
|
||||
context += '}{l|}{}\\relax '
|
||||
self.table.col += extracols
|
||||
if len(node.traverse(nodes.paragraph)) >= 2:
|
||||
self.table.has_problematic = True
|
||||
self.context.append(context)
|
||||
@ -1361,6 +1379,17 @@ class LaTeXTranslator(nodes.NodeVisitor):
|
||||
self.body.append(line)
|
||||
self.body.append(self.context.pop()) # header
|
||||
|
||||
cell = self.table.cell()
|
||||
self.table.col += cell.width
|
||||
|
||||
# fill column if next one is a wide-multirow
|
||||
nextcell = self.table.cell()
|
||||
if nextcell and nextcell.row != self.table.row: # bottom part of multirow cell
|
||||
self.table.col += nextcell.width
|
||||
self.body.append('&')
|
||||
if nextcell.width > 1: # use \multicolumn for wide multirow cell
|
||||
self.body.append('\\multicolumn{%d}{l|}{}\\relax ' % nextcell.width)
|
||||
|
||||
def visit_acks(self, node):
|
||||
# type: (nodes.Node) -> None
|
||||
# this is a list in the source, but should be rendered as a
|
||||
|
@ -12,6 +12,23 @@ cell2-1 cell2-2
|
||||
cell3-1 cell3-2
|
||||
======= =======
|
||||
|
||||
grid table
|
||||
----------
|
||||
|
||||
+---------+---------+---------+
|
||||
| header1 | header2 | header3 |
|
||||
+=========+=========+=========+
|
||||
| cell1-1 | cell1-2 | cell1-3 |
|
||||
+---------+ +---------+
|
||||
| cell2-1 | | cell2-2 |
|
||||
+ +---------+---------+
|
||||
| | cell3-2 |
|
||||
+---------+ |
|
||||
| cell4-1 | |
|
||||
+---------+---------+---------+
|
||||
| cell5-1 |
|
||||
+---------+---------+---------+
|
||||
|
||||
table having :widths: option
|
||||
----------------------------
|
||||
|
||||
|
@ -839,6 +839,23 @@ def test_latex_table(app, status, warning):
|
||||
assert ('\\hline\ncell3-1\n&\ncell3-2\n\\\\' in table)
|
||||
assert ('\\hline\n\\end{tabulary}' in table)
|
||||
|
||||
# grid table
|
||||
table = tables['grid table']
|
||||
assert ('\\noindent\\begin{tabulary}{\\linewidth}{|L|L|L|}' in table)
|
||||
assert ('\\hline\n'
|
||||
'\\sphinxstylethead{\\relax \nheader1\n\\unskip}\\relax &'
|
||||
'\\sphinxstylethead{\\relax \nheader2\n\\unskip}\\relax &'
|
||||
'\\sphinxstylethead{\\relax \nheader3\n\\unskip}\\relax \\\\' in table)
|
||||
assert ('\\hline\ncell1-1\n&\\multirow{2}{*}{\\relax \ncell1-2\n\\unskip}\\relax &\n'
|
||||
'cell1-3\n\\\\' in table)
|
||||
assert ('\\cline{1-1}\\cline{3-3}\\multirow{2}{*}{\\relax \ncell2-1\n\\unskip}\\relax &&\n'
|
||||
'cell2-2\n\\\\' in table)
|
||||
assert ('\\cline{2-3}&\\multicolumn{2}{l|}{\\relax \\multirow{2}{*}{\\relax \n'
|
||||
'cell3-2\n\\unskip}\\relax \\unskip}\\relax \\\\' in table)
|
||||
assert ('\\cline{1-1}\ncell4-1\n&\\multicolumn{2}{l|}{}\\relax \\\\' in table)
|
||||
assert ('\\hline\\multicolumn{3}{|l|}{\\relax \ncell5-1\n\\unskip}\\relax \\\\\n'
|
||||
'\\hline\n\\end{tabulary}' in table)
|
||||
|
||||
# table having :widths: option
|
||||
table = tables['table having :widths: option']
|
||||
assert ('\\noindent\\begin{tabular}{|\\X{30}{100}|\\X{70}{100}|}' in table)
|
||||
|
Loading…
Reference in New Issue
Block a user