summaryrefslogtreecommitdiff
path: root/Master/texmf-dist/scripts/make4ht/mkutils.lua
blob: 56c413ffb431c9600037caeb1286daf8ad7ab0c3 (plain)
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
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
module(...,package.seeall)

local make4ht = require("make4ht-lib")
--template engine
function interp(s, tab)
	local tab = tab or {}
	return (s:gsub('($%b{})', function(w) return tab[w:sub(3, -2)] or w end))
end
--print( interp("${name} is ${value}", {name = "foo", value = "bar"}) )

function addProperty(s,prop)
	if prop ~=nil then
		return s .." "..prop
	else
		return s
	end
end
getmetatable("").__mod = interp
getmetatable("").__add = addProperty 

--print( "${name} is ${value}" % {name = "foo", value = "bar"} )
-- Outputs "foo is bar"

function string:split(sep)
	local sep, fields = sep or ":", {}
	local pattern = string.format("([^%s]+)", sep)
	self:gsub(pattern, function(c) fields[#fields+1] = c end)
	return fields
end

function remove_extension(path)
	local found, len, remainder = string.find(path, "^(.*)%.[^%.]*$")
	if found then
		return remainder
	else
		return path
	end
end

-- 

function file_exists(file)
	local f = io.open(file, "rb")
	if f then f:close() end
	return f ~= nil
end

-- searching for converted images
function parse_lg(filename)
  print("Parse LG")
  local outputimages,outputfiles,status={},{},nil
  local fonts, used_fonts = {},{}
  if not file_exists(filename) then
    print("Cannot read log file: "..filename)
  else
    local usedfiles={}
    for line in io.lines(filename) do
			--- needs --- pokus.idv[1] ==> pokus0x.png --- 
      -- line:gsub("needs --- (.+?)[([0-9]+) ==> ([%a%d%p%.%-%_]*)",function(name,page,k) table.insert(outputimages,k)end)
      line:gsub("needs %-%-%- (.+)%[([0-9]+)%] ==> ([%a%d%p%.%-%_]*)",
			  function(file,page,output) 
					local rec = {
						source = file,
						page=page,
						output = output
					}
					table.insert(outputimages,rec)
				end
			)
      line:gsub("File: (.*)",  function(k) 
        if not usedfiles[k] then
          table.insert(outputfiles,k)
          usedfiles[k] = true
        end
      end)
      line:gsub("htfcss: ([^%s]+)(.*)",function(k,r)
        local fields = {}
        r:gsub("[%s]*([^%:]+):[%s]*([^;]+);",function(c,v)
          fields[c] = v
        end)
        fonts[k] = fields
      end)

      line:gsub('Font("([^"]+)","([%d]+)","([%d]+)","([%d]+)"',function(n,s1,s2,s3)
        table.insert(used_fonts,{n,s1,s2,s3})
      end)

    end
    status=true
  end
  return {files = outputfiles, images = outputimages},status
end


-- 
local cp_func = os.type == "unix" and "cp" or "copy"
-- maybe it would be better to actually move the files
-- in reality it isn't.
-- local cp_func = os.type == "unix" and "mv" or "move"
function cp(src,dest)
	local command = string.format('%s "%s" "%s"', cp_func, src, dest)
	if cp_func == "copy" then command = command:gsub("/",'\\') end
	print("Copy: "..command)
	os.execute(command)
end

local used_dir = {}

function prepare_path(path)
	--local dirs = path:split("/")
	local dirs = {}
	if path:match("^/") then dirs = {""}
	elseif path:match("^~") then
		local home = os.getenv "HOME"
		dirs = home:split "/"
		path = path:gsub("^~/","")
		table.insert(dirs,1,"")
	end
	if path:match("/$")then path = path .. " " end
	for _,d in pairs(path:split "/") do
		table.insert(dirs,d)
	end
	table.remove(dirs,#dirs)
	return dirs,table.concat(dirs,"/")
end

-- Find which part of path already exists
-- and which directories have to be created
function find_directories(dirs, pos)
	local pos = pos or #dirs
	-- we tried whole path and no dir exist
	if pos < 1 then return dirs end
	local path = ""
	-- in the case of unix absolute path, empty string is inserted in dirs
	if pos == 1 and dirs[pos] == "" then
		path = "/"
	else
		path = table.concat(dirs,"/", 1,pos) .. "/"
	end
	if not lfs.chdir(path)  then -- recursion until we succesfully changed dir
	-- or there are no elements in the dir table
	return find_directories(dirs,pos - 1)
elseif pos ~= #dirs then -- if we succesfully changed dir
	-- and we have dirs to create
	local p = {}
	for i = pos+1, #dirs do
		table.insert(p, dirs[i])
	end
	return p
else  -- whole path exists
	return {}
end
end

function mkdirectories(dirs)
	if type(dirs) ~="table" then
		return false, "mkdirectories: dirs is not table"
	end
	for _,d in pairs(dirs) do
		local stat,msg = lfs.mkdir(d)
		if not stat then return false, "makedirectories error: "..msg end
		lfs.chdir(d)
	end
	return true
end

function copy_filter(src,dest, filter)
  local src_f=io.open(src,"rb")
  local dst_f=io.open(dest,"w")
  local contents = src_f:read("*all")
  local filter = filter or function(s) return s end
  src_f:close()
  dst_f:write(filter(contents))
  dst_f:close()
end



function copy(filename,outfilename)
	local currdir = lfs.currentdir()
	if filename == outfilename then return true end
	local parts, path = prepare_path(outfilename)
	if not used_dir[path] then 
		local to_create, msg = find_directories(parts)
		if not to_create then
			print(msg)
			return false
		end
		used_dir[path] = true
		local stat, msg = mkdirectories(to_create)
		if not stat then print(msg) end
	end
	lfs.chdir(currdir)
	cp(filename, path)
	return true
end

-- Config loading
local function run(untrusted_code, env)
	if untrusted_code:byte(1) == 27 then return nil, "binary bytecode prohibited" end
	local untrusted_function = nil
	if not loadstring then  
		untrusted_function, message = load(untrusted_code, nil, "t",env)
	else
		untrusted_function, message = loadstring(untrusted_code)
	end
	if not untrusted_function then return nil, message end
	if not setfenv then setfenv = function(a,b) return true end end
	setfenv(untrusted_function, env)
	return pcall(untrusted_function)
end

local main_settings = {}
main_settings.fonts = {}
local env = {}

-- We make sandbox for make script, all functions must be explicitely declared
-- Function declarations:
env.pairs  = pairs
env.ipairs = ipairs
env.print  = print
env.split  = split
env.string = string
env.table  = table
env.copy   = copy
env.mkdirectories = mkdirectories
env.require = require
env.texio  = texio
env.type   = type
env.lfs    = lfs
env.os     = os
env.io     = io
env.unicode = unicode
env.Font   = function(s)
	local font_name = s["name"]
	if not font_name then return nil, "Cannot find font name" end
	env.settings.fonts[font_name] = s
end

env.Make   = make4ht.Make
env.Make.params = env.settings
env.Make:add("test","no takže ${tex4ht_sty_par} ${htlatex} ${input} ${config}")
--env.Make:add("htlatex", "${htlatex} ${latex_par} '\\\makeatletter\\def\\HCode{\\futurelet\\HCode\\HChar}\\def\\HChar{\\ifx\"\\HCode\\def\\HCode\"##1\"{\\Link##1}\\expandafter\\HCode\\else\\expandafter\\Link\\fi}\\def\\Link#1.a.b.c.{\\g@addto@macro\\@documentclasshook{\\RequirePackage[#1,html]{tex4ht}\\let\\HCode\\documentstyle\\def\\documentstyle{\\let\\documentstyle\\HCode\\expandafter\\def\\csname tex4ht\\endcsname{#1,html}\\def\\HCode####1{\\documentstyle[tex4ht,}\\@ifnextchar[{\\HCode}{\\documentstyle[tex4ht]}}}\\makeatother\\HCode '${config}${tex4ht_sty_par}'.a.b.c.\\input ' ${input}")
env.Make:add("htlatex",function(par) 
	local command = 
"${htlatex} ${latex_par} '\\makeatletter"..
"\\def\\HCode{\\futurelet\\HCode\\HChar}\\def\\HChar{\\ifx\"\\HCode"..
"\\def\\HCode\"##1\"{\\Link##1}\\expandafter\\HCode\\else"..
"\\expandafter\\Link\\fi}\\def\\Link#1.a.b.c.{\\g@addto@macro"..
"\\@documentclasshook{\\RequirePackage[#1,html]{tex4ht}${packages}}"..
"\\let\\HCode\\documentstyle\\def\\documentstyle{\\let\\documentstyle"..
"\\HCode\\expandafter\\def\\csname tex4ht\\endcsname{#1,html}\\def"..
"\\HCode####1{\\documentstyle[tex4ht,}\\@ifnextchar[{\\HCode}{"..
"\\documentstyle[tex4ht]}}}\\makeatother\\HCode ${tex4ht_sty_par}.a.b.c."..
"\\input ${tex_file}'" 
  if os.type == "windows" then
    command = command:gsub("'",'')
  end
  command = command % par
  print("LaTeX call: "..command)
  os.execute(command)
	local logfile = par.input .. ".log"
	local f = io.open(logfile,"r")
	if not f then 
		print("Make4ht: cannot open log file "..logfile)
		return 1
	end
	local len = f:seek("end")
	f:seek("set", len - 1256)
	local text = f:read("*a")
	f:close()
	if text:match("No pages of output") or text:match("TeX capacity exceeded, sorry") then return 1 end
	return 0 
end
,{correct_exit=0})
env.Make:add("tex4ht","tex4ht ${tex4ht_par} \"${input}.${dvi}\"", nil, 1)
env.Make:add("t4ht","t4ht ${t4ht_par} \"${input}.${ext}\"",{ext="dvi"},1)

function load_config(settings, config_name)
	local settings = settings or main_settings
	env.settings = settings
	env.mode = settings.mode
	local config_name = kpse.find_file(config_name, 'texmfscripts') or config_name
	local f = io.open(config_name,"r")
	if not f then 
    print("Cannot open config file", config_name)
    return  env
  end
  print("Using build file", config_name)
	local code = f:read("*all")
	local fn, msg = run(code,env)
	if not fn then print(msg) end
	assert(fn)
	return env
end