diff options
author | Karl Berry <karl@freefriends.org> | 2015-07-03 23:24:59 +0000 |
---|---|---|
committer | Karl Berry <karl@freefriends.org> | 2015-07-03 23:24:59 +0000 |
commit | f1073f0dfaaeabd2edda00c472a3d814fad132fa (patch) | |
tree | f72e3d8d9e989b6fa399a13600be3d88168eebca /Master/texmf-dist/scripts | |
parent | 0562fc367d1605303c6b2674d0d6b73b37d1d888 (diff) |
make4ht (29jul15)
git-svn-id: svn://tug.org/texlive/trunk@37750 c570f23f-e606-0410-a88d-b1316a301751
Diffstat (limited to 'Master/texmf-dist/scripts')
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 +-- , 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 + + |