typo-mar.lua /size: 33 Kb    last modification: 2021-10-28 13:50
1
if
not
modules
then
modules
=
{
}
end
modules
[
'
typo-mar
'
]
=
{
2
version
=
1
.
001
,
3
comment
=
"
companion to typo-mar.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
-- todo:
10
--
11
-- * autoleft/right depending on available space (or distance to margin)
12
-- * floating margin data, with close-to-call anchoring
13 14
local
format
,
validstring
=
string
.
format
,
string
.
valid
15
local
insert
,
remove
,
sortedkeys
,
fastcopy
=
table
.
insert
,
table
.
remove
,
table
.
sortedkeys
,
table
.
fastcopy
16
local
setmetatable
,
next
,
tonumber
=
setmetatable
,
next
,
tonumber
17
local
formatters
=
string
.
formatters
18
local
toboolean
=
toboolean
19
local
settings_to_hash
=
utilities
.
parsers
.
settings_to_hash
20 21
local
attributes
=
attributes
22
local
nodes
=
nodes
23
local
variables
=
variables
24
local
context
=
context
25 26
local
trace_margindata
=
false
trackers
.
register
(
"
typesetters.margindata
"
,
function
(
v
)
trace_margindata
=
v
end
)
27
local
trace_marginstack
=
false
trackers
.
register
(
"
typesetters.margindata.stack
"
,
function
(
v
)
trace_marginstack
=
v
end
)
28
local
trace_margingroup
=
false
trackers
.
register
(
"
typesetters.margindata.group
"
,
function
(
v
)
trace_margingroup
=
v
end
)
29 30
local
report_margindata
=
logs
.
reporter
(
"
margindata
"
)
31 32
local
tasks
=
nodes
.
tasks
33
local
prependaction
=
tasks
.
prependaction
34
local
disableaction
=
tasks
.
disableaction
35
local
enableaction
=
tasks
.
enableaction
36 37
local
variables
=
interfaces
.
variables
38 39
local
conditionals
=
tex
.
conditionals
40
local
systemmodes
=
tex
.
systemmodes
41 42
local
v_top
=
variables
.
top
43
local
v_depth
=
variables
.
depth
44
local
v_local
=
variables
[
"
local
"
]
45
local
v_global
=
variables
[
"
global
"
]
46
local
v_left
=
variables
.
left
47
local
v_right
=
variables
.
right
48
local
v_inner
=
variables
.
inner
49
local
v_outer
=
variables
.
outer
50
local
v_margin
=
variables
.
margin
51
local
v_edge
=
variables
.
edge
52
local
v_default
=
variables
.
default
53
local
v_normal
=
variables
.
normal
54
local
v_yes
=
variables
.
yes
55
local
v_continue
=
variables
.
continue
56
local
v_first
=
variables
.
first
57
local
v_text
=
variables
.
text
58
local
v_paragraph
=
variables
.
paragraph
59
local
v_line
=
variables
.
line
60 61
local
nuts
=
nodes
.
nuts
62
local
tonode
=
nuts
.
tonode
63 64
local
hpacknodes
=
nuts
.
hpack
65
local
traverseid
=
nuts
.
traverseid
66
local
flushnodelist
=
nuts
.
flushlist
67 68
local
getnext
=
nuts
.
getnext
69
local
getprev
=
nuts
.
getprev
70
local
getid
=
nuts
.
getid
71
local
getattr
=
nuts
.
getattr
72
local
setattr
=
nuts
.
setattr
73
local
getsubtype
=
nuts
.
getsubtype
74
local
getlist
=
nuts
.
getlist
75
local
getwhd
=
nuts
.
getwhd
76
local
setlist
=
nuts
.
setlist
77
local
setlink
=
nuts
.
setlink
78
local
getshift
=
nuts
.
getshift
79
local
setshift
=
nuts
.
setshift
80
local
getwidth
=
nuts
.
getwidth
81
local
setwidth
=
nuts
.
setwidth
82
local
getheight
=
nuts
.
getheight
83 84
local
setattrlist
=
nuts
.
setattrlist
85 86
local
getbox
=
nuts
.
getbox
87
local
takebox
=
nuts
.
takebox
88 89
local
setprop
=
nuts
.
setprop
90
local
getprop
=
nuts
.
getprop
91 92
local
nodecodes
=
nodes
.
nodecodes
93
local
listcodes
=
nodes
.
listcodes
94
local
whatsitcodes
=
nodes
.
whatsitcodes
95 96
local
hlist_code
=
nodecodes
.
hlist
97
local
vlist_code
=
nodecodes
.
vlist
98
local
whatsit_code
=
nodecodes
.
whatsit
99
local
userdefined_code
=
whatsitcodes
.
userdefined
100 101
local
nodepool
=
nuts
.
pool
102 103
local
new_hlist
=
nodepool
.
hlist
104
local
new_usernode
=
nodepool
.
usernode
105
local
latelua
=
nodepool
.
latelua
106 107
local
texgetdimen
=
tex
.
getdimen
108
local
texgetcount
=
tex
.
getcount
109
local
texget
=
tex
.
get
110 111
local
isleftpage
=
layouts
.
status
.
isleftpage
112
local
registertogether
=
builders
.
paragraphs
.
registertogether
113 114
local
paragraphs
=
typesetters
.
paragraphs
115
local
addtoline
=
paragraphs
.
addtoline
116
local
moveinline
=
paragraphs
.
moveinline
117
local
calculatedelta
=
paragraphs
.
calculatedelta
118 119
local
a_linenumber
=
attributes
.
private
(
'
linenumber
'
)
120 121
local
inline_mark
=
nodepool
.
userids
[
"
margins.inline
"
]
122 123
local
jobpositions
=
job
.
positions
124
local
getposition
=
jobpositions
.
get
125
local
setposition
=
jobpositions
.
set
126
local
getreserved
=
jobpositions
.
getreserved
127 128
local
margins
=
{
}
129
typesetters
.
margins
=
margins
130 131
local
locations
=
{
v_left
,
v_right
,
v_inner
,
v_outer
}
-- order might change
132
local
categories
=
{
}
133
local
displaystore
=
{
}
-- [category][location][scope]
134
local
inlinestore
=
{
}
-- [number]
135
local
nofsaved
=
0
136
local
nofstored
=
0
137
local
nofinlined
=
0
138
local
nofdelayed
=
0
139
local
nofinjected
=
0
140
local
h_anchors
=
0
141
local
v_anchors
=
0
142 143
local
mt1
=
{
144
__index
=
function
(
t
,
location
)
145
local
v
=
{
[
v_local
]
=
{
}
,
[
v_global
]
=
{
}
}
146
t
[
location
]
=
v
147
return
v
148
end
149
}
150 151
local
mt2
=
{
152
__index
=
function
(
stores
,
category
)
153
categories
[
#
categories
+
1
]
=
category
154
local
v
=
{
}
155
setmetatable
(
v
,
mt1
)
156
stores
[
category
]
=
v
157
return
v
158
end
159
}
160 161
setmetatable
(
displaystore
,
mt2
)
162 163
local
defaults
=
{
164
__index
=
{
165
location
=
v_left
,
166
align
=
v_normal
,
-- not used
167
method
=
"
"
,
168
name
=
"
"
,
169
threshold
=
0
,
-- .25ex
170
margin
=
v_normal
,
171
scope
=
v_global
,
172
distance
=
0
,
173
hoffset
=
0
,
174
voffset
=
0
,
175
category
=
v_default
,
176
line
=
0
,
177
vstack
=
0
,
178
dy
=
0
,
179
baseline
=
false
,
180
inline
=
false
,
181
leftskip
=
0
,
182
rightskip
=
0
,
183
option
=
{
}
184
}
185
}
186 187
local
enablelocal
,
enableglobal
-- forward reference (delayed initialization)
188 189
local
function
showstore
(
store
,
banner
,
location
)
190
if
next
(
store
)
then
191
for
i
,
si
in
table
.
sortedpairs
(
store
)
do
192
local
si
=
store
[
i
]
193
report_margindata
(
"
%s: stored in %a at %s: %a => %s
"
,
banner
,
location
,
i
,
validstring
(
si
.
name
,
"
no name
"
)
,
nodes
.
toutf
(
getlist
(
si
.
box
)
)
)
194
end
195
else
196
report_margindata
(
"
%s: nothing stored in location %a
"
,
banner
,
location
)
197
end
198
end
199 200
function
margins
.
save
(
t
)
201
setmetatable
(
t
,
defaults
)
202
local
content
=
takebox
(
t
.
number
)
203
local
location
=
t
.
location
204
local
category
=
t
.
category
205
local
inline
=
t
.
inline
206
local
scope
=
t
.
scope
207
local
name
=
t
.
name
208
local
option
=
t
.
option
209
local
stack
=
t
.
stack
210
if
option
then
211
option
=
settings_to_hash
(
option
)
212
t
.
option
=
option
213
end
214
if
not
content
then
215
report_margindata
(
"
ignoring empty margin data %a
"
,
location
or
"
unknown
"
)
216
return
217
end
218
setprop
(
content
,
"
specialcontent
"
,
"
margindata
"
)
219
local
store
220
if
inline
then
221
store
=
inlinestore
222
else
223
store
=
displaystore
[
category
]
[
location
]
224
if
not
store
then
225
report_margindata
(
"
invalid location %a
"
,
location
)
226
return
227
end
228
store
=
store
[
scope
]
229
end
230
if
not
store
then
231
report_margindata
(
"
invalid scope %a
"
,
scope
)
232
return
233
end
234
if
enablelocal
and
scope
=
=
v_local
then
235
enablelocal
(
)
236
if
enableglobal
then
237
enableglobal
(
)
-- is the fallback
238
end
239
elseif
enableglobal
and
scope
=
=
v_global
then
240
enableglobal
(
)
241
end
242
nofsaved
=
nofsaved
+
1
243
nofstored
=
nofstored
+
1
244
if
trace_marginstack
then
245
showstore
(
store
,
"
before
"
,
location
)
246
end
247
if
name
and
name
~
=
"
"
then
248
-- this can be used to overload
249
if
inlinestore
then
-- todo: inline store has to be done differently (not sparse)
250
local
t
=
sortedkeys
(
store
)
for
j
=
#
t
,
1
,
-1
do
local
i
=
t
[
j
]
251
local
si
=
store
[
i
]
252
if
si
.
name
=
=
name
then
253
local
s
=
remove
(
store
,
i
)
254
flushnodelist
(
s
.
box
)
255
end
256
end
257
else
258
for
i
=
#
store
,
1
,
-1
do
259
local
si
=
store
[
i
]
260
if
si
.
name
=
=
name
then
261
local
s
=
remove
(
store
,
i
)
262
flushnodelist
(
s
.
box
)
263
end
264
end
265
end
266
if
trace_marginstack
then
267
showstore
(
store
,
"
between
"
,
location
)
268
end
269
end
270
if
t
.
number
then
271
local
leftmargindistance
=
texgetdimen
(
"
naturalleftmargindistance
"
)
272
local
rightmargindistance
=
texgetdimen
(
"
naturalrightmargindistance
"
)
273
local
strutbox
=
getbox
(
"
strutbox
"
)
274
local
_
,
strutht
,
strutdp
=
getwhd
(
strutbox
)
275
-- better make a new table and make t entry in t
276
t
.
box
=
content
277
t
.
n
=
nofsaved
278
-- used later (we will clean up this natural mess later)
279
-- nice is to make a special status table mechanism
280
t
.
strutheight
=
strutht
281
t
.
strutdepth
=
strutdp
282
-- beware: can be different from the applied one (we're not in forgetall)
283
t
.
leftskip
=
texget
(
"
leftskip
"
,
false
)
284
t
.
rightskip
=
texget
(
"
rightskip
"
,
false
)
285
--
286
t
.
leftmargindistance
=
leftmargindistance
-- todo:layoutstatus table
287
t
.
rightmargindistance
=
rightmargindistance
288
t
.
leftedgedistance
=
texgetdimen
(
"
naturalleftedgedistance
"
)
289
+
texgetdimen
(
"
leftmarginwidth
"
)
290
+
leftmargindistance
291
t
.
rightedgedistance
=
texgetdimen
(
"
naturalrightedgedistance
"
)
292
+
texgetdimen
(
"
rightmarginwidth
"
)
293
+
rightmargindistance
294
t
.
lineheight
=
texgetdimen
(
"
lineheight
"
)
295
--
296
-- t.realpageno = texgetcount("realpageno")
297
if
inline
then
298
local
n
=
new_usernode
(
inline_mark
,
nofsaved
)
299
setattrlist
(
n
,
true
)
300
context
(
tonode
(
n
)
)
-- or use a normal node
301
store
[
nofsaved
]
=
t
-- no insert
302
nofinlined
=
nofinlined
+
1
303
else
304
insert
(
store
,
t
)
305
end
306
end
307
if
trace_marginstack
then
308
showstore
(
store
,
"
after
"
,
location
)
309
end
310
if
trace_margindata
then
311
report_margindata
(
"
saved %a, location %a, scope %a, inline %a
"
,
nofsaved
,
location
,
scope
,
inline
)
312
end
313
end
314 315
-- Actually it's an advantage to have them all anchored left (tags and such)
316
-- we could keep them in store and flush in stage two but we might want to
317
-- do more before that so we need the content to be there unless we can be
318
-- sure that we flush this first which might not be the case in the future.
319
--
320
-- When the prototype inner/outer code that was part of this proved to be
321
-- okay it was moved elsewhere.
322 323
local
function
realign
(
current
,
candidate
)
324
local
location
=
candidate
.
location
325
local
margin
=
candidate
.
margin
326
local
hoffset
=
candidate
.
hoffset
327
local
distance
=
candidate
.
distance
328
local
hsize
=
candidate
.
hsize
329
local
width
=
candidate
.
width
330
local
align
=
candidate
.
align
331
local
inline
=
candidate
.
inline
332
local
anchor
=
candidate
.
anchor
333
local
hook
=
candidate
.
hook
334
local
scope
=
candidate
.
scope
335
local
option
=
candidate
.
option
336
local
reverse
=
hook
.
reverse
337
local
atleft
=
true
338
local
hmove
=
0
339
local
delta
=
0
340
local
leftpage
=
isleftpage
(
)
341
local
leftdelta
=
0
342
local
rightdelta
=
0
343
local
leftdistance
=
distance
344
local
rightdistance
=
distance
345
--
346
if
not
anchor
or
anchor
=
=
"
"
then
347
anchor
=
v_text
-- this has to become more clever: region:0|column:n|column
348
end
349
if
margin
=
=
v_normal
then
350
--
351
elseif
margin
=
=
v_local
then
352
leftdelta
=
-
candidate
.
leftskip
353
rightdelta
=
candidate
.
rightskip
354
elseif
margin
=
=
v_margin
then
355
leftdistance
=
candidate
.
leftmargindistance
356
rightdistance
=
candidate
.
rightmargindistance
357
elseif
margin
=
=
v_edge
then
358
leftdistance
=
candidate
.
leftedgedistance
359
rightdistance
=
candidate
.
rightedgedistance
360
end
361
if
leftpage
then
362
leftdistance
,
rightdistance
=
rightdistance
,
leftdistance
363
end
364
if
location
=
=
v_right
then
365
atleft
=
false
366
elseif
location
=
=
v_inner
then
367
if
leftpage
then
368
atleft
=
false
369
end
370
elseif
location
=
=
v_outer
then
371
if
not
leftpage
then
372
atleft
=
false
373
end
374
else
375
-- v_left
376
end
377 378
local
islocal
=
scope
=
=
v_local
379
local
area
=
(
not
islocal
or
option
[
v_text
]
)
and
anchor
or
nil
380 381
if
atleft
then
382
delta
=
hoffset
+
leftdelta
+
leftdistance
383
else
384
delta
=
hoffset
+
rightdelta
+
rightdistance
385
end
386 387
local
delta
,
hmove
=
calculatedelta
(
388
hook
,
-- the line
389
width
,
-- width of object
390
delta
,
-- offset
391
atleft
,
392
islocal
,
-- islocal
393
option
[
v_paragraph
]
,
-- followshape
394
area
-- relative to area
395
)
396 397
if
hmove
~
=
0
then
398
delta
=
delta
+
hmove
399
if
trace_margindata
then
400
report_margindata
(
"
realigned %a, location %a, margin %a, move %p
"
,
candidate
.
n
,
location
,
margin
,
hmove
)
401
end
402
else
403
if
trace_margindata
then
404
report_margindata
(
"
realigned %a, location %a, margin %a
"
,
candidate
.
n
,
location
,
margin
)
405
end
406
end
407
moveinline
(
hook
,
candidate
.
node
,
delta
)
408
end
409 410
local
function
realigned
(
current
,
candidate
)
411
realign
(
current
,
candidate
)
412
nofdelayed
=
nofdelayed
-
1
413
setprop
(
current
,
"
margindata
"
,
false
)
414
return
true
415
end
416 417
-- Stacking is done in two ways: the v_yes option stacks per paragraph (or line,
418
-- depending on what gets by) and mostly concerns margin data dat got set at more or
419
-- less the same time. The v_continue option uses position tracking and works on
420
-- larger range. However, crossing pages is not part of it. Anyway, when you have
421
-- such messed up margin data you'd better think twice.
422
--
423
-- The stacked table keeps track (per location) of the offsets (the v_yes case). This
424
-- table gets saved when the v_continue case is active. We use a special variant
425
-- of position tracking, after all we only need the page number and vertical position.
426 427
local
validstacknames
=
{
428
[
v_left
]
=
v_left
,
429
[
v_right
]
=
v_right
,
430
[
v_inner
]
=
v_inner
,
431
[
v_outer
]
=
v_outer
,
432
}
433 434
local
cache
=
{
}
435
local
stacked
=
{
[
v_yes
]
=
{
}
,
[
v_continue
]
=
{
}
}
436
local
anchors
=
{
[
v_yes
]
=
{
}
,
[
v_continue
]
=
{
}
}
437 438
local
function
resetstacked
(
all
)
439
stacked
[
v_yes
]
=
{
}
440
anchors
[
v_yes
]
=
{
}
441
if
all
then
442
stacked
[
v_continue
]
=
{
}
443
anchors
[
v_continue
]
=
{
}
444
end
445
end
446 447
-- anchors are only set for lines that have a note
448 449
local
function
sa
(
specification
)
-- maybe l/r keys ipv left/right keys
450
local
tag
=
specification
.
tag
451
local
p
=
cache
[
tag
]
452
if
p
then
453
if
trace_marginstack
then
454
report_margindata
(
"
updating anchor %a
"
,
tag
)
455
end
456
p
.
p
=
true
457
p
.
y
=
true
458
-- maybe settobesaved first
459
setposition
(
"
md:v
"
,
tag
,
p
)
460
cache
[
tag
]
=
nil
-- do this later, per page a cleanup
461
end
462
end
463 464
local
function
setanchor
(
v_anchor
)
-- freezes the global here
465
return
latelua
{
action
=
sa
,
tag
=
v_anchor
}
466
end
467 468
local
function
aa
(
specification
)
-- maybe l/r keys ipv left/right keys
469
local
tag
=
specification
.
tag
470
local
n
=
specification
.
n
471
local
p
=
jobpositions
.
gettobesaved
(
'
md:v
'
,
tag
)
472
if
p
then
473
if
trace_marginstack
then
474
report_margindata
(
"
updating injected %a
"
,
tag
)
475
end
476
local
pages
=
p
.
pages
477
if
not
pages
then
478
pages
=
{
}
479
p
.
pages
=
pages
480
end
481
pages
[
n
]
=
texgetcount
(
"
realpageno
"
)
482
elseif
trace_marginstack
then
483
report_margindata
(
"
not updating injected %a
"
,
tag
)
484
end
485
end
486 487
local
function
addtoanchor
(
v_anchor
,
n
)
-- freezes the global here
488
return
latelua
{
action
=
aa
,
tag
=
v_anchor
,
n
=
n
}
489
end
490 491
local
function
markovershoot
(
current
)
-- todo: alleen als offset > line
492
v_anchors
=
v_anchors
+
1
493
cache
[
v_anchors
]
=
fastcopy
(
stacked
)
494
local
anchor
=
setanchor
(
v_anchors
)
495
-- local list = hpacknodes(setlink(anchor,getlist(current))) -- not ok, we need to retain width
496
-- local list = setlink(anchor,getlist(current)) -- why not this ... better play safe
497
local
list
=
hpacknodes
(
setlink
(
anchor
,
getlist
(
current
)
)
,
getwidth
(
current
)
,
"
exactly
"
)
--
498
if
trace_marginstack
then
499
report_margindata
(
"
marking anchor %a
"
,
v_anchors
)
500
end
501
setlist
(
current
,
list
)
502
end
503 504
local
function
inject
(
parent
,
head
,
candidate
)
505
local
box
=
candidate
.
box
506
if
not
box
then
507
return
head
,
nil
,
false
-- we can have empty texts
508
end
509
local
width
,
height
,
depth
510
=
getwhd
(
box
)
511
local
shift
=
getshift
(
box
)
512
local
stack
=
candidate
.
stack
513
local
stackname
=
candidate
.
stackname
514
local
location
=
candidate
.
location
515
local
method
=
candidate
.
method
516
local
voffset
=
candidate
.
voffset
517
local
line
=
candidate
.
line
518
local
baseline
=
candidate
.
baseline
519
local
strutheight
=
candidate
.
strutheight
520
local
strutdepth
=
candidate
.
strutdepth
521
local
inline
=
candidate
.
inline
522
local
psubtype
=
getsubtype
(
parent
)
523
-- This stackname is experimental and therefore undocumented and basically
524
-- unsupported. It was introduced when we needed to support overlapping
525
-- of different anchors.
526
if
not
stackname
or
stackname
=
=
"
"
then
527
stackname
=
location
528
else
529
stackname
=
validstacknames
[
stackname
]
or
location
530
end
531
local
isstacked
=
stack
=
=
v_continue
or
stack
=
=
v_yes
532
local
offset
=
isstacked
and
stacked
[
stack
]
[
stackname
]
533
local
firstonstack
=
offset
=
=
false
or
offset
=
=
nil
534
nofinjected
=
nofinjected
+
1
535
nofdelayed
=
nofdelayed
+
1
536
-- yet untested
537
baseline
=
tonumber
(
baseline
)
538
if
not
baseline
then
539
baseline
=
toboolean
(
baseline
)
540
end
541
--
542
if
baseline
=
=
true
then
543
baseline
=
false
544
else
545
baseline
=
tonumber
(
baseline
)
546
if
not
baseline
or
baseline
<
=
0
then
547
-- in case we have a box of width 0 that is not analyzed
548
baseline
=
false
-- strutheight -- actually a hack
549
end
550
end
551
candidate
.
width
=
width
552
candidate
.
hsize
=
getwidth
(
parent
)
-- we can also pass textwidth
553
candidate
.
psubtype
=
psubtype
554
candidate
.
stackname
=
stackname
555
if
trace_margindata
then
556
report_margindata
(
"
processing, index %s, height %p, depth %p, parent %a, method %a
"
,
candidate
.
n
,
height
,
depth
,
listcodes
[
psubtype
]
,
method
)
557
end
558
-- Overlap detection is somewhat complex because we have display and inline
559
-- notes mixed as well as inner and outer positioning. We do need to
560
-- handle it in the stream because we also keep lines together so we keep
561
-- track of page numbers of notes.
562 563
if
isstacked
then
564
firstonstack
=
true
565
local
anchor
=
getposition
(
"
md:v
"
)
566
if
anchor
and
(
location
=
=
v_inner
or
location
=
=
v_outer
)
then
567
local
pages
=
anchor
.
pages
568
if
pages
then
569
local
page
=
pages
[
nofinjected
]
570
if
page
then
571
if
isleftpage
(
page
)
then
572
stackname
=
location
=
=
v_inner
and
v_right
or
v_left
573
else
574
stackname
=
location
=
=
v_inner
and
v_left
or
v_right
575
end
576
candidate
.
stackname
=
stackname
577
offset
=
stack
and
stack
~
=
"
"
and
stacked
[
stack
]
[
stackname
]
578
end
579
end
580
end
581
local
current
=
v_anchors
+
1
582
local
previous
=
anchors
[
stack
]
[
stackname
]
583
if
trace_margindata
then
584
report_margindata
(
"
anchor %i, offset so far %p
"
,
current
,
offset
or
0
)
585
end
586
local
ap
=
anchor
and
anchor
[
previous
]
587
local
ac
=
anchor
and
anchor
[
current
]
588
if
not
previous
then
589
elseif
previous
=
=
current
then
590
firstonstack
=
false
591
elseif
ap
and
ac
and
ap
.
p
=
=
ac
.
p
then
592
local
distance
=
(
ap
.
y
or
0
)
-
(
ac
.
y
or
0
)
593
if
trace_margindata
then
594
report_margindata
(
"
distance %p
"
,
distance
)
595
end
596
if
offset
>
distance
then
597
-- we already overflow
598
offset
=
offset
-
distance
599
firstonstack
=
false
600
else
601
offset
=
0
602
end
603
else
604
-- what to do
605
end
606
anchors
[
v_yes
]
[
stackname
]
=
current
607
anchors
[
v_continue
]
[
stackname
]
=
current
608
if
firstonstack
then
609
offset
=
0
610
end
611
offset
=
offset
+
candidate
.
dy
-- always
612
shift
=
shift
+
offset
613
else
614
if
firstonstack
then
615
offset
=
0
616
end
617
offset
=
offset
+
candidate
.
dy
-- always
618
shift
=
shift
+
offset
619
end
620
-- Maybe we also need to patch offset when we apply methods, but how ...
621
-- This needs a bit of playing as it depends on the stack setting of the
622
-- following which we don't know yet ... so, consider stacking partially
623
-- experimental.
624
if
method
=
=
v_top
then
625
local
delta
=
height
-
getheight
(
parent
)
626
if
trace_margindata
then
627
report_margindata
(
"
top aligned by %p
"
,
delta
)
628
end
629
if
delta
<
candidate
.
threshold
then
-- often we need a negative threshold here
630
shift
=
shift
+
voffset
+
delta
631
end
632
elseif
method
=
=
v_line
then
633
local
_
,
ph
,
pd
=
getwhd
(
parent
)
634
if
pd
=
=
0
then
635
local
delta
=
height
-
ph
636
if
trace_margindata
then
637
report_margindata
(
"
top aligned by %p (no depth)
"
,
delta
)
638
end
639
if
delta
<
candidate
.
threshold
then
-- often we need a negative threshold here
640
shift
=
shift
+
voffset
+
delta
641
end
642
end
643
elseif
method
=
=
v_first
then
644
if
baseline
then
645
shift
=
shift
+
voffset
+
height
-
baseline
-- option
646
else
647
shift
=
shift
+
voffset
-- normal
648
end
649
if
trace_margindata
then
650
report_margindata
(
"
first aligned
"
)
651
end
652
elseif
method
=
=
v_depth
then
653
local
delta
=
strutdepth
654
if
trace_margindata
then
655
report_margindata
(
"
depth aligned by %p
"
,
delta
)
656
end
657
shift
=
shift
+
voffset
+
delta
658
elseif
method
=
=
v_height
then
659
local
delta
=
-
strutheight
660
if
trace_margindata
then
661
report_margindata
(
"
height aligned by %p
"
,
delta
)
662
end
663
shift
=
shift
+
voffset
+
delta
664
elseif
voffset
~
=
0
then
665
if
trace_margindata
then
666
report_margindata
(
"
voffset %p applied
"
,
voffset
)
667
end
668
shift
=
shift
+
voffset
669
end
670
-- -- --
671
if
line
~
=
0
then
672
local
delta
=
line
*
candidate
.
lineheight
673
if
trace_margindata
then
674
report_margindata
(
"
offset %p applied to line %s
"
,
delta
,
line
)
675
end
676
shift
=
shift
+
delta
677
offset
=
offset
+
delta
678
end
679
setshift
(
box
,
shift
)
680
setwidth
(
box
,
0
)
-- not needed when wrapped
681
--
682
if
isstacked
then
683
setlink
(
box
,
addtoanchor
(
v_anchors
,
nofinjected
)
)
684
box
=
new_hlist
(
box
)
685
-- set height / depth ?
686
end
687
--
688
candidate
.
hook
,
candidate
.
node
=
addtoline
(
parent
,
box
)
689
--
690
setprop
(
box
,
"
margindata
"
,
candidate
)
691
if
trace_margindata
then
692
report_margindata
(
"
injected, location %a, stack %a, shift %p
"
,
location
,
stackname
,
shift
)
693
end
694
-- we need to add line etc to offset as well
695
offset
=
offset
+
depth
696
local
room
=
{
697
height
=
height
,
698
depth
=
offset
,
699
slack
=
candidate
.
bottomspace
,
-- todo: 'depth' => strutdepth
700
lineheight
=
candidate
.
lineheight
,
-- only for tracing
701
stacked
=
inline
and
isstacked
,
702
}
703
offset
=
offset
+
height
704
-- we need a restart ... when there is no overlap at all
705
stacked
[
v_yes
]
[
stackname
]
=
offset
706
stacked
[
v_continue
]
[
stackname
]
=
offset
707
-- todo: if no real depth then zero
708
if
trace_margindata
then
709
report_margindata
(
"
status, offset %s
"
,
offset
)
710
end
711
return
getlist
(
parent
)
,
room
,
inline
and
isstacked
or
(
stack
=
=
v_continue
)
712
end
713 714
local
function
flushinline
(
parent
,
head
)
715
local
current
=
head
716
local
done
=
false
717
local
continue
=
false
718
local
room
,
don
,
con
,
list
719
while
current
and
nofinlined
>
0
do
720
local
id
=
getid
(
current
)
721
if
id
=
=
whatsit_code
then
722
if
getsubtype
(
current
)
=
=
userdefined_code
and
getprop
(
current
,
"
id
"
)
=
=
inline_mark
then
723
local
n
=
getprop
(
current
,
"
data
"
)
724
local
candidate
=
inlinestore
[
n
]
725
if
candidate
then
-- no vpack, as we want to realign
726
inlinestore
[
n
]
=
nil
727
nofinlined
=
nofinlined
-
1
728
head
,
room
,
con
=
inject
(
parent
,
head
,
candidate
)
-- maybe return applied offset
729
done
=
true
730
continue
=
continue
or
con
731
nofstored
=
nofstored
-
1
732
if
room
and
room
.
stacked
then
733
-- for now we also check for inline+yes/continue, maybe someday no such check
734
-- will happen; we can assume most inlines are one line heigh; also this
735
-- together feature can become optional
736
registertogether
(
parent
,
room
)
737
end
738
end
739
end
740
elseif
id
=
=
hlist_code
or
id
=
=
vlist_code
then
741
-- optional (but sometimes needed)
742
list
,
don
,
con
=
flushinline
(
current
,
getlist
(
current
)
)
743
setlist
(
current
,
list
)
744
continue
=
continue
or
con
745
done
=
done
or
don
746
end
747
current
=
getnext
(
current
)
748
end
749
return
head
,
done
,
continue
750
end
751 752
local
function
flushed
(
scope
,
parent
)
-- current is hlist
753
local
head
=
getlist
(
parent
)
754
local
done
=
false
755
local
continue
=
false
756
local
room
,
con
,
don
757
for
c
=
1
,
#
categories
do
758
local
category
=
categories
[
c
]
759
for
l
=
1
,
#
locations
do
760
local
location
=
locations
[
l
]
761
local
store
=
displaystore
[
category
]
[
location
]
[
scope
]
762
if
store
then
763
while
true
do
764
local
candidate
=
remove
(
store
,
1
)
-- brr, local stores are sparse
765
if
candidate
then
-- no vpack, as we want to realign
766
head
,
room
,
con
=
inject
(
parent
,
head
,
candidate
)
767
done
=
true
768
continue
=
continue
or
con
769
nofstored
=
nofstored
-
1
770
if
room
then
771
registertogether
(
parent
,
room
)
772
end
773
else
774
break
775
end
776
end
777
else
778
-- report_margindata("fatal error: invalid category %a",category or "?")
779
end
780
end
781
end
782
if
nofinlined
>
0
then
783
if
done
then
784
setlist
(
parent
,
head
)
785
end
786
head
,
don
,
con
=
flushinline
(
parent
,
head
)
787
continue
=
continue
or
con
788
done
=
done
or
don
789
end
790
if
done
then
791
local
a
=
getattr
(
head
,
a_linenumber
)
-- hack .. we need a more decent critical attribute inheritance mechanism
792
if
false
then
793
local
l
=
hpacknodes
(
head
,
getwidth
(
parent
)
,
"
exactly
"
)
794
setlist
(
parent
,
l
)
795
if
a
then
796
setattr
(
l
,
a_linenumber
,
a
)
797
end
798
else
799
-- because packing messes up profiling
800
setlist
(
parent
,
head
)
801
if
a
then
802
setattr
(
parent
,
a_linenumber
,
a
)
803
end
804
end
805
end
806
return
done
,
continue
807
end
808 809
-- only when group : vbox|vmode_par
810
-- only when subtype : line, box (no indent alignment cell)
811 812
local
function
handler
(
scope
,
head
,
group
)
813
if
nofstored
>
0
then
814
if
trace_margindata
then
815
report_margindata
(
"
flushing stage one, stored %s, scope %s, delayed %s, group %a
"
,
nofstored
,
scope
,
nofdelayed
,
group
)
816
end
817
local
current
=
head
818
local
done
=
false
-- for tracing only
819
while
current
do
820
local
id
=
getid
(
current
)
821
if
(
id
=
=
vlist_code
or
id
=
=
hlist_code
)
and
getprop
(
current
,
"
margindata
"
)
=
=
nil
then
822
local
don
,
continue
=
flushed
(
scope
,
current
)
823
if
don
then
824
done
=
true
825
setprop
(
current
,
"
margindata
"
,
false
)
-- signal to prevent duplicate processing
826
if
continue
then
827
markovershoot
(
current
)
828
end
829
if
nofstored
<
=
0
then
830
break
831
end
832
end
833
end
834
current
=
getnext
(
current
)
835
end
836
if
trace_margindata
then
837
if
done
then
838
report_margindata
(
"
flushing stage one, done, %s left
"
,
nofstored
)
839
else
840
report_margindata
(
"
flushing stage one, nothing done, %s left
"
,
nofstored
)
841
end
842
end
843
resetstacked
(
)
844
end
845
return
head
846
end
847 848
local
trialtypesetting
=
context
.
trialtypesetting
849 850
-- maybe change this to an action applied to the to be shipped out box (that is
851
-- the mvl list in there so that we don't need to traverse global
852 853
function
margins
.
localhandler
(
head
,
group
)
-- sometimes group is "" which is weird
854 855
if
trialtypesetting
(
)
then
856
return
head
857
end
858 859
local
inhibit
=
conditionals
.
inhibitmargindata
860
if
inhibit
then
861
if
trace_margingroup
then
862
report_margindata
(
"
ignored 3, group %a, stored %s, inhibit %a
"
,
group
,
nofstored
,
inhibit
)
863
end
864
return
head
865
end
866
if
nofstored
>
0
then
867
return
handler
(
v_local
,
head
,
group
)
868
end
869
if
trace_margingroup
then
870
report_margindata
(
"
ignored 4, group %a, stored %s, inhibit %a
"
,
group
,
nofstored
,
inhibit
)
871
end
872
return
head
873
end
874 875
function
margins
.
globalhandler
(
head
,
group
)
-- check group
876 877
if
trialtypesetting
(
)
then
878
return
head
,
false
879
end
880 881
local
inhibit
=
conditionals
.
inhibitmargindata
882
if
inhibit
or
nofstored
=
=
0
then
883
if
trace_margingroup
then
884
report_margindata
(
"
ignored 1, group %a, stored %s, inhibit %a
"
,
group
,
nofstored
,
inhibit
)
885
end
886
return
head
887
elseif
group
=
=
"
hmode_par
"
then
888
return
handler
(
v_global
,
head
,
group
)
889
elseif
group
=
=
"
vmode_par
"
then
-- experiment (for alignments)
890
return
handler
(
v_global
,
head
,
group
)
891
-- this needs checking as we then get quite some one liners to process and
892
-- we cannot look ahead then:
893
elseif
group
=
=
"
box
"
then
-- experiment (for alignments)
894
return
handler
(
v_global
,
head
,
group
)
895
elseif
group
=
=
"
alignment
"
then
-- experiment (for alignments)
896
return
handler
(
v_global
,
head
,
group
)
897
else
898
if
trace_margingroup
then
899
report_margindata
(
"
ignored 2, group %a, stored %s, inhibit %a
"
,
group
,
nofstored
,
inhibit
)
900
end
901
return
head
902
end
903
end
904 905
local
function
finalhandler
(
head
)
906
if
nofdelayed
>
0
then
907
local
current
=
head
908
while
current
and
nofdelayed
>
0
do
909
local
id
=
getid
(
current
)
910
if
id
=
=
hlist_code
then
-- only lines?
911
local
a
=
getprop
(
current
,
"
margindata
"
)
912
if
not
a
then
913
finalhandler
(
getlist
(
current
)
)
914
elseif
realigned
(
current
,
a
)
then
915
if
nofdelayed
=
=
0
then
916
return
head
,
true
917
end
918
end
919
elseif
id
=
=
vlist_code
then
920
finalhandler
(
getlist
(
current
)
)
921
end
922
current
=
getnext
(
current
)
923
end
924
end
925
return
head
926
end
927 928
function
margins
.
finalhandler
(
head
)
929
if
nofdelayed
>
0
then
930
if
trace_margindata
then
931
report_margindata
(
"
flushing stage two, instore: %s, delayed: %s
"
,
nofstored
,
nofdelayed
)
932
end
933
head
=
finalhandler
(
head
)
934
resetstacked
(
nofdelayed
=
=
0
)
935
else
936
resetstacked
(
)
937
end
938
return
head
939
end
940 941
-- Somehow the vbox builder (in combinations) gets pretty confused and decides to
942
-- go horizontal. So this needs more testing.
943 944
enablelocal
=
function
(
)
945
enableaction
(
"
finalizers
"
,
"
typesetters.margins.localhandler
"
)
946
enableaction
(
"
shipouts
"
,
"
typesetters.margins.finalhandler
"
)
947
enablelocal
=
nil
948
end
949 950
enableglobal
=
function
(
)
951
enableaction
(
"
mvlbuilders
"
,
"
typesetters.margins.globalhandler
"
)
952
enableaction
(
"
shipouts
"
,
"
typesetters.margins.finalhandler
"
)
953
enableglobal
=
nil
954
end
955 956
statistics
.
register
(
"
margin data
"
,
function
(
)
957
if
nofsaved
>
0
then
958
return
format
(
"
%s entries, %s pending
"
,
nofsaved
,
nofdelayed
)
959
else
960
return
nil
961
end
962
end
)
963 964
interfaces
.
implement
{
965
name
=
"
savemargindata
"
,
966
actions
=
margins
.
save
,
967
arguments
=
{
968
{
969
{
"
location
"
}
,
970
{
"
method
"
}
,
971
{
"
category
"
}
,
972
{
"
name
"
}
,
973
{
"
scope
"
}
,
974
{
"
number
"
,
"
integer
"
}
,
975
{
"
margin
"
}
,
976
{
"
distance
"
,
"
dimen
"
}
,
977
{
"
hoffset
"
,
"
dimen
"
}
,
978
{
"
voffset
"
,
"
dimen
"
}
,
979
{
"
dy
"
,
"
dimen
"
}
,
980
{
"
bottomspace
"
,
"
dimen
"
}
,
981
{
"
baseline
"
}
,
-- dimen or string or
982
{
"
threshold
"
,
"
dimen
"
}
,
983
{
"
inline
"
,
"
boolean
"
}
,
984
{
"
anchor
"
}
,
985
-- { "leftskip", "dimen" },
986
-- { "rightskip", "dimen" },
987
{
"
align
"
}
,
988
{
"
option
"
}
,
989
{
"
line
"
,
"
integer
"
}
,
990
{
"
index
"
,
"
integer
"
}
,
991
{
"
stackname
"
}
,
992
{
"
stack
"
}
,
993
}
994
}
995
}
996