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
21
22
23
24
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
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)
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 |