diff --git a/legalmd/latex_renderer.py b/legalmd/latex_renderer.py index e82b2ef..9b92edd 100644 --- a/legalmd/latex_renderer.py +++ b/legalmd/latex_renderer.py @@ -35,6 +35,7 @@ class LaTeXRenderer(mistletoe.latex_renderer.LaTeXRenderer): def __init__(self, *extras): super().__init__(*extras) + self.render_map['NumberedHeading'] = self.render_numbered_heading self.render_map['Subrules'] = self.render_subrules self.render_map['SubrulesItem'] = self.render_subrules_item self.render_map['Note'] = self.render_note @@ -43,11 +44,31 @@ class LaTeXRenderer(mistletoe.latex_renderer.LaTeXRenderer): self.heading_last = False def render_raw_text(self, token): - result = super().render_inner(token) + result = super().render_raw_text(token) result = result.replace('★★★', r'\texorpdfstring{\freeserif ★★★}{★★★}') return result def render_heading(self, token): + if token.level == 1: + heading_last, self.heading_last = self.heading_last, True + return format(r'{\par\vspace{1cm plus 0.3cm minus 0.3cm}\bfseries\fontsize{13pt}{15pt}\selectfont\centering\uppercase{}\phantomsection\addcontentsline{toc}{section}{}\nopagebreak\par}', + content=self.render_inner(token) + ) + + if token.level == 2: + heading_last, self.heading_last = self.heading_last, True + return format(r'{\par\vspace{}\bfseries\fontsize{13pt}{15pt}\selectfont\centering \phantomsection\addcontentsline{toc}{subsection}{}\nopagebreak\par}', + space_above='1cm plus 0.3cm minus 0.3cm' if not heading_last else '0cm', + content=self.render_inner(token) + ) + + if token.level == 3: + heading_last, self.heading_last = self.heading_last, False + return format(r'{\par\bfseries\makebox[][l]{~}\phantomsection\addcontentsline{toc}{subsubsection}{}\nopagebreak\par}', + content=self.render_inner(token) + ) + + def render_numbered_heading(self, token): if token.level == 1: # Part heading_last, self.heading_last = self.heading_last, True @@ -117,6 +138,51 @@ class LaTeXRenderer(mistletoe.latex_renderer.LaTeXRenderer): content=self.render_inner(token) ) + def render_table(self, token): + align = ['X' for col in token.column_align] + if hasattr(token, 'header'): + for i, col in enumerate(token.header.children): + if '[' in col.children[0].content.split(' ')[0]: + param = col.children[0].content[col.children[0].content.index('['):col.children[0].content.index(']')+1] + align[i] += param + + result = [ + format('{\\footnotesize\\begin{longtabu} to \\dimexpr\\columnwidth-\\relax [r] {'), + ' '.join(align), + '}\\toprule\n'] + + if hasattr(token, 'header'): + result.append(self.render_table_header(token.header) + '\\midrule[\\heavyrulewidth]\n') + + for i, row in enumerate(token.children): + result.append(self.render_table_row(row)) + if i != len(token.children) - 1: + result.append('\\midrule\n') + + result.append(r'\bottomrule\end{longtabu}}') + + return ''.join(result) + + def render_table_header(self, token): + cells1 = ['{\\bfseries Column ' + self.render_inner(child).split(' ')[0].split('[')[0] + '}' for child in token.children] + cells2 = ['{\\bfseries ' + ' '.join(self.render_inner(child).split(' ')[1:]) + '}' for child in token.children] + return ' & '.join(cells1) + ' \\\\\\midrule\n' + ' & '.join(cells2) + ' \\\\' + + def render_table_row(self, token): + cells = [self.render_table_cell(child, colnum) for colnum, child in enumerate(token.children)] + return ' & '.join(cells) + ' \\\\' + + def render_table_cell(self, token, colnum): + inner = self.render_inner(token) + + if colnum == 0: + return format(r'\makebox[1.8em][l]{.}', + rownum=inner.split(' ')[0], + content=' '.join(inner.split(' ')[1:]) + ) + else: + return inner + def render_document(self, token): template = r''' \documentclass[a4paper,12pt]{article} @@ -131,6 +197,7 @@ class LaTeXRenderer(mistletoe.latex_renderer.LaTeXRenderer): \usepackage{fancyhdr} \setlength{\emergencystretch}{3em} \usepackage{microtype} +\usepackage{longtable}\usepackage{tabu}\usepackage{booktabs} \newlength\notetaglength \newlength\lastalign\newcommand{\updatealign}{\global\lastalign=\dimexpr\leftskip+\hangindent\relax} % TOC format diff --git a/legalmd/legal_token.py b/legalmd/legal_token.py index 5fa93fa..0987107 100644 --- a/legalmd/legal_token.py +++ b/legalmd/legal_token.py @@ -20,8 +20,8 @@ import mistletoe mistletoe.block_token.remove_token(mistletoe.block_token.BlockCode) -class Heading(mistletoe.block_token.BlockToken): - pattern = re.compile(r'(#{1,6})\s*([0-9A-Z]+)\s+(.+)') +class NumberedHeading(mistletoe.block_token.BlockToken): + pattern = re.compile(r'(#{1,6})\s*([0-9A-Z]+|xx)\s+(.+)') def __init__(self, match): self.level, self.label, content = match @@ -45,8 +45,8 @@ class Heading(mistletoe.block_token.BlockToken): return level, label, content -mistletoe.block_token.remove_token(mistletoe.block_token.Heading) -mistletoe.block_token.add_token(Heading) +#mistletoe.block_token.remove_token(mistletoe.block_token.Heading) +mistletoe.block_token.add_token(NumberedHeading) class Subrules(mistletoe.block_token.BlockToken): pattern = re.compile(r'\t*\(([0-9A-Za-z–]+)\)\s+.') diff --git a/legalmd/rtf_renderer.py b/legalmd/rtf_renderer.py index 0fd60fe..7abd24d 100644 --- a/legalmd/rtf_renderer.py +++ b/legalmd/rtf_renderer.py @@ -38,6 +38,7 @@ class RTFRenderer(mistletoe.base_renderer.BaseRenderer): def __init__(self, *extras): super().__init__(*extras) + self.render_map['NumberedHeading'] = self.render_numbered_heading self.render_map['Subrules'] = self.render_subrules self.render_map['SubrulesItem'] = self.render_subrules_item self.render_map['Note'] = self.render_note @@ -74,6 +75,31 @@ class RTFRenderer(mistletoe.base_renderer.BaseRenderer): raise Exception('NYI') def render_heading(self, token): + if token.level == 1: + # Part + heading_last, self.heading_last = self.heading_last, True + return format(r'{\sb\keepn\b\fs26\qc\caps \par}', + space_above=cm_to_twip(1), + content=self.render_inner(token) + ) + + if token.level == 2: + # Division + heading_last, self.heading_last = self.heading_last, True + return format(r'{\sb\keepn\b\fs26\qc \par}', + space_above=cm_to_twip(1 if not heading_last else 0), + content=self.render_inner(token) + ) + + if token.level == 3: + # Section + heading_last, self.heading_last = self.heading_last, False + return format(r'{\keepn\b\li \par}', + hangindent=cm_to_twip(LMARG), + content=self.render_inner(token) + ) + + def render_numbered_heading(self, token): if token.level == 1: # Part heading_last, self.heading_last = self.heading_last, True @@ -125,13 +151,57 @@ class RTFRenderer(mistletoe.base_renderer.BaseRenderer): raise Exception('NYI') def render_table(self, token): - raise Exception('NYI') + result = [format(r'{\sa0\trowd\trgaph120\trleft')] + + # Get column weights + weights = [] + if hasattr(token, 'header'): + for col in token.header.children: + if '[' in col.children[0].content.split(' ')[0]: + weights.append(int(col.children[0].content[col.children[0].content.index('[')+1:col.children[0].content.index(']')])) + else: + weights.append(1) + else: + weights = [1 for col in token.column_align] + + # Get running totals + cellx = [sum(weights[0:i+1]) for i in range(len(weights))] + # Convert to widths + cellx = [x * cm_to_twip(21-LMARG-4) / cellx[-1] + cm_to_twip(LMARG) for x in cellx] + + for x in cellx: + result.append(format(r'\clbrdrt\brdrs\clbrdrb\brdrs\cellx<>', int(x))) + result.append(' ') + + if hasattr(token, 'header'): + result.append(self.render_table_header(token.header)) + + for i, row in enumerate(token.children): + result.append(self.render_table_row(row)) + + result.append(r'}') + return ''.join(result) + + def render_table_header(self, token): + cells1 = ['{\\b Column ' + self.render_inner(child).split(' ')[0].split('[')[0] + '\\intbl\\cell}' for child in token.children] + cells2 = ['{\\b ' + ' '.join(self.render_inner(child).split(' ')[1:]) + '\\intbl\\cell}' for child in token.children] + return ''.join(cells1) + '\\row' + ''.join(cells2) + '\\row ' def render_table_row(self, token): - raise Exception('NYI') + return ''.join([self.render_table_cell(cell, i) for i, cell in enumerate(token.children)]) + '\\row ' - def render_table_cell(self, token): - raise Exception('NYI') + def render_table_cell(self, token, colnum): + inner = self.render_inner(token) + + if colnum == 0: + return format(r'{. \intbl\cell}', + rownum=inner.split(' ')[0], + content=' '.join(inner.split(' ')[1:]) + ) + else: + return format(r'{\intbl\cell}', + content=inner + ) def render_thematic_break(self, token): raise Exception('NYI')