s-fonts-features.lua /size: 9 Kb    last modification: 2021-10-28 13:51
1if not modules then modules = { } end modules ['s-fonts-features'] = {
2    version   = 1.001,
3    comment   = "companion to s-fonts-features.mkiv",
4    author    = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
5    copyright = "PRAGMA ADE / ConTeXt Development Team",
6    license   = "see context related readme files"
7}
8
9moduledata.fonts          = moduledata.fonts          or { }
10moduledata.fonts.features = moduledata.fonts.features or { }
11
12-- for the moment only otf
13
14local rawget = rawget
15local insert, remove, sortedhash = table.insert, table.remove, table.sortedhash
16
17local v_yes  = interfaces.variables.yes
18local v_no   = interfaces.variables.no
19local c_name = interfaces.constants.name
20
21local NC, NR, bold = context.NC, context.NR, context.bold
22
23function moduledata.fonts.features.showused(specification)
24
25    specification = interfaces.checkedspecification(specification)
26
27 -- local list = utilities.parsers.settings_to_set(specification.list or "all")
28
29    context.starttabulate { "|T|T|T|T|T|" }
30
31        context.HL()
32
33            NC() bold("feature")
34            NC()
35            NC() bold("description")
36            NC() bold("value")
37            NC() bold("internal")
38            NC() NR()
39
40        context.HL()
41
42            local usedfeatures = fonts.handlers.otf.statistics.usedfeatures
43            local features     = fonts.handlers.otf.tables.features
44            local descriptions = fonts.handlers.otf.features.descriptions
45
46            for feature, keys in sortedhash(usedfeatures) do
47             -- if list.all or (list.otf and rawget(features,feature)) or (list.extra and rawget(descriptions,feature)) then
48                    local done = false
49                    for k, v in sortedhash(keys) do
50                        if done then
51                            NC()
52                            NC()
53                            NC()
54                        elseif rawget(descriptions,feature) then
55                            NC() context(feature)
56                            NC() context("+") -- extra
57                            NC() context.escaped(descriptions[feature])
58                            done = true
59                        elseif rawget(features,feature) then
60                            NC() context(feature)
61                            NC()              -- otf
62                            NC() context.escaped(features[feature])
63                            done = true
64                        else
65                            NC() context(feature)
66                            NC() context("-") -- unknown
67                            NC()
68                            done = true
69                        end
70                        NC() context(k)
71                        NC() context(tostring(v))
72                        NC() NR()
73                    end
74             -- end
75            end
76
77        context.HL()
78
79    context.stoptabulate()
80
81end
82
83local function collectkerns(tfmdata,feature)
84    local combinations = { }
85    local resources    = tfmdata.resources
86    local characters   = tfmdata.characters
87    local sequences    = resources.sequences
88    local lookuphash   = resources.lookuphash
89    local feature      = feature or "kern"
90    if sequences then
91        for i=1,#sequences do
92            local sequence = sequences[i]
93            if sequence.features and sequence.features[feature] then
94                local steps = sequence.steps
95                for i=1,#steps do
96                    local step   = steps[i]
97                    local format = step.format
98                    for unicode, hash in table.sortedhash(step.coverage) do
99                        local kerns = combinations[unicode]
100                        if not kerns then
101                            kerns = { }
102                            combinations[unicode] = kerns
103                        end
104                        for otherunicode, kern in table.sortedhash(hash) do
105                            if format == "pair" then
106                                local f = kern[1]
107                                local s = kern[2]
108                                if f then
109                                    if s then
110                                        -- todo
111                                    else
112                                        if not kerns[otherunicode] and f[3] ~= 0 then
113                                            kerns[otherunicode] = f[3]
114                                        end
115                                    end
116                                elseif s then
117                                    -- todo
118                                end
119                            elseif format == "kern" then
120                                if not kerns[otherunicode] and kern ~= 0 then
121                                    kerns[otherunicode] = kern
122                                end
123                            end
124                        end
125                    end
126                end
127            end
128        end
129    end
130
131    return combinations
132end
133
134local showkernpair = context.showkernpair
135
136function moduledata.fonts.features.showbasekerns(specification)
137    -- assumes that the font is loaded in base mode
138    specification = interfaces.checkedspecification(specification)
139    local id, cs  = fonts.definers.internal(specification,"<module:fonts:features:font>")
140    local tfmdata = fonts.hashes.identifiers[id]
141    local done    = false
142    for unicode, character in sortedhash(tfmdata.characters) do
143        local kerns = character.kerns
144        if kerns then
145            context.par()
146            for othercode, kern in sortedhash(kerns) do
147                showkernpair(unicode,kern,othercode)
148            end
149            context.par()
150            done = true
151        end
152    end
153    if not done then
154        context("no kern pairs found")
155        context.par()
156    end
157end
158
159function moduledata.fonts.features.showallkerns(specification)
160    specification    = interfaces.checkedspecification(specification)
161    local id, cs     = fonts.definers.internal(specification,"<module:fonts:features:font>")
162    local tfmdata    = fonts.hashes.identifiers[id]
163    local allkerns   = collectkerns(tfmdata)
164    local characters = tfmdata.characters
165    local hfactor    = tfmdata.parameters.hfactor
166    if next(allkerns) then
167        for first, pairs in sortedhash(allkerns) do
168            context.par()
169            for second, kern in sortedhash(pairs) do
170             -- local kerns = characters[first].kerns
171             -- if not kerns and pairs[second] then
172             --     -- weird
173             -- end
174                showkernpair(first,kern*hfactor,second)
175            end
176            context.par()
177        end
178    else
179        context("no kern pairs found")
180        context.par()
181    end
182end
183
184function moduledata.fonts.features.showfeatureset(specification)
185    specification = interfaces.checkedspecification(specification)
186    local name = specification[c_name]
187    if name then
188        local s = fonts.specifiers.contextsetups[name]
189        if s then
190            local t = table.copy(s)
191            t.number = nil
192            if t and next(t) then
193                context.starttabulate { "|T|T|" }
194                    for k, v in sortedhash(t) do
195                       NC() context(k) NC() context(v == true and v_yes or v == false and v_no or tostring(v)) NC() NR()
196                    end
197                context.stoptabulate()
198            end
199        end
200    end
201end
202
203-- The next one looks a bit like the collector in font-oup.lua.
204
205local function collectligatures(tfmdata)
206    local sequences = tfmdata.resources.sequences
207
208    if not sequences then
209        return
210    end
211
212    -- Mostly the same as s-fonts-tables so we should make a helper.
213
214    local series = { }
215    local stack  = { }
216    local max    = 0
217
218    local function add(v)
219        local n = #stack
220        if n > max then
221            max = n
222        end
223        series[#series+1] = { v, unpack(stack) }
224    end
225
226    local function make(tree)
227        for k, v in sortedhash(tree) do
228            if k == "ligature" then
229                add(v)
230            elseif tonumber(v) then
231                insert(stack,k)
232                add(v)
233                remove(stack)
234            else
235                insert(stack,k)
236                make(v)
237                remove(stack)
238            end
239        end
240    end
241
242    for i=1,#sequences do
243        local sequence = sequences[i]
244        if sequence.type == "gsub_ligature" then
245            local steps = sequence.steps
246            for i=1,#steps do
247                local step     = steps[i]
248                local coverage = step.coverage
249                if coverage then
250                    make(coverage)
251                end
252            end
253        end
254    end
255
256    return series, max
257end
258
259function moduledata.fonts.features.showallligatures(specification)
260    specification      = interfaces.checkedspecification(specification)
261    local id, cs       = fonts.definers.internal(specification,"<module:fonts:features:font>")
262    local tfmdata      = fonts.hashes.identifiers[id]
263    local allligatures,
264          max          = collectligatures(tfmdata)
265    local characters   = tfmdata.characters
266    local descriptions = tfmdata.descriptions
267    if #allligatures > 0 then
268        context.starttabulate { "|T|" .. string.rep("|",max) .. "|T|T|" }
269        for i=1,#allligatures do
270            local s = allligatures[i]
271            local n = #s
272            local u = s[1]
273            local c = characters[u]
274            local d = descriptions[u]
275            NC()
276            context("%U",u)
277            NC()
278            context("\\setfontid%i\\relax",id)
279            context.char(u)
280            NC()
281            context("\\setfontid%i\\relax",id)
282            for i=2,n do
283                context.char(s[i])
284                NC()
285            end
286            for i=n+1,max do
287                NC()
288            end
289            context(d.name)
290            NC()
291            context(c.tounicode)
292            NC()
293            NR()
294        end
295        context.stoptabulate()
296    else
297        context("no ligatures found")
298        context.par()
299    end
300end
301