good-ctx.lua /size: 8995 b    last modification: 2021-10-28 13:50
1if not modules then modules = { } end modules ['good-ctx'] = {
2    version   = 1.000,
3    comment   = "companion to font-lib.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
9-- depends on ctx
10
11local type, next, tonumber = type, next, tonumber
12local find, splitup = string.find, string.splitup
13
14local fonts              = fonts
15local nodes              = nodes
16local attributes         = attributes
17
18----- trace_goodies      = false  trackers.register("fonts.goodies", function(v) trace_goodies = v end)
19----- report_goodies     = logs.reporter("fonts","goodies")
20
21local allocate           = utilities.storage.allocate
22local setmetatableindex  = table.setmetatableindex
23
24local implement          = interfaces.implement
25
26local registerotffeature = fonts.handlers.otf.features.register
27----- registerafmfeature = fonts.handlers.afm.features.register
28----- registertfmfeature = fonts.handlers.tfm.features.register
29
30local fontgoodies        = fonts.goodies or { }
31
32local nuts               = nodes.nuts
33local tonut              = nuts.tonut
34local getattr            = nuts.getattr
35local getprop            = nuts.getprop
36local nextglyph          = nuts.traversers.glyph
37
38-- colorschemes
39
40local colorschemes       = fontgoodies.colorschemes or allocate { }
41fontgoodies.colorschemes = colorschemes
42colorschemes.data        = colorschemes.data or { }
43
44local privatestoo        = true
45
46local function setcolorscheme(tfmdata,scheme)
47    if type(scheme) == "string" then
48        local goodies = tfmdata.goodies
49        -- todo : check for already defined in shared
50        if goodies then
51            local what
52            for i=1,#goodies do
53                -- last one counts
54                local g = goodies[i]
55                what = g.colorschemes and g.colorschemes[scheme] or what
56            end
57            if type(what) == "table" then
58                -- this is font bound but we can share them if needed
59                -- just as we could hash the conversions (per font)
60                local hash       = tfmdata.resources.unicodes
61                local reverse    = { }
62                local characters = tfmdata.characters
63                for i=1,#what do
64                    local w = what[i]
65                    local force = w.force
66                    local list  = w.list or w
67                    for j=1,#list do
68                        local name = list[j]
69                        local kind = type(name)
70                        if name == "*" then
71                            -- inefficient but only used for tracing anyway
72                            for _, unicode in next, hash do
73                                reverse[unicode] = i
74                            end
75                        elseif kind == "number" then
76                            reverse[name] = i
77                        elseif kind ~= "string" then
78                            -- ignore invalid entries
79                        elseif find(name,":",1,true) then
80                            local start, stop = splitup(name,":")
81                            start = tonumber(start)
82                            stop  = tonumber(stop)
83                            if start and stop then
84                                -- limited usage: we only deal with non reassigned
85                                -- maybe some day I'll also support the ones with a
86                                -- tounicode in this range
87                                if force then
88                                    for unicode=start,stop do
89                                        reverse[unicode] = i
90                                    end
91                                else
92                                    for unicode=start,stop do
93                                        if characters[unicode] then
94                                            reverse[unicode] = i
95                                        end
96                                    end
97                                end
98                            end
99                        else
100                            local unicode = hash[name]
101                            if unicode then
102                                reverse[unicode] = i
103                            end
104                        end
105                    end
106                end
107                if privatestoo then
108                    local privateoffset = fonts.constructors.privateoffset
109                    local descriptions  = tfmdata.descriptions
110                    for unicode, data in next, characters do
111                        if unicode >= privateoffset then
112                            if not reverse[unicode] then
113                                local d = descriptions[unicode]
114                                if d then
115                                    local u = d.unicode
116                                    if u then
117                                        local r = reverse[u] -- also catches tables
118                                        if r then
119                                            reverse[unicode] = r
120                                        end
121                                    end
122                                end
123                            end
124                        end
125                    end
126                end
127                tfmdata.properties.colorscheme = reverse
128                return
129            end
130        end
131    end
132    tfmdata.properties.colorscheme = false
133end
134
135local fontproperties = fonts.hashes.properties
136local a_colorscheme  = attributes.private('colorscheme')
137local setnodecolor   = nodes.tracers.colors.set
138local cache          = { } -- this could be a weak table
139
140setmetatableindex(cache,function(t,a)
141    local v = { }
142    setmetatableindex(v,function(t,c)
143        local v = "colorscheme:" .. a .. ":" .. c
144        t[c] = v
145        return v
146    end)
147    t[a]= v
148    return v
149end)
150
151function colorschemes.coloring(head)
152    local lastfont   = nil
153    local lastattr   = nil
154    local lastcache  = nil
155    local lastscheme = nil
156    for n, char, f in nextglyph, head do
157        local a = getattr(n,a_colorscheme)
158        if a then
159            if f ~= lastfont then
160                lastfont   = f
161                lastscheme = fontproperties[f].colorscheme
162                if not lastscheme then
163                    local p = getprop(n, "original")
164                    if p then
165                        lastfont   = p.font
166                        lastscheme = fontproperties[lastfont].colorscheme
167                    end
168                end
169            end
170            if a ~= lastattr then
171                lastattr  = a
172                lastcache = cache[a]
173            end
174            if lastscheme then
175                local sc = lastscheme[char]
176                if sc then
177                    -- todo: use new lmtx mechanism instead
178                    setnodecolor(n,lastcache[sc]) -- we could inline this one
179                end
180            end
181        end
182    end
183    return head
184end
185
186function colorschemes.enable()
187    nodes.tasks.enableaction("processors","fonts.goodies.colorschemes.coloring")
188    function colorschemes.enable() end
189end
190
191registerotffeature {
192    name        = "colorscheme",
193    description = "goodie color scheme",
194    initializers = {
195        base = setcolorscheme,
196        node = setcolorscheme,
197    }
198}
199
200-- kern hackery:
201--
202-- yes  : use goodies table
203-- auto : assume features to be set (often ccmp only)
204
205local function setkeepligatures(tfmdata)
206    if not tfmdata.properties.keptligatures then
207        local goodies = tfmdata.goodies
208        if goodies then
209            for i=1,#goodies do
210                local g = goodies[i]
211                local letterspacing = g.letterspacing
212                if letterspacing then
213                    local keptligatures = letterspacing.keptligatures
214                    if keptligatures then
215                        local unicodes = tfmdata.resources.unicodes -- so we accept names
216                        local hash = { }
217                        for k, v in next, keptligatures do
218                            local u = unicodes[k]
219                            if u then
220                                hash[u] = true
221                            else
222                                -- error: unknown name
223                            end
224                        end
225                        tfmdata.properties.keptligatures = hash
226                    end
227                end
228            end
229        end
230    end
231end
232
233registerotffeature {
234    name         = "keepligatures",
235    description  = "keep ligatures in letterspacing",
236    initializers = {
237        base = setkeepligatures,
238        node = setkeepligatures,
239    }
240}
241
242if implement then
243
244    implement {
245        name      = "enablefontcolorschemes",
246        onlyonce  = true,
247        actions   = colorschemes.enable,
248        overload  = true, -- for now, permits new font loader
249    }
250
251end
252