trac-riv.lmt /size: 10 Kb    last modification: 2024-01-16 10:22
1if not modules then modules = { } end modules ['trac-riv'] = {
2    version   = 1.001,
3    optimize  = true,
4    comment   = "companion to trac-riv.mkxl",
5    author    = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
6    copyright = "PRAGMA ADE / ConTeXt Development Team",
7    license   = "see context related readme files"
8}
9
10local concat = table.concat
11local floor, ceiling = math.floor, math.ceiling
12local newindex = lua.newindex
13
14local nuts              = nodes.nuts
15
16local getbox            = nuts.getbox
17
18local getid             = nuts.getid
19local getsubtype        = nuts.getsubtype
20local getnext           = nuts.getnext
21local getwhd            = nuts.getwhd
22local getwidth          = nuts.getwidth
23local getkern           = nuts.getkern
24local getlist           = nuts.getlist
25local getleader         = nuts.getleader
26local getreplace        = nuts.getreplace
27local getheight         = nuts.getheight
28local getdepth          = nuts.getdepth
29
30local setlink           = nuts.setlink
31local setlist           = nuts.setlist
32local setoffsets        = nuts.setoffsets
33local setkern           = nuts.setkern
34
35local effectiveglue     = nuts.effectiveglue
36
37local setcolor          = nodes.tracers.colors.set
38
39local newrule           = nuts.pool.rule
40local newkern           = nuts.pool.kern
41
42local nextnode          = nuts.traversers.node
43
44local nodecodes         = nodes.nodecodes
45local gluecodes         = nodes.gluecodes
46
47local glyph_code        = nodecodes.glyph
48local disc_code         = nodecodes.disc
49local penalty_code      = nodecodes.penalty
50local kern_code         = nodecodes.kern
51local glue_code         = nodecodes.glue
52local hlist_code        = nodecodes.hlist
53local vlist_code        = nodecodes.vlist
54local math_code         = nodecodes.math
55local rule_code         = nodecodes.rule
56
57local spaceskip_code    = gluecodes.spaceskip
58local xspaceskip_code   = gluecodes.xspaceskip
59local lineskip_code     = gluecodes.lineskip
60local baselineskip_code = gluecodes.baselineskip
61
62local fontkern_code     = nodes.kerncodes.fontkern
63local linelist_code     = nodes.listcodes.line
64
65local unit    = 65536
66local step    =     2 * unit
67local margin  =    -1 * unit
68local skip    =    10 * unit
69local height  =     2 * unit
70local depth   =     2 * unit
71
72local function analyze(line,max)
73
74    local width    = 0
75    local position = 0
76    local profile  = newindex(max+2,0)
77    local wd       = 0
78    local state    = 0
79
80    local function process(current) -- called nested in disc replace
81        for current, id, subtype in nextnode, current do
82            if id == glyph_code then
83             -- wd = getwidth(current)
84                local w, h, d = getwhd(current)
85                wd = w
86                if d <= depth and h <= height then
87                    local n = getnext(current)
88                    if n and getid(n) == glue_code then
89                        local s = getsubtype(n)
90                        if s == spaceskip_code or s == xspaceskip_code then
91                            state = 1
92                        end
93                    end
94                 end
95            elseif id == kern_code then
96                wd = getkern(current)
97                if wd > 0 and subtype ~= fontkern_code then
98                    state = 1
99                end
100            elseif id == disc_code then
101                local replace = getreplace(current)
102                if replace then
103                    process(replace)
104                end
105                goto done
106            elseif id == glue_code then
107                wd = effectiveglue(current,line)
108                if wd > 0 and subtype == spaceskip_code or subtype == xspaceskip_code then
109                    state = 1
110                end
111            elseif id == hlist_code then
112                wd = getwidth(current)
113            elseif id == vlist_code then
114                wd = getwidth(current)
115            elseif id == rule_code then
116                wd = getwidth(current)
117            elseif id == math_code then
118                wd = getkern(current) + getwidth(current) -- surround
119                state = 1
120            else
121                goto done
122            end
123            -- progress
124            position = width
125            width    = position + wd
126            p = floor((position - margin)/step + 0.5)
127            w = floor((width    + margin)/step - 0.5)
128            if p < 0 then
129                p = 0
130            end
131            if w < 0 then
132                w = 0
133            end
134            if p > w then
135                w, p = p, w
136            end
137            if state == 1 then
138                for i=p+1,w+1 do
139                    profile[i] = state
140                end
141                state = 0
142            end
143          ::done::
144        end
145    end
146
147    process(getlist(line))
148
149    return profile
150
151end
152
153function builders.profiling.getrivers(specification)
154    local box  = getbox(specification.box)
155    local head = getlist(box)
156    if head then
157
158        step   = (specification.step          or  2 * unit)
159        margin = (specification.margin        or -1 * unit)
160        skip   = (specification.gluethreshold or 10 * unit)
161        height = (specification.height        or  2 * unit)
162        depth  = (specification.depth         or  2 * unit)
163
164        local profiles = { }
165        local max      = ceiling(getwidth(box)/step) + 1
166
167        local p = 0
168
169        p = p + 1 ; profiles[p] = newindex(max+2,1)
170        p = p + 1 ; profiles[p] = newindex(max+2,0)
171
172        for current, id, subtype in nextnode, head do
173            if id == hlist_code then
174                local amount = getwidth(current)
175                if amount > 0 then
176                    p = p + 1 ; profiles[p] = analyze(current,max)
177                end
178            elseif id == glue_code then
179                if subtype == baselineskip_code or subtype == lineskip_code then
180                    -- okay
181                elseif getwidth(current) > skip then
182                    p = p + 1 ; profiles[p] = newindex(max+2,0)
183                end
184            elseif id == vlist_code then
185                p = p + 1 ; profiles[p] = newindex(max+2,0)
186            end
187        end
188
189        p = p + 1 ; profiles[p] = newindex(max+2,0)
190        p = p + 1 ; profiles[p] = newindex(max+2,1)
191
192        for i=1,p do
193            local pi = profiles[i]
194            pi[0] = 1 ; pi[max ]  = 1
195            pi[1] = 1 ; pi[max+1] = 1
196            profiles[i] = concat(pi,"",0,max+1)
197        end
198        profiles = concat(profiles,"\n")
199        potrace.setbitmap("profile", profiles)
200    end
201end
202
203function builders.profiling.addrivers(specification)
204    local box  = getbox(specification.box)
205    local head = getlist(box)
206    if head then
207
208        step   = (specification.step          or  2 * unit)
209        margin = (specification.margin        or -1 * unit)
210        skip   = (specification.gluethreshold or 10 * unit)
211        height = (specification.height        or  2 * unit)
212        depth  = (specification.depth         or  2 * unit)
213
214        local profiles = { }
215        local max      = ceiling(getwidth(box)/step) + 1
216
217        local previous = false
218        local spacing  = 0
219
220        local topline  = false
221        local botline  = false
222
223        local list     = newindex(max+2,false)
224
225        for current, id, subtype in nextnode, head do
226            if id == hlist_code then
227                local amount = getwidth(current)
228                if amount > 0 then
229                    botline = analyze(current,max)
230                    if topline then
231                        local head = false
232                        local tail = false
233                        for i=1,max do
234                            if topline[i] ~= 0 and botline[i] ~= 0 then
235                                local _, hp, dp = getwhd(previous)
236                                local _, hc, dc = getwhd(previous)
237                                local rule = newrule(step,hp + dp + spacing + hc,dc)
238                                local kern = newkern()
239                                setkern(kern,-step)
240                                setoffsets(rule,i*step,0)
241                                setlink(rule,kern)
242                                if tail then
243                                    setlink(tail,rule)
244                                else
245                                    head = rule
246                                end
247                                tail = kern
248                                local li = list[i]
249                                if li then
250                                    li[#li+1] = rule
251                                    for i=1,#li do
252                                        setcolor(li[i],"darkred")
253                                    end
254                                else
255                                    list[i] = { rule }
256                                    setcolor(rule,"darkblue")
257                                end
258                            else
259                                list[i] = false
260                            end
261                        end
262                        if head then
263                            setlink(tail,getlist(current))
264                            setlist(current,head)
265                        end
266                    end
267                    topline  = botline
268                    botline  = false
269                    previous = current
270                end
271            elseif id == glue_code then
272                local width = getwidth(current)
273                if subtype == baselineskip_code or subtype == lineskip_code then
274                    spacing = width
275                elseif width > skip then
276                    topline = false
277                    botline = false
278                end
279            elseif id == vlist_code then
280                topline = false
281                botline = false
282            end
283        end
284
285    end
286end
287
288interfaces.implement {
289    name      = "getrivers",
290    protected = true,
291    actions   = builders.profiling.getrivers,
292    arguments = {
293        {
294            { "box", "integer" },
295            { "height", "dimension" },
296            { "depth", "dimension" },
297            { "step", "dimension" },
298            { "margin", "dimension" },
299            { "skip", "dimension" },
300        }
301    }
302}
303
304-- can become option
305
306interfaces.implement {
307    name      = "addrivers",
308    protected = true,
309    actions   = builders.profiling.addrivers,
310    arguments = {
311        {
312            { "box", "integer" },
313            { "height", "dimension" },
314            { "depth", "dimension" },
315            { "step", "dimension" },
316            { "margin", "dimension" },
317            { "skip", "dimension" },
318        }
319    }
320}
321