summaryrefslogtreecommitdiff
path: root/tex/context/base/mkiv/font-gbn.lua
blob: f81c877f295266806d7a2bc59a58fc3a3670af31 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
if not modules then modules = { } end modules ['font-gbn'] = {
    version   = 1.001,
    comment   = "companion to luatex-*.tex",
    author    = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
    copyright = "PRAGMA ADE / ConTeXt Development Team",
    license   = "see context related readme files"
}

-- generic [base|node] mode handler

if context then
    texio.write_nl("fatal error: this module is not for context")
    os.exit()
end

local fonts = fonts
local nodes = nodes

local nuts        = nodes.nuts -- context abstraction of direct nodes

local traverse_id = nuts.traverse_id
local free_node   = nuts.free
local remove_node = nuts.remove

local glyph_code  = nodes.nodecodes.glyph
local disc_code   = nodes.nodecodes.disc

local tonode      = nuts.tonode
local tonut       = nuts.tonut

local getfont     = nuts.getfont
local getchar     = nuts.getchar
local getid       = nuts.getid
local getprev     = nuts.getprev
local getnext     = nuts.getnext
local getdisc     = nuts.getdisc
local setchar     = nuts.setchar
local setlink     = nuts.setlink

-- from now on we apply ligaturing and kerning here because it might interfere with complex
-- opentype discretionary handling where the base ligature pass expect some weird extra
-- pointers (which then confuse the tail slider that has some checking built in)

local n_ligaturing = node.ligaturing
local n_kerning    = node.kerning

local ligaturing   = nuts.ligaturing
local kerning      = nuts.kerning

local basemodepass = true

local function l_warning() texio.write_nl("warning: node.ligaturing called directly") l_warning = nil end
local function k_warning() texio.write_nl("warning: node.kerning called directly")    k_warning = nil end

function node.ligaturing(...)
    if basemodepass and l_warning then
        l_warning()
    end
    return n_ligaturing(...)
end

function node.kerning(...)
    if basemodepass and k_warning then
        k_warning()
    end
    return n_kerning(...)
end

function nodes.handlers.setbasemodepass(v)
    basemodepass = v
end

function nodes.handlers.nodepass(head)
    local fontdata = fonts.hashes.identifiers
    if fontdata then
        local nuthead   = tonut(head)
        local usedfonts = { }
        local basefonts = { }
        local prevfont  = nil
        local basefont  = nil
        local variants  = nil
        local redundant = nil
        for n in traverse_id(glyph_code,nuthead) do
            local font = getfont(n)
            if font ~= prevfont then
                if basefont then
                    basefont[2] = getprev(n)
                end
                prevfont = font
                local used = usedfonts[font]
                if not used then
                    local tfmdata = fontdata[font] --
                    if tfmdata then
                        local shared = tfmdata.shared -- we need to check shared, only when same features
                        if shared then
                            local processors = shared.processes
                            if processors and #processors > 0 then
                                usedfonts[font] = processors
                            elseif basemodepass then
                                basefont = { n, nil }
                                basefonts[#basefonts+1] = basefont
                            end
                        end
                        local resources = tfmdata.resources
                        variants = resources and resources.variants
                        variants = variants and next(variants) and variants or false
                    end
                else
                    local tfmdata = fontdata[prevfont]
                    if tfmdata then
                        local resources = tfmdata.resources
                        variants = resources and resources.variants
                        variants = variants and next(variants) and variants or false
                    end
                end
            end
            if variants then
                local char = getchar(n)
                if char >= 0xFE00 and (char <= 0xFE0F or (char >= 0xE0100 and char <= 0xE01EF)) then
                    local hash = variants[char]
                    if hash then
                        local p = getprev(n)
                        if p and getid(p) == glyph_code then
                            local variant = hash[getchar(p)]
                            if variant then
                                setchar(p,variant)
                                if not redundant then
                                    redundant = { n }
                                else
                                    redundant[#redundant+1] = n
                                end
                            end
                        end
                    end
                end
            end
        end
        if redundant then
            for i=1,#redundant do
                local n = redundant[i]
                remove_node(nuthead,n)
                free_node(n)
            end
        end
        for d in traverse_id(disc_code,nuthead) do
            local _, _, r = getdisc(d)
            if r then
                for n in traverse_id(glyph_code,r) do
                    local font = getfont(n)
                    if font ~= prevfont then
                        prevfont = font
                        local used = usedfonts[font]
                        if not used then
                            local tfmdata = fontdata[font] --
                            if tfmdata then
                                local shared = tfmdata.shared -- we need to check shared, only when same features
                                if shared then
                                    local processors = shared.processes
                                    if processors and #processors > 0 then
                                        usedfonts[font] = processors
                                    end
                                end
                            end
                        end
                    end
                end
            end
        end
        if next(usedfonts) then
            for font, processors in next, usedfonts do
                for i=1,#processors do
                    head = processors[i](head,font,0) or head
                end
            end
        end
        if basemodepass and #basefonts > 0 then
            for i=1,#basefonts do
                local range = basefonts[i]
                local start = range[1]
                local stop  = range[2]
                if start then
                    local front = nuthead == start
                    local prev, next
                    if stop then
                        next = getnext(stop)
                        start, stop = ligaturing(start,stop)
                        start, stop = kerning(start,stop)
                    else
                        prev  = getprev(start)
                        start = ligaturing(start)
                        start = kerning(start)
                    end
                    if prev then
                        setlink(prev,start)
                    end
                    if next then
                        setlink(stop,next)
                    end
                    if front and nuthead ~= start then
                        head = tonode(start)
                    end
                end
            end
        end
        return head, true
    else
        return head, false
    end
end

function nodes.handlers.basepass(head)
    if not basemodepass then
        head = n_ligaturing(head)
        head = n_kerning(head)
    end
    return head, true
end

local nodepass    = nodes.handlers.nodepass
local basepass    = nodes.handlers.basepass
local injectpass  = nodes.injections.handler
local protectpass = nodes.handlers.protectglyphs

function nodes.simple_font_handler(head)
    if head then
        head = nodepass(head)
        head = injectpass(head)
        if not basemodepass then
            head = basepass(head)
        end
        protectpass(head)
        return head, true
    else
        return head, false
    end
end