summaryrefslogtreecommitdiff
path: root/tex/context/base/mkiv/font-imp-spacekerns.lua
blob: 0e2b0f9b0ae4b8ac79b3b9260949ea8fc1c57f04 (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
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
if not modules then modules = { } end modules ['font-imp-spacekerns'] = {
    version   = 1.001,
    comment   = "companion to font-ini.mkiv and hand-ini.mkiv",
    author    = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
    copyright = "PRAGMA ADE / ConTeXt Development Team",
    license   = "see context related readme files"
}

if not context then return end

-- This is an experiment. See font-ots.lua for original implementation.

local type, next = type, next
local insert, setmetatableindex = table.insert, table.setmetatableindex

local fonts              = fonts
local otf                = fonts.handlers.otf
local fontdata           = fonts.hashes.identifiers
local fontfeatures       = fonts.hashes.features
local otffeatures        = fonts.constructors.features.otf
local registerotffeature = otffeatures.register
local handlers           = otf.handlers
local setspacekerns      = nodes.injections.setspacekerns

function handlers.trigger_space_kerns(head,dataset,sequence,initialrl,font,attr)
    local features = fontfeatures[font]
    local enabled  = features and features.spacekern
    if enabled then
        setspacekerns(font,sequence) -- called quite often, each glyphrun
    end
    return head, enabled
end

local function hasspacekerns(data)
    local resources = data.resources
    local sequences = resources.sequences
    local validgpos = resources.features.gpos
    if validgpos and sequences then
        for i=1,#sequences do
            local sequence = sequences[i]
            local steps    = sequence.steps
            if steps then -- and sequence.features[tag] then
                local kind = sequence.type
                if kind == "gpos_pair" then -- or kind == "gpos_single" then
                    for i=1,#steps do
                        local step     = steps[i]
                        local coverage = step.coverage
                        local rules    = step.rules
                     -- if rules then
                     --     -- not now: analyze (simple) rules
                     -- elseif not coverage then
                     --     -- nothing to do
                     -- elseif kind == "gpos_single" then
                     --     -- maybe a message that we ignore
                     -- elseif kind == "gpos_pair" then
                        if coverage and not rules then
                            local format = step.format
                            if format == "move" or format == "kern" then
                                local kerns  = coverage[32]
                                if kerns then
                                    return true
                                end
                                for k, v in next, coverage do
                                    if v[32] then
                                        return true
                                    end
                                end
                            elseif format == "pair" then
                                local kerns  = coverage[32]
                                if kerns then
                                    for k, v in next, kerns do
                                        local one = v[1]
                                        if one and one ~= true then
                                            return true
                                        end
                                    end
                                end
                                for k, v in next, coverage do
                                    local kern = v[32]
                                    if kern then
                                        local one = kern[1]
                                        if one and one ~= true then
                                            return true
                                        end
                                    end
                                end
                            end
                        end
                    end
                end
            end
        end
    end
    return false
end

otf.readers.registerextender {
    name   = "spacekerns",
    action = function(data)
        data.properties.hasspacekerns = hasspacekerns(data)
    end
}

local function newdata(t,k)
    local v = {
        left  = { },
        right = { },
        last  = 0,
        feat  = nil,
    }
    t[k] = v
    return v
end

local function spaceinitializer(tfmdata,value) -- attr
    local resources  = tfmdata.resources
    local spacekerns = resources and resources.spacekerns
    if value and spacekerns == nil then
        local rawdata    = tfmdata.shared and tfmdata.shared.rawdata
        local properties = rawdata.properties
        if properties and properties.hasspacekerns then
            local sequences = resources.sequences
            local validgpos = resources.features.gpos
            if validgpos and sequences then
                local data = setmetatableindex(newdata)
                for i=1,#sequences do
                    local sequence = sequences[i]
                    local steps    = sequence.steps
                    if steps then
                        -- we don't support space kerns in other features
                     -- local kern = sequence.features[tag]
                     -- if kern then
                        for tag, kern in next, sequence.features do

                            local d     = data[tag]
                            local left  = d.left
                            local right = d.right
                            local last  = d.last
                            local feat  = d.feat

                            local kind = sequence.type
                            if kind == "gpos_pair" then -- or kind == "gpos_single" then
                                if feat then
                                    for script, languages in next, kern do
                                        local f = feat[script]
                                        if f then
                                            for l in next, languages do
                                                f[l] = true
                                            end
                                        else
                                            feat[script] = languages
                                        end
                                    end
                                else
                                    feat = kern
    d.feat = feat
                                end
                                for i=1,#steps do
                                    local step     = steps[i]
                                    local coverage = step.coverage
                                    local rules    = step.rules
                                 -- if rules then
                                 --     -- not now: analyze (simple) rules
                                 -- elseif not coverage then
                                 --     -- nothing to do
                                 -- elseif kind == "gpos_single" then
                                 --     -- makes no sense in TeX
                                 -- elseif kind == "gpos_pair" then
                                    if coverage and not rules then
                                        local format = step.format
                                        if format == "move" or format == "kern" then
                                            local kerns  = coverage[32]
                                            if kerns then
                                                for k, v in next, kerns do
                                                    right[k] = v
                                                end
                                            end
                                            for k, v in next, coverage do
                                                local kern = v[32]
                                                if kern then
                                                    left[k] = kern
                                                end
                                            end
                                        elseif format == "pair" then
                                            local kerns  = coverage[32]
                                            if kerns then
                                                for k, v in next, kerns do
                                                    local one = v[1]
                                                    if one and one ~= true then
                                                        right[k] = one[3]
                                                    end
                                                end
                                            end
                                            for k, v in next, coverage do
                                                local kern = v[32]
                                                if kern then
                                                    local one = kern[1]
                                                    if one and one ~= true then
                                                        left[k] = one[3]
                                                    end
                                                end
                                            end
                                        end
                                    end
                                end
                                last = i
                            end
d.last = last
                        end
                    end
                end

                for tag, d in next, data do
                    local left  = d.left
                    local right = d.right
                    left  = next(left)  and left  or false
                    right = next(right) and right or false
                    if left or right then

                        local last  = d.last
                        local feat  = d.feat

                        if last > 0 then
                            local triggersequence = {
                                -- no steps, see (!!)
                                features = { [tag] = feat or { dflt = { dflt = true, } } },
                                flags    = noflags,
                                name     = "trigger_space_kerns",
                                order    = { tag },
                                type     = "trigger_space_kerns",
                                left     = left,
                                right    = right,
                            }
                            insert(sequences,last,triggersequence)
                            d.last = d.last + 1
                            spacekerns = true
                        end
                    end

                end
            end
        end
        if not spacekerns then
            spacekerns = false
        end
        resources.spacekerns = spacekerns
    end
    return spacekerns
end

registerotffeature {
    name         = "spacekern",
    description  = "space kern injection",
    default      = true,
    initializers = {
        node     = spaceinitializer,
    },
}