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