-- preprocess R literate sources or Markdown files to LaTeX local M = {} local log = logging.new "preprocess_input" local mkutils = require "mkutils" local commands = { knitr = { command = 'Rscript -e "library(knitr); knit(\'${tex_file}\', output=\'${tmp_file}\')"'}, pandoc = { command = 'pandoc -f ${input_format} -s -o \'${tmp_file}\' -t latex \'${tex_file}\''}, render = { command = 'Rscript -e "library(rmarkdown); render(\'${tex_file}\', output_file=\'${tmp_file}\',output_format = \'latex_document\')"'} } local filetypes = { rnw = {sequence = {"knitr"} }, rtex = {sequence = {"knitr"}}, rmd = {sequence = {"render"}}, rrst = {sequence = {"knitr", "pandoc"}, options = {input_format = "rst"}}, md = {sequence = {"pandoc"}, options = {input_format = "markdown"}}, rst = {sequence = {"pandoc"}, options = {input_format = "rst"}}, } local function get_temp_name(arg,curr, length) -- we don't want to use the temp dir, because graphics would be then generated outside of -- the directory of the source document. so we will make local tmp_name = os.tmpname() if pos == sequence then -- base tmp_name on the input name in the last step of sequence -- so the generated images won't have random names tmp_name = arg.input .. "-preprocess_input" else tmp_name = tmp_name:match("([^/\\]+)$") end return tmp_name end local function execute_sequence(sequence, arg, make) -- keep track of all generated tmp files local temp_files = {} -- the temporary file for the current compilation step -- should become the tex_file for the next one. It doesn't -- matter that it isn't TeX file in some cases local previous_temp for pos, cmd_name in ipairs(sequence) do local tmp_name = get_temp_name(arg,pos, #sequence) temp_files[#temp_files+1] = tmp_name -- make the temp file name accessible to the executed commands arg.tmp_file = tmp_name -- the current temporary file should become tex_file in the next step -- in the first execution of the compilation sequence we will use the -- actual input file name arg.tex_file = previous_temp or arg.tex_file previous_temp = tmp_name -- get the command to execute local cmd = commands[cmd_name] -- fill the command template with make4ht arguments and execute local command = cmd.command % arg log:info(command) mkutils.execute(command) end return temp_files end local function get_preprocessing_pipeline(input_file) -- detect the file extension local extension = input_file:match("%.(.-)$") if not extension then return nil, "Cannot get extension: " .. input_file end -- the table with file actions is case insensitive -- the extension is converted to lowercase in order -- to support both .rnw and .Rnw extension = string.lower(extension) local matched = filetypes[extension] if not matched then return nil, "Unsupported extension: " .. extension end return matched end -- join the make4ht params and command options tables local function make_options(arg, command_options) local options = {} local command_options = command_options or {} for k,v in pairs(arg) do options[k] = v end for k,v in pairs(command_options) do options[k] = v end return options end M.modify_build = function(make) -- get access to the main argumens local arg = make.params -- get the execution sequence for the input format local matched, msg = get_preprocessing_pipeline(arg.tex_file) if not matched then log:error("preprocess_input error: ".. msg) return end -- prepare options local options = make_options(arg, matched.options) -- run the execution sequence local temp_files = execute_sequence(matched.sequence or {}, options, make) -- the last temporary file contains the actual TeX file local last_temp_file = temp_files[#temp_files] -- remove the intermediate temp files if #temp_files > 2 then for i = 1, #temp_files - 1 do log:debug("Removing temporary file", temp_files[i]) os.remove(temp_files[i]) end end if last_temp_file then -- update all commands in the .mk4 file with the temp file as tex_file local update_params = function(cmd) local params = cmd.params params.tex_file = last_temp_file params.is_tmp_file = true end for _, cmd in ipairs(make.build_seq) do update_params(cmd) end -- also update the main params update_params(make) end return make end return M