luatex-fonts-gbn.lua /size: 9881 b    last modification: 2021-10-28 13:51
1if not modules then modules = { } end modules ['luatex-fonts-gbn'] = {
2    version   = 1.001,
3    comment   = "companion to luatex-*.tex",
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-- generic [base|node] mode handler
10
11if context then
12    os.exit()
13end
14
15local next = next
16
17local fonts = fonts
18local nodes = nodes
19
20local nuts       = nodes.nuts -- context abstraction of direct nodes
21
22local traverseid = nuts.traverseid
23local flushnode  = nuts.flushnode
24
25local glyph_code = nodes.nodecodes.glyph
26local disc_code  = nodes.nodecodes.disc
27
28local tonode     = nuts.tonode
29local tonut      = nuts.tonut
30
31local getfont    = nuts.getfont
32local getchar    = nuts.getchar
33local getid      = nuts.getid
34local getboth    = nuts.getboth
35local getprev    = nuts.getprev
36local getnext    = nuts.getnext
37local getdisc    = nuts.getdisc
38local setchar    = nuts.setchar
39local setlink    = nuts.setlink
40local setprev    = nuts.setprev
41
42-- from now on we apply ligaturing and kerning here because it might interfere with complex
43-- opentype discretionary handling where the base ligature pass expect some weird extra
44-- pointers (which then confuse the tail slider that has some checking built in)
45
46local n_ligaturing = node.ligaturing
47local n_kerning    = node.kerning
48
49local d_ligaturing = nuts.ligaturing
50local d_kerning    = nuts.kerning
51
52local basemodepass = true
53
54local function l_warning() logs.report("fonts","don't call 'node.ligaturing' directly") l_warning = nil end
55local function k_warning() logs.report("fonts","don't call 'node.kerning' directly")    k_warning = nil end
56
57function node.ligaturing(...)
58    if basemodepass and l_warning then
59        l_warning()
60    end
61    return n_ligaturing(...)
62end
63
64function node.kerning(...)
65    if basemodepass and k_warning then
66        k_warning()
67    end
68    return n_kerning(...)
69end
70
71function nuts.ligaturing(...)
72    if basemodepass and l_warning then
73        l_warning()
74    end
75    return d_ligaturing(...)
76end
77
78function nuts.kerning(...)
79    if basemodepass and k_warning then
80        k_warning()
81    end
82    return d_kerning(...)
83end
84
85-- direct.ligaturing = nuts.ligaturing
86-- direct.kerning    = nuts.kerning
87
88function nodes.handlers.setbasemodepass(v)
89    basemodepass = v
90end
91
92local function nodepass(head,groupcode,size,packtype,direction)
93    local fontdata = fonts.hashes.identifiers
94    if fontdata then
95        local usedfonts = { }
96        local basefonts = { }
97        local prevfont  = nil
98        local basefont  = nil
99        local variants  = nil
100        local redundant = nil
101        local nofused   = 0
102        for n in traverseid(glyph_code,head) do
103            local font = getfont(n)
104            if font ~= prevfont then
105                if basefont then
106                    basefont[2] = getprev(n)
107                end
108                prevfont = font
109                local used = usedfonts[font]
110                if not used then
111                    local tfmdata = fontdata[font] --
112                    if tfmdata then
113                        local shared = tfmdata.shared -- we need to check shared, only when same features
114                        if shared then
115                            local processors = shared.processes
116                            if processors and #processors > 0 then
117                                usedfonts[font] = processors
118                                nofused = nofused + 1
119                            elseif basemodepass then
120                                basefont = { n, nil }
121                                basefonts[#basefonts+1] = basefont
122                            end
123                        end
124                        local resources = tfmdata.resources
125                        variants = resources and resources.variants
126                        variants = variants and next(variants) and variants or false
127                    end
128                else
129                    local tfmdata = fontdata[prevfont]
130                    if tfmdata then
131                        local resources = tfmdata.resources
132                        variants = resources and resources.variants
133                        variants = variants and next(variants) and variants or false
134                    end
135                end
136            end
137            if variants then
138                local char = getchar(n)
139                if (char >= 0xFE00 and char <= 0xFE0F) or (char >= 0xE0100 and char <= 0xE01EF) then
140                    local hash = variants[char]
141                    if hash then
142                        local p = getprev(n)
143                        if p and getid(p) == glyph_code then
144                            local variant = hash[getchar(p)]
145                            if variant then
146                                setchar(p,variant)
147                            end
148                        end
149                    end
150                    -- per generic user request we always remove selectors
151                    if not redundant then
152                        redundant = { n }
153                    else
154                        redundant[#redundant+1] = n
155                    end
156                end
157            end
158        end
159        local nofbasefonts = #basefonts
160        if redundant then
161            for i=1,#redundant do
162                local r = redundant[i]
163                local p, n = getboth(r)
164                if r == head then
165                    head = n
166                    setprev(n)
167                else
168                    setlink(p,n)
169                end
170                if nofbasefonts > 0 then
171                    for i=1,nofbasefonts do
172                        local bi = basefonts[i]
173                        if r == bi[1] then
174                            bi[1] = n
175                        end
176                        if r == bi[2] then
177                            bi[2] = n
178                        end
179                    end
180                end
181                flushnode(r)
182            end
183        end
184        for d in traverseid(disc_code,head) do
185            local _, _, r = getdisc(d)
186            if r then
187                for n in traverseid(glyph_code,r) do
188                    local font = getfont(n)
189                    if font ~= prevfont then
190                        prevfont = font
191                        local used = usedfonts[font]
192                        if not used then
193                            local tfmdata = fontdata[font] --
194                            if tfmdata then
195                                local shared = tfmdata.shared -- we need to check shared, only when same features
196                                if shared then
197                                    local processors = shared.processes
198                                    if processors and #processors > 0 then
199                                        usedfonts[font] = processors
200                                        nofused = nofused + 1
201                                    end
202                                end
203                            end
204                        end
205                    end
206                end
207            end
208        end
209        if next(usedfonts) then
210            for font, processors in next, usedfonts do
211                for i=1,#processors do
212                    head = processors[i](head,font,0,direction,nofused) or head
213                end
214            end
215        end
216        if basemodepass and nofbasefonts > 0 then
217            for i=1,nofbasefonts do
218                local range = basefonts[i]
219                local start = range[1]
220                local stop  = range[2]
221                if start then
222                    local front = head == start
223                    local prev, next
224                    if stop then
225                        next = getnext(stop)
226                        start, stop = d_ligaturing(start,stop)
227                        start, stop = d_kerning(start,stop)
228                    else
229                        prev  = getprev(start)
230                        start = d_ligaturing(start)
231                        start = d_kerning(start)
232                    end
233                    if prev then
234                        setlink(prev,start)
235                    end
236                    if next then
237                        setlink(stop,next)
238                    end
239                    if front and head ~= start then
240                        head = start
241                    end
242                end
243            end
244        end
245    end
246    return head
247end
248
249local function basepass(head)
250    if basemodepass then
251        head = d_ligaturing(head)
252        head = d_kerning(head)
253    end
254    return head
255end
256
257local protectpass = node.direct.protectglyphs or node.direct.protect_glyphs
258local injectpass  = nodes.injections.handler
259
260-- This is the only official public interface and this one can be hooked into a callback (chain) and
261-- everything else can change!@ Functione being visibel doesn't mean that it's part of the api.
262
263function nodes.handlers.nodepass(head,...)
264    if head then
265        return tonode(nodepass(tonut(head),...))
266    end
267end
268
269function nodes.handlers.basepass(head)
270    if head then
271        return tonode(basepass(tonut(head)))
272    end
273end
274
275function nodes.handlers.injectpass(head)
276    if head then
277        return tonode(injectpass(tonut(head)))
278    end
279end
280
281function nodes.handlers.protectpass(head)
282    if head then
283        protectpass(tonut(head))
284        return head
285    end
286end
287
288function nodes.simple_font_handler(head,groupcode,size,packtype,direction)
289    if head then
290        head = tonut(head)
291        head = nodepass(head,groupcode,size,packtype,direction)
292        head = injectpass(head)
293        if not basemodepass then
294            head = basepass(head)
295        end
296        protectpass(head)
297        head = tonode(head)
298    end
299    return head
300end
301