back-imp-lua.lmt /size: 10 Kb    last modification: 2025-02-21 11:03
1if not modules then modules = { } end modules ['back-imp-lua'] = {
2    version   = 1.001,
3    optimize  = true,
4    comment   = "companion to back-imp-lua.mkiv",
5    author    = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
6    copyright = "PRAGMA ADE / ConTeXt Development Team",
7    license   = "see context related readme files"
8}
9
10-- we can remap fonts
11
12local fontproperties    = fonts.hashes.properties
13local fontparameters    = fonts.hashes.parameters
14local fontshapes        = fonts.hashes.shapes
15
16local starttiming       = statistics.starttiming
17local stoptiming        = statistics.stoptiming
18
19local texgetbox         = tex.getbox
20
21local bpfactor          <const> = number.dimenfactors.bp
22
23local rulecodes         <const> = nodes.rulecodes
24local normalrule_code   <const> = rulecodes.normal
25----- boxrule_code      <const> = rulecodes.box
26----- imagerule_code    <const> = rulecodes.image
27----- emptyrule_code    <const> = rulecodes.empty
28----- userrule_code     <const> = rulecodes.user
29----- overrule_code     <const> = rulecodes.over
30----- underrule_code    <const> = rulecodes.under
31----- fractionrule_code <const> = rulecodes.fraction
32----- radicalrule_code  <const> = rulecodes.radical
33local outlinerule_code  <const> = rulecodes.outline
34
35-- todo : per instance
36
37local pages     = { }
38local fonts     = { }
39local names     = { }
40local mapping   = { }
41local used      = { }
42local shapes    = { }
43local glyphs    = { }
44local buffer    = { }
45local metadata  = nil
46local b         = 0
47local converter = nil
48
49local compact   = false -- true
50local shapestoo = true
51
52local x, y, d, f, c, w, h, t, r, o
53
54local function reset()
55    buffer = { }
56    b      = 0
57    t      = nil
58    x      = nil
59    y      = nil
60    d      = nil
61    f      = nil
62    c      = nil
63    w      = nil
64    h      = nil
65    r      = nil
66    o      = nil
67end
68
69local function result()
70    return {
71        metadata = metadata,
72        fonts    = fonts,
73        pages    = pages,
74        shapes   = shapes,
75    }
76end
77
78-- actions
79
80local function outputfilename(driver)
81    return tex.jobname .. "-output.lua"
82end
83
84local function save() -- might become a driver function that already is plugged into stopactions
85    local filename = outputfilename()
86    drivers.report("saving result in %a",filename)
87    starttiming(drivers)
88    local data = result()
89    if data then
90        io.savedata(filename,table.serialize(data))
91    end
92    stoptiming(drivers)
93end
94
95local function prepare(driver)
96    converter = drivers.converters.lmtx
97 -- if not environment.initex then
98        backends.initialize("lua")
99 -- end
100    luatex.registerstopactions(1,function()
101        save()
102    end)
103end
104
105local function initialize(driver,details)
106    reset()
107end
108
109local function finalize(driver,details)
110    local n = details.pagenumber
111    local b = details.boundingbox
112    pages[n] = {
113        number      = n,
114        content     = buffer,
115        boundingbox = {
116            b[1] * bpfactor,
117            b[2] * bpfactor,
118            b[3] * bpfactor,
119            b[4] * bpfactor,
120        },
121    }
122    if not metadata then
123        -- this has to happen while we're still running tex because we do
124        -- prerolls
125        local identity = interactions.general.getidentity()
126        local jobname  = environment.jobname or tex.jobname or "unknown"
127        metadata = {
128            unit     = "bp",
129            jobname  = jobname,
130            title    = identity.title,
131            subject  = identity.subject,
132            author   = identity.author,
133            keywords = identity.keywords,
134            time     = os.date("%Y-%m-%d %H:%M"),
135            engine   = environment.luatexengine .. " " .. environment.luatexversion,
136            context  = environment.version,
137        }
138    end
139end
140
141local function wrapup(driver)
142end
143
144local function cleanup(driver)
145    reset()
146end
147
148local function convert(driver,boxnumber,pagenumber)
149    converter(driver,texgetbox(boxnumber),"page",pagenumber)
150end
151
152-- flushers
153
154local function updatefontstate(id)
155    if not mapping[id] then
156        local fn = #fonts + 1
157        mapping[id] = fn
158        local properties = fontproperties[id]
159        local parameters = fontparameters[id]
160        local filename   = file.basename(properties.filename)
161        local fontname   = properties.fullname or properties.fontname
162        if shapestoo then
163            if not names[fontname] then
164                local sn = #shapes+1
165                names[fontname] = sn
166                shapes[sn] = { }
167                glyphs[sn] = fontshapes[id].glyphs
168            end
169        end
170        fonts[fn] = {
171            filename = filename,
172            name     = fontname,
173            size     = parameters.size * bpfactor,
174            shapes   = shapestoo and names[fontname] or nil,
175        }
176    end
177end
178
179local function flushcharacter(current, pos_h, pos_v, pos_r, font, char)
180    local fnt = mapping[font]
181    b = b + 1
182    buffer[b] = {
183        t = "glyph" ~= t and "glyph" or nil,
184        f = font    ~= f and fnt or nil,
185        c = char    ~= c and char or nil,
186        x = pos_h   ~= x and (pos_h * bpfactor) or nil,
187        y = pos_v   ~= y and (pos_v * bpfactor) or nil,
188        d = pos_r   ~= d and (pos_r == 1 and "r2l" or "l2r") or nil,
189    }
190    t = "glyph"
191    f = font
192    c = char
193    x = pos_h
194    y = pos_v
195    d = pos_r
196    if shapestoo then
197        -- can be sped up if needed
198        local sn = fonts[fnt].shapes
199        local shp = shapes[sn]
200        if not shp[char] then
201            shp[char] = glyphs[sn][char]
202        end
203    end
204end
205
206local function flush_rule(current, pos_h, pos_v, pos_r, size_h, size_v, rule_s, rule_o)
207    b = b + 1
208    buffer[b] = {
209        t = "rule" ~= t and "rule" or nil,
210        r = rule_s ~= r and rule_s or nil,
211        o = rule_s == "outline" and rule_o ~= o and (rule_o * bpfactor) or nil,
212        w = size_h ~= w and (size_h * bpfactor) or nil,
213        h = size_v ~= h and (size_v * bpfactor) or nil,
214        x = pos_h  ~= x and (pos_h  * bpfactor) or nil,
215        y = pos_v  ~= y and (pos_v  * bpfactor) or nil,
216        d = pos_r  ~= d and (pos_r == 1 and "r2l" or "l2r") or nil,
217    }
218    t = "rule"
219    w = size_h
220    h = size_v
221    x = pos_h
222    y = pos_v
223    d = pos_r
224end
225
226local function flushrule(current, pos_h, pos_v, pos_r, size_h, size_v, subtype)
227    local rule_s, rule_o
228    if subtype == normalrule_code then
229        rule_s = normal_rule_code
230    elseif subtype == outlinerule_code then
231        rule_s = outline_rule_code
232        rule_o = getdata(current)
233    else
234        return
235    end
236    return flush_rule(pos_h, pos_v, pos_r, size_h, size_v, rule_s, rule_o)
237end
238
239local function flushsimplerule(pos_h, pos_v, pos_r, size_h, size_v)
240    return flush_rule(false,pos_h,pos_v,pos_r,size_h,size_v,normalrule_code,nil)
241end
242
243local function flushspecialrule(pos_h, pos_v, pos_r, w, h, d, l, outline)
244    return flush_rule(false,pos_h,pos_v-d,pos_r,w,h+d,outline and outlinerule_code or normalrule_code)
245end
246
247-- file stuff too
248-- todo: default flushers
249-- also color (via hash)
250
251-- installer
252
253drivers.install {
254    name    = "lua",
255    actions = {
256        prepare         = prepare,
257        initialize      = initialize,
258        finalize        = finalize,
259        wrapup          = wrapup,
260        cleanup         = cleanup,
261        convert         = convert,
262        outputfilename  = outputfilename,
263    },
264    flushers = {
265        updatefontstate = updatefontstate,
266        character       = flushcharacter,
267        rule            = flushrule,
268        simplerule      = flushsimplerule,
269        specialrule     = flushspecialrule,
270    }
271}
272
273-- actions
274
275local function outputfilename(driver)
276    return tex.jobname .. "-output.json"
277end
278
279local function save() -- might become a driver function that already is plugged into stopactions
280    local filename = outputfilename()
281    drivers.report("saving result in %a",filename)
282    starttiming(drivers)
283    local data = result()
284    if data then
285        if not utilities.json then
286            require("util-jsn")
287        end
288        io.savedata(filename,utilities.json.tostring(data,not compact))
289    end
290    stoptiming(drivers)
291end
292
293local function prepare(driver)
294    converter = drivers.converters.lmtx
295    luatex.registerstopactions(1,function()
296        save()
297    end)
298end
299
300-- installer
301
302drivers.install {
303    name    = "json",
304    actions = {
305        prepare         = prepare,
306        wrapup          = wrapup,
307        cleanup         = cleanup,
308        --
309        initialize      = initialize,
310        convert         = convert,
311        finalize        = finalize,
312        --
313        outputfilename  = outputfilename,
314    },
315    flushers = {
316        updatefontstate = updatefontstate,
317        character       = flushcharacter,
318        rule            = flushrule,
319        simplerule      = flushsimplerule,
320        specialrule     = flushspecialrule,
321        setstate        = function() end,
322    }
323}
324
325-- actions
326
327local function outputfilename(driver)
328    return tex.jobname .. "-output.js"
329end
330
331local function save() -- might become a driver function that already is plugged into stopactions
332    local filename = outputfilename()
333    drivers.report("saving result in %a",filename)
334    starttiming(drivers)
335    local data = result()
336    if data then
337        if not utilities.json then
338            require("util-jsn")
339        end
340        io.savedata(filename,
341            "const JSON_CONTEXT = JSON.parse ( `" ..
342            utilities.json.tostring(data,not compact) ..
343            "` ) ;\n"
344        )
345    end
346    stoptiming(drivers)
347end
348
349local function prepare(driver)
350    converter = drivers.converters.lmtx
351 -- if not environment.initex then
352        backends.initialize("js")
353 -- end
354    luatex.registerstopactions(1,function()
355        save()
356    end)
357end
358
359-- installer
360
361drivers.install {
362    name    = "js",
363    actions = {
364        prepare         = prepare,
365        initialize      = initialize,
366        finalize        = finalize,
367        wrapup          = wrapup,
368        cleanup         = cleanup,
369        convert         = convert,
370        outputfilename  = outputfilename,
371    },
372    flushers = {
373        updatefontstate = updatefontstate,
374        character       = flushcharacter,
375        rule            = flushrule,
376        simplerule      = flushsimplerule,
377        specialrule     = flushspecialrule,
378    }
379}
380