summaryrefslogtreecommitdiff
path: root/Master/texmf-dist/scripts/make4ht
diff options
context:
space:
mode:
authorKarl Berry <karl@freefriends.org>2015-07-03 23:24:59 +0000
committerKarl Berry <karl@freefriends.org>2015-07-03 23:24:59 +0000
commitf1073f0dfaaeabd2edda00c472a3d814fad132fa (patch)
treef72e3d8d9e989b6fa399a13600be3d88168eebca /Master/texmf-dist/scripts/make4ht
parent0562fc367d1605303c6b2674d0d6b73b37d1d888 (diff)
make4ht (29jul15)
git-svn-id: svn://tug.org/texlive/trunk@37750 c570f23f-e606-0410-a88d-b1316a301751
Diffstat (limited to 'Master/texmf-dist/scripts/make4ht')
-rw-r--r--Master/texmf-dist/scripts/make4ht/filters/make4ht-cleanspan-nat.lua12
-rw-r--r--Master/texmf-dist/scripts/make4ht/filters/make4ht-cleanspan.lua33
-rw-r--r--Master/texmf-dist/scripts/make4ht/filters/make4ht-entities.lua14
-rw-r--r--Master/texmf-dist/scripts/make4ht/filters/make4ht-filter.lua41
-rw-r--r--Master/texmf-dist/scripts/make4ht/filters/make4ht-fixligatures.lua16
-rw-r--r--Master/texmf-dist/scripts/make4ht/filters/make4ht-hruletohr.lua11
-rwxr-xr-xMaster/texmf-dist/scripts/make4ht/lapp-mk4.lua317
-rwxr-xr-xMaster/texmf-dist/scripts/make4ht/make4ht62
-rwxr-xr-xMaster/texmf-dist/scripts/make4ht/make4ht-lib.lua204
-rwxr-xr-xMaster/texmf-dist/scripts/make4ht/mkparams.lua135
-rwxr-xr-xMaster/texmf-dist/scripts/make4ht/mkutils.lua290
11 files changed, 1135 insertions, 0 deletions
diff --git a/Master/texmf-dist/scripts/make4ht/filters/make4ht-cleanspan-nat.lua b/Master/texmf-dist/scripts/make4ht/filters/make4ht-cleanspan-nat.lua
new file mode 100644
index 00000000000..896132160c1
--- /dev/null
+++ b/Master/texmf-dist/scripts/make4ht/filters/make4ht-cleanspan-nat.lua
@@ -0,0 +1,12 @@
+-- cleanspan function submitted by Nat Kuhn
+-- http://www.natkuhn.com/
+
+local function filter(s)
+ local pattern = "(<span%s+([^>]+)>[^<]*)</span>(%s*)<span%s+%2>"
+ repeat
+ s, n = s:gsub(pattern, "%1%3")
+ until n == 0
+ return s
+end
+
+return filter
diff --git a/Master/texmf-dist/scripts/make4ht/filters/make4ht-cleanspan.lua b/Master/texmf-dist/scripts/make4ht/filters/make4ht-cleanspan.lua
new file mode 100644
index 00000000000..ede5c6c74e6
--- /dev/null
+++ b/Master/texmf-dist/scripts/make4ht/filters/make4ht-cleanspan.lua
@@ -0,0 +1,33 @@
+-- make4ht-cleanspan4ht.lua
+-- fixes spurious <span> elements in tex4ht output
+
+
+function filter(input)
+ local parse_args = function(s)
+ local at = {}
+ s:gsub("(%w+)%s*=%s*\"([^\"]-)\"", function(k,w)
+ at[k]=w
+ end)
+ return at
+ end
+ -- local pattern = "(<?/?[%w]*>?)<span[%s]*class=\"([^\"]+)\"[%s]*>"
+ local pattern = "(<?/?[%w]*>?)([%s]*)<span[%s]*([^>]-)>"
+ local last_class = ""
+ local depth = 0
+ return input:gsub(pattern, function(tag,space, args)
+ local attr = parse_args(args) or {}
+ local class = attr["class"] or ""
+ if tag == "</span>" then
+ if class == last_class and class~= "" then
+ last_class = class
+ return space .. ""
+ end
+ elseif tag == "" then
+ class=""
+ end
+ last_class = class
+ return tag ..space .. '<span '..args ..'>'
+ end)
+end
+
+return filter
diff --git a/Master/texmf-dist/scripts/make4ht/filters/make4ht-entities.lua b/Master/texmf-dist/scripts/make4ht/filters/make4ht-entities.lua
new file mode 100644
index 00000000000..f9f6c883d1f
--- /dev/null
+++ b/Master/texmf-dist/scripts/make4ht/filters/make4ht-entities.lua
@@ -0,0 +1,14 @@
+-- Fix bad entities
+-- Sometimes, tex4ht produce named xml entities, which are prohobited in epub
+-- &nbsp;, for example
+function filter(s)
+ local replaces = {
+ nbsp = "#160"
+ }
+ return s:gsub("&(%w+);",function(x)
+ local m = replaces[x] or x
+ return "&"..m..";"
+ end)
+end
+
+return filter
diff --git a/Master/texmf-dist/scripts/make4ht/filters/make4ht-filter.lua b/Master/texmf-dist/scripts/make4ht/filters/make4ht-filter.lua
new file mode 100644
index 00000000000..4c7ce409a07
--- /dev/null
+++ b/Master/texmf-dist/scripts/make4ht/filters/make4ht-filter.lua
@@ -0,0 +1,41 @@
+local filters = {}
+
+local function load_filter(filtername)
+ return require("filters.make4ht-"..filtername)
+end
+
+function filter(filters)
+ local sequence = {}
+ if type(filters) == "string" then
+ table.insert(sequence,load_filter(filters))
+ elseif type(filters) == "table" then
+ for _,n in pairs(filters) do
+ if type(n) == "string" then
+ table.insert(sequence,load_filter(n))
+ elseif type(n) == "function" then
+ table.insert(sequence, n)
+ end
+ end
+ elseif type(filters) == "function" then
+ table.insert(sequence, filters)
+ else
+ return false, "Argument to filter must be either\ntable with filter names, or single filter name"
+ end
+ return function(filename)
+ if not filename then return false, "filters: no filename" end
+ local input = nil
+
+ if filename then
+ local file = io.open(filename,"r")
+ input = file:read("*all")
+ file:close()
+ end
+ for _,f in pairs(sequence) do
+ input = f(input)
+ end
+ local file = io.open(filename,"w")
+ file:write(input)
+ file:close()
+ end
+end
+return filter
diff --git a/Master/texmf-dist/scripts/make4ht/filters/make4ht-fixligatures.lua b/Master/texmf-dist/scripts/make4ht/filters/make4ht-fixligatures.lua
new file mode 100644
index 00000000000..01b31ed3067
--- /dev/null
+++ b/Master/texmf-dist/scripts/make4ht/filters/make4ht-fixligatures.lua
@@ -0,0 +1,16 @@
+-- fix ligatures
+-- replace ligatures produced by tex4ht with their components
+-- this prevents problems with some readers
+local gsub = unicode.utf8.gsub
+function filter(s)
+ local replaces = {
+ ["fi"] = "fi",
+ ["ffi"] = "ffi",
+ ["fl"] = "fl",
+ ["ffl"] = "ffl",
+ ["ff"] = "ff"
+ }
+ return gsub(s, "([fiffiflfflff])",function (x) return replaces[x] or x end)
+end
+
+return filter
diff --git a/Master/texmf-dist/scripts/make4ht/filters/make4ht-hruletohr.lua b/Master/texmf-dist/scripts/make4ht/filters/make4ht-hruletohr.lua
new file mode 100644
index 00000000000..a9c0c5e9df2
--- /dev/null
+++ b/Master/texmf-dist/scripts/make4ht/filters/make4ht-hruletohr.lua
@@ -0,0 +1,11 @@
+-- hruletohr
+-- \hrule primitive is impossible to redefine catching all possible arguments
+-- with tex4ht, it is converted as series of underscores
+-- it seems that these underscores are always part of previous paragraph
+-- this assumption may be wrong, needs more real world testing
+
+local hruletohr = function(s)
+ return s:gsub("___+(.-)</p>","%1</p>\n<hr class=\"hrule\" />")
+end
+
+return hruletohr
diff --git a/Master/texmf-dist/scripts/make4ht/lapp-mk4.lua b/Master/texmf-dist/scripts/make4ht/lapp-mk4.lua
new file mode 100755
index 00000000000..71ed3704077
--- /dev/null
+++ b/Master/texmf-dist/scripts/make4ht/lapp-mk4.lua
@@ -0,0 +1,317 @@
+-- lapp.lua
+-- Simple command-line parsing using human-readable specification
+-----------------------------
+--~ -- args.lua
+--~ local args = require ('lapp') [[
+--~ Testing parameter handling
+--~ -p Plain flag (defaults to false)
+--~ -q,--quiet Plain flag with GNU-style optional long name
+--~ -o (string) Required string option
+--~ -n (number) Required number option
+--~ -s (default 1.0) Option that takes a number, but will default
+--~ <start> (number) Required number argument
+--~ <input> (default stdin) A parameter which is an input file
+--~ <output> (default stdout) One that is an output file
+--~ ]]
+--~ for k,v in pairs(args) do
+--~ print(k,v)
+--~ end
+-------------------------------
+--~ > args -pq -o help -n 2 2.3
+--~ input file (781C1B78)
+--~ p true
+--~ s 1
+--~ output file (781C1B98)
+--~ quiet true
+--~ start 2.3
+--~ o help
+--~ n 2
+--------------------------------
+
+lapp = {}
+
+local append = table.insert
+local usage
+local open_files = {}
+local parms = {}
+local aliases = {}
+local parmlist = {}
+
+local filetypes = {
+ stdin = {io.stdin,'file-in'}, stdout = {io.stdout,'file-out'},
+ stderr = {io.stderr,'file-out'}
+}
+
+local function quit(msg,no_usage)
+ if msg then
+ io.stderr:write(msg..'\n\n')
+ end
+ if not no_usage then
+ io.stderr:write(usage)
+ end
+ os.exit(1);
+end
+
+local function error(msg,no_usage)
+ quit(arg[0]:gsub('.+[\\/]','')..':'..msg,no_usage)
+end
+
+local function ltrim(line)
+ return line:gsub('^%s*','')
+end
+
+local function rtrim(line)
+ return line:gsub('%s*$','')
+end
+
+local function trim(s)
+ return ltrim(rtrim(s))
+end
+
+local function open (file,opt)
+ local val,err = io.open(file,opt)
+ if not val then error(err,true) end
+ append(open_files,val)
+ return val
+end
+
+local function xassert(condn,msg)
+ if not condn then
+ error(msg)
+ end
+end
+
+local function range_check(x,min,max,parm)
+ xassert(min <= x and max >= x,parm..' out of range')
+end
+
+local function xtonumber(s)
+ local val = tonumber(s)
+ if not val then error("unable to convert to number: "..s) end
+ return val
+end
+
+local function is_filetype(type)
+ return type == 'file-in' or type == 'file-out'
+end
+
+local types = {}
+
+local function convert_parameter(ps,val)
+ if ps.converter then
+ val = ps.converter(val)
+ end
+ if ps.type == 'number' then
+ val = xtonumber(val)
+ elseif is_filetype(ps.type) then
+ val = open(val,(ps.type == 'file-in' and 'r') or 'w' )
+ elseif ps.type == 'boolean' then
+ val = true
+ end
+ if ps.constraint then
+ ps.constraint(val)
+ end
+ return val
+end
+
+function lapp.add_type (name,converter,constraint)
+ types[name] = {converter=converter,constraint=constraint}
+end
+
+local function force_short(short)
+ xassert(#short==1,short..": short parameters should be one character")
+end
+
+function process_options_string(str)
+ local res = {}
+ local varargs
+
+ local function check_varargs(s)
+ local res,cnt = s:gsub('%.%.%.$','')
+ varargs = cnt > 0
+ return res
+ end
+
+ local function set_result(ps,parm,val)
+ if not ps.varargs then
+ res[parm] = val
+ else
+ if not res[parm] then
+ res[parm] = { val }
+ else
+ append(res[parm],val)
+ end
+ end
+ end
+
+ usage = str
+
+ for line in str:gmatch('([^\n]*)\n') do
+ local optspec,optparm,i1,i2,defval,vtype,constraint
+ line = ltrim(line)
+ -- flags: either -<short> or -<short>,<long>
+ i1,i2,optspec = line:find('^%-(%S+)')
+ if i1 then
+ optspec = check_varargs(optspec)
+ local short,long = optspec:match('([^,]+),(.+)')
+ if short then
+ optparm = long:sub(3)
+ aliases[short] = optparm
+ force_short(short)
+ else
+ optparm = optspec
+ force_short(optparm)
+ end
+ else -- is it <parameter_name>?
+ i1,i2,optparm = line:find('(%b<>)')
+ if i1 then
+ -- so <input file...> becomes input_file ...
+ optparm = check_varargs(optparm:sub(2,-2)):gsub('%A','_')
+ append(parmlist,optparm)
+ end
+ end
+ if i1 then -- this is not a pure doc line
+ local last_i2 = i2
+ local sval
+ line = ltrim(line:sub(i2+1))
+ -- do we have (default <val>) or (<type>)?
+ i1,i2,typespec = line:find('^%s*(%b())')
+ if i1 then
+ typespec = trim(typespec:sub(2,-2)) -- trim the parens and any space
+ sval = typespec:match('default%s+(.+)')
+ if sval then
+ local val = tonumber(sval)
+ if val then -- we have a number!
+ defval = val
+ vtype = 'number'
+ elseif filetypes[sval] then
+ local ft = filetypes[sval]
+ defval = ft[1]
+ vtype = ft[2]
+ else
+ defval = sval
+ vtype = 'string'
+ end
+ else
+ local min,max = typespec:match '([^%.]+)%.%.(.+)'
+ if min then -- it's (min..max)
+ vtype = 'number'
+ min = xtonumber(min)
+ max = xtonumber(max)
+ constraint = function(x)
+ range_check(x,min,max,optparm)
+ end
+ else -- () just contains type of required parameter
+ vtype = typespec
+ end
+ end
+ else -- must be a plain flag, no extra parameter required
+ defval = false
+ vtype = 'boolean'
+ end
+ local ps = {
+ type = vtype,
+ defval = defval,
+ required = defval == nil,
+ comment = line:sub((i2 or last_i2)+1) or optparm,
+ constraint = constraint,
+ varargs = varargs
+ }
+ if types[vtype] then
+ local converter = types[vtype].converter
+ if type(converter) == 'string' then
+ ps.type = converter
+ else
+ ps.converter = converter
+ end
+ ps.constraint = types[vtype].constraint
+ end
+ parms[optparm] = ps
+ end
+ end
+ -- cool, we have our parms, let's parse the command line args
+ local iparm = 1
+ local iextra = 1
+ local i = 1
+ local parm,ps,val
+ while i <= #arg do
+ -- look for a flag, -<short flags> or --<long flag>
+ local i1,i2,dash,parmstr = arg[i]:find('^(%-+)(%a.*)')
+ if i1 then -- we have a flag
+ if #dash == 2 then -- long option
+ parm = parmstr
+ else -- short option
+ if #parmstr == 1 then
+ parm = parmstr
+ else -- multiple flags after a '-',?
+ parm = parmstr:sub(1,1)
+ if parmstr:find('^%a%d+') then
+ -- a short option followed by a digit? (exception for AW ;))
+ -- push ahead into the arg array
+ table.insert(arg,i+1,parmstr:sub(2))
+ else
+ -- push multiple flags into the arg array!
+ for k = 2,#parmstr do
+ table.insert(arg,i+k-1,'-'..parmstr:sub(k,k))
+ end
+ end
+ end
+ end
+ if parm == 'h' or parm == 'help' then
+ quit()
+ end
+ if aliases[parm] then parm = aliases[parm] end
+ ps = parms[parm]
+ if not ps then error("unrecognized parameter: "..parm) end
+ if ps.type ~= 'boolean' then -- we need a value! This should follow
+ val = arg[i+1]
+ i = i + 1
+ xassert(val,parm.." was expecting a value")
+ end
+ else -- a parameter
+ parm = parmlist[iparm]
+ if not parm then
+ -- extra unnamed parameters are indexed starting at 1
+ parm = iextra
+ iextra = iextra + 1
+ ps = { type = 'string' }
+ else
+ ps = parms[parm]
+ end
+ if not ps.varargs then
+ iparm = iparm + 1
+ end
+ val = arg[i]
+ end
+ ps.used = true
+ val = convert_parameter(ps,val)
+ set_result(ps,parm,val)
+ if is_filetype(ps.type) then
+ set_result(ps,parm..'_name',arg[i])
+ end
+ if lapp.callback then
+ lapp.callback(parm,arg[i],res)
+ end
+ i = i + 1
+ end
+ -- check unused parms, set defaults and check if any required parameters were missed
+ for parm,ps in pairs(parms) do
+ if not ps.used then
+ if ps.required then error("missing required parameter: "..parm) end
+ set_result(ps,parm,ps.defval)
+ end
+ end
+ return res
+end
+
+setmetatable(lapp, {
+ __call = function(tbl,str) return process_options_string(str) end,
+ __index = {
+ open = open,
+ quit = quit,
+ error = error,
+ assert = xassert,
+ }
+})
+
+return lapp
diff --git a/Master/texmf-dist/scripts/make4ht/make4ht b/Master/texmf-dist/scripts/make4ht/make4ht
new file mode 100755
index 00000000000..22b2e7cf2fb
--- /dev/null
+++ b/Master/texmf-dist/scripts/make4ht/make4ht
@@ -0,0 +1,62 @@
+#!/usr/bin/env texlua
+kpse.set_program_name("luatex")
+
+
+local make4ht = require("make4ht-lib")
+local lapp = require("lapp-mk4")
+local mkutils = require("mkutils")
+local mkparams = require("mkparams")
+-- args string is here just as sample, we dont pass it it to
+-- mkparams.get_args() so default args string is used
+local args = [[
+make4ht - build system for tex4ht
+Usage:
+make4ht [options] filename ["tex4ht.sty op." "tex4ht op." "t4ht op" "latex op"]
+-c,--config (default xhtml) Custom config file
+-d,--output-dir (default nil) Output directory
+-l,--lua Use lualatex for document compilation
+-s,--shell-escape Enables running external programs from LaTeX
+-u,--utf8 For output documents in utf8 encoding
+-x,--xetex Use xelatex for document compilation
+<filename> (string) Input file name
+]]
+
+local args = mkparams.get_args()
+
+local parameters = mkparams.process_args(args)
+
+local mode = parameters.mode
+local build_file = parameters.build_file
+
+local make = mkutils.load_config(parameters, build_file)["Make"]
+make.params = parameters
+if make:length() < 1 then
+ if mode == "draft" then
+ make:htlatex()
+ else
+ make:htlatex()
+ make:htlatex()
+ make:htlatex()
+ end
+end
+
+
+if not args["no-tex4ht"] then
+ make:tex4ht()
+end
+
+local ext = args.xetex and "xdv" or "dvi"
+if #make.image_patterns > 0 then
+ make.params.t4ht_par = make.params.t4ht_par .. " -p"
+end
+make:t4ht {ext = ext}
+make:match("tmp$", function() return false,"tmp file" end)
+make:match(".*",function(filename,par)
+ local outdir = '' --par["outdir"] and par["outdir"] .."/" or ''
+ if par['outdir'] ~= "" then outdir = par['outdir'] .. '/' end
+ print("outdir: "..outdir)
+ local outfilename = outdir .. filename
+ mkutils.copy(filename,outfilename)
+ return true
+end)
+make:run()
diff --git a/Master/texmf-dist/scripts/make4ht/make4ht-lib.lua b/Master/texmf-dist/scripts/make4ht/make4ht-lib.lua
new file mode 100755
index 00000000000..3b550b9995c
--- /dev/null
+++ b/Master/texmf-dist/scripts/make4ht/make4ht-lib.lua
@@ -0,0 +1,204 @@
+-- Simple make system for tex4ht
+--kpse.set_program_name("luatex")
+-- module(...,package.seeall)
+local m = {}
+
+Make = {}
+--Make.params = {}
+Make.build_seq = {}
+-- Patterns for matching output filenames
+Make.matches = {}
+Make.image_patterns = {}
+Make.run_count = {}
+
+Make.add = function(self,name,fn,par,rep)
+ local par = par or {}
+ self.params = self.params or {}
+ Make[name] = function(self,p,typ)
+ local params = {}
+ for k,v in pairs(self.params) do params[k] = v end
+ for k,v in pairs(par) do params[k] = v; print("setting param "..k) end
+ local typ = typ or "make"
+ local p = p or {}
+ local fn = fn
+ for k,v in pairs(p) do
+ params[k]=v
+ print("Adding: ",k,v)
+ end
+ -- print( fn % params)
+ local command = {
+ name=name,
+ type=typ,
+ command = fn,
+ params = params,
+ repetition = rep
+ }
+ table.insert(self.build_seq,command)
+ end
+end
+
+Make.length = function(self)
+ return #self.build_seq
+end
+
+Make.match = function(self, pattern, command, params)
+ local params = params or {}
+ table.insert(self.matches,{pattern = pattern, command = command, params = params})
+end
+
+Make.run_command = function(self,filename,s)
+ local command = s.command
+ local params = s.params
+ params["filename"] = filename
+ print("parse_lg process file: "..filename)
+ --for k,v in pairs(params) do print(k..": "..v) end
+ if type(command) == "function" then
+ return command(filename,params)
+ elseif type(command) == "string" then
+ local run = command % params
+ print("Execute: " .. run)
+ return os.execute(run)
+ end
+ return false, "parse_lg: Command is not string or function"
+end
+
+Make.image = function(self, pattern, command, params)
+ local tab = {
+ pattern = pattern,
+ command = command,
+ params = params
+ }
+ table.insert(self.image_patterns, tab)
+end
+
+Make.image_convert = function(self, images)
+ local image_patterns = self.image_patterns or {}
+ for i, r in pairs(image_patterns) do
+ local p = self.params or {}
+ local v = r.params or {}
+ for k,v in pairs(v) do
+ p[k]= v
+ end
+ image_patterns[i].params = p
+ end
+ for _,i in pairs(images) do
+ local output = i.output
+ for _, x in pairs(image_patterns) do
+ local pattern = x.pattern
+ if output:match(pattern) then
+ local command = x.command
+ local p = x.params or {}
+ p.output = output
+ p.page= i.page
+ p.source = i.source
+ if type(command) == "function" then
+ command(p)
+ elseif type(command) == "string" then
+ local c = command % p
+ print("Make4ht convert: "..c)
+ os.execute(c)
+ end
+ break
+ end
+ end
+ end
+end
+
+Make.file_matches = function(self, files)
+ local statuses = {}
+ -- First make params for all matchers
+ for k,v in pairs(self.matches) do
+ local v = self.matches[k].params or {}
+ local p = self.params or {}
+ for i,j in pairs(p) do
+ v[i] = j
+ end
+ self.matches[k].params = v
+ end
+ -- Loop over files, run command on matched
+ for _, file in pairs(files)do
+ statuses[file] = {}
+ for _, s in pairs(self.matches) do
+ local pattern= s.pattern
+ if file:match(pattern) then
+ local status, msg = self:run_command(file,s)
+ msg = msg or "No message given"
+ table.insert(statuses[file],status)
+ if status == false then
+ print(msg)
+ break
+ end
+ end
+ end
+ end
+ return statuses
+end
+
+Make.run = function(self)
+ local return_codes = {}
+ local params = self.params or {}
+ for _,v in ipairs(self.build_seq) do
+ --print("sekvence: "..v.name)
+ for p,n in pairs(v.params) do params[p] = n end
+ --for c,_ in pairs(params) do print("build param: "..c) end
+ if type(v.command)=="function" then
+ table.insert(return_codes,{name=v.name,status = v.command(params)})
+ elseif type(v.command) =="string" then
+ local command = v.command % params
+ -- Some commands should be executed only limited times, typicaly once
+ -- tex4ht or t4ht for example
+ local run_count = self.run_count[v.command] or 0
+ run_count = run_count + 1
+ self.run_count[v.command] = run_count
+ local repetition = v.repetition
+ if repetition and run_count > repetition then
+ print ("Make4ht: ".. command .." can be executed only "..repetition .."x")
+ else
+ print("Make4ht: " .. command)
+ local status = os.execute(command)
+ table.insert(return_codes,{name=v.name,status=status})
+ end
+ else
+ print("Unknown command type, must be string or function - " ..v.name..": "..type(v.command))
+ end
+ local correct_exit = params.correct_exit or nil
+ if correct_exit then
+ local last_return = return_codes[#return_codes] or {}
+ local current_status = last_return.status or 0
+ if current_status ~= correct_exit then
+ local last_name = last_return.name or "unknown"
+ print("Make4ht: Fatal error. Command "..last_name .." returned exit code "..current_status)
+ os.exit(1)
+ end
+ end
+ end
+ local lgfile = params.input and params.input .. ".lg" or nil
+ if lgfile then
+ local lg = mkutils.parse_lg(lgfile)
+ -- First convert images from lg files
+ self:image_convert(lg["images"])
+ -- Then run file matchers on lg files and converted images
+ local files = lg["files"]
+ for _,v in pairs(lg["images"]) do
+ local v = v.output
+ -- print(v)
+ table.insert(files,v)
+ end
+ self:file_matches(files)
+ else
+ print("No lg file. tex4ht run failed?")
+ end
+ return return_codes
+end
+
+m.Make = Make
+
+return m
+
+--[[Make:add("hello", "hello ${world}", {world = "world"})
+Make:add("ajaj", "ajaj")
+Make:hello()
+Make:hello{world="světe"}
+Make:hello()
+Make:run()
+--]]
diff --git a/Master/texmf-dist/scripts/make4ht/mkparams.lua b/Master/texmf-dist/scripts/make4ht/mkparams.lua
new file mode 100755
index 00000000000..5b614192029
--- /dev/null
+++ b/Master/texmf-dist/scripts/make4ht/mkparams.lua
@@ -0,0 +1,135 @@
+local lapp = require "lapp-mk4"
+local mkutils = require "mkutils"
+local m = {} -- use ugly module system for new lua versions support
+
+m.optiontext = [[
+${progname} - build system for tex4ht
+Usage:
+${progname} [options] filename ["tex4ht.sty op." "tex4ht op." "t4ht op" "latex op"]
+-b,--backend (default tex4ht) Backend used for xml generation.
+ possible values: tex4ht or lua4ht
+-c,--config (default xhtml) Custom config file
+-d,--output-dir (default nil) Output directory
+-e,--build-file (default nil) If build file is different than `filename`.mk4
+-l,--lua Use lualatex for document compilation
+-m,--mode (default default) Switch which can be used in the makefile
+-n,--no-tex4ht Disable dvi file processing with tex4ht command
+-s,--shell-escape Enables running external programs from LaTeX
+-u,--utf8 For output documents in utf8 encoding
+-x,--xetex Use xelatex for document compilation
+]]
+local function get_args(parameters, optiontext)
+ local parameters = parameters or {}
+ parameters.progname = parameters.progname or "make4ht"
+ parameters.postparams = parameters.postparams or ""
+ local optiontext = optiontext or m.optiontext
+ parameters.postfile = parameters.postfile or ""
+ optiontext = optiontext .. parameters.postparams .."<filename> (string) Input file name\n" .. parameters.postfile
+ --print("--------------\n" .. optiontext .."--------------\n")
+ return lapp(optiontext % parameters)
+end
+
+
+local function process_args(args)
+ local function get_inserter(args,tb)
+ return function(key, value)
+ --local v = args[key] and value or ""
+ local v = ""
+ if args[key] then v = value end
+ table.insert(tb,v)
+ end
+ end
+
+ local outdir = ""
+ local packages = ""
+
+ if args["output-dir"] ~= "nil" then
+ outdir = args["output-dir"]
+ outdir = outdir:gsub('\\','/')
+ outdir = outdir:gsub('/$','')
+ end
+
+ if args.backend == "lua4ht" then
+ args.lua = true
+ args.xetex = nil
+ args.utf8 = true
+ args["no-tex4ht"] = true
+ packages = packages .."\\RequirePackage{lua4ht}"
+ end
+
+
+ local compiler = args.lua and "dvilualatex" or args.xetex and "xelatex --no-pdf" or "latex"
+ local input = mkutils.remove_extension(args.filename)
+
+ local latex_params = {}
+ local insert_latex = get_inserter(args,latex_params)
+ insert_latex("shell-escape","-shell-escape")
+ table.insert(latex_params,"-jobname="..input)
+ table.insert(latex_params,args[4] or "")
+ --table.insert(latex_params,args["shell-escape"] and "-shell-escape")
+
+
+ local t4sty = args[1] or ""
+ -- test if first option is custom config file
+ local cfg_tmp = t4sty:match("([^,^ ]+)")
+ if cfg_tmp and cfg_tmp ~= args.config then
+ local fn = cfg_tmp..".cfg"
+ local f = io.open(fn,"r")
+ if f then
+ args.config = cfg_tmp
+ f:close()
+ end
+ end
+ --[[if args[1] and args[1] ~= "" then
+ t4sty = args[1]
+ else
+ --]]
+ -- Different behaviour from htlatex
+ local utf = args.utf8 and ",charset=utf-8" or ""
+ t4sty = args.config .. "," .. t4sty .. utf
+ --end
+
+ local tex4ht = ""
+ if args[2] and args[2] ~="" then
+ tex4ht = args[2]
+ else
+ tex4ht = args.utf8 and " -cunihtf -utf8" or ""
+ local xdv = args.xetex and " -.xdv" or ""
+ tex4ht = tex4ht .. xdv
+ end
+
+ local t4ht = args[3] or ""
+
+ local mode = args.mode or "default"
+
+ local build_file = input.. ".mk4"
+
+ if args["build-file"] and args["build-file"] ~= "nil" then
+ build_file = args["build-file"]
+ end
+
+ local parameters = {
+ htlatex = compiler
+ ,input=input
+ ,packages=packages
+ ,latex_par=table.concat(latex_params," ")
+ --,config=ebookutils.remove_extension(args.config)
+ ,tex4ht_sty_par=t4sty
+ ,tex4ht_par=tex4ht
+ ,t4ht_par=t4ht
+ ,mode = mode
+ ,build_file = build_file
+ --,t4ht_dir_format=t4ht_dir_format
+ }
+ if outdir then parameters.outdir = outdir end
+ print("Output dir: ",outdir)
+ print("Compiler: ", compiler)
+ print("Latex options: ", table.concat(latex_params," "))
+ print("tex4ht.sty :",t4sty)
+ print("tex4ht",tex4ht)
+ print("build_file", build_file)
+ return parameters
+end
+m.get_args = get_args
+m.process_args = process_args
+return m
diff --git a/Master/texmf-dist/scripts/make4ht/mkutils.lua b/Master/texmf-dist/scripts/make4ht/mkutils.lua
new file mode 100755
index 00000000000..ca9e2ff208a
--- /dev/null
+++ b/Master/texmf-dist/scripts/make4ht/mkutils.lua
@@ -0,0 +1,290 @@
+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"
+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 ${input}'"
+ 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 - 256)
+ local text = f:read("*a")
+ f:close()
+ if text:match("No pages of output") then return 1 end
+ return 0
+end
+,{correct_exit=0})
+env.Make:add("tex4ht","tex4ht ${tex4ht_par} ${input}", 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 = config_name or "config.lua"
+ local f = io.open(config_name,"r")
+ if not f then return env, "Cannot open config file" end
+ local code = f:read("*all")
+ local fn, msg = run(code,env)
+ if not fn then print(msg) end
+ assert(fn)
+ return env
+end
+
+