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)
262print(nuts.tonode(c))
263 if char then
264 showchar(char,id)
265 elseif id == dir_code or (id == par_code and startofpar(c)) then
266 context("[%s]",getdirection(c) or "?")
267 elseif id == disc_code then
268 local pre, post, replace = getdisc(c)
269 if pre or post or replace then
270 context("[")
271 context[space]()
272 showdisc(c,pre,"pre")
273 showdisc(c,post,"post")
274 showdisc(c,replace,"replace")
275 context[space]()
276 context("]")
277 else
278 context("[disc]")
279 end
280 else
281 context("[%s]",nodecodes[id])
282 end
283 c = getnext(c)
284 end
285end
286
287function step_tracers.messages(i,command,split)
288 local list = messages[i]
289 if list then
290 for i=1,#list do
291 local l = list[i]
292 if not command then
293 context("(%s)",l)
294 elseif split then
295 local a, b = match(l,"^(.-)%s*:%s*(.*)$")
296 context[command](a or l or "",b or "")
297 else
298 context[command](l)
299 end
300 end
301 end
302end
303
304
305
306function step_tracers.check(head)
307 if collecting then
308 step_tracers.reset()
309 local n = copy_node_list(head)
310 freeze(n,"check")
311 injections.keepcounts()
312 local l = injections.handler(n,"trace")
313 if l then
314 n = l
315 end
316 protectglyphs(n)
317 collection[1] = n
318 end
319end
320
321function step_tracers.register(head)
322 if collecting then
323 local nc = #collection+1
324 if messages[nc] then
325 local n = copy_node_list(head)
326 freeze(n,"register")
327 injections.keepcounts()
328 local l = injections.handler(n,"trace")
329 if l then
330 n = l
331 end
332 protectglyphs(n)
333 collection[nc] = n
334 end
335 end
336end
337
338function step_tracers.message(str,...)
339 str = formatter(str,...)
340 if collecting then
341 local n = #collection + 1
342 local m = messages[n]
343 if not m then m = { } messages[n] = m end
344 m[#m+1] = str
345 end
346 return str
347end
348
349
350
351local threshold = 65536
352
353local function toutf(list,result,nofresult,stopcriterium,nostrip)
354 if list then
355 for n, id in nextnode, tonut(list) do
356 if id == glyph_code then
357 local c, f = isglyph(n)
358 if c > 0 then
359 local fc = fontcharacters[f]
360 if fc then
361 local fcc = fc[c]
362 if fcc then
363 local u = fcc.unicode
364 if not u then
365 nofresult = nofresult + 1
366 result[nofresult] = utfchar(c)
367 elseif type(u) == "table" then
368 for i=1,#u do
369 nofresult = nofresult + 1
370 result[nofresult] = utfchar(u[i])
371 end
372 else
373 nofresult = nofresult + 1
374 result[nofresult] = utfchar(u)
375 end
376 else
377 nofresult = nofresult + 1
378 result[nofresult] = utfchar(c)
379 end
380 else
381 nofresult = nofresult + 1
382 result[nofresult] = f_unicode(c)
383 end
384 else
385 nofresult = nofresult + 1
386 result[nofresult] = f_badcode(c)
387 end
388 elseif id == disc_code then
389 local replace = getreplace(n)
390 result, nofresult = toutf(replace,result,nofresult,false,true)
391 elseif id == hlist_code or id == vlist_code then
392
393
394
395
396 result, nofresult = toutf(getlist(n),result,nofresult,false,true)
397 elseif id == glue_code then
398 if nofresult > 0 and result[nofresult] ~= " " and getwidth(n) > threshold then
399 nofresult = nofresult + 1
400 result[nofresult] = " "
401 end
402 elseif id == kern_code then
403 if nofresult > 0 and result[nofresult] ~= " " and getkern(n) > threshold then
404 nofresult = nofresult + 1
405 result[nofresult] = " "
406 end
407 end
408 if n == stopcriterium then
409 break
410 end
411 end
412 end
413 if not nostrip and nofresult > 0 and result[nofresult] == " " then
414 result[nofresult] = nil
415 nofresult = nofresult - 1
416 end
417 return result, nofresult
418end
419
420function nodes.toutf(list,stopcriterium)
421 local result, nofresult = toutf(list,{},0,stopcriterium)
422 return concat(result)
423end
424 |