How can I alter the answer from this question (which uses a lua-filter), so that it outputs tikz figures in a docx document, but also extracts content from non-pandoc friendly environments (as it does by default in v2.2.3), such as tcolorbox. For example, creating main.tex:



  Some content and an equation $a=b$

\draw (0,0) -- (4,4);


If I run:

pandoc main.tex -t docx -o main.docx

I will get the content of the tcolorbox in the docx but not the tikz figure. Whereas, If I run:

pandoc main.tex --from latex+raw_tex -t docx -o main.docx --lua-filter=tikz-to-png.lua

I will get the tikz figure, but not the content of tcolorbox in the docx.

I'm thinking that maybe this section of the lua-filter code should return something other than nil:

function RawBlock(el)
-- Don't alter element if it's not a tikzpicture environment
if not el.text:match'^\\begin{tikzpicture}' then
    return nil

Having looked in the Documentation at the lua-filters on Github, I have a feeling I need to use something like pandoc.walk_block, but can't figure it out.

(it would also be nice to but the tcolorbox content in a box, but that's for later)


To answer my own question:

I believe the pandocfilters and the newer panflute Python packages offer an easier approach to writing filters. Slightly adapting this filter did the trick:

Pandoc filter to process raw latex tikz environments into images.
Assumes that pdflatex is in the path, and that the standalone
package is available.  Also assumes that ImageMagick's convert
is in the path. Images are put in the tikz-images directory.

import hashlib
import re
import os
import sys
import shutil
import panflute as pf
from subprocess import Popen, PIPE, call
from tempfile import mkdtemp

imagedir = "tikz-images"

def sha1(x):
    return hashlib.sha1(x.encode(sys.getfilesystemencoding())).hexdigest()

def tikz2image(tikz, filetype, outfile):
    tmpdir = mkdtemp()
    olddir = os.getcwd()
    f = open('tikz.tex', 'w')
    p = call(["pdflatex", 'tikz.tex'], stdout=sys.stderr)
    if filetype == 'pdf':
        shutil.copyfile(tmpdir + '/tikz.pdf', outfile + '.pdf')
        call(["convert", tmpdir + '/tikz.pdf', outfile + '.' + filetype])

def action(elem, doc):
    return None -> element unchanged
    return [] -> delete element

    if type(elem) == pf.RawBlock and elem.format == "latex":

        code = elem.text

        if code.strip().startswith(r"\begin{tikzpicture}"):
            outfile = imagedir + '/' + sha1(code)
            filetype = {'html': 'png', 'latex': 'pdf'}.get(doc.format, 'png')
            src = outfile + '.' + filetype
            if not os.path.isfile(src):
                    sys.stderr.write('Created directory ' + imagedir + '\n')
                except OSError:
                tikz2image(code, filetype, outfile)
                sys.stderr.write('Created image ' + src + '\n')

            return pf.Para(pf.Image(url=src))
            return pf.convert_text(code, input_format="latex")

def main(doc=None):
    return pf.run_filter(action, doc=doc)

if __name__ == "__main__":

In particular, the line return pf.convert_text(code, input_format="latex") ensures that all other raw_tex is processed.

  • Can you provide exactly the steps to run this filter? 1) It is not immediately clear whether you are using pandoc (with this filter) or running panflute directly with Python. 2) Please provide an example of the exact command that you used after the prereqs were installed/setup. – whatisit Oct 28 '18 at 22:04

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.