spac-ovl.lmt /size: 7123 b    last modification: 2025-02-21 11:03
1if not modules then modules = { } end modules ['spac-ovl'] = {
2    version   = 1.001,
3    comment   = "companion to spac-brk.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 next, type, tonumber, tostring = next, type, tonumber, tostring
10
11local settings_to_array  = utilities.parsers.settings_to_array
12local settings_to_hash   = utilities.parsers.settings_to_hash
13
14local nuts               = nodes.nuts
15local tonut              = nodes.tonut
16
17local newrule            = nuts.pool.virtualrule
18local insertbefore       = nuts.insertbefore
19local insertafter        = nuts.insertafter
20local hpack_string       = nuts.typesetters.tohpack
21local getwidth           = nuts.getwidth
22local getid              = nuts.getid
23local getprev            = nuts.getprev
24local setwhd             = nuts.setwhd
25local setoffsets         = nuts.setoffsets
26
27local setcolor           = nodes.tracers.colors.set
28local settransparency    = nodes.tracers.transparencies.set
29
30local texgetnest         = tex.getnest
31local texgetcount        = tex.getcount
32local texsetcount        = tex.setcount
33
34local nodecodes          = nodes.nodecodes
35
36local penalty_code       <const> = nodecodes.penalty
37local glue_code          <const> = nodecodes.glue
38local disc_code          <const> = nodecodes.disc
39local kern_code          <const> = nodecodes.kern
40local math_code          <const> = nodecodes.math
41
42local breakcodes         = tex.breakcodes
43
44local report             = logs.reporter("linebreaks")
45
46local c_tracinglousiness <const> = tex.iscount("tracinglousiness")
47local c_linebreakchecks  <const> = tex.iscount("linebreakchecks")
48
49local width      =      65536 / 2
50local height     =  9 * 65536
51local depth      =  3 * 65536
52local xoffset    =  1 * 65536
53local yoffset    = -3 * 65536
54
55local serial     = false
56local usedfont   = false
57local nestlevel  = false
58local overload   = false
59local order      = 0
60local saved      = 0
61
62local actions = {
63    [breakcodes.initialize] = function()
64        if texgetnest("ptr") == nestlevel then
65            usedfont = nodes.visualizers.getusedfont()
66            serial   = false
67            order    = order + 1
68        end
69    end,
70    [breakcodes.start] = function(checks,pass,subpass)
71        if texgetnest("ptr") == nestlevel then
72            if pass == 2 then
73                serial = { }
74            end
75        end
76    end,
77    [breakcodes.report] = function(checks,pass,subpass,currentserial,previousserial,line,kind,class,classes,badness,demerits,breakpoint,short,glue)
78        if serial and breakpoint and texgetnest("ptr") == nestlevel then
79            local s = {
80                serial     = currentserial,
81                badness    = badness,
82                demerits   = demerits,
83                breakpoint = breakpoint,
84            }
85            serial[currentserial] = s
86            local found = overload and overload[currentserial]
87            if found then
88                report("pass %i, overloading serial %i.%i demerits from %i to %i",2,order,currentserial,demerits,found)
89                return found
90            end
91        end
92        return demerits
93    end,
94    [breakcodes.collect] = function()
95        if serial and texgetnest("ptr") == nestlevel and texgetcount(c_tracinglousiness) >= 1 then
96            for currentserial=1,#serial do
97                local data       = serial[currentserial]
98                local breakpoint = data.breakpoint
99                local current    = tonut(breakpoint)
100                while current do
101                    local id = getid(current)
102                    if id == penalty_code or id == glue_code or id == kern_code or id == math_code then
103                        current = getprev(current)
104                    else
105                        break
106                    end
107                end
108                if current then
109                    local rule   = newrule(width,height,depth,currentserial)
110                    data.rule    = rule
111                    insertafter(current,current,rule)
112                end
113            end
114        end
115    end,
116    [breakcodes.wrapup] = function()
117        if serial and texgetnest("ptr") == nestlevel then
118            local n     = #serial
119            local trace = texgetcount(c_tracinglousiness)
120            if trace >= 2 then
121                report("%i demerits in set %i",n,order)
122            end
123            for currentserial=1,n do
124                local data = serial[currentserial]
125                local demerits = data.demerits
126                if trace >= 1 then
127                    local rule = data.rule
128                    if rule then
129                        local tag  = tostring(order).."."..tostring(currentserial)
130                        local text = hpack_string(tag,usedfont)
131                        local size = getwidth(text)
132                        setwhd(text,0,0,0)
133                        setoffsets(text,-size-xoffset,yoffset)
134                        insertafter(rule,rule,text)
135                    end
136                end
137                serial[currentserial] = demerits
138                if trace >= 2 then
139                    report("% 4i : %8i",currentserial,demerits)
140                end
141            end
142            overload = false
143            texsetcount("linebreakchecks",saved)
144        end
145    end,
146}
147
148local function action(what,...)
149    local action = actions[what]
150    if action then
151        return action(...)
152    end
153end
154
155local function show(str)
156    nestlevel = texgetnest("ptr")
157    saved = texgetcount("linebreakchecks")
158    if str and str ~= "" then
159        if type(str) == "table" then
160            overload = str
161        else
162            overload = settings_to_hash(str)
163            for k, v in next, overload do
164                overload[tonumber(k)] = tonumber(v) or -1
165            end
166        end
167    else
168        overload = false
169    end
170    texsetcount("linebreakchecks",3)
171end
172
173local function last()
174    return serial or { }
175end
176
177nodes.handlers.linebreakchecks[3] = action
178
179-- interface
180
181local context     = context
182local scaninteger = tokens.scanners.integer
183
184local function feedback()
185    local l = last()
186    local n = #l
187    if n > 0 then
188        local t = { n }
189        local m = 1
190        for i=1,#l do
191            m = m + 1 ; t[m] = i
192            m = m + 1 ; t[m] = l[i]
193        end
194        context("% t",t)
195    end
196end
197
198interfaces.implement {
199    name      = "lousiness",
200    public    = true,
201    protected = true,
202    usage     = "value",
203    actions   = function(what)
204        if what == "value" then
205            feedback()
206        else
207            local n = scaninteger()
208            local t = { }
209            for i=1,n do
210                t[scaninteger()] = scaninteger()
211            end
212            show(t)
213        end
214    end,
215}
216
217interfaces.implement {
218    name      = "silliness",
219    public    = true,
220    protected = true,
221    usage     = "value",
222    actions   = function(what)
223        if what == "value" then
224            feedback()
225        else
226            show { [scaninteger()] = 0 }
227        end
228    end,
229}
230