1if not modules then modules = { } end modules ['font-nod'] = {
2 version = 1.001,
3 optimize = true,
4 comment = "companion to font-ini.mkiv",
5 author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
6 copyright = "PRAGMA ADE / ConTeXt Development Team",
7 license = "see context related readme files"
8}
9
10local utfchar = utf.char
11local concat, fastcopy = table.concat, table.fastcopy
12local match, rep = string.match, string.rep
13
14fonts = fonts or { }
15nodes = nodes or { }
16
17local fonts = fonts
18local nodes = nodes
19local context = context
20
21local tracers = nodes.tracers or { }
22nodes.tracers = tracers
23
24local tasks = nodes.tasks or { }
25nodes.tasks = tasks
26
27local handlers = nodes.handlers or { }
28nodes.handlers = handlers
29
30local nuts = nodes.nuts
31local tonut = nuts.tonut
32local tonode = nuts.tonode
33
34local injections = nodes.injections or { }
35nodes.injections = injections
36
37local step_tracers = tracers.steppers or { }
38tracers.steppers = step_tracers
39
40local nodecodes = nodes.nodecodes
41
42local glyph_code = nodecodes.glyph
43local hlist_code = nodecodes.hlist
44local vlist_code = nodecodes.vlist
45local disc_code = nodecodes.disc
46local glue_code = nodecodes.glue
47local kern_code = nodecodes.kern
48local dir_code = nodecodes.dir
49local par_code = nodecodes.par
50
51local getnext = nuts.getnext
52local getprev = nuts.getprev
53local getid = nuts.getid
54local getfont = nuts.getfont
55local getsubtype = nuts.getsubtype
56local getlist = nuts.getlist
57local getdisc = nuts.getdisc
58local getreplace = nuts.getreplace
59local isglyph = nuts.isglyph
60local getkern = nuts.getkern
61local getdirection = nuts.getdirection
62local getwidth = nuts.getwidth
63
64local setbox = nuts.setbox
65local setchar = nuts.setchar
66local setsubtype = nuts.setsubtype
67
68local copy_node_list = nuts.copylist
69local hpacknodelist = nuts.hpack
70local flushnodelist = nuts.flushlist
71local protectglyphs = nuts.protectglyphs
72local startofpar = nuts.startofpar
73
74local nextnode = nuts.traversers.node
75local nextglyph = nuts.traversers.glyph
76
77local nodepool = nuts.pool
78local new_glyph = nodepool.glyph
79
80local formatters = string.formatters
81local formatter = string.formatter
82
83local hashes = fonts.hashes
84
85local fontidentifiers = hashes.identifiers
86local fontdescriptions = hashes.descriptions
87local fontcharacters = hashes.characters
88local fontproperties = hashes.properties
89local fontparameters = hashes.parameters
90
91local properties = nodes.properties.data
92
93local function freeze(h,where)
94 for n in nextnode, h do
95 local p = properties[n]
96 if p then
97 local i = p.injections if i then p.injections = fastcopy(i) end
98
99
100
101
102 end
103 end
104end
105
106local f_unicode = formatters["%U"]
107local f_badcode = formatters["{%i}"]
108
109local stack = { }
110
111function tracers.start(tag)
112 stack[#stack+1] = tag
113 local tracer = tracers[tag]
114 if tracer and tracer.start then
115 tracer.start()
116 end
117end
118function tracers.stop()
119 local tracer = stack[#stack]
120 if tracer and tracer.stop then
121 tracer.stop()
122 end
123 stack[#stack] = nil
124end
125
126
127
128local collection, collecting, messages = { }, false, { }
129
130function step_tracers.start()
131 collecting = true
132end
133
134function step_tracers.stop()
135 collecting = false
136end
137
138function step_tracers.reset()
139 for i=1,#collection do
140 local c = collection[i]
141 if c then
142 flushnodelist(c)
143 end
144 end
145 collection, messages = { }, { }
146end
147
148function step_tracers.nofsteps()
149 return context(#collection)
150end
151
152function step_tracers.glyphs(n,i)
153 local c = collection[i]
154 if c then
155 local c = copy_node_list(c)
156 local b = hpacknodelist(c)
157 setbox(n,b)
158 end
159end
160
161function step_tracers.features()
162 local f = collection[1]
163 for n, char, font in nextglyph, f do
164 local tfmdata = fontidentifiers[font]
165 local features = tfmdata.resources.features
166 local result_1 = { }
167 local result_2 = { }
168 local gpos = features and features.gpos or { }
169 local gsub = features and features.gsub or { }
170 for feature, value in table.sortedhash(tfmdata.shared.features) do
171 if feature == "number" or feature == "features" then
172 value = false
173 elseif type(value) == "boolean" then
174 if value then
175 value = "yes"
176 else
177 value = false
178 end
179 else
180
181 end
182 if value then
183 if gpos[feature] or gsub[feature] or feature == "language" or feature == "script" then
184 result_1[#result_1+1] = formatters["%s=%s"](feature,value)
185 else
186 result_2[#result_2+1] = formatters["%s=%s"](feature,value)
187 end
188 end
189 end
190 if #result_1 > 0 then
191 context("{\\bf[basic:} %, t{\\bf]} ",result_1)
192 else
193 context("{\\bf[}no basic features{\\bf]} ")
194 end
195 if #result_2 > 0 then
196 context("{\\bf[extra:} %, t{\\bf]}",result_2)
197 else
198 context("{\\bf[}no extra features{\\bf]}")
199 end
200 return
201 end
202end
203
204function tracers.fontchar(font,char)
205 local n = new_glyph(font,char)
206 setsubtype(n,256)
207 context(tonode(n))
208end
209
210function step_tracers.font(command)
211 local c = collection[1]
212 for n, char, font in nextglyph, c do
213 local name = file.basename(fontproperties[font].filename or "unknown")
214 local size = fontparameters[font].size or 0
215 if command then
216 context[command](font,name,size)
217 else
218 context("[%s: %s @ %p]",font,name,size)
219 end
220 return
221 end
222end
223
224local colors = {
225 pre = { "darkred" },
226 post = { "darkgreen" },
227 replace = { "darkblue" },
228}
229
230function step_tracers.codes(i,command,space)
231 local c = collection[i]
232
233 local function showchar(c,f)
234 if command then
235 local d = fontdescriptions[f]
236 local d = d and d[c]
237 context[command](f,c,d and d.class or "")
238 else
239 context("[%s:U+%X]",f,c)
240 end
241 end
242
243 local function showdisc(d,w,what)
244 if w then
245 context.startcolor(colors[what])
246 context("%s:",what)
247 for c, id in nextnode, w do
248 if id == glyph_code then
249 local c, f = isglyph(c)
250 showchar(c,f)
251 else
252 context("[%s]",nodecodes[id])
253 end
254 end
255 context[space]()
256 context.stopcolor()
257 end
258 end
259
260 while c do
261 local char, id = isglyph(c)
262 if char then
263 showchar(char,id)
264 elseif id == dir_code or (id == par_code and startofpar(c)) then
265 context("[%s]",getdirection(c) or "?")
266 elseif id == disc_code then
267 local pre, post, replace = getdisc(c)
268 if pre or post or replace then
269 context("[")
270 context[space]()
271 showdisc(c,pre,"pre")
272 showdisc(c,post,"post")
273 showdisc(c,replace,"replace")
274 context[space]()
275 context("]")
276 else
277 context("[disc]")
278 end
279 else
280 context("[%s]",nodecodes[id])
281 end
282 c = getnext(c)
283 end
284end
285
286function step_tracers.messages(i,command,split)
287 local list = messages[i]
288 if list then
289 for i=1,#list do
290 local l = list[i]
291 if not command then
292 context("(%s)",l)
293 elseif split then
294 local a, b = match(l,"^(.-)%s*:%s*(.*)$")
295 context[command](a or l or "",b or "")
296 else
297 context[command](l)
298 end
299 end
300 end
301end
302
303
304
305function step_tracers.check(head)
306 if collecting then
307 step_tracers.reset()
308 local n = copy_node_list(head)
309 freeze(n,"check")
310 injections.keepcounts()
311 local l = injections.handler(n,"trace")
312 if l then
313 n = l
314 end
315 protectglyphs(n)
316 collection[1] = n
317 end
318end
319
320function step_tracers.register(head)
321 if collecting then
322 local nc = #collection+1
323 if messages[nc] then
324 local n = copy_node_list(head)
325 freeze(n,"register")
326 injections.keepcounts()
327 local l = injections.handler(n,"trace")
328 if l then
329 n = l
330 end
331 protectglyphs(n)
332 collection[nc] = n
333 end
334 end
335end
336
337function step_tracers.message(str,...)
338 str = formatter(str,...)
339 if collecting then
340 local n = #collection + 1
341 local m = messages[n]
342 if not m then m = { } messages[n] = m end
343 m[#m+1] = str
344 end
345 return str
346end
347
348
349
350local threshold = 65536
351
352local function toutf(list,result,nofresult,stopcriterium,nostrip)
353 if list then
354 for n, id in nextnode, tonut(list) do
355 if id == glyph_code then
356 local c, f = isglyph(n)
357 if c > 0 then
358 local fc = fontcharacters[f]
359 if fc then
360 local fcc = fc[c]
361 if fcc then
362 local u = fcc.unicode
363 if not u then
364 nofresult = nofresult + 1
365 result[nofresult] = utfchar(c)
366 elseif type(u) == "table" then
367 for i=1,#u do
368 nofresult = nofresult + 1
369 result[nofresult] = utfchar(u[i])
370 end
371 else
372 nofresult = nofresult + 1
373 result[nofresult] = utfchar(u)
374 end
375 else
376 nofresult = nofresult + 1
377 result[nofresult] = utfchar(c)
378 end
379 else
380 nofresult = nofresult + 1
381 result[nofresult] = f_unicode(c)
382 end
383 else
384 nofresult = nofresult + 1
385 result[nofresult] = f_badcode(c)
386 end
387 elseif id == disc_code then
388 local replace = getreplace(n)
389 result, nofresult = toutf(replace,result,nofresult,false,true)
390 elseif id == hlist_code or id == vlist_code then
391
392
393
394
395 result, nofresult = toutf(getlist(n),result,nofresult,false,true)
396 elseif id == glue_code then
397 if nofresult > 0 and result[nofresult] ~= " " and getwidth(n) > threshold then
398 nofresult = nofresult + 1
399 result[nofresult] = " "
400 end
401 elseif id == kern_code then
402 if nofresult > 0 and result[nofresult] ~= " " and getkern(n) > threshold then
403 nofresult = nofresult + 1
404 result[nofresult] = " "
405 end
406 end
407 if n == stopcriterium then
408 break
409 end
410 end
411 end
412 if not nostrip and nofresult > 0 and result[nofresult] == " " then
413 result[nofresult] = nil
414 nofresult = nofresult - 1
415 end
416 return result, nofresult
417end
418
419function nodes.toutf(list,stopcriterium)
420 local result, nofresult = toutf(list,{},0,stopcriterium)
421 return concat(result)
422end
423 |