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!

Boreq
  • 103
  • Could you post your code? – DG' Oct 20 '20 at 16:24
  • I have now added the code of my tex files – Boreq Oct 20 '20 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 '20 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 '20 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 '20 at 19:09
  • Thank you for your advice, I have pruned the code even further to only focus on the lua filter problem. Hopefully this is better now! – Boreq Oct 21 '20 at 06:27

1 Answers1

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

DG'
  • 21,727
  • 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 '20 at 07: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 '20 at 07: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 '20 at 08:32
  • It seem like convert still has some trouble to correctly create the image, right? – Boreq Oct 22 '20 at 08:34
  • Thank you for the reply and your effort. I am going to reinstall ImageMagick to see if the issue lies there... I will report it if I am successful. – Boreq Oct 22 '20 at 09:23
  • I changed to code in order to use pdftoppm – DG' Oct 22 '20 at 10:19
  • Thank you so much, DG! This did the trick. I followed this guide to install Poppler, which now creates the figures. So the problem was in ImageMagick in the end... – Boreq Oct 22 '20 at 14:16
  • Just one more problem, shamefully. If I use more complex TeX files and more elaborate TikZ figures, I get the same issue as before: pandoc runs quickly, but only prints the title of the file without giving any error messages. In LaTeX, I use \include{} and write chapters in different files, could this be giving trouble? I have included all the information about the packages I use in the lua filter file, but still no success. Strange, as the filter now works on simpler files! – Boreq Oct 22 '20 at 14:24
  • 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 '20 at 17:28
  • Thanks, you're a genius. Using the first of your two suggestions above,this now works for simple files. However, more complex TikZ figures are not created. I use the TikZ packagearrows.meta. You mention adding the packages I want to use to the lua filter. I cannot find the syntax to do so. Could you please illustrate what this would look like with the first option? – Boreq Oct 23 '20 at 07:31
  • In the code is a comment reading: -- include all packages needed to compile your images. You need to add statements like f:write("\\usetikzlibrary{arrows.meta,positioning}\n"). Don't forget to escape special characters – DG' Oct 23 '20 at 07:57
  • 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 '20 at 13:30