spac-pas.lmt /size: 8422 b    last modification: 2024-01-16 10:22
1if not modules then modules = { } end modules ['spac-pas'] = {
2    version   = 1.001,
3    comment   = "companion to spac-pas.mkxl",
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 rawget, tonumber = rawget, tonumber
10
11local report         = logs.reporter("break quality")
12
13local integer_value  = tokens.values.integer
14local currentfile    = luatex.currentfile
15
16local toobad         = 20
17local tracing        = false
18local details        = false
19
20-- builders.passnames   = utilities.storage.mark(builders.passnames or { })
21-- local passnames      = builders.passnames
22-- local nofidentifiers = #passnames
23--
24-- storage.register("builders/passnames", passnames, "builders.passnames")
25
26local passnames      = { }
27local nofidentifiers = 0
28
29table.setmetatableindex(passnames,function(t,k)
30    if type(k) == "number" and k >= 0 then
31        return k
32    else
33        nofidentifiers = nofidentifiers - 1
34        t[k] = nofidentifiers
35        t[nofidentifiers] = k
36        return nofidentifiers
37    end
38end)
39
40interfaces.implement {
41    name      = "parpassidentifier",
42    public    = true,
43    usage     = "value",
44    arguments = "string",
45    actions   = function(s)
46        return integer_value, passnames[s]
47    end,
48}
49
50local function linebreak_quality_console(id,line,pass,subpass,subpasses,overfull,underfull,badness,classes)
51    local filename = currentfile()
52    local passname = passnames[id]
53    if pass == 1 then
54        if details then
55            report("file %a, line %i, pass 1",filename,line)
56        end
57    elseif overfull ~= 0 then
58        if subpass > 0 then
59            report("file %a, line %i, pass %i, subpass %i of %i, overfull %p, classes 0x%02X, id %a",filename,line,pass,subpass,subpasses,overfull,classes,passname)
60        else
61            report("file %a, line %i, pass %i, classes 0x%02X, overfull %p",filename,line,pass,classes,overfull)
62        end
63    elseif underfull ~= 0 then
64        if subpass > 0 then
65            report("file %a, line %i, pass %i, subpass %i of %i, underfull %p, classes 0x%02X, id %a",filename,line,pass,subpass,subpasses,underfull,classes,passname)
66        else
67            report("file %a, line %i, pass %i, classes 0x%02X, underfull %p",filename,line,pass,classes,underfull)
68        end
69    elseif badness > toobad then
70        if subpass > 0 then
71            report("file %a, line %i, pass %i, subpass %i of %i, badness %s, classes 0x%02X, id %a",filename,line,pass,subpass,subpasses,badness,classes,passname)
72        else
73            report("file %a, line %i, classes 0x%02X, badness %i, okay",filename,line,pass,classes,badness)
74        end
75    else
76        if subpass > 0 then
77            report("file %a, line %i, pass %i, subpass %i of %i, okay, classes 0x%02X, id %a",filename,line,pass,subpass,subpasses,classes,passname)
78        elseif details then
79            report("file %a, line %i, okay",filename,line,pass)
80        end
81    end
82end
83
84local collected    = table.setmetatableindex("table")
85local noffirst     = 0
86local nofsecond    = 0
87local nofthird     = 0
88local nofsubpasses = 0
89local nofsubdone   = 0
90local nofoverfull  = 0
91local nofunderfull = 0
92local nofbadness   = 0
93
94local function linebreak_quality_summary(id,line,pass,subpass,subpasses,overfull,underfull,badness,classes)
95    if pass == 1 then
96     -- t = { pass = 1, line = line }
97        noffirst = noffirst + 1
98    else
99        if pass == 2 then
100            nofsecond = nofsecond + 1
101        else
102            nofthird = nofthird + 1
103        end
104        local o = overfull  ~= 0
105        local u = underfull ~= 0
106        local b = badness    > toobad
107        local s = subpass    > 0
108        if o then
109            nofoverfull = nofoverfull + 1
110        end
111        if u then
112            nofunderfull = nofunderfull + 1
113        end
114        if b then
115            nofbadness = nofbadness + 1
116        end
117        if s then
118            nofsubpasses = nofsubpasses + 1
119            nofsubdone   = nofsubdone   + subpass
120        end
121        if o then
122            local c = collected[currentfile()]
123            c[#c+1] = {
124                pass      = pass,
125                line      = line,
126                subpass   = subpass,
127                subpasses = subpasses,
128                overfull  = overfull,
129                classes   = classes,
130                id        = passnames[id],
131            }
132        elseif b then
133            local c = collected[currentfile()]
134            c[#c+1] = {
135                pass      = pass,
136                line      = line,
137                subpass   = subpass,
138                subpasses = subpasses,
139                badness   = badness,
140                classes   = classes,
141                id        = passnames[id],
142            }
143        end
144    end
145end
146
147logs.registerfinalactions(function()
148    if tracing and next(collected) then
149        logs.startfilelogging(report,"overfull lines")
150        for filename, list in table.sortedhash(collected) do
151            for i=1,#list do
152                local t = list[i]
153                local b = t.badness
154                local o = t.overfull
155                if b then
156                    report("  file %a, line %i, pass %i, subpass %i of %i, classes 0x%02X, badness %i", filename,t.line,t.pass,t.subpass,t.subpasses,t.classes,b)
157                elseif o then
158                    report("  file %a, line %i, pass %i, subpass %i of %i, classes 0x%02X, overfull %p",filename,t.line,t.pass,t.subpass,t.subpasses,t.classes,o)
159                end
160            end
161        end
162        logs.stopfilelogging()
163    end
164end)
165
166statistics.register("linebreak quality",function()
167    if tracing then
168        return string.formatters["%i first, %i second, %i third, %i subpasses, %i subruns, %i overfull, %i underfull, %i badness"](
169            noffirst, nofsecond, nofthird,
170            nofsubpasses, nofsubdone,
171            nofoverfull, nofunderfull,
172            nofbadness
173        )
174    end
175end)
176
177local registercallback = callback.register
178
179trackers.register("paragraphs.passes", function(v)
180    details = false
181    if not v then
182        tracing = nil
183    elseif v == "summary" then
184        tracing = linebreak_quality_summary
185    elseif v == "details" then
186        details = true
187        tracing = linebreak_quality_console
188    else
189        tracing = linebreak_quality_console
190    end
191    registercallback("linebreak_quality",tracing)
192end)
193
194-- -- -- --
195
196local nuts    = nodes.nuts
197local tonut   = nodes.tonut
198local setprop = nuts.setprop
199local getprop = nuts.getprop
200
201local trace_callbacks = false
202
203trackers.register("paragraphs.passes.callbacks", function(v)
204    trace_callbacks = v
205end)
206
207local actions     = { }
208local identifiers = table.setmetatableindex(function(t,k)
209    local v = #t + 1
210    local s = tostring(k) -- no number
211    t[s] = v
212    t[v] = s
213    return v
214end)
215
216callback.register("paragraph_pass", function(
217        head,identifier,callback,
218        overfull,underfull,verdict,classified,
219        threshold,badness,classes
220    )
221    local action = actions[callback]
222    if action then
223        local head = tonut(head)
224        local step = getprop(head,"par_pass_done") or 1
225        setprop(head,"par_pass_done",step+1)
226        local result, again = action(
227            head,rawget(identifiers,identifier) or identifier,callback,step,
228            overfull,underfull,verdict,classified,
229            threshold,badness,classes
230        )
231        if trace_callbacks then
232            if type(result) == "table" then
233                report("subpass identifier %a, callback %a, %s",identifier,callback,"process with data")
234            elseif result then
235                report("subpass identifier %a, callback %a, %s",identifier,callback,"process")
236            else
237                report("subpass identifier %a, callback %a, %s",identifier,callback,"quit")
238            end
239        end
240        return result, again
241    else
242        report("subpass identifier %a, unknown callback %a",identifier,callback)
243        return false
244    end
245end)
246
247function builders.registerpaspasscallback(identifier,action)
248    local index = identifiers[identifier]
249    actions[index] = action
250    return index
251end
252
253interfaces.implement {
254    name      = "parpasscallback",
255    public    = true,
256    usage     = "value",
257    arguments = "string",
258    actions   = function(s)
259        return integer_value, identifiers[s]
260    end,
261}
262
263