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