1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
|
-- 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}\''}
}
local filetypes = {
rnw = {sequence = {"knitr"} },
rtex = {sequence = {"knitr"}},
rmd = {sequence = {"knitr", "pandoc"}, options = {input_format = "markdown"}},
rrst = {sequence = {"knitr", "pandoc"}, options = {input_format = "rst"}},
md = {sequence = {"pandoc"}, options = {input_format = "markdown"}},
rst = {sequence = {"pandoc"}, options = {input_format = "rst"}},
}
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 _, cmd_name in ipairs(sequence) do
local tmp_name = os.tmpname()
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
|