Escaping, table layout, and document fixes
Quoted Klammertext specials (^@ ^| ^# ^^ ^: ^*) and ^'...'^ regions now survive re-processing (held as escape markers until final output); :after_apply phase functions receive and return raw target text. Tables: :hpos element position (center|left|right|<length>) replaces the unimplemented :center/:indent; the ranged cell override is renamed :justify; :column_width works in html (colgroup widths) and gains 'fill' -- the remaining width, capped at the column's widest entry, in both targets; a table wider than the text column warns on the console; table edges without an outer line set their text flush on the margins. @document: no empty title bar for untitled documents; @vfill fills to the bottom of the window in html (pure CSS); @vspace in plain text; new @dot klammer; monospace email links.
This commit is contained in:
@@ -21,11 +21,18 @@ td {
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
/*
|
||||
td:first-child {
|
||||
/* Edge cells of a table with no outer vertical line (classes emitted by
|
||||
table.py): the outer padding is dropped so the cell text aligns with
|
||||
the text margin. With an outer line the padding stays -- text against
|
||||
a border looks worse than text inset from a margin. The tex
|
||||
counterpart is @{} in the column spec. */
|
||||
.Fl {
|
||||
padding-left: 0;
|
||||
}
|
||||
*/
|
||||
|
||||
.Fr {
|
||||
padding-right: 0;
|
||||
}
|
||||
.line_top {
|
||||
border-top: 1px black solid;
|
||||
}
|
||||
@@ -75,6 +82,21 @@ td:first-child {
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
/* A cell of a 'fit' column mixed with sized columns: nowrap floors the
|
||||
column at its widest entry (the tex \widthof semantics). In a
|
||||
full-width table (fractions/'*') the Wpct 1% width is added -- the
|
||||
classic shrink idiom, so the column survives surplus distribution and
|
||||
the extra window width flows to the sized columns. In a content-sized
|
||||
'fill' table Wpct must NOT be used: a percentage cell blows an
|
||||
auto-width table up to full width. */
|
||||
.Wfit {
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.Wpct {
|
||||
width: 1%;
|
||||
}
|
||||
|
||||
.cell_arrow {
|
||||
padding: 1rem;
|
||||
font-size: 1.5rem;
|
||||
|
||||
@@ -28,3 +28,11 @@
|
||||
|
||||
\newsavebox{\tablebox}
|
||||
\newlength{\tableboxwidth}
|
||||
|
||||
% Computed widths of :column_width 'fill' columns (up to four per table):
|
||||
% min(share of the remaining width, widest entry), set per table in the
|
||||
% generated LaTeX via calc's \minof/\ratio.
|
||||
\newlength{\klfilla}
|
||||
\newlength{\klfillb}
|
||||
\newlength{\klfillc}
|
||||
\newlength{\klfilld}
|
||||
|
||||
@@ -30,11 +30,16 @@
|
||||
|
||||
@@@argtype column_width |
|
||||
width of the table columns. Each column is one of 'fit' (widest line of the
|
||||
cells in that column), a fraction 0.0->1.0 (that fraction of the total table
|
||||
width), or '*' (use the remaining width of the table; there can only be one
|
||||
column with '*'). If there are fewer positions than columns in the table, the
|
||||
last value is repeated. Extra positions generate a warning.
|
||||
:pattern (fit^|f^|0?\.\d+^|\*^|\s+)+
|
||||
cells in that column), 'fill' (the remaining width of the table after the
|
||||
other columns, but no more than the column's widest line -- the table stops
|
||||
growing once nothing needs a line break), a fraction 0.0->1.0 (that
|
||||
fraction of the total table width), or '*' (the remaining width,
|
||||
unconditionally -- the table always spans the full width). Several 'fill'
|
||||
columns divide the remaining width in proportion to their widest lines;
|
||||
'fill' cannot be combined with a fraction or '*'. If there are fewer
|
||||
positions than columns in the table, the last value is repeated. Extra
|
||||
positions generate a warning.
|
||||
:pattern (fill^|fit^|f^|0?\.\d+^|\*^|\s+)+
|
||||
:python_cast (lambda s : s.split())
|
||||
:default fit
|
||||
@@@
|
||||
@@ -154,15 +159,15 @@
|
||||
:default period
|
||||
@@@
|
||||
|
||||
@@@argtype table_hpos |
|
||||
cell position overrides, as one or more <cells> <position> pairs
|
||||
@@@argtype table_justify |
|
||||
cell justification overrides, as one or more <cells> <position> pairs
|
||||
separated by semicolons (the same list style as ^:calc). <cells> is an
|
||||
indexed_range selecting cells; <position> is l, c, or r and overrides
|
||||
the column position given by ^:cell_hpos for those cells. A colspan
|
||||
anchor's override positions the whole merged cell. For example,
|
||||
the column justification given by ^:cell_hpos for those cells. A colspan
|
||||
anchor's override justifies the whole merged cell. For example,
|
||||
"-3--1(3) r" right-justifies the cells in column 3 of the last three
|
||||
rows.
|
||||
# Coarse check ("<cells> <position>" pairs); hpos_overrides() in
|
||||
# Coarse check ("<cells> <position>" pairs); justify_overrides() in
|
||||
# table.py validates the range and position.
|
||||
:pattern \s*([^^\s;]+\s+[lcr]\s*(;\s*^|\s*$))+
|
||||
@@@
|
||||
@@ -188,8 +193,7 @@
|
||||
@@table rows.rest(2)
|
||||
:id
|
||||
@caption_arguments@
|
||||
:center.bool true
|
||||
:indent.length 1em
|
||||
:hpos.element_hpos
|
||||
:header.bool true
|
||||
:allow_break.bool false
|
||||
:column_width.column_width
|
||||
@@ -197,7 +201,7 @@
|
||||
:vline.table_vline
|
||||
:grid.bool false
|
||||
:cell_hpos.cell_hpos
|
||||
:hpos.table_hpos
|
||||
:justify.table_justify
|
||||
:header_font.font i
|
||||
:font.font_list
|
||||
:colspan.table_span
|
||||
|
||||
@@ -85,7 +85,38 @@ class Table(klammer_base.Klammer_base):
|
||||
self.cell_hpos = extend(parse_hpos(self.K_target, self.cell_hpos), self.row_size)
|
||||
#self.cell_hpos = self.cell_hpos.split(";")
|
||||
self.font = extend(self.font, self.row_size)
|
||||
self.hpos_map = self.hpos_overrides() if self.hpos else {}
|
||||
# In html, a 'fit' column mixed with sized columns must be clamped
|
||||
# to its widest entry (tex's \widthof semantics) or it absorbs the
|
||||
# window width: its cells get nowrap (class Wfit; see table.css).
|
||||
# In a full-width table (fractions/'*') they also need the classic
|
||||
# 1%-shrink width (class Wpct) to survive surplus distribution; in
|
||||
# a 'fill' table the table is content-sized (max-width) and a
|
||||
# percentage would blow it up to full width, so nowrap alone.
|
||||
# All-'fit' tables shrink to content anyway and keep wrapping.
|
||||
widths = extend(self.column_width, self.row_size)
|
||||
self.fill_columns = [i for i, w in enumerate(widths) if w == "fill"]
|
||||
if self.fill_columns:
|
||||
if any(w not in ("fit", "f", "fill") for w in widths):
|
||||
raise Exception(
|
||||
':column_width: "fill" cannot be combined with a '
|
||||
'fraction or "*" -- fill computes the remaining width '
|
||||
'itself')
|
||||
if len(self.fill_columns) > 4:
|
||||
raise Exception(
|
||||
':column_width: at most four "fill" columns are '
|
||||
'supported')
|
||||
mixed = not all(w in ("fit", "f") for w in widths)
|
||||
self.fit_columns = {i for i, w in enumerate(widths)
|
||||
if mixed and w in ("fit", "f")}
|
||||
self.fit_class = "Wfit" if self.fill_columns else "Wfit Wpct"
|
||||
# A table edge with no outer vertical line drops its outer cell
|
||||
# padding (html Fl/Fr classes, tex @{}) so the edge cells' text
|
||||
# aligns with the text margin; with an outer line the padding
|
||||
# stays -- text against a border looks worse than text inset
|
||||
# from a margin.
|
||||
self.flush_l = 0 not in self.s_vline.by_index
|
||||
self.flush_r = self.row_size not in self.s_vline.by_index
|
||||
self.justify_map = self.justify_overrides() if self.justify else {}
|
||||
self.make_cells(self.rows)
|
||||
|
||||
# Calculated cell values (:calc). A calculation is
|
||||
@@ -438,10 +469,10 @@ class Table(klammer_base.Klammer_base):
|
||||
# colspan anchor's override positions the whole merged cell; in the tex
|
||||
# target an ordinary overridden cell is wrapped in \multicolumn{1}.
|
||||
|
||||
def hpos_overrides(self):
|
||||
def justify_overrides(self):
|
||||
result = {}
|
||||
for stmt in [s.strip() for s in self.hpos.split(";") if s.strip()]:
|
||||
ctx = f'In :hpos "{stmt}"'
|
||||
for stmt in [s.strip() for s in self.justify.split(";") if s.strip()]:
|
||||
ctx = f'In :justify "{stmt}"'
|
||||
parts = stmt.split()
|
||||
if len(parts) != 2 or parts[1] not in ("l", "c", "r"):
|
||||
self.selector_error(
|
||||
@@ -476,7 +507,7 @@ class Table(klammer_base.Klammer_base):
|
||||
# boundary at the END of the merged region.
|
||||
right_i = cell_i + max(cspan, 1)
|
||||
bottom_i = row_i + max(rspan, 1)
|
||||
hpos = self.hpos_map.get((row_i, cell_i), self.cell_hpos[cell_i])
|
||||
hpos = self.justify_map.get((row_i, cell_i), self.cell_hpos[cell_i])
|
||||
row_cells.append(
|
||||
table_cell.Cell(
|
||||
cell,
|
||||
@@ -489,13 +520,54 @@ class Table(klammer_base.Klammer_base):
|
||||
self.s_vline.by_index.get(right_i),
|
||||
rspan, cspan,
|
||||
first_column=(cell_i == 0),
|
||||
hpos_forced=(row_i, cell_i) in self.hpos_map))
|
||||
hpos_forced=(row_i, cell_i) in self.justify_map,
|
||||
fit_class=(self.fit_class
|
||||
if cell_i in self.fit_columns else ""),
|
||||
flush_left=(self.flush_l and cell_i == 0),
|
||||
flush_right=(self.flush_r and
|
||||
cell_i + max(cspan, 1) == self.row_size)))
|
||||
cells.append(row_cells)
|
||||
self.cells = cells
|
||||
self.column_width_text()
|
||||
|
||||
# HTML
|
||||
|
||||
def html_colgroup(self):
|
||||
# The CSS counterpart of tex_hpos()'s width resolution: 'fit' is the
|
||||
# widest entry of the column and no larger (tex's \widthof — the
|
||||
# cells' Wfit class clamps it there), a
|
||||
# fraction is of the text column, and '*' shares the width the sized
|
||||
# columns leave over. All-'fit' (the default) needs no markup at
|
||||
# all. With no 'fit' column the fixed layout makes the fractions
|
||||
# exact (long content wraps, as LaTeX's p{} columns do); a 'fit'
|
||||
# column forces the auto layout, where the fraction widths are
|
||||
# honored approximately.
|
||||
widths = extend(self.column_width, self.row_size)
|
||||
if all(w in ("fit", "f") for w in widths):
|
||||
return "", "", None
|
||||
fractions = sum(float(w) for w in widths if w not in ("fit", "f", "*"))
|
||||
if any(w in ("fit", "f", "*") for w in widths):
|
||||
table_width = "100%"
|
||||
else:
|
||||
table_width = f"{min(fractions, 1) * 100:g}%"
|
||||
cols = ""
|
||||
for w in widths:
|
||||
if w in ("fit", "f"):
|
||||
# Clamped to the widest entry by the cells' Wfit class
|
||||
# (width 1% + nowrap; see table.css) -- a px width on the
|
||||
# <col> does NOT survive surplus distribution when no '*'
|
||||
# column exists (seen in both Firefox and Chrome).
|
||||
cols += E("col").str()
|
||||
elif w == "*":
|
||||
cols += E("col").str()
|
||||
else:
|
||||
share = float(w) if table_width == "100%" else float(w) / fractions
|
||||
cols += E("col").attr("style", f"width: {share * 100:g}%").str()
|
||||
layout = "" if any(w in ("fit", "f") for w in widths) else "table-layout: fixed; "
|
||||
# No blank line before </colgroup>: @document's insert_missing_ids
|
||||
# would wrap it as a stray <p> inside the table.
|
||||
return E("colgroup").body(cols.strip(), newline=False).str(), layout, table_width
|
||||
|
||||
def html(self):
|
||||
result = ""
|
||||
for row_i, row in enumerate(self.cells):
|
||||
@@ -505,21 +577,86 @@ class Table(klammer_base.Klammer_base):
|
||||
continue
|
||||
row_html += cell.html().strip() + "\n"
|
||||
result += E("tr").body(row_html).str()
|
||||
result = E("table").body(result)
|
||||
if self.fill_columns:
|
||||
# 'fill': the table sizes itself -- the browser's auto layout
|
||||
# computes min(available, widest entries) natively, so the fill
|
||||
# column grows only until nothing needs a line break. Several
|
||||
# fill columns share in proportion to their content (the auto
|
||||
# algorithm), matching the tex \ratio division. The max-width
|
||||
# cap goes on the caption wrapper when there is one (the table's
|
||||
# own percentage would be circular in a shrink-to-fit wrapper).
|
||||
result = E("table").body(result)
|
||||
if self.number or self.caption:
|
||||
result = html_util.add_caption(
|
||||
result, "Table", self.number, self.caption,
|
||||
self.caption_font, hpos=self.hpos,
|
||||
side=self.caption_side,
|
||||
font_size=self.caption_font_size, max_width="100%")
|
||||
else:
|
||||
result.attr("style", "max-width: 100%")
|
||||
result = html_util.hpos_container(result, self.hpos).str()
|
||||
return result
|
||||
colgroup, layout, table_width = self.html_colgroup()
|
||||
result = E("table").body(colgroup + result)
|
||||
if self.number or self.caption:
|
||||
# The caption wrapper carries the width (a percentage on the
|
||||
# shrink-to-fit wrapper itself would be circular) and the table
|
||||
# fills it -- which also makes the caption track the table.
|
||||
if table_width:
|
||||
result.attr("style", f"{layout}width: 100%")
|
||||
result = html_util.add_caption(
|
||||
result, "Table", self.number, self.caption, self.caption_font,
|
||||
side=self.caption_side, font_size=self.caption_font_size)
|
||||
hpos=self.hpos, side=self.caption_side,
|
||||
font_size=self.caption_font_size, width=table_width)
|
||||
else:
|
||||
result = result.str()
|
||||
# An uncaptioned table still gets the position container, so
|
||||
# html and tex agree on where the table sits.
|
||||
if table_width:
|
||||
result.attr("style", f"{layout}width: {table_width}")
|
||||
result = html_util.hpos_container(result, self.hpos).str()
|
||||
return result
|
||||
|
||||
# LaTeX
|
||||
|
||||
# The length registers holding computed 'fill' column widths, declared
|
||||
# in table.sty; one per fill column, in column order.
|
||||
fill_registers = ["\\klfilla", "\\klfillb", "\\klfillc", "\\klfilld"]
|
||||
|
||||
def tex_fill_widths(self):
|
||||
# Set each 'fill' column's register to min(its share of the
|
||||
# remaining width, its widest entry) -- the same rule the html auto
|
||||
# layout applies. The shares divide the remaining width in
|
||||
# proportion to the columns' widest entries (calc's \ratio): either
|
||||
# the space covers them all and every column caps at its widest
|
||||
# entry, or no column caps and all the space is used -- no stranded
|
||||
# whitespace, and no iterative redistribution.
|
||||
if not self.fill_columns:
|
||||
return ""
|
||||
widths = extend(self.column_width, self.row_size)
|
||||
fit = [f"\\widthof{{{self.column_widths[i]}}}"
|
||||
for i, w in enumerate(widths) if w in ("fit", "f")]
|
||||
remaining = "\\tablewidth" + "".join(f" - {e}" for e in fit)
|
||||
widest = {i: f"\\widthof{{{self.column_widths[i]}}}"
|
||||
for i in self.fill_columns}
|
||||
total = " + ".join(widest[i] for i in self.fill_columns)
|
||||
result = ""
|
||||
for k, i in enumerate(self.fill_columns):
|
||||
reg = Table.fill_registers[k]
|
||||
if len(self.fill_columns) == 1:
|
||||
result += (f"\\setlength{{{reg}}}"
|
||||
f"{{\\minof{{{remaining}}}{{{widest[i]}}}}}\n")
|
||||
else:
|
||||
result += (f"\\setlength{{{reg}}}{{({remaining})"
|
||||
f"*\\ratio{{{widest[i]}}}{{{total}}}}}\n")
|
||||
result += (f"\\setlength{{{reg}}}"
|
||||
f"{{\\minof{{{reg}}}{{{widest[i]}}}}}\n")
|
||||
return result
|
||||
|
||||
def tex_hpos(self):
|
||||
# One column specification per column: the width comes from
|
||||
# :column_width ('fit', a fraction of \tablewidth, or '*' for the
|
||||
# remaining width), the justification from :cell_hpos.
|
||||
# :column_width ('fit', 'fill' via its precomputed register, a
|
||||
# fraction of \tablewidth, or '*' for the remaining width), the
|
||||
# justification from :cell_hpos.
|
||||
def par_format(s, justification):
|
||||
command = {"l" : "raggedright",
|
||||
"c" : "centering",
|
||||
@@ -527,9 +664,13 @@ class Table(klammer_base.Klammer_base):
|
||||
return f">{{\\{command}}}p{{{s}}}"
|
||||
|
||||
widths = []
|
||||
fill_ordinal = 0
|
||||
for i, w in enumerate(extend(self.column_width, self.row_size)):
|
||||
if w in ("fit", "f"):
|
||||
widths.append(f"\\widthof{{{self.column_widths[i]}}}")
|
||||
elif w == "fill":
|
||||
widths.append(Table.fill_registers[fill_ordinal])
|
||||
fill_ordinal += 1
|
||||
elif w == "*":
|
||||
widths.append(None)
|
||||
else:
|
||||
@@ -548,6 +689,12 @@ class Table(klammer_base.Klammer_base):
|
||||
parts = [""] * (self.row_size * 2 + 1)
|
||||
for i in self.s_vline.by_index:
|
||||
parts[i * 2] = "|"
|
||||
# A lineless table edge drops its outer \tabcolsep (matched by the
|
||||
# \tablewidth arithmetic in tex()).
|
||||
if self.flush_l:
|
||||
parts[0] = "@{}"
|
||||
if self.flush_r:
|
||||
parts[-1] = "@{}"
|
||||
for i, hpos in enumerate(self.tex_hpos()):
|
||||
parts[i * 2 + 1] = hpos
|
||||
# print("tex_column_spec:", "".join(parts))
|
||||
@@ -615,9 +762,45 @@ class Table(klammer_base.Klammer_base):
|
||||
result += "}} \\\\ \\endlastfoot\n"
|
||||
return result
|
||||
|
||||
def tex_position(self):
|
||||
# Position a PAGE-BREAKING table with longtable's own glue (it
|
||||
# cannot be boxed). A boxed table (allow_break false) is positioned
|
||||
# by its :hpos wrapper instead; its glue is left neutral (\fill on
|
||||
# both sides collapses in the exactly-fitting box), because a fixed
|
||||
# length would overflow the box. A length is the left margin.
|
||||
if not self.allow_break:
|
||||
left, right = "\\fill", "\\fill"
|
||||
elif self.hpos == "center":
|
||||
left, right = "\\fill", "\\fill"
|
||||
elif self.hpos == "left":
|
||||
left, right = "0pt", "\\fill"
|
||||
elif self.hpos == "right":
|
||||
left, right = "\\fill", "0pt"
|
||||
else:
|
||||
length, _, _ = kutil.parse_length("tex", self.hpos, 1)
|
||||
left, right = length, "\\fill"
|
||||
return (f"\\setlength{{\\LTleft}}{{{left}}}\n"
|
||||
f"\\setlength{{\\LTright}}{{{right}}}\n")
|
||||
|
||||
def tex_width_check(self, name):
|
||||
# Emit a marker into the xelatex log when the measured table is
|
||||
# wider than the text column (2pt tolerance for exactly-full-width
|
||||
# tables). tex_to_pdf() in document.cpp scans the log for the
|
||||
# marker and prints the console warning with the :column_width
|
||||
# primer -- the widths are only known at LaTeX run time, and prose
|
||||
# kept out of TeX avoids the log's 79-column line wrapping.
|
||||
return ("\\ifdim\\tableboxwidth>\\dimexpr\\textwidth+2pt\\relax\n"
|
||||
f"\\message{{^^JKT-WIDE-TABLE {name} overfull by "
|
||||
"\\the\\dimexpr\\tableboxwidth-\\textwidth\\relax^^J}\n"
|
||||
"\\fi\n")
|
||||
|
||||
def tex(self):
|
||||
result = self.get_width()
|
||||
result += f"\\renewcommand*{{\\arraystretch}}{{{self.leading}}}\n"
|
||||
name = f"Reference-Table-{Table.id}"
|
||||
Table.id += 1
|
||||
# The measuring \savebox must stay OUTSIDE the \tableboxwidth
|
||||
# minipage below: it computes the width the minipage consumes.
|
||||
measure = self.get_width() + self.tex_width_check(name)
|
||||
result = f"\\renewcommand*{{\\arraystretch}}{{{self.leading}}}\n"
|
||||
if self.allow_break:
|
||||
result += "\\vspace*{12pt}\n"
|
||||
result += "\\begin{longtable}{"
|
||||
@@ -629,23 +812,37 @@ class Table(klammer_base.Klammer_base):
|
||||
result += "\\end{longtable}\n"
|
||||
|
||||
if not self.allow_break:
|
||||
# Box the table at its measured width so the caption tracks it
|
||||
# and the box can be positioned as one piece; the LT glue then
|
||||
# has no room and positioning falls to the :hpos wrapper. (The
|
||||
# page-breaking table cannot be boxed -- there the LT glue
|
||||
# positions and make_caption's \multicolumn tracks.)
|
||||
result = latex_util.minipage(
|
||||
result, "\\tableboxwidth", vertical="t", center=False)
|
||||
if self.number or self.caption:
|
||||
result = latex_util.add_caption(
|
||||
result, "Table", self.number, self.caption, "\\tablewidth",
|
||||
side=self.caption_side, font_symbol=self.caption_font,
|
||||
result, "Table", self.number, self.caption, "\\tableboxwidth",
|
||||
hpos=self.hpos, side=self.caption_side,
|
||||
font_symbol=self.caption_font,
|
||||
font_size=self.caption_font_size)
|
||||
else:
|
||||
result = latex_util.caption_wrapper(result, "center")
|
||||
result = latex_util.caption_wrapper(result, self.hpos)
|
||||
result = measure + result
|
||||
|
||||
name = f"Reference-Table-{Table.id}"
|
||||
Table.id += 1
|
||||
result = f"\\hypertarget{{{name}}}{{}}\\label{{Label-{name}}}\n{result}"
|
||||
# The wrapper (add_caption/caption_wrapper) owns all vertical space
|
||||
# around the table; longtable's own glue is zeroed.
|
||||
result = (f"\\setlength{{\\tabcolsep}}{{{self.colsep}}}\n"
|
||||
"\\setlength{\\LTpre}{0pt}\n"
|
||||
"\\setlength{\\LTpost}{0pt}\n"
|
||||
f"\\setlength{{\\tablewidth}}{{\\textwidth - {2 * self.row_size}\\tabcolsep}}\n"
|
||||
+ self.tex_position() +
|
||||
# 2 \tabcolsep per column, minus the ones @{} removes at
|
||||
# flush (lineless) edges.
|
||||
f"\\setlength{{\\tablewidth}}{{\\textwidth - "
|
||||
f"{2 * self.row_size - self.flush_l - self.flush_r}\\tabcolsep}}\n"
|
||||
# Fill widths need \tablewidth and must precede the
|
||||
# measuring \savebox, whose column spec reads them.
|
||||
+ self.tex_fill_widths()
|
||||
+ result)
|
||||
result = re.sub(r"\newline", r"\\\\", result)
|
||||
return result
|
||||
|
||||
@@ -12,7 +12,8 @@ class Cell:
|
||||
def __init__(self, text, font, hpos,
|
||||
top, right, bottom, left,
|
||||
left_all, right_all,
|
||||
rowspan, colspan, first_column=False, hpos_forced=False):
|
||||
rowspan, colspan, first_column=False, hpos_forced=False,
|
||||
fit_class="", flush_left=False, flush_right=False):
|
||||
self.text = text
|
||||
self.font = font
|
||||
self.hpos = hpos
|
||||
@@ -23,6 +24,17 @@ class Cell:
|
||||
self.rowspan = rowspan
|
||||
self.colspan = colspan
|
||||
self.first_column = first_column
|
||||
# html only: the class(es) clamping a 'fit' column's cell to its
|
||||
# widest entry -- "Wfit Wpct" in a full-width table, "Wfit" in a
|
||||
# content-sized ('fill') table, "" when not a fit column.
|
||||
self.fit_class = fit_class
|
||||
# Cell sits on a table edge with no outer vertical line: its outer
|
||||
# padding (html) / \tabcolsep (tex, via @{}) is removed so the text
|
||||
# aligns with the text margin. A tex \multicolumn replaces the
|
||||
# whole preamble entry including the @{}, so edge cells must
|
||||
# re-emit it in their own spec.
|
||||
self.flush_left = flush_left
|
||||
self.flush_right = flush_right
|
||||
#print("Cell:", text, hpos)
|
||||
|
||||
def __str__(self):
|
||||
@@ -47,6 +59,12 @@ class Cell:
|
||||
if pred:
|
||||
result.cls(cls_name)
|
||||
result.cls(f"H{self.hpos}")
|
||||
if self.fit_class:
|
||||
result.cls(self.fit_class)
|
||||
if self.flush_left:
|
||||
result.cls("Fl")
|
||||
if self.flush_right:
|
||||
result.cls("Fr")
|
||||
return result.str()
|
||||
|
||||
def tex(self, debug=False): # , left_line, right_line):
|
||||
@@ -70,6 +88,10 @@ class Cell:
|
||||
pos = "|" + pos
|
||||
if self.border.right:
|
||||
pos = pos + "|"
|
||||
if self.flush_left:
|
||||
pos = "@{}" + pos
|
||||
if self.flush_right:
|
||||
pos = pos + "@{}"
|
||||
return f"\\multicolumn{{{self.colspan}}}{{{pos}}}{{{result}}}"
|
||||
remove_left = self.border.left_all and not self.border.left
|
||||
remove_right = self.border.right_all and not self.border.right
|
||||
@@ -83,6 +105,10 @@ class Cell:
|
||||
pos = "|" + pos
|
||||
if self.border.right_all and self.border.right:
|
||||
pos = pos + "|"
|
||||
if self.flush_left:
|
||||
pos = "@{}" + pos
|
||||
if self.flush_right:
|
||||
pos = pos + "@{}"
|
||||
result = f"\\multicolumn{{1}}{{{pos}}}{{{result}}}"
|
||||
return result
|
||||
|
||||
|
||||
Reference in New Issue
Block a user