1if not modules then modules = { } end modules ['node-fin'] = {
2 version = 1.001,
3 optimize = true,
4 comment = "companion to node-fin.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 next, type, format = next, type, string.format
11local setmetatableindex = table.setmetatableindex
12
13local attributes, nodes, node = attributes, nodes, node
14
15local nuts = nodes.nuts
16local tonut = nodes.tonut
17
18local getnext = nuts.getnext
19local getid = nuts.getid
20local getlist = nuts.getlist
21local getleader = nuts.getleader
22local getattr = nuts.getattr
23local getattrs = nuts.getattrs
24local getwidth = nuts.getwidth
25local getwhd = nuts.getwhd
26local hasgeometry = nuts.hasgeometry
27local hasdimensions = nuts.hasdimensions
28local getbox = nuts.getbox
29
30local setlist = nuts.setlist
31local setleader = nuts.setleader
32
33local copy_node = nuts.copy
34local insertnodebefore = nuts.insertbefore
35local insertnodeafter = nuts.insertafter
36local appendaftertail = nuts.appendaftertail
37
38local nextnode = nuts.traversers.node
39local nextcontent = nuts.traversers.content
40
41local nodecodes = nodes.nodecodes
42local rulecodes = nodes.rulecodes
43
44local boxrule_code <const> = rulecodes.box
45local imagerule_code <const> = rulecodes.image
46local emptyrule_code <const> = rulecodes.empty
47local virtualrule_code <const> = rulecodes.virtual
48
49local container_code <const> = nodes.listcodes.container
50
51local glyph_code <const> = nodecodes.glyph
52local disc_code <const> = nodecodes.disc
53local glue_code <const> = nodecodes.glue
54local rule_code <const> = nodecodes.rule
55local hlist_code <const> = nodecodes.hlist
56local vlist_code <const> = nodecodes.vlist
57
58local texlists = tex.lists
59local texgetnest = tex.getnest
60
61local states = attributes.states
62local numbers = attributes.numbers
63
64local implement = interfaces.implement
65
66local starttiming = statistics.starttiming
67local stoptiming = statistics.stoptiming
68local loadstripped = utilities.lua.loadstripped
69local unsetvalue = attributes.unsetvalue
70
71
72
73function states.enabletriggering () triggering = true end
74function states.disabletriggering() triggering = false end
75
76implement { name = "enablestatetriggering", actions = states.enabletriggering }
77implement { name = "disablestatetriggering", actions = states.disabletriggering }
78
79nodes.plugindata = nil
80
81
82
83local template <const> = [[
84local plugin = nodes.plugindata
85local starttiming = statistics.starttiming
86local stoptiming = statistics.stoptiming
87local namespace = plugin.namespace
88local attribute = namespace.attribute or attributes.numbers[plugin.name]
89local processor = plugin.processor
90local initializer = plugin.initializer
91local resolver = plugin.resolver
92local finalizer = plugin.finalizer
93local flusher = plugin.flusher
94if not processor then
95 return function(head)
96 return head
97 end
98elseif initializer or finalizer or resolver then
99 return function(head)
100 starttiming(attributes)
101 local used, inheritance
102 if resolver then
103 inheritance = resolver()
104 end
105 if initializer then
106 initializer(namespace,attribute,head)
107 end
108 head = processor(namespace,attribute,head,inheritance)
109 if finalizer then
110 head, used = finalizer(namespace,attribute,head)
111 if used and flusher then
112 head = flusher(namespace,attribute,head,used)
113 end
114 end
115 stoptiming(attributes)
116 return head
117 end
118else
119 return function(head)
120 starttiming(attributes)
121 head = processor(namespace,attribute,head)
122 stoptiming(attributes)
123 return head
124 end
125end
126nodes.plugindata = nil
127]]
128
129function nodes.installattributehandler(plugin)
130 nodes.plugindata = plugin
131 return loadstripped(template)()
132end
133
134
135
136local nsdata, nsnone, nslistwise, nsforced, nsselector
137local current, current_selector = 0, 0
138local nsbegin, nsend, nsreset
139
140function states.initialize(namespace,attribute,head)
141 nsdata = namespace.data
142 nsnone = namespace.none
143 nsforced = namespace.forced
144 nsselector = namespace.selector
145 nslistwise = namespace.listwise
146 current = 0
147 current_selector = 0
148 nsstep = namespace.resolve_step
149 if nsstep then
150 nsreset = namespace.resolve_reset
151 nsbegin = namespace.resolve_begin
152 nsend = namespace.resolve_end
153 nspush = namespace.push
154 nspop = namespace.pop
155 end
156end
157
158function states.finalize(namespace,attribute,head)
159 if current > 0 and nsnone then
160 local id = getid(head)
161 if id == hlist_code or id == vlist_code then
162 local content = getlist(head)
163 if content then
164 appendaftertail(content,copy_node(nsnone))
165 end
166 else
167 appendaftertail(head,copy_node(nsnone))
168 end
169 return head, true
170 end
171 return head, false
172end
173
174
175
176local function process(attribute,head,inheritance,default)
177 local check = false
178 local leader = nil
179 for stack, id, subtype, content in nextcontent, head do
180 if id == glyph_code or id == disc_code then
181 check = true
182 elseif id == glue_code then
183 check = true
184 leader = content
185 elseif id == hlist_code or id == vlist_code then
186
187 local outer
188 if subtype == container_code or hasgeometry(stack) then
189 outer = getattr(stack,attribute)
190 if outer then
191 if default and outer == inheritance then
192 if current ~= default then
193 head = insertnodebefore(head,stack,copy_node(nsdata[default]))
194 current = default
195 end
196 elseif current ~= outer then
197 head = insertnodebefore(head,stack,copy_node(nsdata[outer]))
198 current = outer
199 end
200 elseif default and inheritance then
201 if current ~= default then
202 head = insertnodebefore(head,stack,copy_node(nsdata[default]))
203 current = default
204 end
205 elseif current > 0 then
206 head = insertnodebefore(head,stack,copy_node(nsnone))
207 current = 0
208 end
209 end
210
211 local list = process(attribute,content,inheritance,default)
212 if content ~= list then
213 setlist(stack,list)
214 end
215 elseif id == rule_code then
216 check = subtype == virtualrule_code or hasdimensions(stack)
217 end
218
219 if check then
220 local c = getattr(stack,attribute)
221 if c then
222 if default and c == inheritance then
223 if current ~= default then
224 head = insertnodebefore(head,stack,copy_node(nsdata[default]))
225 current = default
226 end
227 elseif current ~= c then
228 head = insertnodebefore(head,stack,copy_node(nsdata[c]))
229 current = c
230 end
231 if leader then
232 local savedcurrent = current
233 local ci = getid(leader)
234 if ci == hlist_code or ci == vlist_code then
235
236
237
238 current = 0
239 end
240 local list = process(attribute,leader,inheritance,default)
241 if leader ~= list then
242 setleader(stack,list)
243 end
244 current = savedcurrent
245 leader = false
246 end
247 elseif default and inheritance then
248 if current ~= default then
249 head = insertnodebefore(head,stack,copy_node(nsdata[default]))
250 current = default
251 end
252 elseif current > 0 then
253 head = insertnodebefore(head,stack,copy_node(nsnone))
254 current = 0
255 end
256 check = false
257 end
258 end
259 return head
260end
261
262states.process = function(namespace,attribute,head,default)
263 return process(attribute,head,default)
264end
265
266local function simple(attribute,head)
267 local check = false
268 local leader = nil
269 for stack, id, subtype, content in nextcontent, head do
270 if id == glyph_code or id == disc_code then
271 check = true
272 elseif id == glue_code then
273 check = true
274 leader = content
275 elseif id == hlist_code or id == vlist_code then
276 if subtype == container_code or hasgeometry(stack) then
277 local outer = getattr(stack,attribute)
278 if outer then
279 if current ~= outer then
280 if current > 0 then
281 head = insertnodebefore(head,stack,copy_node(nsnone))
282 end
283 head = insertnodebefore(head,stack,copy_node(nsdata[c]))
284 current = outer
285 end
286 elseif current > 0 then
287 head = insertnodebefore(head,stack,copy_node(nsnone))
288 current = 0
289 end
290 end
291 local list = simple(attribute,content)
292 if content ~= list then
293 setlist(stack,list)
294 end
295 elseif id == rule_code then
296 check = subtype == virtualrule_code or hasdimensions(stack)
297 end
298 if check then
299 local c = getattr(stack,attribute)
300 if c then
301 if current ~= c then
302 if current > 0 then
303 head = insertnodebefore(head,stack,copy_node(nsnone))
304 end
305 head = insertnodebefore(head,stack,copy_node(nsdata[c]))
306 current = c
307 end
308 if leader then
309 local savedcurrent = current
310 local ci = getid(leader)
311 if ci == hlist_code or ci == vlist_code then
312 current = 0
313 end
314 local list = simple(attribute,leader)
315 if leader ~= list then
316 setleader(stack,list)
317 end
318 current = savedcurrent
319 leader = false
320 end
321 elseif current > 0 then
322 head = insertnodebefore(head,stack,copy_node(nsnone))
323 current = 0
324 end
325 check = false
326 end
327 end
328 return head
329end
330
331states.simple = function(namespace,attribute,head,default)
332 return simple(attribute,head,default)
333end
334
335
336
337
338
339
340
341
342
343local function selective(attribute,head,inheritance,default)
344 local check = false
345 local leader = nil
346
347 for stack, id, subtype, content in nextcontent, head do
348 if id == glyph_code or id == disc_code then
349 check = true
350 elseif id == glue_code then
351 check = true
352 leader = content
353 elseif id == hlist_code or id == vlist_code then
354 local outer, s
355 if subtype == container_code or hasgeometry(stack) then
356 outer, s = getattrs(stack,attribute,nsselector)
357 if outer then
358 if default and outer == inheritance then
359 if current ~= default then
360 local data = nsdata[default][nsforced or nsselector]
361 if data then
362 head = insertnodebefore(head,stack,copy_node(data))
363 end
364 current = default
365 end
366 elseif current ~= outer or current_selector ~= s then
367 local data = nsdata[outer][nsforced or s or nsselector]
368 if data then
369 head = insertnodebefore(head,stack,copy_node(data))
370 end
371 current = outer
372 current_selector = s
373 end
374 elseif default and inheritance then
375 if current ~= default then
376 local data = nsdata[default][nsforced or s or nsselector]
377 if data then
378 head = insertnodebefore(head,stack,copy_node(data))
379 end
380 current = default
381 end
382 elseif current > 0 then
383 head = insertnodebefore(head,stack,copy_node(nsnone))
384 current = 0
385 current_selector = 0
386 end
387 end
388 if content then
389 local list = selective(attribute,content,inheritance,default)
390 if content ~= list then
391 setlist(stack,list)
392 end
393 end
394 elseif id == rule_code then
395 if subtype == virtualrule_code then
396 check = true
397 elseif subtype == boxrule_code or subtype == imagerule_code or subtype == emptyrule_code then
398
399 check = false
400 else
401 check = hasdimensions(stack)
402 end
403 end
404 if check then
405 local c, s = getattrs(stack,attribute,nsselector)
406 if c then
407 if default and c == inheritance then
408 if current ~= default then
409 local data = nsdata[default][nsforced or s or nsselector]
410 if data then
411 head = insertnodebefore(head,stack,copy_node(data))
412 end
413 current = default
414 end
415 elseif current ~= c or current_selector ~= s then
416 local data = nsdata[c][nsforced or s or nsselector]
417 if data then
418 head = insertnodebefore(head,stack,copy_node(data))
419 end
420 current = c
421 current_selector = s
422 end
423 if leader then
424 local list = selective(attribute,leader,inheritance,default)
425 if leader ~= list then
426 setleader(stack,list)
427 end
428 leader = false
429 end
430 elseif default and inheritance then
431 if current ~= default then
432 local data = nsdata[default][nsforced or s or nsselector]
433 if data then
434 head = insertnodebefore(head,stack,copy_node(data))
435 end
436 current = default
437 end
438 elseif current > 0 then
439 head = insertnodebefore(head,stack,copy_node(nsnone))
440 current = 0
441 current_selector = 0
442 end
443 check = false
444 end
445 end
446 return head
447end
448
449states.selective = function(namespace,attribute,head,default)
450 return selective(attribute,head,default)
451end
452
453
454
455
456
457
458
459
460
461
462local function stacked(attribute,head,default)
463 local stack = head
464 local current = default or 0
465 local depth = 0
466 local check = false
467 local leader = false
468 while stack do
469 local id = getid(stack)
470 if id == glyph_code then
471 check = true
472 elseif id == glue_code then
473 leader = getleader(stack)
474 if leader then
475 check = true
476 end
477 elseif id == hlist_code or id == vlist_code then
478 local content = getlist(stack)
479 if content then
480
481 local list
482 if subtype == container_code then
483 check = true
484 current = 0
485 end
486 if nslistwise then
487 local a = getattr(stack,attribute)
488 if a and current ~= a and nslistwise[a] then
489 local p = current
490 current = a
491 head = insertnodebefore(head,stack,copy_node(nsdata[a]))
492 list = stacked(attribute,content,current)
493 head, stack = insertnodeafter(head,stack,copy_node(nsnone))
494 current = p
495 else
496 list = stacked(attribute,content,current)
497 end
498 else
499 list = stacked(attribute,content,current)
500 end
501 if content ~= list then
502 setlist(stack,list)
503 end
504 end
505 elseif id == rule_code then
506 check = subtype == virtualrule_code or hasdimensions(stack)
507 end
508 if check then
509 local a = getattr(stack,attribute)
510 if a then
511 if current ~= a then
512 head = insertnodebefore(head,stack,copy_node(nsdata[a]))
513 depth = depth + 1
514 current = a
515 end
516 if leader then
517 local content = getlist(leader)
518 if content then
519 local list = stacked(attribute,content,current)
520 if leader ~= list then
521 setleader(stack,list)
522 end
523 end
524 leader = false
525 end
526 elseif default > 0 then
527
528 elseif current > 0 then
529 head = insertnodebefore(head,stack,copy_node(nsnone))
530 depth = depth - 1
531 current = 0
532 end
533 check = false
534 end
535 stack = getnext(stack)
536 end
537 while depth > 0 do
538 head = insertnodeafter(head,stack,copy_node(nsnone))
539 depth = depth - 1
540 end
541 return head
542end
543
544states.stacked = function(namespace,attribute,head,default)
545 return stacked(attribute,head,default)
546end
547
548local function stacker(attribute,head,default)
549 local stacked = false
550 local current = head
551 local previous = head
552 local attrib = default or unsetvalue
553 local check = false
554 local leader = false
555 for n, id, subtype, content in nextcontent, head do
556 local current = n
557 if id == glyph_code then
558 check = true
559 elseif id == glue_code then
560 leader = content
561 check = true
562 elseif id == hlist_code or id == vlist_code then
563 local list
564 if subtype == container_code then
565 check = true
566 end
567 if nslistwise then
568 local a = getattr(current,attribute)
569 if a and attrib ~= a and nslistwise[a] then
570 head = insertnodebefore(head,current,copy_node(nsdata[a]))
571 list = stacker(attribute,content,a)
572 if list ~= content then
573 setlist(current,list)
574 end
575 head, current = insertnodeafter(head,current,copy_node(nsnone))
576 else
577 list = stacker(attribute,content,attrib)
578 if list ~= content then
579 setlist(current,list)
580 end
581 end
582 else
583
584 list = stacker(attribute,content,default)
585 if list ~= content then
586 setlist(current,list)
587 end
588
589 end
590 elseif id == rule_code then
591 check = hasdimensions(current)
592 if subtype == virtualrule_code then
593 check = true
594
595 elseif subtype == emptyrule_code then
596 check = false
597 else
598 check = hasdimensions(current)
599 end
600 end
601 if check then
602 local a = getattr(current,attribute) or unsetvalue
603 if a ~= attrib then
604 if not stacked then
605 stacked = true
606 nsbegin()
607 end
608 local n = nsstep(a)
609 if n then
610 head = insertnodebefore(head,current,n)
611 end
612 attrib = a
613 if leader then
614
615 local content = getlist(leader)
616 if content then
617 local list = stacker(attribute,leader,attrib)
618 if leader ~= list then
619 setleader(current,list)
620 end
621 end
622
623 leader = false
624 end
625 end
626 check = false
627 end
628 previous = current
629 end
630 if stacked then
631 local n = nsend()
632 while n do
633 head = insertnodeafter(head,previous,n)
634 n = nsend()
635 end
636 end
637 return head
638end
639
640states.stacker = function(namespace,attribute,head,default)
641 local head = stacker(attribute,head,default)
642 nsreset()
643 return head
644end
645
646
647
648statistics.register("attribute processing time", function()
649 return statistics.elapsedseconds(attributes,"front- and backend")
650end)
651 |