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