driv-ini.lmt /size: 10 Kb    last modification: 2023-12-21 09:44
1if not modules then modules = { } end modules ['driv-ini'] = {
2    version   = 1.001,
3    comment   = "companion to driv-ini.mkiv",
4    author    = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
5    copyright = "PRAGMA ADE / ConTeXt Development Team",
6    license   = "see context related readme files"
7}
8
9local type = type
10local addsuffix = file.addsuffix
11
12local setmetatableindex = table.setmetatableindex
13local formatters        = string.formatters
14
15local starttiming       = statistics.starttiming
16local stoptiming        = statistics.stoptiming
17
18local report            = logs.reporter("drivers")
19
20local instances         = { }
21local helpers           = { }
22local converters        = { }
23local prepared          = { }
24local wrappedup         = { }
25local cleanedup         = { }
26local currentdriver     = "default"
27local currentinstance   = nil
28
29local shipout           = tex.shipout
30local texgetbox         = tex.getbox
31local texgetcount       = tex.getcount
32
33local c_realpageno      = tex.iscount("realpageno")
34
35function converters.engine(driver,boxnumber,mode,number,specification)
36    return shipout(boxnumber)
37end
38
39local prepare        = nil
40local convert        = nil
41local wrapup         = nil
42local cleanup        = nil
43local outputfilename = nil
44
45drivers = drivers or {
46    instances   = instances,
47    helpers     = helpers,
48    converters  = converters,
49    lmtxversion = 0.10,
50    report      = report,
51}
52
53local dummy = function() end
54
55local defaulthandlers = {
56    prepare         = dummy,
57    initialize      = dummy,
58    finalize        = dummy,
59    updatefontstate = dummy,
60    wrapup          = dummy,
61    cleanup         = dummy,
62    convert         = dummy,
63    outputfilename  = dummy,
64}
65
66local installwhatsits  do
67
68    local round       = math.round
69
70    local leaderlevel = 0
71    local backends    = backends
72    local trace       = true
73
74    local latelua     = backends.latelua
75    local writeout    = backends.writeout
76    local openout     = backends.openout
77    local closeout    = backends.closeout
78
79    trackers.register("backends.whatsits",function(v) trace = v end)
80
81    local function pushleaderlevel()
82        leaderlevel = leaderlevel + 1
83    end
84
85    local function popleaderlevel()
86        leaderlevel = leaderlevel - 1
87    end
88
89    local function flushlatelua(current,h,v)
90        -- Here we assume management by the lua function so currently we don't
91        -- need to check for leaderlevel and it can even be counterproductive.
92        return latelua(current,h,v)
93    end
94
95    local function flushwriteout(current)
96        if leaderlevel == 0 then
97            writeout(current)
98        end
99    end
100
101    local function flushopenout(current)
102        if leaderlevel == 0 then
103            openout(current)
104        end
105    end
106
107    local function flushcloseout(current)
108        if leaderlevel == 0 then
109            closeout(current)
110        end
111    end
112
113    local function flushsavepos(current,pos_h,pos_v)
114        local jp = job.positions
115        jp.lastx = round(pos_h)
116        jp.lasty = round(pos_v)
117    end
118
119    local function flushuserdefined()
120        -- nothing here
121    end
122
123    local whatsitcodes = nodes.whatsitcodes
124
125    installwhatsits = function(name,flushers)
126
127        -- latelua     : specific
128        -- userdefined : special purpose, handled in callbacks
129        -- savepos     : only used by generic packages
130        -- open        : generic
131        -- close       : generic
132        -- write       : generic
133
134        -- An alternative is to have a first time setter.
135
136        local function checkagain(t, k)
137            local v = rawget(flushers,k) -- in case it's registered later
138            if not v then
139                if trace then
140                    report("unsupported whatsit %a in driver %a",whatsitcodes[k] or k,name)
141                end
142                v = function() end
143            end
144            t[k] = v
145            return v
146        end
147
148        -- delayed
149
150        local whatsit ; whatsit = setmetatableindex ( {
151            [whatsitcodes.literal]        = flushers.literal        or function(...) return checkagain(whatsit,whatsitcodes.literal       )(...) end,
152            [whatsitcodes.save]           = flushers.save           or function(...) return checkagain(whatsit,whatsitcodes.save          )(...) end,
153            [whatsitcodes.restore]        = flushers.restore        or function(...) return checkagain(whatsit,whatsitcodes.restore       )(...) end,
154            [whatsitcodes.setmatrix]      = flushers.setmatrix      or function(...) return checkagain(whatsit,whatsitcodes.setmatrix     )(...) end,
155            [whatsitcodes.startmatrix]    = flushers.startmatrix    or function(...) return checkagain(whatsit,whatsitcodes.startmatrix   )(...) end,
156            [whatsitcodes.stopmatrix]     = flushers.stopmatrix     or function(...) return checkagain(whatsit,whatsitcodes.stopmatrix    )(...) end,
157            [whatsitcodes.startscaling]   = flushers.startscaling   or function(...) return checkagain(whatsit,whatsitcodes.startscaling  )(...) end,
158            [whatsitcodes.stopscaling]    = flushers.stopscaling    or function(...) return checkagain(whatsit,whatsitcodes.stopscaling   )(...) end,
159            [whatsitcodes.startrotation]  = flushers.startrotation  or function(...) return checkagain(whatsit,whatsitcodes.startrotation )(...) end,
160            [whatsitcodes.stoprotation]   = flushers.stoprotation   or function(...) return checkagain(whatsit,whatsitcodes.stoprotation  )(...) end,
161            [whatsitcodes.startmirroring] = flushers.startmirroring or function(...) return checkagain(whatsit,whatsitcodes.startmirroring)(...) end,
162            [whatsitcodes.stopmirroring]  = flushers.stopmirroring  or function(...) return checkagain(whatsit,whatsitcodes.stopmirroring )(...) end,
163            [whatsitcodes.startclipping]  = flushers.startclipping  or function(...) return checkagain(whatsit,whatsitcodes.startclipping )(...) end,
164            [whatsitcodes.stopclipping]   = flushers.stopclipping   or function(...) return checkagain(whatsit,whatsitcodes.stopclippin   )(...) end,
165            [whatsitcodes.setstate]       = flushers.setstate       or function(...) return checkagain(whatsit,whatsitcodes.setstate      )(...) end,
166            --
167            [whatsitcodes.latelua]        = flushlatelua,
168            [whatsitcodes.userdefined]    = flushuserdefined,
169            [whatsitcodes.savepos]        = flushsavepos,
170            [whatsitcodes.open]           = flushopenout,
171            [whatsitcodes.close]          = flushcloseout,
172            [whatsitcodes.write]          = flushwriteout,
173        }, checkagain)
174
175        flushers.whatsit = whatsit
176
177        flushers.pushleaderlevel = pushleaderlevel
178        flushers.popleaderlevel  = popleaderlevel
179
180    end
181
182end
183
184function drivers.install(specification)
185    local name = specification.name
186    if not name then
187        report("missing driver name")
188        return
189    end
190    local actions = specification.actions
191    if not actions then
192        report("no actions for driver %a",name)
193        return
194    end
195    local flushers = specification.flushers
196    if not flushers then
197        report("no flushers for driver %a",name)
198        return
199    end
200    installwhatsits(name,flushers)
201 -- report("driver %a is installed",name)
202    setmetatableindex(actions, defaulthandlers)
203    setmetatableindex(flushers, function() return dummy end)
204    instances[name] = specification
205end
206
207function drivers.convert(boxnumber)
208    if currentinstance then
209        callbacks.functions.start_page_number()
210        starttiming(drivers)
211        convert(currentinstance,boxnumber,texgetcount(c_realpageno))
212        stoptiming(drivers)
213        callbacks.functions.stop_page_number()
214    end
215end
216
217function drivers.outputfilename()
218    if currentinstance then
219        return outputfilename(currentinstance)
220    end
221end
222
223luatex.wrapup(function()
224    if currentinstance and wrapup and not wrappedup[currentdriver] then
225        starttiming(drivers)
226        wrapup(currentinstance)
227        stoptiming(drivers)
228        wrappedup[currentdriver] = true
229        cleanedup[currentdriver] = true
230    end
231end)
232
233luatex.cleanup(function()
234    if currentinstance and cleanup and not cleanedup[currentdriver] then
235        starttiming(drivers)
236        cleanup(currentinstance)
237        stoptiming(drivers)
238        wrappedup[currentdriver] = true
239        cleanedup[currentdriver] = true
240    end
241end)
242
243function drivers.enable(name)
244    if name ~= currentdriver then
245        if currentinstance then
246            starttiming(drivers)
247            cleanup(currentinstance)
248            stoptiming(drivers)
249        end
250        currentdriver   = name or "none"
251        currentinstance = instances[currentdriver]
252        if currentinstance then
253            local actions  = currentinstance.actions
254            prepare        = actions.prepare
255            wrapup         = actions.wrapup
256            cleanup        = actions.cleanup
257            convert        = actions.convert
258            outputfilename = actions.outputfilename
259            --
260            if prepare and not prepared[currentdriver] then
261                starttiming(drivers)
262                prepare(currentinstance)
263                stoptiming(drivers)
264                prepared[currentdriver] = true
265            end
266        else
267            report("bad driver")
268        end
269    end
270end
271
272statistics.register("driver time",function()
273    return statistics.elapsedseconds(drivers)
274end)
275
276interfaces.implement {
277    name      = "shipoutpage",
278    arguments = "integer",
279    actions   = drivers.convert,
280}
281
282interfaces.implement {
283    name      = "enabledriver",
284    arguments = "string",
285    actions   = drivers.enable,
286}
287
288do
289
290    local function prepare(driver)
291        converter = drivers.converters.lmtx
292    end
293
294    local function convert(driver,boxnumber,pagenumber)
295        converter(driver,texgetbox(boxnumber),"page",pagenumber)
296    end
297
298    drivers.install {
299        name     = "empty",
300        actions  = {
301            prepare = prepare,
302            convert = convert,
303        },
304        flushers = { },
305    }
306
307end
308
309setmetatableindex(instances,function() return instances.default end)
310
311-- We default to no driver at all:
312
313drivers.install {
314    name     = "none",
315    actions  = { },
316    flushers = { },
317}
318
319drivers.enable("none")
320