1if modules then modules = { } end modules ['typo-bld'] = {
2 version = 1.001,
3 comment = "companion to typo-bld.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
9
10
11local insert, remove = table.insert, table.remove
12
13builders = builders or { }
14local builders = builders
15
16builders.paragraphs = builders.paragraphs or { }
17local parbuilders = builders.paragraphs
18
19parbuilders.constructors = parbuilders.constructors or { }
20local constructors = parbuilders.constructors
21
22builders.hpack = builders.hpack or { }
23builders.vpack = builders.vpack or { }
24
25local hpackbuilders = builders.hpack
26local vpackbuilders = builders.vpack
27
28constructors.names = constructors.names or { }
29local names = constructors.names
30
31constructors.numbers = constructors.numbers or { }
32local numbers = constructors.numbers
33
34constructors.methods = constructors.methods or { }
35local methods = constructors.methods
36
37local a_parbuilder = attributes.numbers['parbuilder'] or 999
38constructors.attribute = a_parbuilder
39
40local unsetvalue = attributes.unsetvalue
41local texsetattribute = tex.setattribute
42local texnest = tex.nest
43local texget = tex.get
44local texset = tex.set
45
46local getspeciallist = nodes.nuts.getspeciallist
47local setspeciallist = nodes.nuts.setspeciallist
48
49local nodes = nodes
50local nodeidstostring = nodes.idstostring
51local nodepool = nodes.pool
52local new_baselineskip = nodepool.baselineskip
53local new_lineskip = nodepool.lineskip
54local insertnodebefore = nodes.insertbefore
55local hpack_node = nodes.hpack
56
57local nuts = nodes.nuts
58local tonode = nodes.tonode
59local tonut = nodes.tonut
60local getattr = nuts.getattr
61local flush = nuts.flush
62
63local starttiming = statistics.starttiming
64local stoptiming = statistics.stoptiming
65
66local registercallback = callbacks.register
67
68storage.register("builders/paragraphs/constructors/names", names, "builders.paragraphs.constructors.names")
69storage.register("builders/paragraphs/constructors/numbers", numbers, "builders.paragraphs.constructors.numbers")
70
71local trace_page_builder = false trackers.register("builders.page", function(v) trace_page_builder = v end)
72local trace_vbox_builder = false trackers.register("builders.vbox", function(v) trace_vbox_builder = v end)
73local trace_post_builder = false trackers.register("builders.post", function(v) trace_post_builder = v end)
74
75local report_page_builder = logs.reporter("builders","page")
76local report_vbox_builder = logs.reporter("builders","vbox")
77local report_par_builder = logs.reporter("builders","par")
78
79local mainconstructor = nil
80local nofconstructors = 0
81local stack = { }
82
83function constructors.define(name)
84 nofconstructors = nofconstructors + 1
85 names[nofconstructors] = name
86 numbers[name] = nofconstructors
87end
88
89function constructors.set(name)
90 if name then
91 mainconstructor = numbers[name] or unsetvalue
92 else
93 mainconstructor = stack[#stack] or unsetvalue
94 end
95 texsetattribute(a_parbuilder,mainconstructor)
96 if mainconstructor ~= unsetvalue then
97 constructors.enable()
98 end
99end
100
101function constructors.start(name)
102 local number = numbers[name]
103 insert(stack,number)
104 mainconstructor = number or unsetvalue
105 texsetattribute(a_parbuilder,mainconstructor)
106 if mainconstructor ~= unsetvalue then
107 constructors.enable()
108 end
109
110end
111
112function constructors.stop()
113 remove(stack)
114 mainconstructor = stack[#stack] or unsetvalue
115 texsetattribute(a_parbuilder,mainconstructor)
116 if mainconstructor == unsetvalue then
117 constructors.disable()
118 end
119
120end
121
122
123
124
125
126
127
128function constructors.handler(head,followed_by_display)
129 if type(head) == "boolean" then
130 return head
131 else
132 local attribute = getattr(head,a_parbuilder)
133 if attribute then
134 local method = names[attribute]
135 if method then
136 local handler = methods[method]
137 if handler then
138 return handler(head,followed_by_display)
139 else
140 report_par_builder("contructor method %a is not defined",tostring(method))
141 return true
142 end
143 end
144 end
145 return true
146 end
147end
148
149
150
151function constructors.methods.default(head,followed_by_display)
152 return true
153end
154
155
156
157function parbuilders.constructors.methods.oneline(head,followed_by_display)
158
159 local t = texnest[texnest.ptr]
160 local h = hpack_node(head)
161 local d = texget("baselineskip",false) - t.prevdepth - h.height
162 t.prevdepth = h.depth
163 t.prevgraf = 1
164 if d < texget("lineskiplimit") then
165 return insertnodebefore(h,h,new_lineskip(texget("lineskip",false)))
166 else
167 return insertnodebefore(h,h,new_baselineskip(d))
168 end
169end
170
171
172
173
174
175
176
177
178local actions = constructors.handler
179local enabled = false
180
181local function processor(head,followed_by_display)
182
183 if enabled then
184 starttiming(parbuilders)
185 head = tonode(actions(tonut(head),followed_by_display))
186 stoptiming(parbuilders)
187 return head
188 else
189 return true
190 end
191end
192
193function constructors.enable () enabled = true end
194function constructors.disable() enabled = false end
195
196registercallback("linebreak_filter", processor, "breaking paragraps into lines")
197
198statistics.register("linebreak processing time", function()
199 return statistics.elapsedseconds(parbuilders)
200end)
201
202
203
204nodes.builders = nodes.builder or { }
205local builders = nodes.builders
206
207local vpackactions = nodes.tasks.actions("vboxbuilders")
208
209function builders.vpack_filter(head,groupcode,size,packtype,maxdepth,direction)
210 if head then
211 starttiming(builders)
212 head = vpackactions(head,groupcode)
213 stoptiming(builders)
214 end
215 return head
216end
217
218
219
220
221local pageactions = nodes.tasks.actions("mvlbuilders")
222
223
224local function report(pagecontext,head)
225 report_page_builder("trigger: %s at level %i",pagecontext,texnest.ptr)
226 report_page_builder(" vsize : %p",texget("vsize"))
227 report_page_builder(" pagegoal : %p",texget("pagegoal"))
228 report_page_builder(" pagetotal: %p",texget("pagetotal"))
229 report_page_builder(" list : %s",head and nodeidstostring(head) or "<empty>")
230end
231
232
233
234
235
236function builders.buildpage_filter(pagecontext)
237 local head, tail = getspeciallist("contributehead")
238 if head then
239 if trace_page_builder then
240 report(pagecontext,head)
241 end
242 starttiming(builders)
243 head = pageactions(head,pagecontext)
244 stoptiming(builders)
245 setspeciallist("contributehead", head)
246 else
247 if trace_page_builder then
248 report(pagecontext)
249 end
250 end
251end
252
253registercallback("vpack_filter", builders.vpack_filter, "vertical spacing etc")
254registercallback("buildpage_filter", builders.buildpage_filter, "vertical spacing etc (mvl)")
255
256local vboxactions = nodes.tasks.actions("vboxhandlers")
257
258function builders.vbox_filter(head,groupcode)
259 if head then
260 starttiming(builders)
261 head = vboxactions(head,groupcode)
262 stoptiming(builders)
263 end
264 return head
265end
266
267registercallback("packed_vbox_filter", builders.vbox_filter, "packed vbox treatments")
268
269statistics.register("v-node processing time", function()
270 return statistics.elapsedseconds(builders)
271end)
272
273local implement = interfaces.implement
274
275implement { name = "defineparbuilder", actions = constructors.define, arguments = "string" }
276implement { name = "setparbuilder", actions = constructors.set, arguments = "string" }
277implement { name = "startparbuilder", actions = constructors.start, arguments = "string" }
278implement { name = "stopparbuilder", actions = constructors.stop }
279implement { name = "enableparbuilder", actions = constructors.enable }
280implement { name = "disableparbuilder", actions = constructors.disable }
281
282
283
284local nuts = nodes.nuts
285local tonut = nodes.tonut
286local setcolor = nodes.tracers.colors.set
287local listtoutf = nodes.listtoutf
288local new_kern = nuts.pool.kern
289local new_rule = nuts.pool.rule
290local hpack = nuts.hpack
291local getheight = nuts.getheight
292local getdepth = nuts.getdepth
293local getdirection = nuts.getdirection
294local getlist = nuts.getlist
295local setwidth = nuts.setwidth
296local setdirection = nuts.setdirection
297local setlink = nuts.setlink
298local tonode = nuts.tonode
299
300local list = { }
301
302local report_quality = logs.reporter("pack quality")
303
304
305
306local report = true trackers.register("builders.vpack.quality", function(v) report = v end)
307local collect = false trackers.register("builders.vpack.collect", function(v) collect = v end)
308
309
310function vpackbuilders.report(how,detail,n,first,last,filename)
311 if report then
312 if last <= 0 then
313 report_quality("%s vbox",how)
314 elseif first > 0 and first < last then
315 report_quality("%s vbox at line %i - %i in file %a",how,first,last,filename or "?")
316 else
317 report_quality("%s vbox at line %i in file %a",how,last,filename or "?")
318 end
319 end
320 if collect then
321 list[#list+1] = { "hbox", how, filename, first, last, how, detail }
322 end
323end
324
325
326
327
328
329
330local report = true trackers.register("builders.hpack.quality", function(v) report = v end)
331local collect = false trackers.register("builders.hpack.collect", function(v) collect = v end)
332local show = false trackers.register("builders.hpack.overflow",function(v) show = v end)
333
334function hpackbuilders.report(how,detail,n,first,last,filename)
335 local str = (report or collect) and listtoutf(getlist(n),"",true,nil,true)
336 if report then
337 local overfull = how == "overfull"
338 if last <= 0 then
339 report_quality(
340 overfull and "%s hbox: %s (%p)" or "%s hbox: %s (badness %i)",
341 how,str,detail
342 )
343 elseif first > 0 and first < last then
344 report_quality(
345 overfull and "%s hbox at line %i - %i in file %a: %s (%p)" or "%s hbox at line %i - %i in file %a: %s (badness %i)",
346 how,first,last,filename or "?",str,detail
347 )
348 else
349 report_quality(
350 overfull and "%s hbox at line %i in file %a: %s (%p)" or "%s hbox at line %i in file %a: %s (badness %i)",
351 how,last,filename or "?",str,detail
352 )
353 end
354 end
355 if collect then
356 list[#list+1] = { "hbox", how, filename, first, last, str, detail }
357 end
358end
359
360function hpackbuilders.show(how,detail,n,first,last,filename,rule)
361 if show then
362 local width = 2*65536
363 local height = getheight(n)
364 local depth = getdepth(n)
365 local direction = getdirection(n)
366 if height < 4*65526 then
367 height = 4*65526
368 end
369 if depth < 2*65526 then
370 depth = 2*65526
371 end
372 if rule then
373 flush(rule)
374 end
375 rule = new_rule(width,height,depth)
376 setdirection(rule,direction)
377 if how == "overfull" then
378 setcolor(rule,"red")
379 local kern = new_kern(-detail)
380 setlink(kern,rule)
381 rule = kern
382 elseif how == "underfull" then
383 setcolor(rule,"blue")
384 elseif how == "loose" then
385 setcolor(rule,"magenta")
386 elseif how == "tight" then
387 setcolor(rule,"cyan")
388 end
389 rule = hpack(rule)
390 setwidth(rule,0)
391 setdirection(rule,direction)
392 else
393 local width = texget("overfullrule")
394 if width > 0 then
395 rule = new_rule(width)
396 end
397 end
398 return rule
399end
400
401
402
403local hqualityactions = nodes.tasks.actions("hquality")
404local vqualityactions = nodes.tasks.actions("vquality")
405
406function hpackbuilders.qualityactions(how,detail,n,first,last,filename)
407 local rul = nil
408
409 rul = hqualityactions(how,detail,n,first,last,filename)
410
411 return rul
412end
413function vpackbuilders.qualityactions(how,detail,n,first,last,filename)
414 local rul = nil
415
416 rul = vqualityactions(how,detail,n,first,last,filename)
417
418 return rul
419end
420
421registercallback("hpack_quality", hpackbuilders.qualityactions, "report horizontal packing quality")
422registercallback("vpack_quality", vpackbuilders.qualityactions, "report vertical packing quality")
423
424statistics.register("quality reports", function()
425 local n = #list
426 if n > 0 then
427 local t = table.setmetatableindex("number")
428 local fw = 0
429 local hw = 0
430 for i=1,n do
431 local f = list[i][1]
432 local h = list[i][2]
433 if #f > fw then
434 fw = #f
435 end
436 if #h > hw then
437 hw = #h
438 end
439 t[h] = t[h] + 1
440 end
441 logs.startfilelogging(report_quality)
442 for i=1,n do
443 local l = list[i]
444 local how = l[2]
445 report_quality(
446 how == "overfull"
447 and "%-" .. fw .. "s [%04i - %04i] : %-" .. hw .. "s %s : %s (%p)"
448 or "%-" .. fw .. "s [%04i - %04i] : %-" .. hw .. "s %s : %s (badness %i)",
449 file.basename(l[3]),l[4],l[5],how,l[1],l[6],l[7]
450 )
451 end
452 logs.stopfilelogging()
453 report_quality()
454 report_quality("%i entries added to the log file : %s",n,table.sequenced(t))
455 report_quality()
456 end
457end)
458 |