8

What I want to do is to convert a .tex file, containing several TikZ figures to a .docx-file using pandoc. I have tried to follow the pandoc documentation and to use lua filters to achieve this. Problem: whenever I use the lua filter, a file is generated that contains only the title of my .tex file and all the rest of the file is omitted. I do not see the TikZ figures either.

This is my input for pandoc in the commandline:

 pandoc --from latex+raw_tex --lua-filter=tikz.lua -s file.tex -o test.docx

My lua filter file (tikz.lua) is changed from the original filter suggested by pandoc, on the basis of the advice given in this post. I have adopted the suggested changes there one-on-one to this file:

local function file_exists(name)
  local f = io.open(name, 'r')
  if f ~= nil then io.close(f); return true
  else return false end
end

function RawBlock(el)
  -- Don't alter element if it's not a tikzpicture environment
  if not el.text:match'^\\begin{tikzpicture}' then
    return nil
    -- Alternatively, parse the contained LaTeX now:
    -- return pandoc.read(el.text, 'latex').blocks
  end  
  local fname = pandoc.sha1(el.text) .. ".png"
  if not file_exists(fname) then
    tikz2image(el.text, fname)
  end
  return pandoc.Para({pandoc.Image({}, fname)})
end

--- Create a standalone LaTeX document which contains only the TikZ picture.
--- Convert to png via Imagemagick.
local function tikz2image(src, outfile)
  local tmp = os.tmpname()
  local tmpdir = string.match(tmp, "^(.*[\\/])") or "."
  local f = io.open(tmp .. ".tex", 'w')
  f:write("\\documentclass{standalone}\n")
  -- include all packages needed to compile your images
  f:write("\\usepackage{tikz}\n\\usepackage{stanli}\n")
  f:write("\\begin{document}\n")
  f:write(src)
  f:write("\n\\end{document}\n")
  f:close()
  os.execute("pdflatex -output-directory " .. tmpdir  .. " " .. tmp)
  os.execute("convert " .. tmp .. ".pdf " .. outfile)
  os.remove(tmp .. ".tex")
  os.remove(tmp .. ".pdf")
  os.remove(tmp .. ".log")
  os.remove(tmp .. ".aux")
end

pdflatex and pdf2svg are both installed, as well as ImageMagick.

In short, I should be good to go but something goes wrong and all I see is my title. Any advice would be really welcome!

Here is my code:

\documentclass{book}

\usepackage{graphicx}
\usepackage{tikz}
\usetikzlibrary{arrows.meta,positioning}

\begin{document}

\begin{tikzpicture}

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

\end{tikzpicture}

\end{document}

When rendering as a .pdf file in TexStudio, the TikZ figure shows up. When using pandoc, nothing happens.

Hopefully you can spot something I have missed!

6
  • Could you post your code?
    – DG'
    Oct 20, 2020 at 16:24
  • I have now added the code of my tex files
    – Boreq
    Oct 20, 2020 at 17:34
  • Thank you for posting a working example! At the same time, much of your code does not seem to be relevant to the question you're asking here. Please limit the example to only the code required for your issue to appear. You can have a look at this guide for how to prune your code for this purpose.
    – DG'
    Oct 20, 2020 at 17:46
  • Ah, thanks for reminding me of this. I think I have simplified the code now, without loss of crucial information. Thanks for the link.
    – Boreq
    Oct 20, 2020 at 18:28
  • 1
    Is your issue really related to including files and biblatex? And, more importantly, which pandoc filter are you using? You might want to post tikz.luaas well
    – DG'
    Oct 20, 2020 at 19:09

1 Answer 1

7

The filter throws an error, because convert doesn't like to make a grey scale PNG. The solution is to add the option -colorspace RGB. In the end ImageMagick seems to have other issues too, so I suggest using pdftoppm -png form the poppler tools instead:

local function file_exists(name)
  local f = io.open(name, 'r')
  if f ~= nil then io.close(f); return true
  else return false end
end

--- Create a standalone LaTeX document which contains only the TikZ picture.
--- Convert to png via Imagemagick.
local function tikz2image(src, outfile)
  local tmp = os.tmpname()
  local tmpdir = string.match(tmp, "^(.*[\\/])") or "."
  -- local tmpdir = "."
  local f = io.open(tmp .. ".tex", 'w')
  f:write("\\documentclass{standalone}\n")
  -- include all packages needed to compile your images
  f:write("\\usepackage{tikz}\n\\usepackage{stanli}\n")
  f:write("\\begin{document}\n")
  f:write(src)
  f:write("\n\\end{document}\n")
  f:close()
  os.execute("pdflatex -output-directory " .. tmpdir  .. " " .. tmp)
  -- os.execute("convert " .. tmp .. ".pdf " .. "-colorspace RGB " .. outfile)
  os.execute("pdftoppm -png " .. tmp .. ".pdf " .. "> " .. outfile)
  os.remove(tmp .. ".tex")
  os.remove(tmp .. ".pdf")
  os.remove(tmp .. ".log")
  os.remove(tmp .. ".aux")
