1if not modules then modules = { } end modules ["page-mix"] = {
2 version = 1.001,
3 comment = "companion to page-mix.mkiv",
4 author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
5 copyright = "PRAGMA ADE / ConTeXt Development Team",
6 license = "see context related readme files"
7}
8
9
10
11
12
13
14
15
16
17
18local next, type = next, type
19local concat = table.concat
20local ceil = math.ceil
21
22local trace_state = false trackers.register("mixedcolumns.trace", function(v) trace_state = v end)
23local trace_details = false trackers.register("mixedcolumns.details", function(v) trace_details = v end)
24
25local report_state = logs.reporter("mixed columns")
26
27local context = context
28
29local nodecodes = nodes.nodecodes
30
31local hlist_code = nodecodes.hlist
32local vlist_code = nodecodes.vlist
33local kern_code = nodecodes.kern
34local glue_code = nodecodes.glue
35local penalty_code = nodecodes.penalty
36local insert_code = nodecodes.insert
37local mark_code = nodecodes.mark
38local rule_code = nodecodes.rule
39
40local nuts = nodes.nuts
41local tonode = nuts.tonode
42local listtoutf = nodes.listtoutf
43
44local vpack = nuts.vpack
45local flushnode = nuts.flush
46local concatnodes = nuts.concat
47local slidenodes = nuts.slide
48
49local setlink = nuts.setlink
50local setlist = nuts.setlist
51local setnext = nuts.setnext
52local setprev = nuts.setprev
53local setbox = nuts.setbox
54local setwhd = nuts.setwhd
55local setheight = nuts.setheight
56local setdepth = nuts.setdepth
57
58local getnext = nuts.getnext
59local getprev = nuts.getprev
60local getid = nuts.getid
61local getlist = nuts.getlist
62local getindex = nuts.getindex or nuts.getsubtype
63local getbox = nuts.getbox
64local getattr = nuts.getattr
65local getwhd = nuts.getwhd
66local getkern = nuts.getkern
67local getpenalty = nuts.getpenalty
68local getwidth = nuts.getwidth
69local getheight = nuts.getheight
70local getdepth = nuts.getdepth
71
72local theprop = nuts.theprop
73
74local nodepool = nuts.pool
75
76local new_vlist = nodepool.vlist
77local new_glue = nodepool.glue
78
79local points = number.points
80
81local setinsertcontent = tex.setinsertcontent or tex.setbox
82
83local settings_to_hash = utilities.parsers.settings_to_hash
84
85local variables = interfaces.variables
86local v_yes = variables.yes
87local v_global = variables["global"]
88local v_local = variables["local"]
89local v_none = variables.none
90local v_halfline = variables.halfline
91
92local context = context
93local implement = interfaces.implement
94
95pagebuilders = pagebuilders or { }
96pagebuilders.mixedcolumns = pagebuilders.mixedcolumns or { }
97local mixedcolumns = pagebuilders.mixedcolumns
98
99local a_checkedbreak = attributes.private("checkedbreak")
100local forcedbreak = -123
101
102
103
104
105
106
107
108
109
110
111
112
113local function collectinserts(result,nxt,nxtid)
114 local inserts, currentskips, nextskips, inserttotal = { }, 0, 0, 0
115 local i = result.i
116 if not i then
117 i = 0
118 result.i = i
119 end
120 while nxt do
121 if nxtid == insert_code then
122 i = i + 1
123 result.i = i
124 inserttotal = inserttotal + getheight(nxt)
125 local s = getindex(nxt)
126 local c = inserts[s]
127 if trace_details then
128 report_state("insert of class %s found",s)
129 end
130 if not c then
131 local width = structures.notes.check_spacing(s,i)
132 c = { }
133 inserts[s] = c
134 if not result.inserts[s] then
135 currentskips = currentskips + width
136 end
137 nextskips = nextskips + width
138 end
139 c[#c+1] = nxt
140 elseif nxtid == mark_code then
141 if trace_details then
142 report_state("mark found")
143 end
144 else
145 break
146 end
147 nxt = getnext(nxt)
148 if nxt then
149 nxtid = getid(nxt)
150 else
151 break
152 end
153 end
154 return nxt, inserts, currentskips, nextskips, inserttotal
155end
156
157local function appendinserts(ri,inserts)
158 for class, collected in next, inserts do
159 local ric = ri[class]
160 if not ric then
161
162 ri[class] = collected
163 else
164
165 for j=1,#collected do
166 ric[#ric+1] = collected[j]
167 end
168 end
169 end
170end
171
172local function discardtopglue(current,discarded)
173 local size = 0
174 while current do
175 local id = getid(current)
176 if id == glue_code then
177 size = size + getwidth(current)
178 discarded[#discarded+1] = current
179 current = getnext(current)
180 elseif id == penalty_code then
181 if getpenalty(current) == forcedbreak then
182 discarded[#discarded+1] = current
183 current = getnext(current)
184 while current and getid(current) == glue_code do
185 size = size + getwidth(current)
186 discarded[#discarded+1] = current
187 current = getnext(current)
188 end
189 else
190 discarded[#discarded+1] = current
191 current = getnext(current)
192 end
193 else
194 break
195 end
196 end
197 if current then
198 setprev(current)
199 end
200 return current, size
201end
202
203local function stripbottomglue(results,discarded)
204 local height = 0
205 for i=1,#results do
206 local r = results[i]
207 local t = r.tail
208 while t and t ~= r.head do
209 local prev = getprev(t)
210 if not prev then
211 break
212 end
213 local id = getid(t)
214 if id == penalty_code then
215 if getpenalty(t) == forcedbreak then
216 break
217 else
218 discarded[#discarded+1] = t
219 r.tail = prev
220 t = prev
221 end
222 elseif id == glue_code then
223 discarded[#discarded+1] = t
224 local width = getwidth(t)
225 if trace_state then
226 report_state("columns %s, discarded bottom glue %p",i,width)
227 end
228 r.height = r.height - width
229 r.tail = prev
230 t = prev
231 else
232 break
233 end
234 end
235 if r.height > height then
236 height = r.height
237 end
238 end
239 return height
240end
241
242local function preparesplit(specification)
243 local box = specification.box
244 if not box then
245 report_state("fatal error, no box")
246 return
247 end
248 local list = getbox(box)
249 if not list then
250 report_state("fatal error, no list")
251 return
252 end
253 local head = nil
254 if getid(list) == hlist_code then
255 head = list
256 else
257 head = getlist(list) or specification.originalhead
258 end
259 if not head then
260 report_state("fatal error, no head")
261 return
262 end
263
264 slidenodes(head)
265
266 local discarded = { }
267 local originalhead = head
268 local originalwidth = specification.originalwidth or getwidth(list)
269 local originalheight = specification.originalheight or getheight(list)
270 local current = head
271 local skipped = 0
272 local height = 0
273 local depth = 0
274 local skip = 0
275 local handlenotes = specification.notes or false
276 local splitmethod = specification.splitmethod or false
277 if splitmethod == v_none then
278 splitmethod = false
279 end
280 local options = settings_to_hash(specification.option or "")
281 local stripbottom = specification.alternative == v_local
282 local cycle = specification.cycle or 1
283 local nofcolumns = specification.nofcolumns or 1
284 if nofcolumns == 0 then
285 nofcolumns = 1
286 end
287 local preheight = specification.preheight or 0
288 local extra = specification.extra or 0
289 local maxheight = specification.maxheight
290 local optimal = originalheight/nofcolumns
291 local noteheight = specification.noteheight or 0
292
293 maxheight = maxheight - noteheight
294
295 if specification.balance ~= v_yes then
296 optimal = maxheight
297 end
298 local topback = 0
299 local target = optimal + extra
300 local overflow = target > maxheight - preheight
301 local threshold = specification.threshold or 0
302 if overflow then
303 target = maxheight - preheight
304 end
305 if trace_state then
306 report_state("cycle %s, maxheight %p, preheight %p, target %p, overflow %a, extra %p",
307 cycle, maxheight, preheight , target, overflow, extra)
308 end
309 local results = { }
310 for i=1,nofcolumns do
311 results[i] = {
312 head = false,
313 tail = false,
314 height = 0,
315 depth = 0,
316 inserts = { },
317 delta = 0,
318 back = 0,
319 }
320 end
321
322 local column = 1
323 local line = 0
324 local result = results[1]
325 local lasthead = nil
326 local rest = nil
327 local lastlocked = nil
328 local lastcurrent = nil
329 local lastcontent = nil
330 local backtracked = false
331
332 if trace_state then
333 report_state("setting collector to column %s",column)
334 end
335
336 local function unlock(case,penalty)
337 if lastlocked then
338 if trace_state then
339 report_state("penalty %s, unlocking in column %s, case %i",penalty or "-",column,case)
340 end
341 lastlocked = nil
342 else
343 if trace_state then
344 report_state("penalty %s, ignoring in column %s, case %i",penalty or "-",column,case)
345 end
346 end
347 lastcurrent = nil
348 lastcontent = nil
349 end
350
351 local function lock(case,penalty,current)
352 if trace_state then
353 report_state("penalty %s, locking in column %s, case %i",penalty,column,case)
354 end
355 lastlocked = penalty
356 lastcurrent = current or lastcurrent
357 lastcontent = nil
358 end
359
360 local function backtrack(start)
361 local current = start
362
363 while current do
364 local id = getid(current)
365 if id == glue_code then
366 if trace_state then
367 report_state("backtracking over %s in column %s, value %p","glue",column,getwidth(current))
368 end
369 current = getprev(current)
370 elseif id == penalty_code then
371 if trace_state then
372 report_state("backtracking over %s in column %s, value %i","penalty",column,getpenalty(current))
373 end
374 current = getprev(current)
375 else
376 break
377 end
378 end
379
380 while current do
381 local id = getid(current)
382 if id == glue_code then
383 if trace_state then
384 report_state("quitting at %s in column %s, value %p","glue",column,getwidth(current))
385 end
386 break
387 elseif id == penalty_code then
388 if trace_state then
389 report_state("quitting at %s in column %s, value %i","penalty",column,getpenalty(current))
390 end
391 break
392 else
393 current = getprev(current)
394 end
395 end
396 if not current then
397 if trace_state then
398 report_state("no effective backtracking in column %s",column)
399 end
400 current = start
401 end
402 return current
403 end
404
405 local function gotonext()
406 if lastcurrent then
407 if current ~= lastcurrent then
408 if trace_state then
409 report_state("backtracking to preferred break in column %s",column)
410 end
411
412 if true then
413 current = backtrack(lastcurrent)
414 else
415 current = lastcurrent
416 end
417 backtracked = true
418 end
419 lastcurrent = nil
420 if lastlocked then
421 if trace_state then
422 report_state("unlocking in column %s",column)
423 end
424 lastlocked = nil
425 end
426 end
427 if head == lasthead then
428 if trace_state then
429 report_state("empty column %s, needs more work",column)
430 end
431 rest = current
432 return false, 0
433 else
434 lasthead = head
435 result.head = head
436 if current == head then
437 result.tail = head
438 else
439 result.tail = getprev(current)
440 end
441 result.height = height
442 result.depth = depth
443 end
444 head = current
445 height = 0
446 depth = 0
447 if column == nofcolumns then
448 column = 0
449 rest = head
450
451 return false, 0
452 else
453 local skipped
454 column = column + 1
455 result = results[column]
456 if trace_state then
457 report_state("setting collector to column %s",column)
458 end
459 current, skipped = discardtopglue(current,discarded)
460 if trace_details and skipped ~= 0 then
461 report_state("check > column 1, discarded %p",skipped)
462 end
463 head = current
464 return true, skipped
465 end
466 end
467
468 local function checked(advance,where,locked)
469 local total = skip + height + depth + advance
470 local delta = total - target
471 local state = "same"
472 local okay = false
473 local skipped = 0
474 local curcol = column
475 if delta > threshold then
476 result.delta = delta
477 okay, skipped = gotonext()
478 if okay then
479 state = "next"
480 else
481 state = "quit"
482 end
483 end
484 if trace_details then
485 report_state("%-8s > column %s, delta %p, threshold %p, advance %p, total %p, target %p => %a (height %p, depth %p, skip %p)",
486 where,curcol,delta,threshold,advance,total,target,state,height,depth,skip)
487 end
488 return state, skipped
489 end
490
491 current, skipped = discardtopglue(current,discarded)
492 if trace_details and skipped ~= 0 then
493 report_state("check > column 1, discarded %p",skipped)
494 end
495
496
497
498
499
500
501
502
503
504
505 head = current
506
507 local function process_skip(current,nxt)
508 local advance = getwidth(current)
509 if advance ~= 0 then
510 local state, skipped = checked(advance,"glue")
511 if trace_state then
512 report_state("%-8s > column %s, state %a, advance %p, height %p","glue",column,state,advance,height)
513 if skipped ~= 0 then
514 report_state("%-8s > column %s, discarded %p","glue",column,skipped)
515 end
516 end
517 if state == "quit" then
518 return true
519 end
520 height = height + depth + skip
521 depth = 0
522 if advance < 0 then
523 height = height + advance
524 skip = 0
525 if height < 0 then
526 height = 0
527 end
528 else
529 skip = height > 0 and advance or 0
530 end
531 if trace_state then
532 report_state("%-8s > column %s, height %p, depth %p, skip %p","glue",column,height,depth,skip)
533 end
534 else
535
536 end
537 if lastcontent then
538 unlock(1)
539 end
540 end
541
542 local function process_kern(current,nxt)
543 local advance = getkern(current)
544 if advance ~= 0 then
545 local state, skipped = checked(advance,"kern")
546 if trace_state then
547 report_state("%-8s > column %s, state %a, advance %p, height %p, state %a","kern",column,state,advance,height)
548 if skipped ~= 0 then
549 report_state("%-8s > column %s, discarded %p","kern",column,skipped)
550 end
551 end
552 if state == "quit" then
553 return true
554 end
555 height = height + depth + skip + advance
556 depth = 0
557 skip = 0
558 if trace_state then
559 report_state("%-8s > column %s, height %p, depth %p, skip %p","kern",column,height,depth,skip)
560 end
561 end
562 end
563
564 local function process_rule(current,nxt)
565
566 local advance = getheight(current)
567 if advance ~= 0 then
568 local state, skipped = checked(advance,"rule")
569 if trace_state then
570 report_state("%-8s > column %s, state %a, rule, advance %p, height %p","rule",column,state,advance,inserttotal,height)
571 if skipped ~= 0 then
572 report_state("%-8s > column %s, discarded %p","rule",column,skipped)
573 end
574 end
575 if state == "quit" then
576 return true
577 end
578 height = height + depth + skip + advance
579
580
581
582
583
584 depth = getdepth(current)
585 skip = 0
586 end
587 lastcontent = current
588 end
589
590
591
592
593
594
595
596 local function process_penalty(current,nxt)
597 local penalty = getpenalty(current)
598 if penalty == 0 then
599 unlock(2,penalty)
600 elseif penalty == forcedbreak then
601 local needed = getattr(current,a_checkedbreak)
602 local proceed = not needed or needed == 0
603 if not proceed then
604 local available = target - height
605 proceed = needed >= available
606 if trace_state then
607 report_state("cycle: %s, column %s, available %p, needed %p, %s break",cycle,column,available,needed,proceed and "forcing" or "ignoring")
608 end
609 end
610 if proceed then
611 unlock(3,penalty)
612 local okay, skipped = gotonext()
613 if okay then
614 if trace_state then
615 report_state("cycle: %s, forced column break, same page",cycle)
616 if skipped ~= 0 then
617 report_state("%-8s > column %s, discarded %p","penalty",column,skipped)
618 end
619 end
620 else
621 if trace_state then
622 report_state("cycle: %s, forced column break, next page",cycle)
623 if skipped ~= 0 then
624 report_state("%-8s > column %s, discarded %p","penalty",column,skipped)
625 end
626 end
627 return true
628 end
629 end
630 elseif penalty < 0 then
631
632 unlock(4,penalty)
633 elseif penalty >= 10000 then
634 if not lastcurrent then
635 lock(1,penalty,current)
636 elseif penalty > lastlocked then
637 lock(2,penalty)
638 elseif trace_state then
639 report_state("penalty %s, ignoring in column %s, case %i",penalty,column,3)
640 end
641 else
642 unlock(5,penalty)
643 end
644 end
645
646 local function process_list(current,nxt)
647 local nxtid = nxt and getid(nxt)
648 line = line + 1
649 local inserts, insertskips, nextskips, inserttotal = nil, 0, 0, 0
650 local wd, ht, dp = getwhd(current)
651 local advance = ht
652 local more = nxt and (nxtid == insert_code or nxtid == mark_code)
653 if trace_state then
654 report_state("%-8s > column %s, content: %s","line (1)",column,listtoutf(getlist(current),true,true))
655 end
656 if more and handlenotes then
657 nxt, inserts, insertskips, nextskips, inserttotal = collectinserts(result,nxt,nxtid)
658 end
659 local state, skipped = checked(advance+inserttotal+insertskips,more and "line (2)" or "line only",lastlocked)
660 if trace_state then
661 report_state("%-8s > column %s, state %a, line %s, advance %p, insert %p, height %p","line (3)",column,state,line,advance,inserttotal,height)
662 if skipped ~= 0 then
663 report_state("%-8s > column %s, discarded %p","line (4)",column,skipped)
664 end
665 end
666 if state == "quit" then
667 return true
668 end
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686 height = height + depth + skip + advance + inserttotal
687 if state == "next" then
688 height = height + nextskips
689 else
690 height = height + insertskips
691 end
692 depth = dp
693 skip = 0
694 if inserts then
695
696
697 appendinserts(result.inserts,inserts)
698 end
699 if trace_state then
700 report_state("%-8s > column %s, height %p, depth %p, skip %p","line (5)",column,height,depth,skip)
701 end
702 lastcontent = current
703 end
704
705 while current do
706
707 local id = getid(current)
708 local nxt = getnext(current)
709
710 if trace_state then
711 report_state("%-8s > column %s, height %p, depth %p, id %s","node",column,height,depth,nodecodes[id])
712 end
713
714 backtracked = false
715
716 if id == hlist_code or id == vlist_code then
717 if process_list(current,nxt) then break end
718 elseif id == glue_code then
719 if process_skip(current,nxt) then break end
720 elseif id == kern_code then
721 if process_kern(current,nxt) then break end
722 elseif id == penalty_code then
723 if process_penalty(current,nxt) then break end
724 elseif id == rule_code then
725 if process_rule(current,nxt) then break end
726 else
727
728 end
729
730 if backtracked then
731 nxt = current
732 end
733
734 if nxt then
735 current = nxt
736 elseif head == lasthead then
737
738 if trace_state then
739 report_state("quit as head is lasthead")
740 end
741 break
742 else
743 local r = results[column]
744 r.head = head
745 r.tail = current
746 r.height = height
747 r.depth = depth
748 break
749 end
750 end
751
752 if not current then
753 if trace_state then
754 report_state("nothing left")
755 end
756
757
758 elseif rest == lasthead then
759 if trace_state then
760 report_state("rest equals lasthead")
761 end
762
763
764 end
765
766 if stripbottom then
767 local height = stripbottomglue(results,discarded)
768 if height > 0 then
769 target = height
770 end
771 end
772
773 specification.results = results
774 specification.height = target
775 specification.originalheight = originalheight
776 specification.originalwidth = originalwidth
777 specification.originalhead = originalhead
778 specification.targetheight = target or 0
779 specification.rest = rest
780 specification.overflow = overflow
781 specification.discarded = discarded
782 setlist(getbox(specification.box))
783
784 return specification
785end
786
787local function finalize(result)
788 if result then
789 local results = result.results
790 local columns = result.nofcolumns
791 local maxtotal = 0
792 for i=1,columns do
793 local r = results[i]
794 local h = r.head
795 if h then
796 setprev(h)
797 if r.back then
798 local k = new_glue(r.back)
799 setlink(k,h)
800 h = k
801 r.head = h
802 end
803 local t = r.tail
804 if t then
805 setnext(t)
806 else
807 setnext(h)
808 r.tail = h
809 end
810 for c, list in next, r.inserts do
811 local t = { }
812 for i=1,#list do
813 local l = list[i]
814 local h = new_vlist()
815 local g = getlist(l)
816 t[i] = h
817 setlist(h,g)
818 local ht = getheight(l)
819 local dp = getdepth(l)
820 local wd = getwidth(g)
821 setwhd(h,wd,ht,dp)
822 setlist(l)
823 end
824 setprev(t[1])
825 setnext(t[#t])
826 r.inserts[c] = t
827 end
828 end
829 local total = r.height + r.depth
830 if total > maxtotal then
831 maxtotal = total
832 end
833 r.total = total
834 end
835 result.maxtotal = maxtotal
836 for i=1,columns do
837 local r = results[i]
838 r.extra = maxtotal - r.total
839 end
840 end
841end
842
843local splitruns = 0
844
845local function report_deltas(result,str)
846 local t = { }
847 for i=1,result.nofcolumns do
848 t[#t+1] = points(result.results[i].delta or 0)
849 end
850 report_state("%s, cycles %s, deltas % | t",str,result.cycle or 1,t)
851end
852
853local function setsplit(specification)
854 splitruns = splitruns + 1
855 if trace_state then
856 report_state("split run %s",splitruns)
857 end
858 local result = preparesplit(specification)
859 if result then
860 if result.overflow then
861 if trace_state then
862 report_deltas(result,"overflow")
863 end
864
865 elseif result.rest and specification.balance == v_yes then
866 local step = specification.step or 65536*2
867 local cycle = 1
868 local cycles = specification.cycles or 100
869 while result.rest and cycle <= cycles do
870 specification.extra = cycle * step
871 result = preparesplit(specification) or result
872 if trace_state then
873 report_state("cycle: %s.%s, original height %p, total height %p",
874 splitruns,cycle,result.originalheight,result.nofcolumns*result.targetheight)
875 end
876 cycle = cycle + 1
877 specification.cycle = cycle
878 end
879 if cycle > cycles then
880 report_deltas(result,"too many balancing cycles")
881 elseif trace_state then
882 report_deltas(result,"balanced")
883 end
884 elseif trace_state then
885 report_deltas(result,"done")
886 end
887 return result
888 elseif trace_state then
889 report_state("no result")
890 end
891end
892
893local function getsplit(result,n)
894 if not result then
895 report_state("flush, column %s, %s",n,"no result")
896 return
897 end
898 local r = result.results[n]
899 if not r then
900 report_state("flush, column %s, %s",n,"empty")
901 end
902 local h = r.head
903 if not h then
904 return new_glue(result.originalwidth)
905 end
906
907 setprev(h)
908
909 local strutht = result.strutht
910 local strutdp = result.strutdp
911 local lineheight = strutht + strutdp
912 local isglobal = result.alternative == v_global
913
914 local v = new_vlist()
915 setlist(v,h)
916
917
918
919 local c = r.head
920 while c do
921 if c == result.rest then
922 report_state("flush, column %s, %s",n,"suspicous rest")
923 result.rest = nil
924 break
925 else
926 c = getnext(c)
927 end
928 end
929
930
931
932 if isglobal then
933 result.height = result.maxheight
934 end
935
936 local ht = 0
937 local dp = 0
938 local wd = result.originalwidth
939
940 local grid = result.grid
941 local internalgrid = result.internalgrid
942 local httolerance = .25
943 local dptolerance = .50
944 local lineheight = internalgrid == v_halfline and lineheight/2 or lineheight
945
946 local function amount(r,s,t)
947 local l = ceil((r-t)/lineheight)
948 local a = lineheight * l
949 if a > s then
950 return a - s
951 else
952 return s
953 end
954 end
955 if grid then
956
957 if isglobal then
958 local rh = r.height
959
960 ht = amount(rh,strutdp,0)
961 dp = strutdp
962 else
963
964 local rh = r.height
965 local rd = r.depth
966 if rh > ht then
967 ht = amount(rh,strutdp,httolerance*strutht)
968 end
969 if rd > dp then
970 dp = amount(rd,strutht,dptolerance*strutdp)
971 end
972
973 local rh = result.height or 0
974 local rd = result.depth or 0
975 if rh > ht then
976 ht = amount(rh,strutdp,httolerance*strutht)
977 end
978 if rd > dp then
979 dp = amount(rd,strutht,dptolerance*strutdp)
980 end
981
982 if ht < strutht then
983 ht = strutht
984 end
985 if dp < strutdp then
986 dp = strutdp
987 end
988 end
989 else
990 ht = result.height
991 dp = result.depth
992 end
993
994 setwhd(v,wd,ht,dp)
995
996 if trace_state then
997 local id = getid(h)
998 if id == hlist_code then
999 report_state("flush, column %s, grid %a, width %p, height %p, depth %p, %s: %s",n,grid,wd,ht,dp,"top line",listtoutf(getlist(h)))
1000 else
1001 report_state("flush, column %s, grid %a, width %p, height %p, depth %p, %s: %s",n,grid,wd,ht,dp,"head node",nodecodes[id])
1002 end
1003 end
1004
1005 for c, list in next, r.inserts do
1006 local l = concatnodes(list)
1007 for i=1,#list-1 do
1008 setdepth(list[i],0)
1009 end
1010 local b = vpack(l)
1011 setinsertcontent(c,tonode(b))
1012 r.inserts[c] = nil
1013 end
1014
1015 return v
1016end
1017
1018local function getrest(result)
1019 local rest = result and result.rest
1020 result.rest = nil
1021 return rest
1022end
1023
1024local function getlist(result)
1025 local originalhead = result and result.originalhead
1026 result.originalhead = nil
1027 return originalhead
1028end
1029
1030local function cleanup(result)
1031 local discarded = result.discarded
1032 for i=1,#discarded do
1033 flushnode(discarded[i])
1034 end
1035 result.discarded = { }
1036end
1037
1038mixedcolumns.setsplit = setsplit
1039mixedcolumns.getsplit = getsplit
1040mixedcolumns.finalize = finalize
1041mixedcolumns.getrest = getrest
1042mixedcolumns.getlist = getlist
1043mixedcolumns.cleanup = cleanup
1044
1045
1046
1047local result
1048
1049implement {
1050 name = "mixsetsplit",
1051 actions = function(specification)
1052 if result then
1053 for k, v in next, specification do
1054 result[k] = v
1055 end
1056 result = setsplit(result)
1057 else
1058 result = setsplit(specification)
1059 end
1060 end,
1061 arguments = {
1062 {
1063 { "box", "integer" },
1064 { "nofcolumns", "integer" },
1065 { "maxheight", "dimen" },
1066 { "noteheight", "dimen" },
1067 { "step", "dimen" },
1068 { "cycles", "integer" },
1069 { "preheight", "dimen" },
1070 { "prebox", "integer" },
1071 { "strutht", "dimen" },
1072 { "strutdp", "dimen" },
1073 { "threshold", "dimen" },
1074 { "splitmethod" },
1075 { "balance" },
1076 { "alternative" },
1077 { "internalgrid" },
1078 { "grid", "boolean" },
1079 { "notes", "boolean" },
1080 }
1081 }
1082}
1083
1084implement {
1085 name = "mixgetsplit",
1086 arguments = "integer",
1087 actions = function(n)
1088 if result then
1089 local list = getsplit(result,n)
1090 if list then
1091 context(tonode(list))
1092 end
1093 end
1094 end,
1095}
1096
1097implement {
1098 name = "mixfinalize",
1099 actions = function()
1100 if result then
1101 finalize(result)
1102 end
1103 end
1104}
1105
1106implement {
1107 name = "mixflushrest",
1108 actions = function()
1109 if result then
1110 local rest = getrest(result)
1111 if rest then
1112 context(tonode(rest))
1113 end
1114 end
1115 end
1116}
1117
1118implement {
1119 name = "mixflushlist",
1120 actions = function()
1121 if result then
1122 local list = getlist(result)
1123 if list then
1124 context(tonode(list))
1125 end
1126 end
1127 end
1128}
1129
1130implement {
1131 name = "mixstate",
1132 actions = function()
1133 context(result and result.rest and 1 or 0)
1134 end
1135}
1136
1137implement {
1138 name = "mixcleanup",
1139 actions = function()
1140 if result then
1141 cleanup(result)
1142 result = nil
1143 end
1144 end
1145}
1146 |