1if not modules then modules = { } end modules ['spac-ver'] = {
2 version = 1.001,
3 optimize = true,
4 comment = "companion to spac-ver.mkiv",
5 author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
6 copyright = "PRAGMA ADE / ConTeXt Development Team",
7 license = "see context related readme files"
8}
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34local next, type, tonumber = next, type, tonumber
35local gmatch, concat = string.gmatch, table.concat
36local lpegmatch = lpeg.match
37local unpack = unpack or table.unpack
38local allocate = utilities.storage.allocate
39local todimen = string.todimen
40local formatters = string.formatters
41
42local nodes = nodes
43local trackers = trackers
44local attributes = attributes
45local context = context
46local tex = tex
47
48local texlists = tex.lists
49local texget = tex.get
50local texgetcount = tex.getcount
51local texgetdimen = tex.getdimen
52local texgetglue = tex.getglue
53local texset = tex.set
54local texsetdimen = tex.setdimen
55local texsetcount = tex.setcount
56local texnest = tex.nest
57local texgetbox = tex.getbox
58
59local buildpage = tex.triggerbuildpage
60
61local variables = interfaces.variables
62
63local v_local = variables["local"]
64local v_global = variables["global"]
65local v_box = variables.box
66
67local v_split = variables.split
68local v_min = variables.min
69local v_max = variables.max
70local v_none = variables.none
71local v_first = variables.first
72local v_last = variables.last
73local v_top = variables.top
74local v_bottom = variables.bottom
75local v_maxheight = variables.maxheight
76local v_minheight = variables.minheight
77local v_mindepth = variables.mindepth
78local v_maxdepth = variables.maxdepth
79local v_offset = variables.offset
80local v_strut = variables.strut
81
82local v_hfraction = variables.hfraction
83local v_dfraction = variables.dfraction
84local v_bfraction = variables.bfraction
85local v_tlines = variables.tlines
86local v_blines = variables.blines
87
88
89
90local trace_vbox_vspacing = false trackers.register("vspacing.vbox", function(v) trace_vbox_vspacing = v end)
91local trace_page_vspacing = false trackers.register("vspacing.page", function(v) trace_page_vspacing = v end)
92local trace_collect_vspacing = false trackers.register("vspacing.collect", function(v) trace_collect_vspacing = v end)
93local trace_vspacing = false trackers.register("vspacing.spacing", function(v) trace_vspacing = v end)
94local trace_vsnapping = false trackers.register("vspacing.snapping", function(v) trace_vsnapping = v end)
95local trace_specials = false trackers.register("vspacing.specials", function(v) trace_specials = v end)
96
97local remove_math_skips = true directives.register("vspacing.removemathskips", function(v) remnove_math_skips = v end)
98
99local report_vspacing = logs.reporter("vspacing","spacing")
100local report_collapser = logs.reporter("vspacing","collapsing")
101local report_snapper = logs.reporter("vspacing","snapping")
102local report_specials = logs.reporter("vspacing","specials")
103
104local a_skipcategory = attributes.private('skipcategory')
105local a_skippenalty = attributes.private('skippenalty')
106local a_skiporder = attributes.private('skiporder')
107local a_snapmethod = attributes.private('snapmethod')
108local a_snapvbox = attributes.private('snapvbox')
109
110local d_bodyfontstrutheight = tex.isdimen("bodyfontstrutheight")
111local d_bodyfontstrutdepth = tex.isdimen("bodyfontstrutdepth")
112local d_globalbodyfontstrutheight = tex.isdimen("globalbodyfontstrutheight")
113local d_globalbodyfontstrutdepth = tex.isdimen("globalbodyfontstrutdepth")
114
115local d_strutdp = tex.isdimen("strutdp")
116local d_spac_overlay = tex.isdimen("d_spac_overlay")
117
118local c_spac_vspacing_ignore_parskip = tex.iscount("c_spac_vspacing_ignore_parskip")
119
120local nuts = nodes.nuts
121local tonut = nuts.tonut
122
123local getnext = nuts.getnext
124local setlink = nuts.setlink
125local getprev = nuts.getprev
126local getid = nuts.getid
127local getlist = nuts.getlist
128local setlist = nuts.setlist
129local getattr = nuts.getattr
130local setattr = nuts.setattr
131local getsubtype = nuts.getsubtype
132local getbox = nuts.getbox
133local getwhd = nuts.getwhd
134local setwhd = nuts.setwhd
135local getprop = nuts.getprop
136local setprop = nuts.setprop
137local getglue = nuts.getglue
138local setglue = nuts.setglue
139local getkern = nuts.getkern
140local getpenalty = nuts.getpenalty
141local setshift = nuts.setshift
142local setwidth = nuts.setwidth
143local getwidth = nuts.getwidth
144local setheight = nuts.setheight
145local getheight = nuts.getheight
146local setdepth = nuts.setdepth
147local getdepth = nuts.getdepth
148local setnext = nuts.setnext
149local setprev = nuts.setprev
150
151local find_node_tail = nuts.tail
152local flushnode = nuts.flushnode
153local remove_node = nuts.remove
154local count_nodes = nuts.countall
155local hpack_node = nuts.hpack
156local vpack_node = nuts.vpack
157
158local startofpar = nuts.startofpar
159
160local write_node = nuts.write
161
162local nextnode = nuts.traversers.node
163local nexthlist = nuts.traversers.hlist
164
165local nodereference = nuts.reference
166
167local listtoutf = nodes.listtoutf
168local nodeidstostring = nodes.idstostring
169
170local nodepool = nuts.pool
171
172local new_penalty = nodepool.penalty
173local new_kern = nodepool.kern
174local new_glue = nodepool.glue
175local new_rule = nodepool.rule
176
177local nodecodes = nodes.nodecodes
178local gluecodes = nodes.gluecodes
179
180
181
182local penalty_code = nodecodes.penalty
183local kern_code = nodecodes.kern
184local glue_code = nodecodes.glue
185local hlist_code = nodecodes.hlist
186local vlist_code = nodecodes.vlist
187local rule_code = nodecodes.rule
188local par_code = nodecodes.par
189local boundary_code = nodecodes.boundary
190
191local userskip_code = gluecodes.userskip
192local lineskip_code = gluecodes.lineskip
193local baselineskip_code = gluecodes.baselineskip
194local parskip_code = gluecodes.parskip
195local topskip_code = gluecodes.topskip
196local splittopskip_code = gluecodes.splittopskip
197
198local linelist_code = nodes.listcodes.line
199
200local setvisual = function(...) setvisual = nuts.setvisual return setvisual(...) end
201
202local properties = nodes.properties.data
203
204local vspacing = builders.vspacing or { }
205builders.vspacing = vspacing
206
207local vspacingdata = vspacing.data or { }
208vspacing.data = vspacingdata
209
210local snapmethods = vspacingdata.snapmethods or { }
211vspacingdata.snapmethods = snapmethods
212
213storage.register("builders/vspacing/data/snapmethods", snapmethods, "builders.vspacing.data.snapmethods")
214
215do
216
217 local default = {
218 [v_maxheight] = true,
219 [v_maxdepth] = true,
220 [v_strut] = true,
221 [v_hfraction] = 1,
222 [v_dfraction] = 1,
223 [v_bfraction] = 0.25,
224 }
225
226 local fractions = {
227 [v_minheight] = v_hfraction, [v_maxheight] = v_hfraction,
228 [v_mindepth] = v_dfraction, [v_maxdepth] = v_dfraction,
229 [v_box] = v_bfraction,
230 [v_top] = v_tlines, [v_bottom] = v_blines,
231 }
232
233 local values = {
234 offset = "offset"
235 }
236
237 local colonsplitter = lpeg.splitat(":")
238
239 local function listtohash(str)
240 local t = { }
241 for s in gmatch(str,"[^, ]+") do
242 local key, detail = lpegmatch(colonsplitter,s)
243 local v = variables[key]
244 if v then
245 t[v] = true
246 if detail then
247 local k = fractions[key]
248 if k then
249 detail = tonumber("0" .. detail)
250 if detail then
251 t[k] = detail
252 end
253 else
254 k = values[key]
255 if k then
256 detail = todimen(detail)
257 if detail then
258 t[k] = detail
259 end
260 end
261 end
262 end
263 else
264 detail = tonumber("0" .. key)
265 if detail then
266 t[v_hfraction] = detail
267 t[v_dfraction] = detail
268 end
269 end
270 end
271 if next(t) then
272 t[v_hfraction] = t[v_hfraction] or 1
273 t[v_dfraction] = t[v_dfraction] or 1
274 return t
275 else
276 return default
277 end
278 end
279
280 function vspacing.definesnapmethod(name,method)
281 local n = #snapmethods + 1
282 local t = listtohash(method)
283 snapmethods[n] = t
284 t.name = name
285 t.specification = method
286 context(n)
287 end
288
289end
290
291local function validvbox(parentid,list)
292 if parentid == hlist_code then
293 local id = getid(list)
294 if id == par_code and startofpar(list) then
295 list = getnext(list)
296 if not next then
297 return nil
298 end
299 end
300 local done = nil
301 for n, id in nextnode, list do
302 if id == vlist_code or id == hlist_code then
303 if done then
304 return nil
305 else
306 done = n
307 end
308 elseif id == glue_code or id == penalty_code then
309
310 else
311 return nil
312 end
313 end
314 if done then
315 local id = getid(done)
316 if id == hlist_code then
317 return validvbox(id,getlist(done))
318 end
319 end
320 return done
321 end
322end
323
324local function already_done(parentid,list,a_snapmethod)
325
326 if list and parentid == hlist_code then
327 local id = getid(list)
328 if id == par_code and startofpar(list) then
329 list = getnext(list)
330 if not list then
331 return false
332 end
333 end
334 for n, id in nextnode, list do
335 if id == hlist_code or id == vlist_code then
336
337
338
339
340
341
342 local p = getprop(n,"snapper")
343 if p then
344 return p
345 end
346 elseif id == glue_code or id == penalty_code then
347
348 else
349 return false
350 end
351 end
352 end
353 return false
354end
355
356
357
358local snap_hlist do
359
360 local v_noheight = variables.noheight
361 local v_nodepth = variables.nodepth
362 local v_line = variables.line
363 local v_halfline = variables.halfline
364 local v_line_m = "-" .. v_line
365 local v_halfline_m = "-" .. v_halfline
366
367 local floor = math.floor
368 local ceil = math.ceil
369
370 local function fixedprofile(current)
371 local profiling = builders.profiling
372 return profiling and profiling.fixedprofile(current)
373 end
374
375
376
377 local function ceiled(n)
378 if n < 0 or n < 0.01 then
379 return 0
380 else
381 return ceil(n)
382 end
383 end
384
385 local function floored(n)
386 if n < 0 or n < 0.01 then
387 return 0
388 else
389 return floor(n)
390 end
391 end
392
393 snap_hlist = function(where,current,method,height,depth)
394 if fixedprofile(current) then
395 return
396 end
397 local list = getlist(current)
398 local t = trace_vsnapping and { }
399 if t then
400 t[#t+1] = formatters["list content: %s"](listtoutf(list))
401 t[#t+1] = formatters["snap method: %s"](method.name)
402 t[#t+1] = formatters["specification: %s"](method.specification)
403 end
404 local snapht, snapdp
405 if method[v_local] then
406
407 snapht = texgetdimen(d_bodyfontstrutheight)
408 snapdp = texgetdimen(d_bodyfontstrutdepth)
409 if t then
410 t[#t+1] = formatters["local: snapht %p snapdp %p"](snapht,snapdp)
411 end
412 elseif method[v_global] then
413 snapht = texgetdimen(d_globalbodyfontstrutheight)
414 snapdp = texgetdimen(d_globalbodyfontstrutdepth)
415 if t then
416 t[#t+1] = formatters["global: snapht %p snapdp %p"](snapht,snapdp)
417 end
418 else
419
420
421 snapht = texgetdimen(d_globalbodyfontstrutheight)
422 snapdp = texgetdimen(d_globalbodyfontstrutdepth)
423 local lsnapht = texgetdimen(d_bodyfontstrutheight)
424 local lsnapdp = texgetdimen(d_bodyfontstrutdepth)
425 if snapht ~= lsnapht and snapdp ~= lsnapdp then
426 snapht, snapdp = lsnapht, lsnapdp
427 end
428 if t then
429 t[#t+1] = formatters["auto: snapht %p snapdp %p"](snapht,snapdp)
430 end
431 end
432
433 local wd, ht, dp = getwhd(current)
434
435 local h = (method[v_noheight] and 0) or height or ht
436 local d = (method[v_nodepth] and 0) or depth or dp
437 local hr = method[v_hfraction] or 1
438 local dr = method[v_dfraction] or 1
439 local br = method[v_bfraction] or 0
440 local ch = h
441 local cd = d
442 local tlines = method[v_tlines] or 1
443 local blines = method[v_blines] or 1
444 local done = false
445 local plusht = snapht
446 local plusdp = snapdp
447 local snaphtdp = snapht + snapdp
448 local extra = 0
449
450 if t then
451 t[#t+1] = formatters["hlist: wd %p ht %p (used %p) dp %p (used %p)"](wd,ht,h,dp,d)
452 t[#t+1] = formatters["fractions: hfraction %s dfraction %s bfraction %s tlines %s blines %s"](hr,dr,br,tlines,blines)
453 end
454
455 if method[v_box] then
456 local br = 1 - br
457 if br < 0 then
458 br = 0
459 elseif br > 1 then
460 br = 1
461 end
462 local n = ceiled((h+d-br*snapht-br*snapdp)/snaphtdp)
463 local x = n * snaphtdp - h - d
464 plusht = h + x / 2
465 plusdp = d + x / 2
466 if t then
467 t[#t+1] = formatters["%s: plusht %p plusdp %p"](v_box,plusht,plusdp)
468 end
469 elseif method[v_max] then
470 local n = ceiled((h+d)/snaphtdp)
471 local x = n * snaphtdp - h - d
472 plusht = h + x / 2
473 plusdp = d + x / 2
474 if t then
475 t[#t+1] = formatters["%s: plusht %p plusdp %p"](v_max,plusht,plusdp)
476 end
477 elseif method[v_min] then
478
479 if method.specification ~= v_min then
480 local n = floored((h+d)/snaphtdp)
481 local x = n * snaphtdp - h - d
482 plusht = h + x / 2
483 plusdp = d + x / 2
484 if plusht < 0 then
485 plusht = 0
486 end
487 if plusdp < 0 then
488 plusdp = 0
489 end
490 end
491 if t then
492 t[#t+1] = formatters["%s: plusht %p plusdp %p"](v_min,plusht,plusdp)
493 end
494 elseif method[v_none] then
495 plusht, plusdp = 0, 0
496 if t then
497 t[#t+1] = formatters["%s: plusht %p plusdp %p"](v_none,0,0)
498 end
499 end
500
501
502 if method[v_halfline] then
503 extra = snaphtdp/2
504 plusht = plusht + extra
505 plusdp = plusdp + extra
506 if t then
507 t[#t+1] = formatters["%s: plusht %p plusdp %p"](v_halfline,plusht,plusdp)
508 end
509 end
510 if method[v_line] then
511 extra = snaphtdp
512 plusht = plusht + extra
513 plusdp = plusdp + extra
514 if t then
515 t[#t+1] = formatters["%s: plusht %p plusdp %p"](v_line,plusht,plusdp)
516 end
517 end
518 if method[v_halfline_m] then
519 extra = - snaphtdp/2
520 plusht = plusht + extra
521 plusdp = plusdp + extra
522 if t then
523 t[#t+1] = formatters["%s: plusht %p plusdp %p"](v_halfline_m,plusht,plusdp)
524 end
525 end
526 if method[v_line_m] then
527 extra = - snaphtdp
528 plusht = plusht + extra
529 plusdp = plusdp + extra
530 if t then
531 t[#t+1] = formatters["%s: plusht %p plusdp %p"](v_line_m,plusht,plusdp)
532 end
533 end
534 if method[v_first] then
535 local thebox = current
536 local id = getid(thebox)
537 if id == hlist_code then
538 thebox = validvbox(id,getlist(thebox))
539 id = thebox and getid(thebox)
540 end
541 if thebox and id == vlist_code then
542 local list = getlist(thebox)
543 local lw, lh, ld
544 for n in nexthlist, list do
545 lw, lh, ld = getwhd(n)
546 break
547 end
548 if lh then
549 local wd, ht, dp = getwhd(thebox)
550 if t then
551 t[#t+1] = formatters["first line: height %p depth %p"](lh,ld)
552 t[#t+1] = formatters["dimensions: height %p depth %p"](ht,dp)
553 end
554 local delta = h - lh
555 ch, cd = lh, delta + d
556 h, d = ch, cd
557 local shifted = hpack_node(getlist(current))
558 setshift(shifted,delta)
559 setlist(current,shifted)
560 done = true
561 if t then
562 t[#t+1] = formatters["first: height %p depth %p shift %p"](ch,cd,delta)
563 end
564 elseif t then
565 t[#t+1] = "first: not done, no content"
566 end
567 elseif t then
568 t[#t+1] = "first: not done, no vbox"
569 end
570 elseif method[v_last] then
571 local thebox = current
572 local id = getid(thebox)
573 if id == hlist_code then
574 thebox = validvbox(id,getlist(thebox))
575 id = thebox and getid(thebox)
576 end
577 if thebox and id == vlist_code then
578 local list = getlist(thebox)
579 local lw, lh, ld
580 for n in nexthlist, list do
581 lw, lh, ld = getwhd(n)
582 end
583 if lh then
584 local wd, ht, dp = getwhd(thebox)
585 if t then
586 t[#t+1] = formatters["last line: height %p depth %p" ](lh,ld)
587 t[#t+1] = formatters["dimensions: height %p depth %p"](ht,dp)
588 end
589 local delta = d - ld
590 cd, ch = ld, delta + h
591 h, d = ch, cd
592 local shifted = hpack_node(getlist(current))
593 setshift(shifted,delta)
594 setlist(current,shifted)
595 done = true
596 if t then
597 t[#t+1] = formatters["last: height %p depth %p shift %p"](ch,cd,delta)
598 end
599 elseif t then
600 t[#t+1] = "last: not done, no content"
601 end
602 elseif t then
603 t[#t+1] = "last: not done, no vbox"
604 end
605 end
606 if method[v_minheight] then
607 ch = floored((h-hr*snapht)/snaphtdp)*snaphtdp + plusht
608 if t then
609 t[#t+1] = formatters["minheight: %p"](ch)
610 end
611 elseif method[v_maxheight] then
612 ch = ceiled((h-hr*snapht)/snaphtdp)*snaphtdp + plusht
613 if t then
614 t[#t+1] = formatters["maxheight: %p"](ch)
615 end
616 else
617 ch = plusht
618 if t then
619 t[#t+1] = formatters["set height: %p"](ch)
620 end
621 end
622 if method[v_mindepth] then
623 cd = floored((d-dr*snapdp)/snaphtdp)*snaphtdp + plusdp
624 if t then
625 t[#t+1] = formatters["mindepth: %p"](cd)
626 end
627 elseif method[v_maxdepth] then
628 cd = ceiled((d-dr*snapdp)/snaphtdp)*snaphtdp + plusdp
629 if t then
630 t[#t+1] = formatters["maxdepth: %p"](cd)
631 end
632 else
633 cd = plusdp
634 if t then
635 t[#t+1] = formatters["set depth: %p"](cd)
636 end
637 end
638 if method[v_top] then
639 ch = ch + tlines * snaphtdp
640 if t then
641 t[#t+1] = formatters["top height: %p"](ch)
642 end
643 end
644 if method[v_bottom] then
645 cd = cd + blines * snaphtdp
646 if t then
647 t[#t+1] = formatters["bottom depth: %p"](cd)
648 end
649 end
650 local offset = method[v_offset]
651 if offset then
652
653 if t then
654 local wd, ht, dp = getwhd(current)
655 t[#t+1] = formatters["before offset: %p (width %p height %p depth %p)"](offset,wd,ht,dp)
656 end
657 local shifted = hpack_node(getlist(current))
658 setshift(shifted,offset)
659 setlist(current,shifted)
660 if t then
661 local wd, ht, dp = getwhd(current)
662 t[#t+1] = formatters["after offset: %p (width %p height %p depth %p)"](offset,wd,ht,dp)
663 end
664 setattr(shifted,a_snapmethod,0)
665 setattr(current,a_snapmethod,0)
666 end
667 if not height then
668 setheight(current,ch)
669 if t then
670 t[#t+1] = formatters["forced height: %p"](ch)
671 end
672 end
673 if not depth then
674 setdepth(current,cd)
675 if t then
676 t[#t+1] = formatters["forced depth: %p"](cd)
677 end
678 end
679 local lines = (ch+cd)/snaphtdp
680 if t then
681 local original = (h+d)/snaphtdp
682 local whatever = (ch+cd)/(texgetdimen(d_globalbodyfontstrutheight) + texgetdimen(d_globalbodyfontstrutdepth))
683 t[#t+1] = formatters["final lines : %p -> %p (%p)"](original,lines,whatever)
684 t[#t+1] = formatters["final height: %p -> %p"](h,ch)
685 t[#t+1] = formatters["final depth : %p -> %p"](d,cd)
686 end
687
688
689
690
691
692
693 if t then
694 report_snapper("trace: %s type %s\n\t%\n\tt",where,nodecodes[getid(current)],t)
695 end
696 if not method[v_split] then
697
698 extra = 0
699 end
700 return h, d, ch, cd, lines, extra
701 end
702
703end
704
705local categories = { [0] =
706 "discard",
707 "largest",
708 "force",
709 "penalty",
710 "add",
711 "disable",
712 "nowhite",
713 "goback",
714 "packed",
715 "overlay",
716 "enable",
717 "notopskip",
718}
719
720categories = allocate(table.swapped(categories,categories))
721vspacing.categories = categories
722
723function vspacing.tocategories(str)
724 local t = { }
725 for s in gmatch(str,"[^, ]") do
726 local n = tonumber(s)
727 if n then
728 t[categories[n]] = true
729 else
730 t[b] = true
731 end
732 end
733 return t
734end
735
736function vspacing.tocategory(str)
737 if type(str) == "string" then
738 return set.tonumber(vspacing.tocategories(str))
739 else
740 return set.tonumber({ [categories[str]] = true })
741 end
742end
743
744vspacingdata.map = vspacingdata.map or { }
745vspacingdata.skip = vspacingdata.skip or { }
746
747storage.register("builders/vspacing/data/map", vspacingdata.map, "builders.vspacing.data.map")
748storage.register("builders/vspacing/data/skip", vspacingdata.skip, "builders.vspacing.data.skip")
749
750local setspecification, getspecification
751
752
753
754
755
756
757
758
759local method = 1
760
761
762
763
764
765
766if method == 1 then
767
768 local registervalue = attributes.registervalue
769 local getvalue = attributes.getvalue
770 local values = attributes.values
771
772 setspecification = function(n,category,penalty,order)
773 local detail = { category, penalty, order or 1 }
774 local value = registervalue(a_skipcategory,detail)
775 setattr(n,a_skipcategory,value)
776 end
777
778 getspecification = function(n)
779 local value = getattr(n,a_skipcategory)
780 if value then
781 local detail = getvalue(a_skipcategory,value)
782
783
784 if detail then
785 return detail[1], detail[2], detail[3]
786 end
787 end
788 return false, false, 1
789 end
790
791elseif method == 2 then
792
793
794
795 local setattrs = nuts.setattrs
796 local getattrs = nuts.getattrs
797
798 setspecification = function(n,category,penalty,order)
799 setattrs(n,false,a_skipcategory,category or nil,a_skippenalty,penalty or nil,a_skiporder,order or 1)
800 end
801
802 getspecification = function(n)
803 local c, p, o = getattrs(n,a_skipcategory,a_skippenalty,a_skiporder)
804 return c or false, p or false, o or 1
805 end
806
807elseif method == 3 then
808
809
810
811 setspecification = function(n,category,penalty,order)
812
813 properties[n] = {
814 [a_skipcategory] = category,
815 [a_skippenalty] = penalty,
816 [a_skiporder] = order or 1,
817 }
818 end
819
820 getspecification = function(n)
821 local p = properties[n]
822 if p then
823 return p[a_skipcategory], p[a_skippenalty], p[a_skiporder]
824 end
825 end
826
827elseif method == 4 then
828
829
830
831 local getdata = nuts.getdata
832 local setdata = nuts.setdata
833
834 setspecification = function(n,category,penalty,order)
835 if not category or category > 0xF then
836 category = 0xF
837 end
838 if not order or order > 0xFF then
839 order = 0xFF
840 end
841 if not penalty or penalty > 0x7FFFF then
842 penalty = 0x7FFFF
843 elseif penalty < -0x7FFFF then
844 penalty = -0x7FFFF
845 end
846
847 setdata(n, (penalty << 12) + (order << 4) + category)
848 end
849
850 getspecification = function(n)
851 local data = getdata(n)
852 if data and data ~= 0 then
853 local category = data & 0x0F
854 local order = (data >> 4) & 0xFF
855 local penalty = data >> 12
856 if category == 0xF then
857 category = nil
858 end
859 if order == 0xFF then
860 order = nil
861 end
862 if penalty == 0x7FFFF then
863 penalty = nil
864 end
865 return category, penalty, order
866 else
867 return nil, nil, nil
868 end
869 end
870
871end
872
873do
874
875 local P, C, R, S, Cc, Cs = lpeg.P, lpeg.C, lpeg.R, lpeg.S, lpeg.Cc, lpeg.Cs
876
877 vspacing.fixed = false
878
879 local map = vspacingdata.map
880 local skip = vspacingdata.skip
881
882 local sign = S("+-")^0
883 local multiplier = C(sign * R("09")^1) * P("*")
884 local singlefier = Cs(sign * Cc(1))
885 local separator = S(", ")
886 local category = P(":") * C((1-separator)^1)
887 local keyword = C((1-category-separator)^1)
888 local splitter = (multiplier + Cc(1)) * keyword * (category + Cc(false))
889
890 local k_fixed = variables.fixed
891 local k_flexible = variables.flexible
892 local k_limit = variables.limit
893
894 local k_category <const> = "category"
895 local k_penalty <const> = "penalty"
896 local k_order <const> = "order"
897
898 local setoptions = nuts.setoptions
899
900 function vspacing.setmap(from,to)
901 map[from] = to
902 end
903
904 function vspacing.setskip(key,value,grid)
905 if value ~= "" then
906 if grid == "" then grid = value end
907 skip[key] = { value, grid }
908 end
909 end
910
911 local expandmacro = token.expandmacro
912
913
914
915 local toscaled = tex.toscaled
916
917 local b_done = false
918 local b_packed = false
919
920 local b_amount = 0
921 local b_stretch = 0
922 local b_shrink = 0
923 local b_category = false
924 local b_penalty = false
925 local b_order = false
926 local b_fixed = false
927 local b_grid = false
928 local b_limit = false
929
930 local pattern = nil
931
932 local packed = categories.packed
933
934 local gluefactor = .25
935
936 local ctx_ignoreparskip = context.core.ignoreparskip
937
938 local function before()
939 b_amount = 0
940 b_stretch = 0
941 b_shrink = 0
942 b_category = 1
943 b_penalty = false
944 b_order = false
945 b_fixed = b_grid
946 b_limit = false
947 end
948
949 local function after()
950 if b_fixed then
951 b_stretch = 0
952 b_shrink = 0
953 else
954 b_stretch = gluefactor * b_amount
955 b_shrink = gluefactor * b_amount
956 end
957 end
958
959
960
961 local function inject()
962 local n = new_glue(b_amount,b_stretch,b_shrink)
963 setspecification(n,b_category,b_penalty,b_order or 1)
964 setvisual(n)
965 write_node(n)
966 if b_limit then
967 setoptions(n,tex.glueoptioncodes.limit)
968 end
969
970 end
971
972 local function flush()
973 after()
974 if b_done then
975 inject()
976 b_done = false
977 end
978 before()
979 end
980
981
982
983
984
985 local s_predefined = tex.isskip("s_spac_vspacing_predefined")
986
987 local function handler(multiplier, keyword, detail)
988 if not keyword then
989 report_vspacing("unknown directive %a",s)
990 else
991 local mk = map[keyword]
992 if mk then
993 lpegmatch(pattern,mk)
994 elseif keyword == k_fixed then
995 b_fixed = true
996 elseif keyword == k_flexible then
997 b_flexible = false
998 elseif keyword == k_category then
999 local category = tonumber(detail)
1000 if category == packed then
1001 b_packed = true
1002 elseif category then
1003 b_category = category
1004 b_done = true
1005 flush()
1006 end
1007 elseif keyword == k_order and detail then
1008 local order = tonumber(detail)
1009 if order then
1010 b_order = order
1011 end
1012 elseif keyword == k_penalty and detail then
1013 local penalty = tonumber(detail)
1014 if penalty then
1015 flush()
1016 b_done = true
1017 b_category = 3
1018 b_penalty = penalty
1019 flush()
1020 end
1021 elseif keyword == k_limit then
1022 b_limit = true
1023 else
1024 local amount, stretch, shrink
1025 multiplier = tonumber(multiplier) or 1
1026 local sk = skip[keyword]
1027 if sk then
1028
1029
1030 expandmacro("vspacingpredefinedvalue",true,keyword)
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043 amount, stretch, shrink = texgetglue(s_predefined)
1044 if not stretch then
1045 stretch = 0
1046 end
1047 if not shrink then
1048 shrink = 0
1049 end
1050 if stretch == 0 and shrink == 0 then
1051 stretch = gluefactor * amount
1052 shrink = stretch
1053 end
1054 else
1055 amount = toscaled(keyword)
1056 stretch = gluefactor * amount
1057 shrink = stretch
1058 end
1059
1060 b_amount = b_amount + multiplier * amount
1061 b_stretch = b_stretch + multiplier * stretch
1062 b_shrink = b_shrink + multiplier * shrink
1063 b_done = true
1064 end
1065 end
1066 end
1067
1068
1069
1070
1071 local splitter = ((multiplier + singlefier) * keyword * (category + Cc(false))) / handler
1072 pattern = (splitter + separator^1)^0
1073
1074 function vspacing.inject(grid,str)
1075 if trace_vspacing then
1076
1077 end
1078 b_done = false
1079 b_packed = false
1080 b_grid = grid == true or grid == 1
1081 before()
1082 lpegmatch(pattern,str)
1083 after()
1084 if b_done then
1085 inject()
1086 end
1087 if b_packed then
1088 ctx_ignoreparskip()
1089 end
1090 if trace_vspacing then
1091
1092 end
1093 end
1094
1095 function vspacing.injectpenalty(penalty)
1096 local n = new_glue()
1097
1098 setspecification(n,categories.penalty,penalty,1)
1099 setvisual(k)
1100 write_node(n)
1101 end
1102
1103 function vspacing.injectskip(amount)
1104 local n = new_glue(amount)
1105
1106 setspecification(n,categories.largest,false,1)
1107 setvisual(k)
1108 write_node(n)
1109 end
1110
1111 function vspacing.injectdisable(amount)
1112 local n = new_glue()
1113
1114 setspecification(n,categories.disable,false,1)
1115 setvisual(k)
1116 write_node(n)
1117 end
1118
1119end
1120
1121
1122
1123
1124
1125function vspacing.snapbox(n,how)
1126 local sv = snapmethods[how]
1127 if sv then
1128 local box = getbox(n)
1129 local list = getlist(box)
1130 if list then
1131 local s = getattr(list,a_snapmethod)
1132 if s == 0 then
1133 if trace_vsnapping then
1134
1135 end
1136 else
1137 local wd, ht, dp = getwhd(box)
1138 if false then
1139
1140 if trace_vsnapping then
1141 report_snapper("box list already snapped at (%p,%p): %s",
1142 ht,dp,listtoutf(list))
1143 end
1144 else
1145 local h, d, ch, cd, lines, extra = snap_hlist("box",box,sv,ht,dp)
1146 setprop(box,"snapper",{
1147 ht = h,
1148 dp = d,
1149 ch = ch,
1150 cd = cd,
1151 extra = extra,
1152 current = current,
1153 })
1154 setwhd(box,wd,ch,cd)
1155 if trace_vsnapping then
1156 report_snapper("box list snapped from (%p,%p) to (%p,%p) using method %a (%s) for %a (%s lines): %s",
1157 h,d,ch,cd,sv.name,sv.specification,"direct",lines,listtoutf(list))
1158 end
1159 setattr(box,a_snapmethod,0)
1160 setattr(list,a_snapmethod,0)
1161 end
1162 end
1163 end
1164 end
1165end
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178do
1179
1180 local insertnodeafter = nuts.insertafter
1181 local insertnodebefore = nuts.insertbefore
1182
1183 local abovedisplayskip_code = gluecodes.abovedisplayskip
1184 local belowdisplayskip_code = gluecodes.belowdisplayskip
1185 local abovedisplayshortskip_code = gluecodes.abovedisplayshortskip
1186 local belowdisplayshortskip_code = gluecodes.belowdisplayshortskip
1187
1188 local w, h, d = 0, 0, 0
1189
1190
1191 local trace_list = { }
1192 local tracing_info = { }
1193 local before = ""
1194 local after = ""
1195
1196 local function nodes_to_string(head)
1197 local current = head
1198 local t = { }
1199 while current do
1200 local id = getid(current)
1201 local ty = nodecodes[id]
1202 if id == penalty_code then
1203 t[#t+1] = formatters["%s:%s"](ty,getpenalty(current))
1204 elseif id == glue_code then
1205 t[#t+1] = formatters["%s:%s:%p"](ty,gluecodes[getsubtype(current)],getwidth(current))
1206 elseif id == kern_code then
1207 t[#t+1] = formatters["%s:%p"](ty,getkern(current))
1208 else
1209 t[#t+1] = ty
1210 end
1211 current = getnext(current)
1212 end
1213 return concat(t," + ")
1214 end
1215
1216 local function reset_tracing(head)
1217 trace_list, tracing_info, before, after = { }, false, nodes_to_string(head), ""
1218 end
1219
1220 local function trace_skip(str,sc,so,sp,data)
1221 trace_list[#trace_list+1] = { "skip", formatters["%s | %p | category %s | order %s | penalty %s | subtype %s"](str, getwidth(data), sc or "-", so or "-", sp or "-", gluecodes[getsubtype(data)]) }
1222 tracing_info = true
1223 end
1224
1225 local function trace_natural(str,data)
1226 trace_list[#trace_list+1] = { "skip", formatters["%s | %p"](str, getwidth(data)) }
1227 tracing_info = true
1228 end
1229
1230 local function trace_info(message, where, what)
1231 trace_list[#trace_list+1] = { "info", formatters["%s: %s/%s"](message,where,what) }
1232 end
1233
1234 local function trace_node(what)
1235 local nt = nodecodes[getid(what)]
1236 local tl = trace_list[#trace_list]
1237 if tl and tl[1] == "node" then
1238 trace_list[#trace_list] = { "node", formatters["%s + %s"](tl[2],nt) }
1239 else
1240 trace_list[#trace_list+1] = { "node", nt }
1241 end
1242 end
1243
1244 local function show_tracing(head)
1245 if tracing_info then
1246 after = nodes_to_string(head)
1247 for i=1,#trace_list do
1248 local tag, text = unpack(trace_list[i])
1249 if tag == "info" then
1250 report_collapser(text)
1251 else
1252 report_collapser(" %s: %s",tag,text)
1253 end
1254 end
1255 report_collapser("before: %s",before)
1256 report_collapser("after : %s",after)
1257 end
1258 end
1259
1260 local function trace_done(str,data)
1261 if getid(data) == penalty_code then
1262 trace_list[#trace_list+1] = { "penalty", formatters["%s | %s"](str,getpenalty(data)) }
1263 else
1264 trace_list[#trace_list+1] = { "glue", formatters["%s | %p"](str,getwidth(data)) }
1265 end
1266 tracing_info = true
1267 end
1268
1269 local function forced_skip(head,current,width,where,trace)
1270 if head == current then
1271 if getsubtype(head) == baselineskip_code then
1272 width = width - getwidth(head)
1273 end
1274 end
1275 if width == 0 then
1276
1277 else
1278 local b = new_rule(w,h,d)
1279 local k = new_kern(width)
1280 local a = new_rule(w,h,d)
1281 setvisual(k)
1282 if where == "after" then
1283 head, current = insertnodeafter(head,current,b)
1284 head, current = insertnodeafter(head,current,k)
1285 head, current = insertnodeafter(head,current,a)
1286 else
1287 local c = current
1288 head, current = insertnodebefore(head,current,b)
1289 head, current = insertnodebefore(head,current,k)
1290 head, current = insertnodebefore(head,current,a)
1291 current = c
1292 end
1293 end
1294 if trace then
1295 report_vspacing("inserting forced skip of %p",width)
1296 end
1297 return head, current
1298 end
1299
1300
1301
1302 local discard = categories.discard
1303 local largest = categories.largest
1304 local force = categories.force
1305 local penalty = categories.penalty
1306 local add = categories.add
1307 local disable = categories.disable
1308 local nowhite = categories.nowhite
1309 local goback = categories.goback
1310 local packed = categories.packed
1311 local overlay = categories.overlay
1312 local enable = categories.enable
1313 local notopskip = categories.notopskip
1314
1315
1316
1317 local special_penalty_min = 32250
1318 local special_penalty_max = 35000
1319 local special_penalty_xxx = 0
1320
1321
1322
1323
1324
1325
1326
1327 local specialmethods = { }
1328 local specialmethod = 1
1329
1330 specialmethods[1] = function(pagehead,pagetail,start,penalty)
1331
1332 if not pagehead or penalty < special_penalty_min or penalty > special_penalty_max then
1333 return
1334 end
1335 local current = pagetail
1336
1337
1338
1339 if trace_specials then
1340 report_specials("checking penalty %a",penalty)
1341 end
1342 while current do
1343 local id = getid(current)
1344 if id == penalty_code then
1345 local p = properties[current]
1346 if p then
1347 local p = p.special_penalty
1348 if not p then
1349 if trace_specials then
1350 report_specials(" regular penalty, continue")
1351 end
1352 elseif p == penalty then
1353 if trace_specials then
1354 report_specials(" context penalty %a, same level, overloading",p)
1355 end
1356 return special_penalty_xxx
1357 elseif p > special_penalty_min and p < special_penalty_max then
1358 if penalty < p then
1359 if trace_specials then
1360 report_specials(" context penalty %a, lower level, overloading",p)
1361 end
1362 return special_penalty_xxx
1363 else
1364 if trace_specials then
1365 report_specials(" context penalty %a, higher level, quitting",p)
1366 end
1367 return
1368 end
1369 elseif trace_specials then
1370 report_specials(" context penalty %a, higher level, continue",p)
1371 end
1372 else
1373 local p = getpenalty(current)
1374 if p < 10000 then
1375
1376 if trace_specials then
1377 report_specials(" regular penalty %a, quitting",p)
1378 end
1379 break
1380 else
1381 if trace_specials then
1382 report_specials(" regular penalty %a, continue",p)
1383 end
1384 end
1385 end
1386 end
1387 current = getprev(current)
1388 end
1389
1390 if trace_specials then
1391 if pagetail then
1392 report_specials(" context penalty, discarding, nothing special")
1393 else
1394 report_specials(" context penalty, discarding, nothing preceding")
1395 end
1396 end
1397 return special_penalty_xxx
1398 end
1399
1400
1401
1402
1403
1404
1405
1406
1407
1408
1409
1410
1411
1412
1413
1414
1415
1416
1417
1418
1419
1420
1421
1422
1423 local function snap_topskip(current,method)
1424 local w = getwidth(current)
1425 setwidth(current,0)
1426 return w, 0
1427 end
1428
1429 local function check_experimental_overlay(head,current)
1430 local p = nil
1431 local c = current
1432 local n = nil
1433 local function overlay(p,n,mvl)
1434 local p_wd, p_ht, p_dp = getwhd(p)
1435 local n_wd, n_ht, n_dp = getwhd(n)
1436 local skips = 0
1437
1438
1439
1440
1441
1442
1443
1444
1445 local c = getnext(p)
1446 local l = c
1447 while c and c ~= n do
1448 local id = getid(c)
1449 if id == glue_code then
1450 local w = getwidth(c)
1451 skips = skips + w
1452 setglue(c,w)
1453 elseif id == kern_code then
1454 skips = skips + getkern(c)
1455 end
1456 l = c
1457 c = getnext(c)
1458 end
1459 local c = getprev(n)
1460 while c and c ~= n and c ~= l do
1461 local id = getid(c)
1462 if id == glue_code then
1463 local w = getwidth(c)
1464 skips = skips + w
1465 setglue(c,w)
1466 elseif id == kern_code then
1467 skips = skips + getkern(c)
1468 end
1469 c = getprev(c)
1470 end
1471
1472 local delta = n_ht + skips + p_dp
1473 texsetdimen("global",d_spac_overlay,-delta)
1474
1475 local k = new_kern(-delta)
1476 setvisual(k)
1477 head = insertnodebefore(head,n,k)
1478 if n_ht > p_ht then
1479 local k = new_kern(n_ht-p_ht)
1480 setvisual(k)
1481 head = insertnodebefore(head,p,k)
1482 end
1483 if trace_vspacing then
1484 report_vspacing("overlaying, prev height: %p, prev depth: %p, next height: %p, skips: %p, move up: %p",p_ht,p_dp,n_ht,skips,delta)
1485 end
1486 return remove_node(head,current,true)
1487 end
1488
1489
1490 while c do
1491 local id = getid(c)
1492 if id == glue_code or id == penalty_code or id == kern_code then
1493
1494 c = getnext(c)
1495 elseif id == hlist_code then
1496 n = c
1497 break
1498 else
1499 break
1500 end
1501 end
1502 if n then
1503
1504 c = current
1505 while c do
1506 local id = getid(c)
1507 if id == glue_code or id == penalty_code then
1508 c = getprev(c)
1509 elseif id == hlist_code then
1510 p = c
1511 break
1512 else
1513 break
1514 end
1515 end
1516 if not p then
1517 if a_snapmethod == a_snapvbox then
1518
1519 else
1520
1521 local c = tonut(texlists.pagehead)
1522 while c and c ~= n do
1523 local id = getid(c)
1524 if id == hlist_code then
1525 p = c
1526 end
1527 c = getnext(c)
1528 end
1529 if p and p ~= n then
1530 return overlay(p,n,true)
1531 end
1532 end
1533 elseif p ~= n then
1534 return overlay(p,n,false)
1535 end
1536 end
1537
1538 return remove_node(head,current,true)
1539 end
1540
1541
1542
1543
1544 local checkslide = false
1545
1546 directives.register("vspacing.checkslide", function(v)
1547 if v then
1548 checkslide = function(head,where,what)
1549 nuts.checkslide(head,where .. " : " .. what)
1550 end
1551 else
1552 checkslide = false
1553 end
1554 end)
1555
1556 local function collapser(head,where,what,trace,snap,a_snapmethod)
1557 if trace then
1558 reset_tracing(head)
1559 end
1560 local current = head
1561 local oldhead = head
1562 local glue_order = 0
1563 local glue_data
1564 local force_glue = false
1565 local penalty_order = 0
1566 local penalty_data
1567 local natural_penalty
1568 local special_penalty
1569 local parskip
1570 local ignore_parskip = false
1571 local ignore_following = false
1572 local ignore_whitespace = false
1573 local keep_together = false
1574 local lastsnap
1575 local pagehead
1576 local pagetail
1577
1578
1579
1580
1581 local function getpagelist()
1582 if not pagehead then
1583 pagehead = texlists.pagehead
1584 if pagehead then
1585 pagehead = tonut(pagehead)
1586 pagetail = find_node_tail(pagehead)
1587 end
1588 end
1589 end
1590
1591
1592
1593
1594
1595
1596
1597 local function compensate(n)
1598 local g = 0
1599 while n and getid(n) == glue_code do
1600 g = g + getwidth(n)
1601 n = getnext(n)
1602 end
1603 if n then
1604 local p = getprop(n,"snapper")
1605 if p then
1606 local extra = p.extra
1607 if extra and extra < 0 then
1608 local h = p.ch
1609
1610
1611 setheight(n,h-2*extra)
1612 p.extra = 0
1613 if trace_vsnapping then
1614 report_snapper("removed extra space at top: %p",extra)
1615 end
1616
1617 end
1618 end
1619 return n
1620 end
1621 end
1622
1623 local function removetopsnap()
1624 getpagelist()
1625 if pagehead then
1626 local n = pagehead and compensate(pagehead)
1627 if n and n ~= pagetail then
1628 local p = getprop(pagetail,"snapper")
1629 if p then
1630 local e = p.extra
1631 if e and e < 0 then
1632 local t = texget("pagetotal")
1633 if t > 0 then
1634 local g = texget("pagegoal")
1635 local d = g - t
1636 if d < -e then
1637 local penalty = new_penalty(1000000)
1638 setvisual(penalty)
1639 setlink(penalty,head)
1640 head = penalty
1641 report_snapper("force pagebreak due to extra space at bottom: %p",e)
1642 end
1643 end
1644 end
1645 end
1646 end
1647 elseif head then
1648 compensate(head)
1649 end
1650 end
1651
1652 local function getavailable()
1653 getpagelist()
1654 if pagehead then
1655 local t = texget("pagetotal")
1656 if t > 0 then
1657 local g = texget("pagegoal")
1658 return g - t
1659 end
1660 end
1661 return false
1662 end
1663
1664 local function flush(why)
1665 if penalty_data then
1666 local p = new_penalty(penalty_data)
1667 setvisual(p)
1668 if trace then
1669 trace_done("flushed due to " .. why,p)
1670 end
1671 if penalty_data >= 10000 then
1672 local prev = getprev(current)
1673 if getid(prev) == glue_code then
1674
1675 head = insertnodebefore(head,prev,p)
1676 else
1677 head = insertnodebefore(head,current,p)
1678 end
1679 else
1680 head = insertnodebefore(head,current,p)
1681 end
1682
1683 local props = properties[p]
1684 if props then
1685 props.special_penalty = special_penalty or penalty_data
1686 else
1687 properties[p] = {
1688 special_penalty = special_penalty or penalty_data
1689 }
1690 end
1691
1692 end
1693 if glue_data then
1694 if force_glue then
1695 if trace then
1696 trace_done("flushed due to forced " .. why,glue_data)
1697 end
1698 head = forced_skip(head,current,getwidth(glue_data,width),"before",trace)
1699 flushnode(glue_data)
1700 else
1701 local width, stretch, shrink = getglue(glue_data)
1702 if width ~= 0 then
1703 if trace then
1704 trace_done("flushed due to non zero " .. why,glue_data)
1705 end
1706 head = insertnodebefore(head,current,glue_data)
1707 elseif stretch ~= 0 or shrink ~= 0 then
1708 if trace then
1709 trace_done("flushed due to stretch/shrink in" .. why,glue_data)
1710 end
1711 head = insertnodebefore(head,current,glue_data)
1712 else
1713
1714 flushnode(glue_data)
1715 end
1716 end
1717 end
1718
1719 if trace then
1720 trace_node(current)
1721 end
1722 glue_order, glue_data, force_glue = 0, nil, false
1723 penalty_order, penalty_data, natural_penalty = 0, nil, nil
1724 parskip, ignore_parskip, ignore_following, ignore_whitespace = nil, false, false, false
1725 end
1726
1727 if trace_vsnapping then
1728 report_snapper("global ht/dp = %p/%p, local ht/dp = %p/%p",
1729 texgetdimen(d_globalbodyfontstrutheight),
1730 texgetdimen(d_globalbodyfontstrutdepth),
1731 texgetdimen(d_bodyfontstrutheight),
1732 texgetdimen(d_bodyfontstrutdepth)
1733 )
1734 end
1735 if trace then
1736 trace_info("start analyzing",where,what)
1737 end
1738 if snap and where == "page" then
1739 removetopsnap()
1740 end
1741 if checkslide then
1742 checkslide(head,where,what)
1743 end
1744 while current do
1745 local id = getid(current)
1746 if id == hlist_code or id == vlist_code then
1747
1748 if snap then
1749 lastsnap = nil
1750 local list = getlist(current)
1751 local s = getattr(current,a_snapmethod)
1752 if not s then
1753
1754
1755
1756 elseif s == 0 then
1757 if trace_vsnapping then
1758 report_snapper("mvl %a not snapped, already done: %s",nodecodes[id],listtoutf(list))
1759 end
1760 else
1761 local sv = snapmethods[s]
1762 if sv then
1763
1764 local done = list and already_done(id,list,a_snapmethod)
1765 if done then
1766
1767 if trace_vsnapping then
1768 local w, h, d = getwhd(current)
1769 report_snapper("mvl list already snapped at (%p,%p): %s",h,d,listtoutf(list))
1770 end
1771 else
1772 local h, d, ch, cd, lines, extra = snap_hlist("mvl",current,sv,false,false)
1773 lastsnap = {
1774 ht = h,
1775 dp = d,
1776 ch = ch,
1777 cd = cd,
1778 extra = extra,
1779 current = current,
1780 }
1781 setprop(current,"snapper",lastsnap)
1782 if trace_vsnapping then
1783 report_snapper("mvl %a snapped from (%p,%p) to (%p,%p) using method %a (%s) for %a (%s lines): %s",
1784 nodecodes[id],h,d,ch,cd,sv.name,sv.specification,where,lines,listtoutf(list))
1785 end
1786 end
1787 elseif trace_vsnapping then
1788 report_snapper("mvl %a not snapped due to unknown snap specification: %s",nodecodes[id],listtoutf(list))
1789 end
1790 setattr(current,a_snapmethod,0)
1791 end
1792 else
1793
1794 end
1795
1796 flush("list")
1797 current = getnext(current)
1798 elseif id == penalty_code then
1799
1800
1801
1802
1803
1804 if trace then
1805 trace_done("kept penalty",current)
1806 end
1807 current = getnext(current)
1808 elseif id == kern_code then
1809 if snap and trace_vsnapping and getkern(current) ~= 0 then
1810 report_snapper("kern of %p kept",getkern(current))
1811 end
1812 flush("kern")
1813 current = getnext(current)
1814 elseif id == glue_code then
1815 local subtype = getsubtype(current)
1816 if subtype == userskip_code then
1817
1818 local sc, sp, so = getspecification(current)
1819 if not so then
1820 so = 1
1821 end
1822 if sp and sc == penalty then
1823 if where == "page" then
1824 getpagelist()
1825 local p = specialmethods[specialmethod](pagehead,pagetail,current,sp)
1826 if p then
1827
1828
1829
1830
1831
1832 special_penalty = sp
1833 sp = p
1834 end
1835 end
1836 if not penalty_data then
1837 penalty_data = sp
1838 elseif penalty_order < so then
1839 penalty_order, penalty_data = so, sp
1840 elseif penalty_order == so and sp > penalty_data then
1841 penalty_data = sp
1842 end
1843 if trace then
1844 trace_skip("penalty in skip",sc,so,sp,current)
1845 end
1846 head, current = remove_node(head,current,true)
1847 elseif not sc then
1848 if glue_data then
1849 if trace then
1850 trace_done("flush",glue_data)
1851 end
1852 head = insertnodebefore(head,current,glue_data)
1853 if trace then
1854 trace_natural("natural",current)
1855 end
1856 current = getnext(current)
1857 glue_data = nil
1858 else
1859
1860
1861 local previous = getprev(current)
1862 if previous and getid(previous) == glue_code and getsubtype(previous) == userskip_code then
1863 local pwidth, pstretch, pshrink, pstretch_order, pshrink_order = getglue(previous)
1864 local cwidth, cstretch, cshrink, cstretch_order, cshrink_order = getglue(current)
1865 if pstretch_order == 0 and pshrink_order == 0 and cstretch_order == 0 and cshrink_order == 0 then
1866 setglue(previous,pwidth + cwidth, pstretch + cstretch, pshrink + cshrink)
1867 if trace then
1868 trace_natural("removed",current)
1869 end
1870 head, current = remove_node(head,current,true)
1871 if trace then
1872 trace_natural("collapsed",previous)
1873 end
1874 else
1875 if trace then
1876 trace_natural("filler",current)
1877 end
1878 current = getnext(current)
1879 end
1880 else
1881 if trace then
1882 trace_natural("natural (no prev)",current)
1883 end
1884 current = getnext(current)
1885 end
1886 end
1887 glue_order = 0
1888 elseif sc == disable or sc == enable then
1889 local next = getnext(current)
1890 if next then
1891 ignore_following = sc == disable
1892 if trace then
1893 trace_skip(sc == disable and "disable" or "enable",sc,so,sp,current)
1894 end
1895 head, current = remove_node(head,current,true)
1896 else
1897 current = next
1898 end
1899 elseif sc == packed then
1900 if trace then
1901 trace_skip("packed",sc,so,sp,current)
1902 end
1903
1904 head, current = remove_node(head,current,true)
1905 elseif sc == nowhite then
1906 local next = getnext(current)
1907 if next then
1908 ignore_whitespace = true
1909 head, current = remove_node(head,current,true)
1910 else
1911 current = next
1912 end
1913 elseif sc == discard then
1914 if trace then
1915 trace_skip("discard",sc,so,sp,current)
1916 end
1917 head, current = remove_node(head,current,true)
1918 elseif sc == overlay then
1919
1920 if trace then
1921 trace_skip("overlay",sc,so,sp,current)
1922 end
1923
1924
1925 head, current = check_experimental_overlay(head,current,a_snapmethod)
1926 elseif ignore_following then
1927 if trace then
1928 trace_skip("disabled",sc,so,sp,current)
1929 end
1930 head, current = remove_node(head,current,true)
1931 elseif not glue_data then
1932 if trace then
1933 trace_skip("assign",sc,so,sp,current)
1934 end
1935 glue_order = so
1936 head, current, glue_data = remove_node(head,current)
1937 elseif glue_order < so then
1938 if trace then
1939 trace_skip("force",sc,so,sp,current)
1940 end
1941 glue_order = so
1942 flushnode(glue_data)
1943 head, current, glue_data = remove_node(head,current)
1944 elseif glue_order == so then
1945
1946 if sc == largest then
1947 local cw = getwidth(current)
1948 local gw = getwidth(glue_data)
1949 if cw > gw then
1950 if trace then
1951 trace_skip("largest",sc,so,sp,current)
1952 end
1953 flushnode(glue_data)
1954 head, current, glue_data = remove_node(head,current)
1955 else
1956 if trace then
1957 trace_skip("remove smallest",sc,so,sp,current)
1958 end
1959 head, current = remove_node(head,current,true)
1960 end
1961 elseif sc == goback then
1962 if trace then
1963 trace_skip("goback",sc,so,sp,current)
1964 end
1965 flushnode(glue_data)
1966 head, current, glue_data = remove_node(head,current)
1967 elseif sc == force then
1968
1969
1970 if trace then
1971 trace_skip("force",sc,so,sp,current)
1972 end
1973 flushnode(glue_data)
1974 head, current, glue_data = remove_node(head,current)
1975 elseif sc == penalty then
1976 if trace then
1977 trace_skip("penalty",sc,so,sp,current)
1978 end
1979 flushnode(glue_data)
1980 glue_data = nil
1981 head, current = remove_node(head,current,true)
1982 elseif sc == add then
1983 if trace then
1984 trace_skip("add",sc,so,sp,current)
1985 end
1986 local cwidth, cstretch, cshrink = getglue(current)
1987 local gwidth, gstretch, gshrink = getglue(glue_data)
1988 setglue(glue_data,gwidth + cwidth, gstretch + cstretch,gshrink + cshrink)
1989
1990 head, current = remove_node(head,current,true)
1991 else
1992 if trace then
1993 trace_skip("unknown",sc,so,sp,current)
1994 end
1995 head, current = remove_node(head,current,true)
1996 end
1997 else
1998 if trace then
1999 trace_skip("unknown",sc,so,sp,current)
2000 end
2001 head, current = remove_node(head,current,true)
2002 end
2003 if sc == force then
2004 force_glue = true
2005 end
2006 elseif subtype == lineskip_code then
2007 if snap then
2008 local s = getattr(current,a_snapmethod)
2009 if s and s ~= 0 then
2010 setattr(current,a_snapmethod,0)
2011 setwidth(current,0)
2012 if trace_vsnapping then
2013 report_snapper("lineskip set to zero")
2014 end
2015 else
2016 if trace then
2017 trace_skip("lineskip",sc,so,sp,current)
2018 end
2019 flush("lineskip")
2020 end
2021 else
2022 if trace then
2023 trace_skip("lineskip",sc,so,sp,current)
2024 end
2025 flush("lineskip")
2026 end
2027 current = getnext(current)
2028 elseif subtype == baselineskip_code then
2029 if snap then
2030 local s = getattr(current,a_snapmethod)
2031 if s and s ~= 0 then
2032 setattr(current,a_snapmethod,0)
2033 setwidth(current,0)
2034 if trace_vsnapping then
2035 report_snapper("baselineskip set to zero")
2036 end
2037 else
2038 if trace then
2039 trace_skip("baselineskip",sc,so,sp,current)
2040 end
2041 flush("baselineskip")
2042 end
2043 else
2044 if trace then
2045 trace_skip("baselineskip",sc,so,sp,current)
2046 end
2047 flush("baselineskip")
2048 end
2049 current = getnext(current)
2050 elseif subtype == parskip_code then
2051
2052 if ignore_whitespace then
2053 if trace then
2054 trace_natural("ignored parskip",current)
2055 end
2056 head, current = remove_node(head,current,true)
2057 elseif glue_data then
2058 local w = getwidth(current)
2059 if w ~= 0 and w > getwidth(glue_data) then
2060 flushnode(glue_data)
2061 glue_data = current
2062 if trace then
2063 trace_natural("taking parskip",current)
2064 end
2065 head, current = remove_node(head,current)
2066 else
2067 if trace then
2068 trace_natural("removed parskip",current)
2069 end
2070 head, current = remove_node(head,current,true)
2071 end
2072 else
2073 if trace then
2074 trace_natural("honored parskip",current)
2075 end
2076 head, current, glue_data = remove_node(head,current)
2077 end
2078 elseif subtype == topskip_code or subtype == splittopskip_code then
2079 local next = getnext(current)
2080
2081 if next and getspecification(next) == notopskip then
2082 setglue(current)
2083 end
2084 if snap then
2085 local s = getattr(current,a_snapmethod)
2086 if s and s ~= 0 then
2087 setattr(current,a_snapmethod,0)
2088 local sv = snapmethods[s]
2089 local w, cw = snap_topskip(current,sv)
2090 if trace_vsnapping then
2091 report_snapper("topskip snapped from %p to %p for %a",w,cw,where)
2092 end
2093 else
2094 if trace then
2095 trace_skip("topskip",sc,so,sp,current)
2096 end
2097 flush("topskip")
2098 end
2099 else
2100 if trace then
2101 trace_skip("topskip",sc,so,sp,current)
2102 end
2103 flush("topskip")
2104 end
2105 current = getnext(current)
2106 elseif subtype == abovedisplayskip_code and remove_math_skips then
2107
2108 if trace then
2109 trace_skip("above display skip (normal)",sc,so,sp,current)
2110 end
2111 flush("above display skip (normal)")
2112 current = getnext(current)
2113
2114 elseif subtype == belowdisplayskip_code and remove_math_skips then
2115
2116 if trace then
2117 trace_skip("below display skip (normal)",sc,so,sp,current)
2118 end
2119 flush("below display skip (normal)")
2120 current = getnext(current)
2121
2122 elseif subtype == abovedisplayshortskip_code and remove_math_skips then
2123
2124 if trace then
2125 trace_skip("above display skip (short)",sc,so,sp,current)
2126 end
2127 flush("above display skip (short)")
2128 current = getnext(current)
2129
2130 elseif subtype == belowdisplayshortskip_code and remove_math_skips then
2131
2132 if trace then
2133 trace_skip("below display skip (short)",sc,so,sp,current)
2134 end
2135 flush("below display skip (short)")
2136 current = getnext(current)
2137
2138 else
2139 if snap and trace_vsnapping then
2140 local w = getwidth(current)
2141 if w ~= 0 then
2142 report_snapper("glue %p of type %a kept",w,gluecodes[subtype])
2143 end
2144 end
2145 if trace then
2146 trace_skip(formatters["glue of type %a"](subtype),sc,so,sp,current)
2147 end
2148 flush("some glue")
2149 current = getnext(current)
2150 end
2151 else
2152 flush(trace and formatters["node with id %a"](id) or "other node")
2153 current = getnext(current)
2154 end
2155 end
2156 if trace then
2157 trace_info("stop analyzing",where,what)
2158 end
2159
2160
2161
2162 if trace and (glue_data or penalty_data) then
2163 trace_info("start flushing",where,what)
2164 end
2165 local tail
2166 if penalty_data then
2167 tail = find_node_tail(head)
2168 local p = new_penalty(penalty_data)
2169 setvisual(p)
2170 if trace then
2171 trace_done("result",p)
2172 end
2173 setlink(tail,p)
2174
2175 local props = properties[p]
2176 if props then
2177 props.special_penalty = special_penalty or penalty_data
2178 else
2179 properties[p] = {
2180 special_penalty = special_penalty or penalty_data
2181 }
2182 end
2183
2184 end
2185 if glue_data then
2186 if not tail then tail = find_node_tail(head) end
2187 if trace then
2188 trace_done("result",glue_data)
2189 end
2190 if force_glue then
2191 head, tail = forced_skip(head,tail,getwidth(glue_data),"after",trace)
2192 flushnode(glue_data)
2193 glue_data = nil
2194 elseif tail then
2195 setlink(tail,glue_data)
2196 setnext(glue_data)
2197 else
2198 head = glue_data
2199 end
2200
2201 texnest[texnest.ptr].prevdepth = 0
2202 end
2203 if trace then
2204 if glue_data or penalty_data then
2205 trace_info("stop flushing",where,what)
2206 end
2207 show_tracing(head)
2208 if oldhead ~= head then
2209 trace_info("head has been changed from %a to %a",nodecodes[getid(oldhead)],nodecodes[getid(head)])
2210 end
2211 end
2212 return head
2213 end
2214
2215
2216
2217
2218
2219
2220
2221
2222
2223
2224
2225
2226
2227
2228
2229
2230
2231 local stackhead, stacktail, stackhack = nil, nil, false
2232
2233 local function report(message,where,lst)
2234 if lst and where then
2235 report_vspacing(message,where,count_nodes(lst,true),nodeidstostring(lst))
2236 else
2237 report_vspacing(message,count_nodes(lst,true),nodeidstostring(lst))
2238 end
2239 end
2240
2241
2242
2243
2244
2245
2246
2247
2248
2249
2250
2251 local forceflush = false
2252
2253 function vspacing.pagehandler(newhead,where)
2254 if newhead then
2255 local newtail = find_node_tail(newhead)
2256 local flush = false
2257 stackhack = true
2258
2259
2260
2261 for n, id, subtype in nextnode, newhead do
2262 if id ~= glue_code then
2263 flush = true
2264 elseif subtype == userskip_code then
2265
2266 local sc = getspecification(n)
2267 if sc then
2268 stackhack = true
2269 else
2270 flush = true
2271 end
2272 elseif subtype == parskip_code then
2273
2274 if texgetcount(c_spac_vspacing_ignore_parskip) > 0 then
2275 setglue(n)
2276
2277 end
2278 end
2279 end
2280 texsetcount(c_spac_vspacing_ignore_parskip,0)
2281
2282 if forceflush then
2283 forceflush = false
2284 flush = true
2285 end
2286
2287 if flush then
2288 if stackhead then
2289 if trace_collect_vspacing then report("%s > appending %s nodes to stack (final): %s",where,newhead) end
2290 setlink(stacktail,newhead)
2291 newhead = stackhead
2292 stackhead = nil
2293 stacktail = nil
2294 end
2295 if stackhack then
2296 stackhack = false
2297 if trace_collect_vspacing then report("%s > processing %s nodes: %s",where,newhead) end
2298 newhead = collapser(newhead,"page",where,trace_page_vspacing,true,a_snapmethod)
2299 else
2300 if trace_collect_vspacing then report("%s > flushing %s nodes: %s",where,newhead) end
2301 end
2302 return newhead
2303 else
2304 if stackhead then
2305 if trace_collect_vspacing then report("%s > appending %s nodes to stack (intermediate): %s",where,newhead) end
2306 setlink(stacktail,newhead)
2307 else
2308 if trace_collect_vspacing then report("%s > storing %s nodes in stack (initial): %s",where,newhead) end
2309 stackhead = newhead
2310 end
2311 stacktail = newtail
2312 end
2313 end
2314 return nil
2315 end
2316
2317 function vspacing.pageoverflow()
2318 local h = 0
2319 if stackhead then
2320 for n, id in nextnode, stackhead do
2321 if id == glue_code then
2322 h = h + getwidth(n)
2323 elseif id == kern_code then
2324 h = h + getkern(n)
2325 end
2326 end
2327 end
2328 return h
2329 end
2330
2331 function vspacing.forcepageflush()
2332 forceflush = true
2333 end
2334
2335 local ignored = table.tohash {
2336 "splitkeep",
2337 "splitoff",
2338
2339 }
2340
2341 function vspacing.vboxhandler(head,where)
2342 if head and not ignored[where] and getnext(head) then
2343
2344 head = collapser(head,"vbox",where,trace_vbox_vspacing,true,a_snapvbox)
2345 end
2346 return head
2347 end
2348
2349 function vspacing.collapsevbox(n,aslist)
2350 local box = getbox(n)
2351 if box then
2352 local list = getlist(box)
2353 if list then
2354 list = collapser(list,"snapper","vbox",trace_vbox_vspacing,true,a_snapmethod)
2355 if aslist then
2356 setlist(box,list)
2357 else
2358 setlist(box,vpack_node(list))
2359 end
2360 end
2361 end
2362 end
2363
2364end
2365
2366
2367
2368
2369
2370do
2371
2372 local outer = texnest[0]
2373
2374 local enabled = true
2375 local trace = false
2376 local report = logs.reporter("vspacing")
2377
2378 trackers.register("vspacing.synchronizepage",function(v)
2379 trace = v
2380 end)
2381
2382 directives.register("vspacing.synchronizepage",function(v)
2383 enabled = v
2384 end)
2385
2386 local function ignoredepth()
2387 return texgetdimen("ignoredepthcriterion")
2388 end
2389
2390
2391
2392
2393
2394
2395
2396
2397 function vspacing.getnofpreviouslines(head)
2398 if enabled then
2399 if not thead then
2400 head = texlists.pagehead
2401 end
2402 local noflines = 0
2403 if head then
2404 local tail = find_node_tail(tonut(head))
2405 while tail do
2406 local id = getid(tail)
2407 if id == hlist_code then
2408 if getsubtype(tail) == linelist_code then
2409 noflines = noflines + 1
2410 else
2411 break
2412 end
2413 elseif id == vlist_code then
2414 break
2415 elseif id == glue_code then
2416 local subtype = getsubtype(tail)
2417 if subtype == baselineskip_code or subtype == lineskip_code then
2418
2419 elseif subtype == parskip_code then
2420 if getwidth(tail) > 0 then
2421 break
2422 else
2423
2424 end
2425 end
2426 elseif id == penalty_code then
2427
2428 elseif id == rule_code or id == kern_code then
2429 break
2430 else
2431
2432 end
2433 tail = getprev(tail)
2434 end
2435 end
2436 return noflines
2437 end
2438 end
2439
2440 interfaces.implement {
2441 name = "getnofpreviouslines",
2442 public = true,
2443 actions = vspacing.getnofpreviouslines,
2444 }
2445
2446 function vspacing.synchronizepage()
2447 if enabled then
2448 if trace then
2449 local newdepth = outer.prevdepth
2450 local olddepth = newdepth
2451 if not texlists.pagehead then
2452 newdepth = ignoredepth()
2453 texset("prevdepth",newdepth)
2454 outer.prevdepth = newdepth
2455 end
2456 report("page %i, prevdepth %p => %p",texgetcount("realpageno"),olddepth,newdepth)
2457
2458 else
2459 if not texlists.pagehead then
2460 local newdepth = ignoredepth()
2461 texset("prevdepth",newdepth)
2462 outer.prevdepth = newdepth
2463 end
2464 end
2465 end
2466 end
2467
2468 local trace = false
2469 local abs = math.abs
2470
2471 local vmode_code = tex.modelevels.vertical
2472 local temp_code = nodecodes.temp
2473 local texgetnest = tex.getnest
2474 local texgetlist = tex.getlist
2475 local getnodetail = nodes.tail
2476
2477
2478
2479
2480
2481 function vspacing.checkstrutdepth(depth)
2482 local nest = texgetnest()
2483 if abs(nest.mode) == vmode_code and nest.head then
2484 local tail = nest.tail
2485 local id = tail.id
2486 if id == hlist_code then
2487 if tail.depth < depth then
2488 tail.depth = depth
2489 end
2490 nest.prevdepth = depth
2491 elseif id == temp_code and texgetnest("ptr") == 0 then
2492 local head = texgetlist("pagehead")
2493 if head then
2494 tail = getnodetail(head)
2495 if tail and tail.id == hlist_code then
2496 if tail.depth < depth then
2497 tail.depth = depth
2498 end
2499 nest.prevdepth = depth
2500
2501 texset("pagedepth",depth)
2502 end
2503 end
2504 end
2505 end
2506 end
2507
2508 interfaces.implement {
2509 name = "checkstrutdepth",
2510 arguments = "dimension",
2511 actions = vspacing.checkstrutdepth,
2512 }
2513
2514
2515
2516
2517
2518
2519
2520
2521
2522
2523
2524
2525
2526
2527
2528
2529
2530
2531
2532
2533
2534
2535
2536
2537
2538
2539
2540
2541
2542
2543
2544
2545
2546
2547
2548
2549
2550
2551
2552
2553
2554
2555
2556
2557
2558
2559
2560
2561
2562
2563
2564
2565
2566
2567
2568
2569
2570
2571
2572
2573
2574
2575
2576
2577 local hlist_code = nodes.nodecodes.hlist
2578 local insert_code = nodes.nodecodes.insert
2579 local mark_code = nodes.nodecodes.mark
2580 local line_code = nodes.listcodes.line
2581
2582
2583
2584
2585
2586
2587 local gettotal = nuts.gettotal
2588 local getspeciallist = nuts.getspeciallist
2589 local setspeciallist = nuts.setspeciallist
2590
2591 local triggerbuildpage = tex.triggerbuildpage
2592
2593
2594
2595 local texgetnest = tex.getnest
2596
2597
2598
2599 local trace = false trackers.register("otr.forcestrutdepth", function(v)
2600 trace = v and function(n)
2601 setvisual(nuts.tonut(n),nodes.visualizers.modes.depth)
2602 end
2603 end)
2604
2605 local treversenode = nuts.treversers.node
2606
2607 local function flushcontributions()
2608 if texgetnest("ptr") == 0 then
2609
2610 local prev = nil
2611 local cycle = 1
2612 while cycle <= 10 do
2613 local head = getspeciallist("contributehead")
2614 if head == prev then
2615
2616
2617 cycle = cycle + 1
2618 else
2619 triggerbuildpage()
2620 prev = head
2621 end
2622 end
2623 return true
2624 else
2625 return false
2626 end
2627 end
2628
2629 vspacing.flushcontributions = flushcontributions
2630
2631 function vspacing.forcestrutdepth()
2632
2633 if flushcontributions() then
2634
2635 local head, tail = getspeciallist("pagehead")
2636 if tail then
2637 for n, id, subtype in treversenode, tail do
2638 if id == hlist_code then
2639 if subtype == line_code then
2640 local strutdp = texgetdimen(d_strutdp)
2641 local delta = strutdp - getdepth(n)
2642 if delta > 0 then
2643
2644 setdepth(n,strutdp)
2645 texset("pagetotal",texget("pagetotal") + delta)
2646 texset("pagedepth",strutdp)
2647 if trace then
2648 trace(n)
2649 end
2650 end
2651 end
2652 break
2653 elseif id == insert_code or id == mark_code then
2654
2655 else
2656
2657
2658
2659 break
2660
2661 end
2662 end
2663 end
2664 else
2665 local nest = texgetnest()
2666
2667 local tail = nest.tail
2668 if tail.id == hlist_code and tail.subtype == line_code then
2669 local strutdp = texgetdimen(d_strutdp)
2670 if tail.depth < strutdp then
2671 tail.depth = strutdp
2672 end
2673 nest.prevdepth = strutdp
2674 if trace then
2675 trace(tail)
2676 end
2677 end
2678
2679 end
2680 end
2681
2682
2683
2684 local setbox = nuts.setbox
2685
2686 function vspacing.interceptsamepagecontent(box)
2687 if vspacing.flushcontributions() then
2688
2689 local head, tail = getspeciallist("pagehead")
2690 if tail and getid(tail) == glue_code then
2691 local prev = getprev(tail)
2692 if prev and getid(prev) == penalty_code then
2693 if getpenalty(prev) >= 10000 then
2694 local state = nil
2695 local first = nil
2696 local last = tail
2697 local c = getprev(prev)
2698 while c do
2699 if getid(c) == glue_code then
2700 local p = getprev(c)
2701 if p and getid(p) == penalty_code then
2702 if getpenalty(p) < 10000 then
2703 state = 1
2704 end
2705 else
2706 state = 2
2707 break
2708 end
2709 end
2710 first = c
2711 c = getprev(c)
2712 end
2713 if first and first ~= head then
2714 setnext(getprev(first))
2715 setprev(first)
2716 local vbox = vpack_node(first)
2717 setvisual(vbox)
2718 setbox(box,vbox)
2719 report_vspacing("same page intercept, case %i")
2720 end
2721 end
2722 end
2723 end
2724 end
2725 end
2726
2727 interfaces.implement {
2728 name = "interceptsamepagecontent",
2729 arguments = "integer",
2730 actions = vspacing.interceptsamepagecontent,
2731 }
2732
2733
2734
2735
2736
2737
2738
2739
2740
2741
2742
2743
2744
2745
2746
2747 function vspacing.pushatsame()
2748
2749 if last then
2750 nuts.setnext(getprev(last))
2751 nuts.setprev(last)
2752 end
2753 end
2754
2755 function vspacing.popatsame()
2756
2757 nuts.write(last)
2758 end
2759
2760end
2761
2762
2763
2764do
2765
2766 local implement = interfaces.implement
2767
2768 implement {
2769 name = "injectvspacing",
2770 actions = vspacing.inject,
2771 arguments = { "integer", "string" },
2772 }
2773
2774 implement {
2775 name = "injectvpenalty",
2776 actions = vspacing.injectpenalty,
2777 arguments = "integer",
2778 }
2779
2780 implement {
2781 name = "injectvskip",
2782 actions = vspacing.injectskip,
2783 arguments = "dimension",
2784 }
2785
2786 implement {
2787 name = "injectdisable",
2788 actions = vspacing.injectdisable,
2789 }
2790
2791
2792
2793 implement {
2794 name = "synchronizepage",
2795 actions = vspacing.synchronizepage,
2796 scope = "private"
2797 }
2798
2799
2800
2801
2802
2803
2804
2805
2806
2807
2808
2809
2810
2811
2812
2813 implement {
2814 name = "forcestrutdepth",
2815 public = true,
2816 protected = true,
2817 actions = vspacing.forcestrutdepth,
2818 }
2819
2820 implement {
2821 name = "pushatsame",
2822 actions = vspacing.pushatsame,
2823 scope = "private"
2824 }
2825
2826 implement {
2827 name = "popatsame",
2828 actions = vspacing.popatsame,
2829 scope = "private"
2830 }
2831
2832 implement {
2833 name = "vspacingsetamount",
2834 actions = vspacing.setskip,
2835 scope = "private",
2836 arguments = "string",
2837 }
2838
2839 implement {
2840 name = "vspacingdefine",
2841 actions = vspacing.setmap,
2842 scope = "private",
2843 arguments = "2 strings",
2844 }
2845
2846 implement {
2847 name = "vspacingcollapse",
2848 actions = vspacing.collapsevbox,
2849 scope = "private",
2850 arguments = "integer"
2851 }
2852
2853 implement {
2854 name = "vspacingcollapseonly",
2855 actions = vspacing.collapsevbox,
2856 scope = "private",
2857 arguments = { "integer", true }
2858 }
2859
2860 implement {
2861 name = "vspacingsnap",
2862 actions = vspacing.snapbox,
2863 scope = "private",
2864 arguments = "2 integers",
2865 }
2866
2867 implement {
2868 name = "definesnapmethod",
2869 actions = vspacing.definesnapmethod,
2870 scope = "private",
2871 arguments = "2 strings",
2872 }
2873
2874
2875
2876
2877
2878
2879
2880
2881
2882
2883
2884
2885
2886
2887
2888
2889 implement {
2890 name = "removelastline",
2891 actions = function()
2892 local head = texlists.pagehead
2893 if head then
2894 local tail = find_node_tail(head)
2895 if tail then
2896
2897 local head = remove_node(head,tail,true)
2898 texlists.pagehead = head
2899 buildpage()
2900 end
2901 end
2902 end
2903 }
2904
2905 implement {
2906 name = "showpagelist",
2907 actions = function()
2908 local head = texlists.pagehead
2909 if head then
2910 print("start")
2911 while head do
2912 print(" " .. tostring(head))
2913 head = head.next
2914 end
2915 end
2916 end
2917 }
2918
2919 implement {
2920 name = "pageoverflow",
2921 actions = { vspacing.pageoverflow, context }
2922 }
2923
2924 implement {
2925 name = "forcepageflush",
2926 actions = vspacing.forcepageflush
2927 }
2928
2929 implement {
2930 name = "injectzerobaselineskip",
2931 protected = true,
2932 public = true,
2933 actions = { nodes.pool.baselineskip, context },
2934 }
2935
2936end
2937 |