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