summaryrefslogtreecommitdiff
path: root/macros/luatex/latex/luaoptions/luaoptions-lib.lua
blob: 4f797da12e998a4785fb8151ee66f9a12b558e92 (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
-- luacheck: ignore ly log self luatexbase internalversion font fonts tex token kpse status
local err, warn, info, log = luatexbase.provides_module({
    name               = "luaoptions-lib",
    version            = '0.8',  --LUAOPTIONS_VERSION
    date               = "2022/10/30",  --LUAOPTIONS_DATE
    description        = "Module luaoptions-lib.",
    author             = "The lualatex-tools Project",
    copyright          = "2015-2022 - the lualatex-tools Project",
    license            = "MIT",
})

local lib = {}
lib.TEX_UNITS = {'bp', 'cc', 'cm', 'dd', 'in', 'mm', 'pc', 'pt', 'sp', 'em',
'ex'}

-------------------------
-- General tool functions

function lib.basename(str)
--[[
  Given the full path to a file, return only the file name (without path).
  If there is no slash, return the full name.
--]]
    return str:gsub(".*/(.*)", "%1") or str
end


function lib.contains(table_var, value)
--[[
  Returns the key if the given table contains the given value, or nil.
  A value of 'false' (string) is considered equal to false (Boolean).
--]]
    for k, v in pairs(table_var) do
        if v == value then return k
        elseif v == 'false' and value == false then return k
        end
    end
end


function lib.contains_key(table_var, key)
-- Returs true if the given key is present in the table, nil otherwise.
    for k in pairs(table_var) do
        if k == key then return true end
    end
end


function lib.convert_unit(value)
--[[
  Convert a LaTeX unit, if possible.
  TODO: Understand what this *really* does, what is accepted and returned.
--]]
    if not value then return 0
    elseif value == '' then return false
    elseif value:match('\\') then
        local n, u = value:match('^%d*%.?%d*'), value:match('%a+')
        if n == '' then n = 1 end
        return tonumber(n) * tex.dimen[u] / tex.sp("1pt")
    else return ('%f'):format(tonumber(value) or tex.sp(value) / tex.sp("1pt"))
    end
end


function lib.current_font_size()
--[[
    Convenience function to return the font size of the current font
--]]
    return lib.fontinfo(font.current()).size
end


function lib.dirname(str)
--[[
  Given the full path to a file, return only the path (without file name),
  including the last slash. If there is no slash, return an empty string.
--]]
    return str:gsub("(.*/).*", "%1") or ''
end


local fontdata = fonts.hashes.identifiers
function lib.fontinfo(id)
--[[
  Return a LuaTeX font object based on the given ID
--]]
    return fontdata[id] or font.getfont(id)
end


function lib.max(a, b)
    a, b = tonumber(a), tonumber(b)
    if a > b then return a else return b end
end


function lib.min(a, b)
    a, b = tonumber(a), tonumber(b)
    if a < b then return a else return b end
end


function lib.mkdirs(str)
    local path
    if str:sub(1, 1) == '/' then path = '' else path = '.' end
    for dir in str:gmatch('([^%/]+)') do
        path = path .. '/' .. dir
        lfs.mkdir(path)
    end
end


function lib.orderedkeys(t)
    local orderedIndex = {}
    for k in pairs(t) do table.insert(orderedIndex, k) end
    table.sort(orderedIndex)
    return orderedIndex
end


function lib.orderedpairs(t)
    local key
    local i = 0
    local orderedIndex = lib.orderedkeys(t)
    return function ()
            i = i + 1
            key = orderedIndex[i]
            if key then return key, t[key] end
        end
end


function lib.print_table(t, indent)
-- Recursively print a table, avoiding recursion loops
    indent = indent or '  '
    local visited = {}
    local skips = 0

    local function _print_table(_t, _ind)
        local keys = lib.orderedkeys(_t)
        local display_keys = {}
        local length = 0
        for key, _ in pairs(_t) do
            length = lib.max(length, #tostring(key))
        end
        for key, _ in pairs(_t) do
            display_key = tostring(key)
            display_keys[key] = display_key .. string.rep(
                ' ', length - #display_key)
        end
        for k, v in lib.orderedpairs(_t) do
            if type(v) == 'table' then
                if visited[v] then
                    skips = skips + 1
                else
                    visited[v] = true
                    print(_ind..display_keys[k], v)
                    if #_ind > 40 then err("k") end
                    _print_table(v, _ind..indent)
                end
            else
                if v == '' then v = '(empty string)' end
                print(_ind..display_keys[k], v)
            end
        end
    end

    print()
    visited[t] = true
    print("Print table:", t)
    _print_table(t, indent)
    print()
    if skips > 0 then
        print(string.format('%s recursive tables skipped', skips))
    end
    print("============")
    print()
end

function lib.readlinematching(s, f)
    if f then
        local result = ''
        while result and not result:find(s) do result = f:read() end
        f:close()
        return result
    end
end


function lib.splitext(str, ext)
--[[
    If 'ext' is supplied return str stripped of the given extension,
    otherwise return the base and extension (if any)
--]]
    return ext and (str:match('(.*)%.'..ext..'$') or str)
        or (str:match('(.*)%.(%w*)$') or str)
end


------------------------------------
-- Engine, version, TeX distribution

local function _loaded(ext)
--[[
    Returns a function that checks whether a class or a package is loaded
    https://tex.stackexchange.com/questions/501635/
--]]
    local fmt = "ver@%s." .. ext
    return function(name)
        local macro = token.get_macro(fmt:format(name))
        return macro ~= nil
    end
end

-- Lua implementations of \@ifpackageloaded and \@ifclassloaded
lib.class_loaded = _loaded('cls')
lib.package_loaded = _loaded('sty')

local tex_engine = {}
setmetatable(tex_engine, tex_engine)

function tex_engine:__call()
--[[
    Defines the properties extracted from the first line of jobname.log.
--]]
    local f = io.open(tex.jobname..'.log')
    if not f then return end
    self.engine, self.engine_version, self.dist, self.dist_version, self.format, self.format_version =
        f:read():match(
            'This is ([^,]*), Version ([^%(]*) %(([^%)]*) ([^%)]*)%)%s+%(format=([^%)]*) ([^)]*)%)'
        )
    f:close()
    return self
end

function tex_engine:__index(k) return rawget(self(), k) end


lib.tex_engine = tex_engine
return lib