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 = rulecodes.box
45local imagerule_code = rulecodes.image
46local emptyrule_code = rulecodes.empty
47local virtualrule_code = rulecodes.virtual
48
49local container_code = nodes.listcodes.container
50
51local glyph_code = nodecodes.glyph
52local disc_code = nodecodes.disc
53local glue_code = nodecodes.glue
54local rule_code = nodecodes.rule
55local hlist_code = nodecodes.hlist
56local vlist_code = 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 local list = process(attribute,content,inheritance,default)
211 if content ~= list then
212 setlist(stack,list)
213 end
214 elseif id == rule_code then
215 check = subtype == virtualrule_code or hasdimensions(stack)
216 end
217
218 if check then
219 local c = getattr(stack,attribute)
220 if c then
221 if default and c == inheritance then
222 if current ~= default then
223 head = insertnodebefore(head,stack,copy_node(nsdata[default]))
224 current = default
225 end
226 elseif current ~= c then
227 head = insertnodebefore(head,stack,copy_node(nsdata[c]))
228 current = c
229 end
230 if leader then
231 local savedcurrent = current
232 local ci = getid(leader)
233 if ci == hlist_code or ci == vlist_code then
234
235
236
237 current = 0
238 end
239 local list = process(attribute,leader,inheritance,default)
240 if leader ~= list then
241 setleader(stack,list)
242 end
243 current = savedcurrent
244 leader = false
245 end
246 elseif default and inheritance then
247 if current ~= default then
248 head = insertnodebefore(head,stack,copy_node(nsdata[default]))
249 current = default
250 end
251 elseif current > 0 then
252 head = insertnodebefore(head,stack,copy_node(nsnone))
253 current = 0
254 end
255 check = false
256 end
257 end
258 return head
259end
260
261states.process = function(namespace,attribute,head,default)
262 return process(attribute,head,default)
263end
264
265local function simple(attribute,head)
266 local check = false
267 local leader = nil
268 for stack, id, subtype, content in nextcontent, head do
269 if id == glyph_code or id == disc_code then
270 check = true
271 elseif id == glue_code then
272 check = true
273 leader = content
274 elseif id == hlist_code or id == vlist_code then
275 if subtype == container_code or hasgeometry(stack) then
276 local outer = getattr(stack,attribute)
277 if outer then
278 if current ~= outer then
279 if current > 0 then
280 head = insertnodebefore(head,stack,copy_node(nsnone))
281 end
282 head = insertnodebefore(head,stack,copy_node(nsdata[c]))
283 current = outer
284 end
285 elseif current > 0 then
286 head = insertnodebefore(head,stack,copy_node(nsnone))
287 current = 0
288 end
289 end
290 local list = simple(attribute,content)
291 if content ~= list then
292 setlist(stack,list)
293 end
294 elseif id == rule_code then
295 check = subtype == virtualrule_code or hasdimensions(stack)
296 end
297 if check then
298 local c = getattr(stack,attribute)
299 if c then
300 if current ~= c then
301 if current > 0 then
302 head = insertnodebefore(head,stack,copy_node(nsnone))
303 end
304 head = insertnodebefore(head,stack,copy_node(nsdata[c]))
305 current = c
306 end
307 if leader then
308 local savedcurrent = current
309 local ci = getid(leader)
310 if ci == hlist_code or ci == vlist_code then
311 current = 0
312 end
313 local list = simple(attribute,leader)
314 if leader ~= list then
315 setleader(stack,list)
316 end
317 current = savedcurrent
318 leader = false
319 end
320 elseif current > 0 then
321 head = insertnodebefore(head,stack,copy_node(nsnone))
322 current = 0
323 end
324 check = false
325 end
326 end
327 return head
328end
329
330states.simple = function(namespace,attribute,head,default)
331 return simple(attribute,head,default)
332end
333
334
335
336
337
338
339
340
341
342local function selective(attribute,head,inheritance,default)
343 local check = false
344 local leader = nil
345 for stack, id, subtype, content in nextcontent, head do
346 if id == glyph_code or id == disc_code then
347 check = true
348 elseif id == glue_code then
349 check = true
350 leader = content
351 elseif id == hlist_code or id == vlist_code then
352 local outer, s
353 if subtype == container_code or hasgeometry(stack) then
354 outer, s = getattrs(stack,attribute,nsselector)
355 if outer then
356 if default and outer == inheritance then
357 if current ~= default then
358 local data = nsdata[default][nsforced or nsselector]
359 if data then
360 head = insertnodebefore(head,stack,copy_node(data))
361 end
362 current = default
363 end
364 elseif current ~= outer or current_selector ~= s then
365 local data = nsdata[outer][nsforced or s or nsselector]
366 if data then
367 head = insertnodebefore(head,stack,copy_node(data))
368 end
369 current = outer
370 current_selector = s
371 end
372 elseif default and inheritance then
373 if current ~= default then
374 local data = nsdata[default][nsforced or s or nsselector]
375 if data then
376 head = insertnodebefore(head,stack,copy_node(data))
377 end
378 current = default
379 end
380 elseif current > 0 then
381 head = insertnodebefore(head,stack,copy_node(nsnone))
382 current, current_selector = 0, 0
383 end
384 end
385 local list = selective(attribute,content,inheritance,default)
386 if content ~= list then
387 setlist(stack,list)
388 end
389 elseif id == rule_code then
390 if subtype == virtualrule_code then
391 check = true
392 elseif subtype == boxrule_code or subtype == imagerule_code or subtype == emptyrule_code then
393
394 check = false
395 else
396 check = hasdimensions(stack)
397 end
398 end
399 if check then
400 local c, s = getattrs(stack,attribute,nsselector)
401 if c then
402 if default and c == inheritance then
403 if current ~= default then
404 local data = nsdata[default][nsforced or s or nsselector]
405 if data then
406 head = insertnodebefore(head,stack,copy_node(data))
407 end
408 current = default
409 end
410 elseif current ~= c or current_selector ~= s then
411 local data = nsdata[c][nsforced or s or nsselector]
412 if data then
413 head = insertnodebefore(head,stack,copy_node(data))
414 end
415 current = c
416 current_selector = s
417 end
418 if leader then
419 local list = selective(attribute,leader,inheritance,default)
420 if leader ~= list then
421 setleader(stack,list)
422 end
423 leader = false
424 end
425 elseif default and inheritance then
426 if current ~= default then
427 local data = nsdata[default][nsforced or s or nsselector]
428 if data then
429 head = insertnodebefore(head,stack,copy_node(data))
430 end
431 current = default
432 end
433 elseif current > 0 then
434 head = insertnodebefore(head,stack,copy_node(nsnone))
435 current, current_selector = 0, 0
436 end
437 check = false
438 end
439 end
440 return head
441end
442
443states.selective = function(namespace,attribute,head,default)
444 return selective(attribute,head,default)
445end
446
447
448
449
450
451
452
453
454
455
456local function stacked(attribute,head,default)
457 local stack = head
458 local current = default or 0
459 local depth = 0
460 local check = false
461 local leader = false
462 while stack do
463 local id = getid(stack)
464 if id == glyph_code then
465 check = true
466 elseif id == glue_code then
467 leader = getleader(stack)
468 if leader then
469 check = true
470 end
471 elseif id == hlist_code or id == vlist_code then
472 local content = getlist(stack)
473 if content then
474
475 local list
476 if subtype == container_code then
477 check = true
478 current = 0
479 end
480 if nslistwise then
481 local a = getattr(stack,attribute)
482 if a and current ~= a and nslistwise[a] then
483 local p = current
484 current = a
485 head = insertnodebefore(head,stack,copy_node(nsdata[a]))
486 list = stacked(attribute,content,current)
487 head, stack = insertnodeafter(head,stack,copy_node(nsnone))
488 current = p
489 else
490 list = stacked(attribute,content,current)
491 end
492 else
493 list = stacked(attribute,content,current)
494 end
495 if content ~= list then
496 setlist(stack,list)
497 end
498 end
499 elseif id == rule_code then
500 check = subtype == virtualrule_code or hasdimensions(stack)
501 end
502 if check then
503 local a = getattr(stack,attribute)
504 if a then
505 if current ~= a then
506 head = insertnodebefore(head,stack,copy_node(nsdata[a]))
507 depth = depth + 1
508 current = a
509 end
510 if leader then
511 local content = getlist(leader)
512 if content then
513 local list = stacked(attribute,content,current)
514 if leader ~= list then
515 setleader(stack,list)
516 end
517 end
518 leader = false
519 end
520 elseif default > 0 then
521
522 elseif current > 0 then
523 head = insertnodebefore(head,stack,copy_node(nsnone))
524 depth = depth - 1
525 current = 0
526 end
527 check = false
528 end
529 stack = getnext(stack)
530 end
531 while depth > 0 do
532 head = insertnodeafter(head,stack,copy_node(nsnone))
533 depth = depth - 1
534 end
535 return head
536end
537
538states.stacked = function(namespace,attribute,head,default)
539 return stacked(attribute,head,default)
540end
541
542local function stacker(attribute,head,default)
543 local stacked = false
544 local current = head
545 local previous = head
546 local attrib = default or unsetvalue
547 local check = false
548 local leader = false
549 for current, id, subtype, content in nextcontent, head do
550 if id == glyph_code then
551 check = true
552 elseif id == glue_code then
553 leader = content
554 check = true
555 elseif id == hlist_code or id == vlist_code then
556 local list
557 if subtype == container_code then
558 check = true
559 end
560 if nslistwise then
561 local a = getattr(current,attribute)
562 if a and attrib ~= a and nslistwise[a] then
563 head = insertnodebefore(head,current,copy_node(nsdata[a]))
564 list = stacker(attribute,content,a)
565 if list ~= content then
566 setlist(current,list)
567 end
568 head, current = insertnodeafter(head,current,copy_node(nsnone))
569 else
570 list = stacker(attribute,content,attrib)
571 if list ~= content then
572 setlist(current,list)
573 end
574 end
575 else
576 list = stacker(attribute,content,default)
577 if list ~= content then
578 setlist(current,list)
579 end
580 end
581 elseif id == rule_code then
582 if subtype == virtualrule_code then
583 check = true
584 elseif subtype == boxrule_code or subtype == imagerule_code or subtype == emptyrule_code then
585
586 check = false
587 else
588 check = hasdimensions(current)
589 end
590 end
591 if check then
592 local a = getattr(current,attribute) or unsetvalue
593 if a ~= attrib then
594 if not stacked then
595 stacked = true
596 nsbegin()
597 end
598 local n = nsstep(a)
599 if n then
600 head = insertnodebefore(head,current,n)
601 end
602 attrib = a
603 if leader then
604
605 local content = getlist(leader)
606 if content then
607 local list = stacker(attribute,leader,attrib)
608 if leader ~= list then
609 setleader(current,list)
610 end
611 end
612
613 leader = false
614 end
615 end
616 check = false
617 end
618
619 previous = current
620 end
621 if stacked then
622 local n = nsend()
623 while n do
624 head = insertnodeafter(head,previous,n)
625 n = nsend()
626 end
627 end
628 return head
629end
630
631states.stacker = function(namespace,attribute,head,default)
632 local head = stacker(attribute,head,default)
633 nsreset()
634 return head
635end
636
637
638
639statistics.register("attribute processing time", function()
640 return statistics.elapsedseconds(attributes,"front- and backend")
641end)
642 |