2019-09-09 13:25:17 +10:00
# legalmd: Markdown-based legal markup
# Copyright © 2019 Lee Yingtong Li (RunasSudo)
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>.
import mistletoe
import mistletoe . latex_renderer
LMARG = ' 1cm '
def format ( fstr , * args , * * kwargs ) :
fstr2 = fstr
fstr2 = fstr2 . replace ( ' \\ N ' , ' \n ' )
fstr2 = fstr2 . replace ( ' { ' , ' {{ ' )
fstr2 = fstr2 . replace ( ' } ' , ' }} ' )
fstr2 = fstr2 . replace ( ' < ' , ' { ' )
fstr2 = fstr2 . replace ( ' > ' , ' } ' )
kwargs [ ' lmarg ' ] = LMARG
return fstr2 . format ( * args , * * kwargs )
class LaTeXRenderer ( mistletoe . latex_renderer . LaTeXRenderer ) :
def __init__ ( self , * extras ) :
super ( ) . __init__ ( * extras )
2019-09-09 22:39:45 +10:00
self . render_map [ ' NumberedHeading ' ] = self . render_numbered_heading
2019-09-09 13:25:17 +10:00
self . render_map [ ' Subrules ' ] = self . render_subrules
self . render_map [ ' SubrulesItem ' ] = self . render_subrules_item
self . render_map [ ' Note ' ] = self . render_note
self . render_map [ ' Definition ' ] = self . render_definition
self . heading_last = False
def render_raw_text ( self , token ) :
2019-09-09 22:39:45 +10:00
result = super ( ) . render_raw_text ( token )
2019-09-09 13:25:17 +10:00
result = result . replace ( ' ★★★ ' , r ' \ texorpdfstring { \ freeserif ★★★} { ★★★} ' )
return result
def render_heading ( self , token ) :
2019-09-09 22:39:45 +10:00
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 { <content>} \ phantomsection \ addcontentsline {toc} {section} { <content>} \ 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 { <space_above>} \ bfseries \ fontsize {13pt} {15pt} \ selectfont \ centering <content> \ phantomsection \ addcontentsline {toc} {subsection} { <content>} \ 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[<lmarg>][l] { ~}<content> \ phantomsection \ addcontentsline {toc} {subsubsection} { <content>} \ nopagebreak \ par} ' ,
content = self . render_inner ( token )
)
def render_numbered_heading ( self , token ) :
2019-09-09 13:25:17 +10:00
if token . level == 1 :
# Part
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 { Part <label>—<content>} \ phantomsection \ addcontentsline {toc} {section} { Part <label>—<content>} \ nopagebreak \ par} ' ,
label = token . label ,
content = self . render_inner ( token )
)
if token . level == 2 :
# Division
heading_last , self . heading_last = self . heading_last , True
return format ( r ' { \ par \ vspace { <space_above>} \ bfseries \ fontsize {13pt} {15pt} \ selectfont \ centering Division <label>—<content> \ phantomsection \ addcontentsline {toc} {subsection} { Division <label>—<content>} \ nopagebreak \ par} ' ,
space_above = ' 1cm plus 0.3cm minus 0.3cm ' if not heading_last else ' 0cm ' ,
label = token . label ,
content = self . render_inner ( token )
)
if token . level == 3 :
# Section
heading_last , self . heading_last = self . heading_last , False
2019-09-11 09:10:52 +10:00
return format ( r ' { \ par \ leftskip= \ quotemargin \ bfseries \ makebox[<lmarg>][l] { <label>}<content> \ phantomsection \ addcontentsline {toc} {subsubsection} { \ protect \ numberline { <label>} <content>} \ nopagebreak \ par} ' ,
2019-09-09 13:25:17 +10:00
label = token . label ,
content = self . render_inner ( token )
)
def render_paragraph ( self , token ) :
self . heading_last = False
2019-09-11 09:10:52 +10:00
return format ( r ' \ N { \ par \ leftskip= \ dimexpr \ quotemargin+<lmarg> \ relax \ updatealign <content> \ par} \ N ' ,
content = self . render_inner ( token )
)
def render_quote ( self , token ) :
return format ( r ' { \ quotemargin=<lmarg> \ renewcommand { \ addcontentsline}[3] {} <content>} ' ,
2019-09-09 13:25:17 +10:00
content = self . render_inner ( token )
)
def render_definition ( self , token ) :
self . heading_last = False
return format ( r ' \ N { \ par \ leftskip= \ dimexpr<lmarg>+1cm \ relax \ hangindent=1cm \ updatealign <content> \ par} \ N ' ,
content = self . render_inner ( token )
)
def render_subrules ( self , token ) :
self . heading_last = False
return format ( r ' \ N { <content>} \ N ' ,
content = self . render_inner ( token )
)
def render_subrules_item ( self , token ) :
if token . label :
2019-09-11 09:10:52 +10:00
return format ( r ' \ N { \ par \ leftskip= \ dimexpr \ quotemargin+<lmarg>+<level>cm \ relax \ hangindent=1cm \ updatealign \ parskip=<parskip> \ makebox[<lmarg>][l] { <label>}<content> \ par} \ N ' ,
2019-09-09 13:25:17 +10:00
parskip = r ' \ parskip ' , # if token.level <= 1 else '0cm',
label = token . label ,
level = token . level ,
content = self . render_inner ( token )
)
else :
return format ( r ' \ N { \ par \ leftskip= \ dimexpr<lmarg>+<level>cm+1cm \ relax \ updatealign \ parskip=<parskip> <content> \ par} \ N ' ,
parskip = r ' \ parskip ' , # if token.level <= 1 else '0cm',
level = token . level ,
content = self . render_inner ( token )
)
def render_note ( self , token ) :
self . heading_last = False
return format ( r ' \ N { \ nopagebreak \ par \ footnotesize \ selectfont \ settowidth { \ notetaglength} { \ bfseries <label>:} \ addtolength { \ notetaglength} {1em} \ leftskip= \ lastalign \ hangindent= \ notetaglength \ makebox[ \ notetaglength][l] { \ bfseries <label>:}<content> \ par} \ N ' ,
label = token . label ,
content = self . render_inner ( token )
)
2019-09-09 22:39:45 +10:00
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-<lmarg> \\ 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 :
2019-09-11 09:10:52 +10:00
return format ( r ' \ makebox[2.2em][l] { <rownum>.}<content> ' ,
2019-09-09 22:39:45 +10:00
rownum = inner . split ( ' ' ) [ 0 ] ,
content = ' ' . join ( inner . split ( ' ' ) [ 1 : ] )
)
else :
return inner
2019-09-09 13:25:17 +10:00
def render_document ( self , token ) :
template = r '''
\documentclass [ a4paper , 12 pt ] { article }
< packages >
% Configuration
\usepackage [ top = 1.25 cm , bottom = 1.13 cm , inner = 2 cm , outer = 2 cm , headheight = 8 pt , headsep = 0.5 cm , footskip = 1 cm , includehead , includefoot ] { geometry }
\frenchspacing
\usepackage [ hidelinks , bookmarksnumbered = true , unicode ] { hyperref }
\usepackage { bookmark } % Non - sequential bookmarks
\usepackage { parskip }
\setlength { \parskip } { 0.35 cm plus 0.1 cm minus 0.1 cm }
\usepackage { fancyhdr }
\setlength { \emergencystretch } { 3 em }
\usepackage { microtype }
2019-09-09 22:39:45 +10:00
\usepackage { longtable } \usepackage { tabu } \usepackage { booktabs }
2019-09-11 09:10:52 +10:00
\newlength \quotemargin
2019-09-09 13:25:17 +10:00
\newlength \notetaglength
\newlength \lastalign \newcommand { \updatealign } { \global \lastalign = \dimexpr \leftskip + \hangindent \relax }
% TOC format
\usepackage { titletoc }
\makeatletter
\newcounter { toc @section } % For some reason \newif doesn ' t work here
\newcommand { \toc @sectiontrue } { \setcounter { toc @section } { 1 } }
\newcommand { \toc @sectionfalse } { \setcounter { toc @section } { 0 } }
\titlecontents { section } [ 0 pt ] { \vspace { 0.21 cm } \toc @sectionfalse \bfseries } { } { \uppercase } { \titlerule * [ 1 pc ] { . } \contentspage } [ ]
\titlecontents { subsection } [ 0 pt ] { \vspace { 0.21 cm } \bfseries \scshape } { } { \toc @sectionfalse } { \titlerule * [ 1 pc ] { . } \contentspage } [ ]
\dottedcontents { subsubsection } [ \dimexpr < lmarg > + 0.5 cm \relax ] { \ifnum \value { toc @section } = 0 \vspace { 0.21 cm } \fi \toc @sectiontrue } { 1 cm } { 1 pc } [ ]
\makeatother
% Fonts
\usepackage [ math - style = ISO , bold - style = ISO ] { unicode - math }
\setmainfont [ RawFeature = - tlig ] { TeX Gyre Termes }
\setsansfont [ RawFeature = - tlig ] { TeX Gyre Heros }
\setmonofont [ RawFeature = - tlig ] { TeX Gyre Cursor }
\setmathfont [ RawFeature = - tlig ] { TeX Gyre Termes Math }
\newfontfamily { \freeserif } { FreeSerif } [ RawFeature = - tlig ]
\renewcommand { \familydefault } { \sfdefault }
% Front matter
\begin { document }
\fancypagestyle { plain } { \fancyhf { } }
\pagestyle { fancy } \fancyhf { } \renewcommand { \headrulewidth } { 0 pt }
\lhead { \textsf { \scriptsize < title > } } \rhead { \textsf { \scriptsize < author > } } \lfoot { \textsf { \scriptsize < footer > } } \rfoot { \textsf { \scriptsize \thepage } }
\pagenumbering { roman }
{ \bfseries \centering \fontsize { 13 pt } { 15 pt } \selectfont \uppercase { < title > } \par INDEX \par }
\makeatletter \@starttoc { toc } \makeatother
\newpage \pagenumbering { arabic }
% Content
< inner >
\end { document } '''
self . footnotes . update ( token . footnotes )
return format ( template ,
inner = self . render_inner ( token ) ,
packages = self . render_packages ( ) ,
title = token . title ,
author = token . author ,
footer = token . footer
)