typo-stc.lmt /size: 5902 b    last modification: 2025-02-21 11:03
1if not modules then modules = { } end modules ['typo-stc'] = {
2    version   = 1.001,
3    comment   = "companion to typo-stc.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 tonumber, next = tonumber, next
10local gmatch = string.gmatch
11local stepper = utilities.parsers.stepper
12
13-- local trace_stacking = false  trackers.register("typesetters.stacking", function(v) trace_stacking = v end)
14--
15-- local report_stacking = logs.reporter("typesetting","stacking")
16
17local nodes, node = nodes, node
18local nuts               = nodes.nuts
19local tonut              = nuts.tonut
20
21local nodecodes          = nodes.nodecodes
22
23local glyph_code         <const> = nodecodes.glyph
24local disc_code          <const> = nodecodes.disc -- unlikely
25local hlist_code         <const> = nodecodes.hlist
26local vlist_code         <const> = nodecodes.vlist
27local whatsit_code       <const> = nodecodes.whatsit
28local rule_code          <const> = nodecodes.rule
29local glue_code          <const> = nodecodes.glue
30
31local emptyrule_code     <const> = nodes.rulecodes.empty
32
33local a_stacking         <const> = attributes.private("stacking")
34
35local getattr            = nuts.getattr
36local getwhd             = nuts.getwhd
37local getlist            = nuts.getlist
38local setlist            = nuts.setlist
39local setleader          = nuts.setleader
40local getsubtype         = nuts.getsubtype
41local setsubtype         = nuts.setsubtype
42local isnextglyph        = nuts.isnextglyph
43
44local replace_node       = nuts.replace
45local remove_node        = nuts.remove
46
47local nextcontent        = nuts.traversers.content
48
49local new_emptyrule      = nuts.pool.emptyrule
50
51
52local unblocked =  {
53     savepos        = true,
54     save           = true,
55     restore        = true,
56     setmatrix      = true,
57     startmatrix    = true,
58     stopmatrix     = true,
59     startscaling   = true,
60     stopscaling    = true,
61     startrotation  = true,
62     stoprotation   = true,
63     startmirroring = true,
64     stopmirroring  = true,
65     startclipping  = true,
66     stopclipping   = true,
67}
68
69local stacking       = typesetters.stacking or { }
70typesetters.stacking = stacking
71
72local nofstacking = 0xFFFF -- we don't mix with numbers
73local registered  = { }
74local currentset  = { }
75local enabled     = false
76
77local function getselection(str)
78    local t = { }
79    stepper(str,function(s)
80        local r = registered[s] or tonumber(s)
81        if r then
82            t[r] = true
83        end
84    end)
85    return t
86end
87
88local function getindex(str)
89    return registered[str] or tonumber(str) or 0
90end
91
92typesetters.stacking.getselection = getselection
93typesetters.stacking.getindex     = getindex
94
95local report = logs.reporter("stacking")
96local trace  = false
97
98interfaces.implement {
99    name      = "newstacking",
100    arguments = "2 strings",
101    usage     = "value",
102    actions   = function(s,n)
103        n = n and tonumber(n)
104        if not n then
105            nofstacking = nofstacking + 1
106            n = nofstacking
107        end
108        registered[s] = n
109        if trace then
110            report("define %a as index %i",s,n)
111        end
112        return tokens.values.integer, n
113    end,
114}
115
116interfaces.implement {
117    name      = "enablestacking",
118    actions   = function()
119        if not enabled then
120            nodes.tasks.enableaction("shipouts", "typesetters.stacking.handler")
121            enabled = true
122        end
123    end,
124}
125
126interfaces.implement {
127    name      = "setstacking",
128    arguments = "string",
129    actions   = function(str)
130        currentset = getselection(str)
131        if not enabled and next(currentset) then
132            if trace then
133                report("selecting %a",str)
134            end
135            nodes.tasks.enableaction("shipouts", "typesetters.stacking.handler")
136            enabled = true
137        end
138    end,
139}
140
141local function process(head)
142    local current = head
143    while current do
144        local nxt, chr, id = isnextglyph(current)
145        if chr then
146            local a = getattr(current,a_stacking)
147            if a and (a == 0 or not currentset[a]) then
148                local w, h, d = getwhd(current)
149                local r = new_emptyrule(w,h,d)
150                head, current = replace_node(head,current,r)
151            end
152        elseif id == glue_code then
153            local a = getattr(current,a_stacking)
154            if a and (a == 0 or not currentset[a]) then
155                setleader(current)
156            end
157        elseif id == hlist_code or id == vlist_code then
158            local a = getattr(current,a_stacking)
159            if a and (a == 0 or not currentset[a]) then
160                setlist(current)
161            else
162                local list = getlist(current)
163                if list then
164                    local l = process(list)
165                    if l ~= list then
166                        setlist(current,l)
167                    end
168                end
169            end
170        elseif id == rule_code then
171            local a = getattr(current,a_stacking)
172            if a and not currentset[a] then
173                setsubtype(current,emptyrule_code)
174            end
175        elseif id == whatsit_code then
176            local a = getattr(current,a_stacking)
177            if a and (a == 0 or not currentset[a]) and not unblocked[getsubtype(current)] then
178                head, current = remove_node(head,current,true)
179            end
180        elseif id == disc_code then
181            -- doesn't happen
182            local a = getattr(current,a_stacking)
183            if a and (a == 0 or not currentset[a]) then
184                local w, h, d = getwhd(current)
185                local r = new_emptyrule(w,h,d)
186                head, current = replace_node(head,current,r)
187            end
188        end
189        current = nxt
190    end
191    return head
192end
193
194stacking.handler = process
195