end

function RawBlock(el)
  -- Don't alter element if it's not a tikzpicture environment
  if not el.text:match'^\\begin{tikzpicture}' then
    return nil
    -- Alternatively, parse the contained LaTeX now:
    -- return pandoc.read(el.text, 'latex').blocks
  end  
  local fname = pandoc.sha1(el.text) .. ".png"
  if not file_exists(fname) then
    tikz2image(el.text, fname)
  end
  return pandoc.Para({pandoc.Image({}, fname)})
end

If you are using more complex tikz code, don't forget to add the used libraries and packages to the filter


You can also extend the original filter to cover .docx:

local system = require 'pandoc.system'

local tikz_doc_template = [[
\documentclass{standalone}
\usepackage{xcolor}
\usepackage{tikz}
\begin{document}
\nopagecolor
%s
\end{document}
]]

local function tikz2image(src, filetype, outfile)
  system.with_temporary_directory('tikz2image', function (tmpdir)
    system.with_working_directory(tmpdir, function()
      local f = io.open('tikz.tex', 'w')
      f:write(tikz_doc_template:format(src))
      f:close()
      os.execute('pdflatex tikz.tex')
      if filetype == 'pdf' then
        os.rename('tikz.pdf', outfile)
      elseif filetype == 'png' then
        os.execute("pdftoppm -png tikz.pdf > " .. outfile)
      else
        os.execute('pdf2svg tikz.pdf ' .. outfile)
      end
    end)
  end)
end

extension_for = {
  docx = 'png',
  html = 'svg',
  html4 = 'svg',
  html5 = 'svg',
  latex = 'pdf',
  beamer = 'pdf' }

local function file_exists(name)
  local f = io.open(name, 'r')
  if f ~= nil then
    io.close(f)
    return true
  else
    return false
  end
end

local function starts_with(start, str)
  return str:sub(1, #start) == start
end


function RawBlock(el)
  if starts_with('\\begin{tikzpicture}', el.text) then
    local filetype = extension_for[FORMAT] or 'svg'
    local fname = system.get_working_directory() .. '/' ..
        pandoc.sha1(el.text) .. '.' .. filetype
    if not file_exists(fname) then
      tikz2image(el.text, filetype, fname)
    end
    return pandoc.Para({pandoc.Image({}, fname)})
  else
   return el
  end
end

Using it with the following document:

\documentclass{book}

\usepackage{graphicx}
\usepackage{tikz}
\usetikzlibrary{arrows.meta,positioning}

\begin{document}

\begin{tikzpicture}

\draw[fill] (1,2) circle (100pt);

\end{tikzpicture}

\end{document}

On the command line:

pandoc --from latex+raw_tex --lua-filter=tikz.lua -s file.tex -o test.docx

enter image description here

12
  • Thank you, DG! Shamefully, I still get an error code libpng warning: Image height is zero in IHDR libpng error: Invalid IHDR dataError: /VMerror in --showpage-- VM status: 3 7465709 8808712 Current allocation mode is local Last OS error: 2 GPL Ghostscript 9.26: Unrecoverable error, exit code 1 convert: no images defined a7d104575554345e50033fe534bb432753f1c46f.png @ error/convert.c/ConvertImageCommand/3285. [WARNING] Could not fetch resource 'a7d104575554345e50033fe534bb432753f1c46f.png': PandocResourceNotFound "a7d104575554345e50033fe534bb432753f1c46f.png"
    – Boreq
    Oct 22, 2020 at 7:50
  • By the way, this error popped up when I implemented the first of your suggestions. I did not fully understand how to use the second suggestion. Will the filter render a .docx file in your second alternative for the filter? What commands should I give to pandoc then? Excuse me for asking such basic questions.
    – Boreq
    Oct 22, 2020 at 7:55
  • Thanks. If I use the second option, the file runs without error message but there is no figure in my .docx file. For the first option, if I now tell tikz to \draw[fill] (1,2) circle (100pt), I get the following error: convert: no decode delegate for this image format' @ error/constitute.c/ReadImage/562. convert: no images defined f50f0be0144d07fc26b680af5551537858520506.png @ error/convert.c/ConvertImageCommand/3285. [WARNING] Could not fetch resource f50f0be0144d07fc26b680af5551537858520506.png: PandocResourceNotFound "f50f0be0144d07fc26b680af5551537858520506.png"
    – Boreq
    Oct 22, 2020 at 8:32
  • 1
    Ok, this is what's happening: For the filter to work, you need the option +raw_tex, which unfortunately turns off pandoc's capability to process \include statements. On way to solve this issue is by using a tool that flattens the latex source, i.e. latexpand: latexpand file.tex | pandoc --from latex+raw_tex --lua-filter=tikz.lua -o test.docx
    – DG'
    Oct 22, 2020 at 17:28
  • 1
    That did it. I got it all working now. Amazing solutions, DG, I learned a lot about pandoc because of you.
    – Boreq
    Oct 23, 2020 at 13:30

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .