Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to control tables in pandoc generated latex?

I'm using pandoc to convert a Markdown document to LaTeX. I saved the default LaTeX template (pandoc -D latex) and modified it to my needs.

I now need to change how the tables look like:

  1. Change the text color of the table captions;
  2. Change the caption location from above the table to under the table;
  3. Use a "grid" table where the cell boundaries are visible;
  4. Change the headers to be bold text on grey background.

I was able to change the color of the table captions (point 1.) using:

\usepackage{caption}
\DeclareCaptionFont{myColor}{\color[RGB]{40,70,119}}
\captionsetup{labelfont=myColor,textfont=myColor,position=bottom}

but the position option is ignored (point 2.)

I would like to be able to control the styling of the pandoc generated LaTeX table. Is that possible? Any suggestion?

Thanks!

like image 731
big_gie Avatar asked Jan 07 '23 02:01

big_gie


1 Answers

It's actually possible to change how the tables are generated, instead of trying to fix them at a later time. What is needed is a pandoc filter.

Instead of telling pandoc to generate the final document, its AST needs to be fed to the filter so the wanted formatting can be generated instead of the default one. Then this modified AST is sent back to pandoc for the final conversion.

This AST is the JSON format.

The process is as follow:

pandoc -t json -s | ./filter.py | pandoc -f json

where filter.py is a filter taking JSON data as stdin and spiting modified JSON to stdout.

This can be done without piping by telling pandoc:

pandoc --filter ./filter.py -s

Here's the filter I am using:

#!/usr/bin/env python2

import pandocfilters as pf

def latex(s):
    return pf.RawBlock('latex', s)

def inlatex(s):
    return pf.RawInline('latex', s)

def tbl_caption(s):
    return pf.Para([inlatex(r'\caption{')] + s + [inlatex('}')])

def tbl_alignment(s):
    aligns = {
        "AlignDefault": 'l',
        "AlignLeft": 'l',
        "AlignCenter": 'c',
        "AlignRight": 'r',
    }
    return ''.join([aligns[e['t']] for e in s])

def tbl_headers(s):
    result = s[0][0]['c'][:]
    # Build the columns. Note how the every column value is bold.
    # We are still missing "\textbf{" for the first column
    # and a "}" for the last column.
    for i in range(1, len(s)):
        result.append(inlatex(r'} & \textbf{'))
        result.extend(s[i][0]['c'])
    # Don't forget to close the last column's "\textbf{" before newline
    result.append(inlatex(r'} \\ \hline'))
    # Put the missing "\textbf{" in front of the list
    result.insert(0, inlatex(r'\textbf{'))
    # Preprend the command to set the row color in front of everything
    result.insert(0, inlatex(r'\rowcolor{grey} '))
    return pf.Para(result)

def tbl_contents(s):
    result = []
    for row in s:
        para = []
        for col in row:
            para.extend(col[0]['c'])
            para.append(inlatex(' & '))
        result.extend(para)
        result[-1] = inlatex(r' \\ \hline' '\n')
    return pf.Para(result)

def do_filter(k, v, f, m):
    if k == "Table":
        # Ensure every alignment characters is surrounded by a pipes.
        # Get the string of the alignment characters
        # and split into an array for every characters.
        split_alignment = [c for c in tbl_alignment(v[1])]
        # Join this list into a single string with pipe symbols
        # between them, plus pipes at start and end.
        # This results in a boxed table.
        new_alignment = "|" + "|".join(split_alignment) + "|"
        return [latex(r'\begin{table}[h]'),
                latex(r'\centering'),
                latex(r'\begin{tabular}{%s} \hline' % new_alignment),
                tbl_headers(v[3]),
                tbl_contents(v[4]),
                latex(r'\end{tabular}'),
                # Put the caption after the tabular so it appears under table.
                tbl_caption(v[0]),
                latex(r'\end{table}')]


if __name__ == "__main__":
    pf.toJSONFilter(do_filter)

Note that it's using the pandocfilters python module. Install it using pip:

pip install pandocfilters

Sources:

  • https://groups.google.com/forum/#!msg/pandoc-discuss/RUC-tuu_qf0/h-H3RRVt1coJ
  • https://github.com/jgm/pandocfilters
like image 185
big_gie Avatar answered Jan 30 '23 01:01

big_gie