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
20
21local register = fonts.collections.register
22local checkenabled = fonts.collections.checkenabled
23local newprivateslot = fonts.helpers.newprivateslot
24
25local currentfont = font.current
26
27
28
29
30local makesetups = formatters["box:%s:%S"]
31
32local a_exportstatus <const> = attributes.private('exportstatus')
33
34local function makeglyphbox(char,spec)
35 token.expand_macro("makeglyphbox",true,spec.setups or tostring(char))
36 local b = tex.takebox("glyphbox")
37
38 spec.code = {
39 width = b.width,
40 height = b.height,
41 depth = b.depth,
42 objnum = tex.boxresources.save(b,nil,nil,true),
43 }
44end
45
46local function setboxglyphs(category,fontid,unicode,specification)
47 local box = specification.code
48 if unicode and box then
49 local tfmdata = fontdata[fontid]
50 local characters = tfmdata.characters
51 local newdata = {
52 width = box.width or 0,
53 height = box.height or 0,
54 depth = box.depth or 0,
55 unicode = specification.tounicode or unicode,
56 }
57
58 characters[unicode] = newdata
59
60 fonts.dropins.swapone("box",tfmdata,specification,unicode)
61
62 addcharacters(fontid, { characters = { [unicode] = newdata } })
63 return fontid, unicode
64 else
65 logs.report("box glyph", "invalid glyph box for %C",unicode)
66 end
67end
68
69local function setboxglyph(specification)
70 if specification then
71 local name = specification.name
72 local unicode = specification.unicode
73 local font = currentfont()
74 if not unicode and name then
75 unicode = newprivateslot(name)
76 specification.unicode = unicode
77 end
78 if unicode then
79 if not specification.setups then
80 specification.setups = name
81 end
82
83 register(font,unicode,function(font,private)
84 makeglyphbox(unicode,specification)
85 return setboxglyphs(category,font,unicode,specification)
86 end)
87 checkenabled()
88 end
89 end
90end
91
92local function setboxdirectly(font,unicode,box,expose)
93 if box then
94 local tfmdata = fontdata[font]
95 local glyphboxes = (tfmdata.glyphboxes or 0) + 1
96 tfmdata.glyphboxes = glyphboxes
97 local private = newprivateslot(formatters["BG:%05X"](glyphboxes))
98
99 local newdata = {
100 width = box.width or 0,
101 height = box.height or 0,
102 depth = box.depth or 0,
103 unicode = unicode,
104 objnum = tex.boxresources.save(box,nil,nil,true),
105 expose = expose,
106 }
107 local specification = {
108 code = newdata
109 }
110 tfmdata.characters[private] = newdata
111 addcharacters(font, { characters = { [private] = newdata } })
112 fonts.dropins.swapone("box",tfmdata,specification,private)
113 checkenabled()
114 return private
115 end
116end
117
118fonts.helpers.setboxdirectly = setboxdirectly
119
120local boxes = table.setmetatableindex("table")
121
122function fonts.helpers.registerglyphbox(specification)
123 local category = specification.category
124 local whatever = specification.unicode or specification.name
125 if category and whatever then
126 boxes[category][whatever] = { action = makeglyphbox }
127 end
128end
129
130local function initializebox(tfmdata,kind,value)
131 local boxes = boxes[value]
132 if value then
133 local font = tfmdata.properties.id
134
135 for char, spec in sortedhash(boxes) do
136 spec.setups = makesetups(value,char)
137 local char = type(char) == "string" and newprivateslot(name) or char
138
139 register(font,char,function(font,char)
140 local action = spec.action
141 if type(action) == "function" then
142 action(char,spec)
143 end
144 return setboxglyphs(value,font,char,spec)
145 end)
146 end
147 checkenabled()
148 end
149end
150
151fonts.helpers.setboxglyphs = setboxglyphs
152fonts.helpers.setboxglyph = setboxglyph
153
154interfaces.implement {
155 name = "registerboxglyph",
156 public = true,
157 protected = true,
158 actions = fonts.helpers.registerglyphbox,
159 arguments = { {
160 { "category" },
161 { "unicode", "integer" },
162 { "tounicode", "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 { "tounicode", "integer" },
176 { "name" },
177 { "*" }
178 } },
179}
180
181fonts.handlers.otf.features.register {
182 name = "box",
183 description = "box glyphs",
184 manipulators = {
185 base = initializebox,
186 node = initializebox,
187 }
188}
189
190
191
192
193
194
195local fontcallbacks = fonts.callbacks or { }
196fonts.callbacks = fontcallbacks
197
198function fontcallbacks.devirtualize(chardata,f,c)
199 if chardata.commands then
200 local h = node.hpack(nodes.pool.glyph(f,c))
201 local p = setboxdirectly(f,c,h)
202 chardata.oldcommands = chardata.commands
203 chardata.commands = { { "char", p } }
204 chardata.callback = false
205 end
206end
207
208local function processcallback(f,c)
209 local characters = fontchars[f]
210 local chardata = characters[c]
211 if chardata then
212 local callback = chardata.callback
213 if callback then
214 local action = type(callback) == "function" and callback or fontcallbacks[callback]
215 if action then
216 action(chardata,f,c,characters)
217 end
218 end
219 end
220end
221
222do
223
224 local unpack = unpack
225
226 local nuts = nodes.nuts
227 local new_glyph = nuts.pool.glyph
228 local new_kern = nuts.pool.kern
229 local tonode = nuts.tonode
230 local hpack = nuts.hpack
231 local setlink = nuts.setlink
232
233 function fontcallbacks.compile(chardata,fnt,chr,characters)
234
235 local c = chardata.commands
236 if c then
237 local t = { }
238 for i=1,#c do
239 local ci = c[i]
240 local c1 = ci[1]
241 local c2 = ci[2]
242 if c1 == "char" then
243 t[i] = new_glyph(fnt,c2)
244 elseif c1 == "slot" then
245 t[i] = new_glyph(c2 == 0 and fnt or c2,ci[3])
246 elseif c1 == "left" then
247 t[i] = new_kern(-c2)
248 elseif c1 == "right" then
249 t[i] = new_kern(c2)
250 elseif c1 == "offset" then
251 local c4 = ci[4]
252 if c4 then
253 t[i] = new_glyph(fnt or 0,c4)
254
255 local c3 = ci[3]
256 local c5 = ci[5]
257 local c6 = ci[6]
258 local c7 = ci[7]
259 if c2 or c3 then
260 nuts.setoffsets(t[i],c2 or 0,c3 or 0)
261 end
262
263
264
265 if c7 then
266 nuts.setslant(t[i],c7*1000)
267 end
268 end
269 else
270 print("fcc todo: " .. c1)
271 goto done
272 end
273 end
274 local box = tonode(hpack(setlink(unpack(t))))
275 local prv = setboxdirectly(fnt,chr,box,true)
276 chardata.commands = { { "char", prv } }
277 characters[prv].unicode = chardata.unicode
278 end
279 ::done::
280 chardata.callback = false
281 end
282
283end
284
285callbacks.register("process_character",processcallback,"apply an action to a character in a font")
286
287fontcallbacks.callback = processcallback
288 |