import functools import collections import re import sys import traceback import pprint import kutil import klammer_base import html_util from html_util import E import latex_util from sequences import Sequences import table_cell import font def extend(lst, count, fill=None): if isinstance(lst, str): lst = lst.strip().split("\\s+") if fill is None: fill = lst[-1] if lst else "" return lst + ([fill] * (count - len(lst))) def parse_hpos(target, pos): if '"' in pos: pos_list = [] parts = pos.split('"') i = 0 while i < len(parts): pos_list += parts[i].split() i += 1 if i == len(parts): break #pos_list.append(f"{{{parts[i]}}}") pos_list.append(kutil.parse_length(target, f'"{parts[i]}"')[0]) i += 1 else: # pos_list = pos.split() pos_list = pos #print("pos_list:", pos_list) return pos_list class Table(klammer_base.Klammer_base): id = 0 def __init__(self, K): super().__init__(K) # pprint.pprint(self.__dict__) if self.grid: self.vline = "all" self.hline = "all" self.number = self.number == "true" self.rows = kutil.rest_args(self.rows, 2) self.row_count = len(self.rows) if self.header: self.hline += f" 1 {self.row_count}" self.row_size = max([len(e) for e in self.rows]) self.s_vline = Sequences(self.row_size + 1, self.row_count - 1, self.vline) self.s_hline = Sequences(self.row_count + 1, self.row_size - 1, self.hline) self.s_rowspan = Sequences(self.row_size, self.row_count, self.rowspan) self.s_colspan = Sequences(self.row_count, self.row_size, self.colspan) self.cell_hpos = extend(parse_hpos(self.K_target, self.cell_hpos.split()), self.row_size) #self.cell_hpos = self.cell_hpos.split(";") self.font = extend(self.font, self.row_size) self.make_cells(self.rows) def span_count(self, span_seq, row_i, col_i): result = 0 row_seq = span_seq[row_i] if row_seq: for range in row_seq.ranges: if range[0] == col_i: result = range[1] - range[0] + 1 return result def remove_redundant_borders(self): remove_right = [] for row_i in range(self.row_count): for cell_i in range(self.row_size): a = self.cells[row_i][cell_i] b = self.cells[row_i][cell_i+1] if a.border.right and b.border.left: a.border.right = False a.border.right_all = False def column_width_text(self): self.column_widths = [] # print("column_width_text:", len(self.cells), len(self.cells[0])) for col_i in range(len(self.cells[0])): longest = "" for row_i in range(len(self.cells)): cell = self.cells[row_i][col_i] if cell is not None: cell_text = cell.text #lines = [e.strip() for e in cell_text.split("\\newline")] lines = [e.strip() for e in cell_text.split("\newline")] lines = sorted(lines, key=len) longest_in_line = lines[-1] if len(longest_in_line) > len(longest): longest = longest_in_line self.column_widths.append(longest) # kutil.msg("column_widths:") # print(self.column_widths) def make_cells(self, rows): result = [] cells = [] for row_i, row in enumerate(rows): row_cells = [] for cell_i, cell in enumerate(row): rspan = self.span_count(self.s_rowspan, row_i, cell_i) cspan = self.span_count(self.s_colspan, row_i, cell_i) font = self.font[cell_i] if row_i == 0 and self.header: font = self.header_font row_cells.append( table_cell.Cell( cell, font, self.cell_hpos[cell_i], self.s_hline.has(row_i, cell_i), self.s_vline.has(cell_i+1, row_i), self.s_hline.has(row_i+1, cell_i), self.s_vline.has(cell_i, row_i), self.s_vline.sequences.get(cell_i), self.s_vline.sequences.get(cell_i+1), rspan, cspan)) cells.append(row_cells) widths = [len(e) for e in cells] max_width = max(widths) self.cells = [extend(row, max_width, None) for row in cells] \ if max_width != min(widths) else cells self.column_width_text() # HTML def html(self): result = "" for row in self.cells: row_html = "" for cell in row: row_html += cell.html().strip() + "\n" result += E("tr").body(row_html).str() 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) else: result = result.str() return result # LaTeX def tex_hpos(self): def par_format(s, justification): command = {"l" : "raggedright", "c" : "centering", "r" : "raggedleft"}[justification] return f">{{\\{command}}}p{{{s}}}" widths = [] hpos_pat = re.compile("(f|(?:0?(\\.\\d+)(t))|\\*)?([lcr]?)") def parse(s, width_text): match = hpos_pat.match(s) # print("MATCH:", match, match.groups()) width, frac, table, just = match.groups() width = width or "f" just = just or "l" if width and width[0] == "{": width = f"\\widthof{{{s}}}" elif table == "t": width = f"{frac}\\tablewidth" elif width == "f": width = f"\\widthof{{{width_text}}}" if width != "*": widths.append(width) result = par_format(width, just) if width != "*" else s # print("PARSE:", result) return result # print("self.column_widths:", len(self.column_widths), self.column_widths) # return extend([parse(e) for e in self.cell_hpos], self.row_size) hpos_list = [] for i, hpos in enumerate(extend(self.cell_hpos, self.row_size)): # print(f" Loop {i}:", hpos) if i >= len(self.column_widths): print(f"Warning: Ignoring table column width: {hpos}") else: hpos_list.append(parse(hpos, self.column_widths[i])) # print("hpos_list:", hpos_list) fill_count = sum([1 if "*" in e else 0 for e in hpos_list]) # print("fill_count:", fill_count) if fill_count > 0: margins = f"(\\tabcolsep * {2 * len(hpos_list)})" # print("MARGINS:", margins) #expr = "\\linewidth - " + " - ".join(widths) + str(" if fill_count == len(hpos_list): expr = f"{1/fill_count}\\tablewidth" else: #expr = f"(\\textwidth - {margins} - {' - '.join(widths)}) / {fill_count}" expr = f"(\\tablewidth - {' - '.join(widths)}) / {fill_count}" # print(expr) result = [] for h in hpos_list: if h[0] == "*": just = h[1] if len(h) > 1 else "l" result.append(par_format(expr, just)) else: result.append(h) else: result = hpos_list # print("tex_hpos:", result) return result def tex_column_spec(self): parts = [""] * (self.row_size * 2 + 1) for i in self.s_vline.sequences: parts[i * 2] = "|" for i, hpos in enumerate(self.tex_hpos()): parts[i * 2 + 1] = hpos # print("tex_column_spec:", "".join(parts)) return "".join(parts) def tex_hline(self, index): hline = "" bottom = index == self.row_count if bottom: index -= 1 count = 0 for i, cell in enumerate(self.cells[index]): has_border = cell.border.bottom if bottom else cell.border.top if has_border: hline += f"\\cline{{{i+1}-{i+1}}} " count += 1 #if count == self.row_size: # hline = "\\hline" return hline.strip() + "\n" def tex_rows(self): result = "" for row_i, row in enumerate(self.cells): result += self.tex_hline(row_i) tab = "" for cell_i, cell in enumerate(row): result += tab + cell.tex() tab = " & " #result += " \\\\\n" result += " \\tabularnewline\n" result += self.tex_hline(self.row_count) return result def get_width(self): box = "\\savebox{\\tablebox}{%\n" box += "\\begin{tabular}{" box += self.tex_column_spec() box += "}\n" box += self.tex_rows() box += "\\end{tabular}}\n" result = box + "\\setlength{\\tableboxwidth}{\\wd\\tablebox}\n" # result += "\\the\\tableboxwidth\n\n" # print(result) return result def make_caption(self): column_count = str(len(self.rows[0])) caption = latex_util.make_caption_text( self.number, "Table", self.caption, self.caption_font, self.caption_font_size) result = "\\multicolumn{" + column_count + "}{c}{\\parbox{\\tableboxwidth}{" result += "\\vspace*{8pt}\\centering\\small\\em " result += caption result += "}} \\\\ \\endlastfoot\n" return result def tex(self): result = self.get_width() result += "\\vspace*{-.75\\baselineskip}" # result = "" result += "\\renewcommand*{\\arraystretch}{1.3}\n" if self.allow_break: result += "\\vspace*{12pt}\n" result += "\\begin{longtable}{" result += self.tex_column_spec() result += "}\n" if self.allow_break: result += self.make_caption() result += self.tex_rows() result += "\\end{longtable}\n" if not self.allow_break: if self.number or self.caption: result = latex_util.add_caption(result, "Table", self.number, self.caption, "\\tablewidth") else: result = latex_util.caption_wrapper(result, "center") name = f"Reference-Table-{Table.id}" result = f"\\hypertarget{{{name}}}{{}}\\label{{Label-{name}}}\n{result}" result = f"\\setlength{{\\tablewidth}}{{\\textwidth - {2 * self.row_count}\\tabcolsep}}\n" + result # result += "\\vspace*{-8pt}" result = re.sub(r"\newline", r"\\\\", result) return result def txt(self): return "TXT"