pandoc / lua-filters

A collection of lua filters for pandoc
MIT License
611 stars 166 forks source link

diagram-generator latex svg output #145

Closed twsh closed 1 year ago

twsh commented 3 years ago

Currently, diagram-generator.lua uses makes svg images when latex is the target output. If I understand correctly, it would be better to just use the pdf directly when making tikz diagrams for LaTeX because svg is problematic with LaTeX. For other sorts of diagram, maybe png would be better, or also pdf.

I tried to do this, and it seems to work. Does it look like it will cause other problems?

if FORMAT == "docx" then
    filetype = "png"
    mimetype = "image/png"
elseif FORMAT == "pptx" then
    filetype = "png"
    mimetype = "image/png"
elseif FORMAT == "rtf" then
    filetype = "png"
    mimetype = "image/png"
elseif FORMAT == "latex" then
    filetype = "pdf"
    mimetype = "application/pdf"
end
--- Compile LaTeX with Tikz code to an image
local function tikz2image(src, filetype, additional_packages)
  return with_temporary_directory("tikz2image", function (tmpdir)
    return with_working_directory(tmpdir, function ()
      -- Define file names:
      local file_template = "%s/tikz-image.%s"
      local tikz_file = file_template:format(tmpdir, "tex")
      local pdf_file = file_template:format(tmpdir, "pdf")
      local outfile = file_template:format(tmpdir, filetype)

      -- Build and write the LaTeX document:
      local f = io.open(tikz_file, 'w')
      f:write(tikz_template:format(additional_packages or '', src))
      f:close()

      -- Execute the LaTeX compiler:
      pandoc.pipe(pdflatex_path, {'-output-directory', tmpdir, tikz_file}, '')

      if filetype ~= 'pdf' then
        local convert = convert_with_inkscape(filetype)
        -- Bail if there is now known way from PDF to the target format.
        if not convert then
            error(string.format("Don't know how to convert pdf to %s.", filetype))
        end
        convert(pdf_file, outfile)
      end

      -- Try to open and read the image:
      local img_data
      local r = io.open(outfile, 'rb')
      if r then
        img_data = r:read("*all")
        r:close()
      else
        -- TODO: print warning
      end

      return img_data
    end)
  end)
end
twsh commented 3 years ago

Sorry, I just realised that that will require all the different diagram generators to make PDF when the format is latex, and only tikz will be able to.

I should have added mimetype = "application/pdf".

I tried adding PDF output to convert_with_inkscape():

local function convert_with_inkscape(filetype)
  -- Build the basic Inkscape command for the conversion
  local inkscape_output_args
  if filetype == 'png' then
    inkscape_output_args = '--export-png="%s" --export-dpi=300'
  elseif filetype == 'svg' then
    inkscape_output_args = '--export-plain-svg="%s"'
  elseif filetype == 'pdf' then
    inkscape_output_args = '--export-type=pdf "%s"'
  else
    return nil
  end
  return function (pdf_file, outfile)
    local inkscape_command = string.format(
      '"%s" --without-gui --file="%s" ' .. inkscape_output_args,
      inkscape_path,
      pdf_file,
      outfile
    )
    io.stderr:write(inkscape_command .. '\n')
    local command_output = io.popen(inkscape_command)
    -- TODO: print output when debugging.
    command_output:close()
  end
end
bpj commented 3 years ago

You can always use rsvg-convert or inkscape to convert svg to pdf. In fact there probably are tools which can convert between any two image formats you can think of. You will have to also add code so that you don't needlessly convert pdf > svg > pdf though. I would suggest creating some lookup tables in place of lots of if elseif else, since it is easier to maintain:

  1. diagram format > image format + function calling command to generate image.
  2. pandoc output format > needed image format.
  3. final image format > intermediate image format.
  4. source image format > target image format > function which calls conversion command.
  5. image format > output format > function which takes image file name and returns appropriate pandoc element, i.e. image with path or embedded image data.

Where the "command" parts are functions which get passed the needed arguments and input as a table from which the function picks values to pass to the pipe function, then returns the same or another table with the "new info" like file names added which then can be passed on to the next function.

Yeah, I really like dispatch tables for tasks like this! :-) (And the tables returned by Lua filters are dispatch tables, so I'm probably preaching to the believers! :-)

-- Better --help|less than helpless

Den lör 21 nov. 2020 14:40Thomas Hodgson notifications@github.com skrev:

Sorry, I just realised that that will require all the different diagram generators to make PDF when the format is latex, and only tikz will be able to.

— You are receiving this because you are subscribed to this thread. Reply to this email directly, view it on GitHub https://github.com/pandoc/lua-filters/issues/145#issuecomment-731581053, or unsubscribe https://github.com/notifications/unsubscribe-auth/AAI3OU5AWV5BYLRIY5QANLTSQ67LVANCNFSM4T5Y7CWQ .

twsh commented 3 years ago

@bpj That would be a big rewrite. Which is not to say that it's not a good idea.

PeterSommerlad commented 2 years ago

I have a similiar request, to be able to just pass through the tikZ code if the target is LaTeX. That means, it will be rendered by the overall latex to pdf of the whole document with its settings. This avoids the multiple conversions back and forth.

twsh commented 2 years ago

@PeterSommerlad I like that idea. One problem that comes to mind is how to handle 'additionalPackages' attributes. At the moment, someone could be using different, incompatible, code for each of their tikz diagrams. One simple fix would be to note that this won't work, and that the user has to provide code in the header of their document suitable for all their diagrams.

PeterSommerlad commented 2 years ago

i would just not output the additional packages in latex output mode and expect the latex template to handle those.

bpj commented 2 years ago

I have usually written something in the README along the lines of "It is up to you, the user, to make sure that the foo package is loaded in your LaTeX template or in a header-includes block in the metadata", followed by an example metadata snippet like

``````yaml
---
header-includes:
  - |
    ```{=latex}
    \usepackage{foo}
    ```
``````

That said it isn't all that hard to actually inject that block into metadata:

return {
  { Meta = function (meta)
      if not meta['header-includes'] then
        meta['header-includes'] = pandoc.MetaList()
      end
      -- Else assume it is a meta list and hope for the best
      local hinc = meta['header-includes']
      hinc[#hinc+1] = pandoc.MetaBlocks{
        pandoc.RawBlock('latex', '\\usepackage{foo}')
      }
      return meta
    end
  },
  main_filter,
}
tarleb commented 1 year ago

The diagram generator is now maintained at https://github.com/pandoc-ext/diagram. The filter was rewritten to just use the generated PDF when converting to LaTeX or ConTeXt.