font-tex.lmt /size: 7038 b    last modification: 2024-01-16 09:02
1if not modules then modules = { } end modules ['font-mpf'] = {
2    version   = 1.001,
3    comment   = "companion to font-ini.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
9local formatters     = string.formatters
10local sortedhash     = table.sortedhash
11
12local addcharacters  = fonts.constructors.addcharacters
13local fontdata       = fonts.hashes.identifiers
14local fontchars      = fonts.hashes.characters
15
16local otf            = fonts.handlers.otf
17local otfregister    = otf.features.register
18
19----- getshapes      = fonts.dropins.getshapes
20
21local register       = fonts.collections.register
22local checkenabled   = fonts.collections.checkenabled
23local newprivateslot = fonts.helpers.newprivateslot
24
25local currentfont    = font.current
26
27-- hm, seems to gobble the first \char
28
29local makesetups = formatters["box:%s:%S"]
30
31local a_exportstatus = attributes.private('exportstatus')
32
33local function makeglyphbox(char,spec)
34    token.expand_macro("makeglyphbox",true,spec.setups or tostring(char))
35    local b = tex.takebox("glyphbox")
36-- b[a_exportstatus] = 0
37    spec.code = {
38        width  = b.width,
39        height = b.height,
40        depth  = b.depth,
41        objnum = tex.boxresources.save(b,nil,nil,true),
42    }
43end
44
45local function setboxglyphs(category,fontid,unicode,specification)
46    local box = specification.code
47    if unicode and box then
48        local tfmdata    = fontdata[fontid]
49        local characters = tfmdata.characters
50        local newdata    = {
51            width   = box.width  or 0,
52            height  = box.height or 0,
53            depth   = box.depth  or 0,
54            unicode = unicode,
55        }
56        -- pass dimensions to lua
57        characters[unicode] = newdata
58        -- this also adapts newdata (add commands)
59        fonts.dropins.swapone("box",tfmdata,specification,unicode)
60        -- pass dimensions to tex
61        addcharacters(fontid, { characters = { [unicode] = newdata } })
62        return fontid, unicode
63    else
64        logs.report("box glyph", "invalid glyph box for %C",unicode)
65    end
66end
67
68local function setboxglyph(specification)
69    if specification then
70        local name    = specification.name
71        local unicode = specification.unicode
72        local font    = currentfont()
73        if not unicode and name then
74            unicode = newprivateslot(name)
75            specification.unicode = unicode
76        end
77        if unicode then
78            if not specification.setups then
79                specification.setups = name
80            end
81            -- we can actually delay font if needed
82            register(font,unicode,function(font,private)
83                makeglyphbox(unicode,specification)
84                return setboxglyphs(category,font,unicode,specification)
85            end)
86            checkenabled()
87        end
88    end
89end
90
91local function setboxdirectly(font,unicode,box,expose) -- hash based on wd/ht/dp
92    if box then
93        local tfmdata      = fontdata[font]
94        local glyphboxes   = (tfmdata.glyphboxes or 0) + 1
95        tfmdata.glyphboxes = glyphboxes
96        local private      = newprivateslot(formatters["BG:%05X"](glyphboxes))
97-- box[a_exportstatus] = 0
98        local newdata      = {
99            width   = box.width  or 0,
100            height  = box.height or 0,
101            depth   = box.depth  or 0,
102            unicode = unicode,
103            objnum  = tex.boxresources.save(box,nil,nil,true),
104            expose  = expose, -- permit color etc
105        }
106        local specification = {
107            code = newdata
108        }
109        tfmdata.characters[private] = newdata
110        addcharacters(font, { characters = { [private] = newdata } })
111        fonts.dropins.swapone("box",tfmdata,specification,private)
112        checkenabled()
113        return private
114    end
115end
116
117fonts.helpers.setboxdirectly = setboxdirectly
118
119local boxes = table.setmetatableindex("table")
120
121function fonts.helpers.registerglyphbox(specification)
122    local category = specification.category
123    local whatever = specification.unicode or specification.name
124    if category and whatever then
125        boxes[category][whatever] = { action = makeglyphbox } -- always
126    end
127end
128
129local function initializebox(tfmdata,kind,value)
130    local boxes = boxes[value]
131    if value then
132        local font = tfmdata.properties.id
133        -- just preregister the list
134        for char, spec in sortedhash(boxes) do
135            spec.setups = makesetups(value,char)
136            if type(char) == "string" then
137                char = newprivateslot(name)
138            end
139            -- check if spec.code
140            register(font,char,function(font,char)
141                local action = spec.action
142                if type(action) == "function" then
143                    action(char,spec)
144                end
145                return setboxglyphs(value,font,char,spec)
146            end)
147        end
148        checkenabled()
149    end
150end
151
152fonts.helpers.setboxglyphs = setboxglyphs
153fonts.helpers.setboxglyph  = setboxglyph
154
155interfaces.implement {
156    name      = "registerboxglyph", -- combine with next one
157    public    = true,
158    protected = true,
159    actions   = fonts.helpers.registerglyphbox,
160    arguments = { {
161        { "category" },
162        { "unicode", "integer" },
163        { "name" },
164    } },
165}
166
167interfaces.implement {
168    name      = "setboxglyph",
169    public    = true,
170    protected = true,
171    actions   = setboxglyph,
172    arguments = { {
173        { "category" },
174        { "unicode", "integer" },
175        { "name" },
176        { "*" }
177    } },
178}
179
180fonts.handlers.otf.features.register {
181    name         = "box",
182    description  = "box glyphs",
183    manipulators = {
184        base = initializebox,
185        node = initializebox,
186    }
187}
188
189-- fonts.helpers.registerboxglyph { category = "demo", unicode = 103 }
190-- fonts.helpers.registerboxglyph { category = "demo", unicode = 104 }
191-- fonts.helpers.registerboxglyph { category = "demo", unicode = 105 }
192-- fonts.helpers.registerboxglyph { category = "demo", unicode = 106 }
193
194local fontcallbacks = fonts.callbacks or { }
195fonts.callbacks     = fontcallbacks
196
197function fontcallbacks.devirtualize(chardata,f,c)
198    if chardata.commands then
199        local h = node.hpack(nodes.pool.glyph(f,c))
200        local p = setboxdirectly(f,c,h)
201        chardata.oldcommands = chardata.commands
202        chardata.commands = { { "char", p } }
203        chardata.callback = false
204    end
205end
206
207local function processcallback(f,c)
208    local characters = fontchars[f]
209    local chardata   = characters[c]
210    if chardata then
211        local callback = chardata.callback
212        if callback then
213            local action = type(callback) == "function" and callback or fontcallbacks[callback]
214            if action then
215                action(chardata,f,c)
216            end
217        end
218    end
219end
220
221callbacks.register("process_character",processcallback,"apply an action to a character in a font")
222
223fontcallbacks.callback = processcallback
224