summaryrefslogtreecommitdiff
path: root/tex/context/base/mkiv/spac-ver.lmt
diff options
context:
space:
mode:
Diffstat (limited to 'tex/context/base/mkiv/spac-ver.lmt')
-rw-r--r--tex/context/base/mkiv/spac-ver.lmt2527
1 files changed, 0 insertions, 2527 deletions
diff --git a/tex/context/base/mkiv/spac-ver.lmt b/tex/context/base/mkiv/spac-ver.lmt
deleted file mode 100644
index 90eeacf26..000000000
--- a/tex/context/base/mkiv/spac-ver.lmt
+++ /dev/null
@@ -1,2527 +0,0 @@
-if not modules then modules = { } end modules ['spac-ver'] = {
- version = 1.001,
- optimize = true,
- comment = "companion to spac-ver.mkiv",
- author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
- copyright = "PRAGMA ADE / ConTeXt Development Team",
- license = "see context related readme files"
-}
-
--- we also need to call the spacer for inserts!
-
--- somehow lists still don't always have proper prev nodes so i need to
--- check all of the luatex code some day .. maybe i should replece the
--- whole mvl handler by lua code .. why not
-
--- todo: use lua nodes with lua data (>0.79)
--- see ** can go when 0.79
-
--- needs to be redone, too many calls and tests now ... still within some
--- luatex limitations
-
--- this code dates from the beginning and is kind of experimental; it
--- will be optimized and improved soon .. it's way too complex now but
--- dates from less possibilities
---
--- the collapser will be redone with user nodes; also, we might get make
--- parskip into an attribute and appy it explicitly thereby getting rid
--- of automated injections; eventually i want to get rid of the currently
--- still needed tex -> lua -> tex > lua chain (needed because we can have
--- expandable settings at the tex end
-
--- todo: strip baselineskip around display math
-
-local next, type, tonumber = next, type, tonumber
-local gmatch, concat = string.gmatch, table.concat
-local ceil, floor, abs = math.ceil, math.floor, math.abs
-local lpegmatch = lpeg.match
-local unpack = unpack or table.unpack
-local allocate = utilities.storage.allocate
-local todimen = string.todimen
-local formatters = string.formatters
-
-local nodes = nodes
-local trackers = trackers
-local attributes = attributes
-local context = context
-local tex = tex
-
-local texlists = tex.lists
-local texget = tex.get
-local texgetcount = tex.getcount
-local texgetdimen = tex.getdimen
-local texgetglue = tex.getglue
-local texset = tex.set
-local texsetdimen = tex.setdimen
-local texsetcount = tex.setcount
-local texnest = tex.nest
-local texgetbox = tex.getbox
-
-local buildpage = tex.triggerbuildpage
-
-local variables = interfaces.variables
-local implement = interfaces.implement
-
-local v_local = variables["local"]
-local v_global = variables["global"]
-local v_box = variables.box
------ v_page = variables.page -- reserved for future use
-local v_split = variables.split
-local v_min = variables.min
-local v_max = variables.max
-local v_none = variables.none
-local v_line = variables.line
-local v_noheight = variables.noheight
-local v_nodepth = variables.nodepth
-local v_line = variables.line
-local v_halfline = variables.halfline
-local v_line_m = "-" .. v_line
-local v_halfline_m = "-" .. v_halfline
-local v_first = variables.first
-local v_last = variables.last
-local v_top = variables.top
-local v_bottom = variables.bottom
-local v_minheight = variables.minheight
-local v_maxheight = variables.maxheight
-local v_mindepth = variables.mindepth
-local v_maxdepth = variables.maxdepth
-local v_offset = variables.offset
-local v_strut = variables.strut
-
-local v_hfraction = variables.hfraction
-local v_dfraction = variables.dfraction
-local v_bfraction = variables.bfraction
-local v_tlines = variables.tlines
-local v_blines = variables.blines
-
--- vertical space handler
-
-local trace_vbox_vspacing = false trackers.register("vspacing.vbox", function(v) trace_vbox_vspacing = v end)
-local trace_page_vspacing = false trackers.register("vspacing.page", function(v) trace_page_vspacing = v end)
-local trace_page_builder = false trackers.register("builders.page", function(v) trace_page_builder = v end)
-local trace_collect_vspacing = false trackers.register("vspacing.collect", function(v) trace_collect_vspacing = v end)
-local trace_vspacing = false trackers.register("vspacing.spacing", function(v) trace_vspacing = v end)
-local trace_vsnapping = false trackers.register("vspacing.snapping", function(v) trace_vsnapping = v end)
-local trace_specials = false trackers.register("vspacing.specials", function(v) trace_specials = v end)
-
-local remove_math_skips = true directives.register("vspacing.removemathskips", function(v) remnove_math_skips = v end)
-
-local report_vspacing = logs.reporter("vspacing","spacing")
-local report_collapser = logs.reporter("vspacing","collapsing")
-local report_snapper = logs.reporter("vspacing","snapping")
-local report_specials = logs.reporter("vspacing","specials")
-
-local a_skipcategory = attributes.private('skipcategory')
-local a_skippenalty = attributes.private('skippenalty')
-local a_skiporder = attributes.private('skiporder')
-local a_snapmethod = attributes.private('snapmethod')
-local a_snapvbox = attributes.private('snapvbox')
-
-local nuts = nodes.nuts
-local tonut = nuts.tonut
-
-local getnext = nuts.getnext
-local setlink = nuts.setlink
-local getprev = nuts.getprev
-local getid = nuts.getid
-local getlist = nuts.getlist
-local setlist = nuts.setlist
-local getattr = nuts.getattr
-local getattrs = nuts.getattrs
-local setattr = nuts.setattr
-local getsubtype = nuts.getsubtype
-local getbox = nuts.getbox
-local getwhd = nuts.getwhd
-local setwhd = nuts.setwhd
-local getprop = nuts.getprop
-local setprop = nuts.setprop
-local getglue = nuts.getglue
-local setglue = nuts.setglue
-local getkern = nuts.getkern
-local getpenalty = nuts.getpenalty
-local setshift = nuts.setshift
-local setwidth = nuts.setwidth
-local getwidth = nuts.getwidth
-local setheight = nuts.setheight
-local getheight = nuts.getheight
-local setdepth = nuts.setdepth
-local getdepth = nuts.getdepth
-local setnext = nuts.setnext
-
-local find_node_tail = nuts.tail
-local flush_node = nuts.flush_node
-local remove_node = nuts.remove
-local count_nodes = nuts.countall
-local hpack_node = nuts.hpack
-local vpack_node = nuts.vpack
-local start_of_par = nuts.start_of_par
-
-local write_node = nuts.write
-
-local nextnode = nuts.traversers.node
-local nexthlist = nuts.traversers.hlist
-
-local nodereference = nuts.reference
-
-local theprop = nuts.theprop
-
-local listtoutf = nodes.listtoutf
-local nodeidstostring = nodes.idstostring
-
-local nodepool = nuts.pool
-
-local new_penalty = nodepool.penalty
-local new_kern = nodepool.kern
-local new_glue = nodepool.glue
-local new_rule = nodepool.rule
-
-local nodecodes = nodes.nodecodes
-local gluecodes = nodes.gluecodes
------ penaltycodes = nodes.penaltycodes
------ listcodes = nodes.listcodes
-
-local penalty_code = nodecodes.penalty
-local kern_code = nodecodes.kern
-local glue_code = nodecodes.glue
-local hlist_code = nodecodes.hlist
-local vlist_code = nodecodes.vlist
-local rule_code = nodecodes.rule
-local par_code = nodecodes.par
-
-local userskip_code = gluecodes.userskip
-local lineskip_code = gluecodes.lineskip
-local baselineskip_code = gluecodes.baselineskip
-local parskip_code = gluecodes.parskip
-local topskip_code = gluecodes.topskip
-local splittopskip_code = gluecodes.splittopskip
-
-local linelist_code = nodes.listcodes.line
-
-local properties = nodes.properties.data
-
-local vspacing = builders.vspacing or { }
-builders.vspacing = vspacing
-
-local vspacingdata = vspacing.data or { }
-vspacing.data = vspacingdata
-
-local snapmethods = vspacingdata.snapmethods or { }
-vspacingdata.snapmethods = snapmethods
-
-storage.register("builders/vspacing/data/snapmethods", snapmethods, "builders.vspacing.data.snapmethods")
-
-do
-
- local default = {
- [v_maxheight] = true,
- [v_maxdepth] = true,
- [v_strut] = true,
- [v_hfraction] = 1,
- [v_dfraction] = 1,
- [v_bfraction] = 0.25,
- }
-
- local fractions = {
- [v_minheight] = v_hfraction, [v_maxheight] = v_hfraction,
- [v_mindepth] = v_dfraction, [v_maxdepth] = v_dfraction,
- [v_box] = v_bfraction,
- [v_top] = v_tlines, [v_bottom] = v_blines,
- }
-
- local values = {
- offset = "offset"
- }
-
- local colonsplitter = lpeg.splitat(":")
-
- local function listtohash(str)
- local t = { }
- for s in gmatch(str,"[^, ]+") do
- local key, detail = lpegmatch(colonsplitter,s)
- local v = variables[key]
- if v then
- t[v] = true
- if detail then
- local k = fractions[key]
- if k then
- detail = tonumber("0" .. detail)
- if detail then
- t[k] = detail
- end
- else
- k = values[key]
- if k then
- detail = todimen(detail)
- if detail then
- t[k] = detail
- end
- end
- end
- end
- else
- detail = tonumber("0" .. key)
- if detail then
- t[v_hfraction] = detail
- t[v_dfraction] = detail
- end
- end
- end
- if next(t) then
- t[v_hfraction] = t[v_hfraction] or 1
- t[v_dfraction] = t[v_dfraction] or 1
- return t
- else
- return default
- end
- end
-
- function vspacing.definesnapmethod(name,method)
- local n = #snapmethods + 1
- local t = listtohash(method)
- snapmethods[n] = t
- t.name = name -- not interfaced
- t.specification = method -- not interfaced
- context(n)
- end
-
-end
-
-local function validvbox(parentid,list)
- if parentid == hlist_code then
- local id = getid(list)
- if id == par_code and start_of_par(list) then
- list = getnext(list)
- if not next then
- return nil
- end
- end
- local done = nil
- for n, id in nextnode, list do
- if id == vlist_code or id == hlist_code then
- if done then
- return nil
- else
- done = n
- end
- elseif id == glue_code or id == penalty_code then
- -- go on
- else
- return nil -- whatever
- end
- end
- if done then
- local id = getid(done)
- if id == hlist_code then
- return validvbox(id,getlist(done))
- end
- end
- return done -- only one vbox
- end
-end
-
-local function already_done(parentid,list,a_snapmethod) -- todo: done when only boxes and all snapped
- -- problem: any snapped vbox ends up in a line
- if list and parentid == hlist_code then
- local id = getid(list)
- if id == par_code and start_of_par(list) then
- list = getnext(list)
- if not list then
- return false
- end
- end
- for n, id in nextnode, list do
- if id == hlist_code or id == vlist_code then
- -- local a = getattr(n,a_snapmethod)
- -- if not a then
- -- -- return true -- not snapped at all
- -- elseif a == 0 then
- -- return true -- already snapped
- -- end
- local p = getprop(n,"snapper")
- if p then
- return p
- end
- elseif id == glue_code or id == penalty_code then -- or id == kern_code then
- -- go on
- else
- return false -- whatever
- end
- end
- end
- return false
-end
-
--- quite tricky: ceil(-something) => -0
-
-local function ceiled(n)
- if n < 0 or n < 0.01 then
- return 0
- else
- return ceil(n)
- end
-end
-
-local function floored(n)
- if n < 0 or n < 0.01 then
- return 0
- else
- return floor(n)
- end
-end
-
--- check variables.none etc
-
-local function fixedprofile(current)
- local profiling = builders.profiling
- return profiling and profiling.fixedprofile(current)
-end
-
-local function snap_hlist(where,current,method,height,depth) -- method[v_strut] is default
- if fixedprofile(current) then
- return
- end
- local list = getlist(current)
- local t = trace_vsnapping and { }
- if t then
- t[#t+1] = formatters["list content: %s"](listtoutf(list))
- t[#t+1] = formatters["snap method: %s"](method.name) -- not interfaced
- t[#t+1] = formatters["specification: %s"](method.specification) -- not interfaced
- end
- local snapht, snapdp
- if method[v_local] then
- -- snapping is done immediately here
- snapht = texgetdimen("bodyfontstrutheight")
- snapdp = texgetdimen("bodyfontstrutdepth")
- if t then
- t[#t+1] = formatters["local: snapht %p snapdp %p"](snapht,snapdp)
- end
- elseif method[v_global] then
- snapht = texgetdimen("globalbodyfontstrutheight")
- snapdp = texgetdimen("globalbodyfontstrutdepth")
- if t then
- t[#t+1] = formatters["global: snapht %p snapdp %p"](snapht,snapdp)
- end
- else
- -- maybe autolocal
- -- snapping might happen later in the otr
- snapht = texgetdimen("globalbodyfontstrutheight")
- snapdp = texgetdimen("globalbodyfontstrutdepth")
- local lsnapht = texgetdimen("bodyfontstrutheight")
- local lsnapdp = texgetdimen("bodyfontstrutdepth")
- if snapht ~= lsnapht and snapdp ~= lsnapdp then
- snapht, snapdp = lsnapht, lsnapdp
- end
- if t then
- t[#t+1] = formatters["auto: snapht %p snapdp %p"](snapht,snapdp)
- end
- end
-
- local wd, ht, dp = getwhd(current)
-
- local h = (method[v_noheight] and 0) or height or ht
- local d = (method[v_nodepth] and 0) or depth or dp
- local hr = method[v_hfraction] or 1
- local dr = method[v_dfraction] or 1
- local br = method[v_bfraction] or 0
- local ch = h
- local cd = d
- local tlines = method[v_tlines] or 1
- local blines = method[v_blines] or 1
- local done = false
- local plusht = snapht
- local plusdp = snapdp
- local snaphtdp = snapht + snapdp
- local extra = 0
-
- if t then
- t[#t+1] = formatters["hlist: wd %p ht %p (used %p) dp %p (used %p)"](wd,ht,h,dp,d)
- t[#t+1] = formatters["fractions: hfraction %s dfraction %s bfraction %s tlines %s blines %s"](hr,dr,br,tlines,blines)
- end
-
- if method[v_box] then
- local br = 1 - br
- if br < 0 then
- br = 0
- elseif br > 1 then
- br = 1
- end
- local n = ceiled((h+d-br*snapht-br*snapdp)/snaphtdp)
- local x = n * snaphtdp - h - d
- plusht = h + x / 2
- plusdp = d + x / 2
- if t then
- t[#t+1] = formatters["%s: plusht %p plusdp %p"](v_box,plusht,plusdp)
- end
- elseif method[v_max] then
- local n = ceiled((h+d)/snaphtdp)
- local x = n * snaphtdp - h - d
- plusht = h + x / 2
- plusdp = d + x / 2
- if t then
- t[#t+1] = formatters["%s: plusht %p plusdp %p"](v_max,plusht,plusdp)
- end
- elseif method[v_min] then
- -- we catch a lone min
- if method.specification ~= v_min then
- local n = floored((h+d)/snaphtdp)
- local x = n * snaphtdp - h - d
- plusht = h + x / 2
- plusdp = d + x / 2
- if plusht < 0 then
- plusht = 0
- end
- if plusdp < 0 then
- plusdp = 0
- end
- end
- if t then
- t[#t+1] = formatters["%s: plusht %p plusdp %p"](v_min,plusht,plusdp)
- end
- elseif method[v_none] then
- plusht, plusdp = 0, 0
- if t then
- t[#t+1] = formatters["%s: plusht %p plusdp %p"](v_none,0,0)
- end
- end
- -- for now, we actually need to tag a box and then check at several points if something ended up
- -- at the top of a page
- if method[v_halfline] then -- extra halfline
- extra = snaphtdp/2
- plusht = plusht + extra
- plusdp = plusdp + extra
- if t then
- t[#t+1] = formatters["%s: plusht %p plusdp %p"](v_halfline,plusht,plusdp)
- end
- end
- if method[v_line] then -- extra line
- extra = snaphtdp
- plusht = plusht + extra
- plusdp = plusdp + extra
- if t then
- t[#t+1] = formatters["%s: plusht %p plusdp %p"](v_line,plusht,plusdp)
- end
- end
- if method[v_halfline_m] then -- extra halfline
- extra = - snaphtdp/2
- plusht = plusht + extra
- plusdp = plusdp + extra
- if t then
- t[#t+1] = formatters["%s: plusht %p plusdp %p"](v_halfline_m,plusht,plusdp)
- end
- end
- if method[v_line_m] then -- extra line
- extra = - snaphtdp
- plusht = plusht + extra
- plusdp = plusdp + extra
- if t then
- t[#t+1] = formatters["%s: plusht %p plusdp %p"](v_line_m,plusht,plusdp)
- end
- end
- if method[v_first] then
- local thebox = current
- local id = getid(thebox)
- if id == hlist_code then
- thebox = validvbox(id,getlist(thebox))
- id = thebox and getid(thebox)
- end
- if thebox and id == vlist_code then
- local list = getlist(thebox)
- local lw, lh, ld
- for n in nexthlist, list do
- lw, lh, ld = getwhd(n)
- break
- end
- if lh then
- local wd, ht, dp = getwhd(thebox)
- if t then
- t[#t+1] = formatters["first line: height %p depth %p"](lh,ld)
- t[#t+1] = formatters["dimensions: height %p depth %p"](ht,dp)
- end
- local delta = h - lh
- ch, cd = lh, delta + d
- h, d = ch, cd
- local shifted = hpack_node(getlist(current))
- setshift(shifted,delta)
- setlist(current,shifted)
- done = true
- if t then
- t[#t+1] = formatters["first: height %p depth %p shift %p"](ch,cd,delta)
- end
- elseif t then
- t[#t+1] = "first: not done, no content"
- end
- elseif t then
- t[#t+1] = "first: not done, no vbox"
- end
- elseif method[v_last] then
- local thebox = current
- local id = getid(thebox)
- if id == hlist_code then
- thebox = validvbox(id,getlist(thebox))
- id = thebox and getid(thebox)
- end
- if thebox and id == vlist_code then
- local list = getlist(thebox)
- local lw, lh, ld
- for n in nexthlist, list do
- lw, lh, ld = getwhd(n)
- end
- if lh then
- local wd, ht, dp = getwhd(thebox)
- if t then
- t[#t+1] = formatters["last line: height %p depth %p" ](lh,ld)
- t[#t+1] = formatters["dimensions: height %p depth %p"](ht,dp)
- end
- local delta = d - ld
- cd, ch = ld, delta + h
- h, d = ch, cd
- local shifted = hpack_node(getlist(current))
- setshift(shifted,delta)
- setlist(current,shifted)
- done = true
- if t then
- t[#t+1] = formatters["last: height %p depth %p shift %p"](ch,cd,delta)
- end
- elseif t then
- t[#t+1] = "last: not done, no content"
- end
- elseif t then
- t[#t+1] = "last: not done, no vbox"
- end
- end
- if method[v_minheight] then
- ch = floored((h-hr*snapht)/snaphtdp)*snaphtdp + plusht
- if t then
- t[#t+1] = formatters["minheight: %p"](ch)
- end
- elseif method[v_maxheight] then
- ch = ceiled((h-hr*snapht)/snaphtdp)*snaphtdp + plusht
- if t then
- t[#t+1] = formatters["maxheight: %p"](ch)
- end
- else
- ch = plusht
- if t then
- t[#t+1] = formatters["set height: %p"](ch)
- end
- end
- if method[v_mindepth] then
- cd = floored((d-dr*snapdp)/snaphtdp)*snaphtdp + plusdp
- if t then
- t[#t+1] = formatters["mindepth: %p"](cd)
- end
- elseif method[v_maxdepth] then
- cd = ceiled((d-dr*snapdp)/snaphtdp)*snaphtdp + plusdp
- if t then
- t[#t+1] = formatters["maxdepth: %p"](cd)
- end
- else
- cd = plusdp
- if t then
- t[#t+1] = formatters["set depth: %p"](cd)
- end
- end
- if method[v_top] then
- ch = ch + tlines * snaphtdp
- if t then
- t[#t+1] = formatters["top height: %p"](ch)
- end
- end
- if method[v_bottom] then
- cd = cd + blines * snaphtdp
- if t then
- t[#t+1] = formatters["bottom depth: %p"](cd)
- end
- end
- local offset = method[v_offset]
- if offset then
- -- we need to set the attr
- if t then
- local wd, ht, dp = getwhd(current)
- t[#t+1] = formatters["before offset: %p (width %p height %p depth %p)"](offset,wd,ht,dp)
- end
- local shifted = hpack_node(getlist(current))
- setshift(shifted,offset)
- setlist(current,shifted)
- if t then
- local wd, ht, dp = getwhd(current)
- t[#t+1] = formatters["after offset: %p (width %p height %p depth %p)"](offset,wd,ht,dp)
- end
- setattr(shifted,a_snapmethod,0)
- setattr(current,a_snapmethod,0)
- end
- if not height then
- setheight(current,ch)
- if t then
- t[#t+1] = formatters["forced height: %p"](ch)
- end
- end
- if not depth then
- setdepth(current,cd)
- if t then
- t[#t+1] = formatters["forced depth: %p"](cd)
- end
- end
- local lines = (ch+cd)/snaphtdp
- if t then
- local original = (h+d)/snaphtdp
- local whatever = (ch+cd)/(texgetdimen("globalbodyfontstrutheight") + texgetdimen("globalbodyfontstrutdepth"))
- t[#t+1] = formatters["final lines : %p -> %p (%p)"](original,lines,whatever)
- t[#t+1] = formatters["final height: %p -> %p"](h,ch)
- t[#t+1] = formatters["final depth : %p -> %p"](d,cd)
- end
--- todo:
---
--- if h < 0 or d < 0 then
--- h = 0
--- d = 0
--- end
- if t then
- report_snapper("trace: %s type %s\n\t%\n\tt",where,nodecodes[getid(current)],t)
- end
- if not method[v_split] then
- -- so extra will not be compensated at the top of a page
- extra = 0
- end
- return h, d, ch, cd, lines, extra
-end
-
-local categories = {
- [0] = "discard",
- [1] = "largest",
- [2] = "force",
- [3] = "penalty",
- [4] = "add",
- [5] = "disable",
- [6] = "nowhite",
- [7] = "goback",
- [8] = "packed",
- [9] = "overlay",
- [10] = "enable",
- [11] = "notopskip",
-}
-
-categories = allocate(table.swapped(categories,categories))
-vspacing.categories = categories
-
-function vspacing.tocategories(str)
- local t = { }
- for s in gmatch(str,"[^, ]") do -- use lpeg instead
- local n = tonumber(s)
- if n then
- t[categories[n]] = true
- else
- t[b] = true
- end
- end
- return t
-end
-
-function vspacing.tocategory(str) -- can be optimized
- if type(str) == "string" then
- return set.tonumber(vspacing.tocategories(str))
- else
- return set.tonumber({ [categories[str]] = true })
- end
-end
-
-vspacingdata.map = vspacingdata.map or { } -- allocate ?
-vspacingdata.skip = vspacingdata.skip or { } -- allocate ?
-
-storage.register("builders/vspacing/data/map", vspacingdata.map, "builders.vspacing.data.map")
-storage.register("builders/vspacing/data/skip", vspacingdata.skip, "builders.vspacing.data.skip")
-
-do
-
- local P, C, R, S, Cc, Cs = lpeg.P, lpeg.C, lpeg.R, lpeg.S, lpeg.Cc, lpeg.Cs
-
- vspacing.fixed = false
-
- local map = vspacingdata.map
- local skip = vspacingdata.skip
-
- local sign = S("+-")^0
- local multiplier = C(sign * R("09")^1) * P("*")
- local singlefier = Cs(sign * Cc(1))
- local separator = S(", ")
- local category = P(":") * C((1-separator)^1)
- local keyword = C((1-category-separator)^1)
- local splitter = (multiplier + Cc(1)) * keyword * (category + Cc(false))
-
- local k_fixed = variables.fixed
- local k_flexible = variables.flexible
- local k_category = "category"
- local k_penalty = "penalty"
- local k_order = "order"
-
- function vspacing.setmap(from,to)
- map[from] = to
- end
-
- function vspacing.setskip(key,value,grid)
- if value ~= "" then
- if grid == "" then grid = value end
- skip[key] = { value, grid }
- end
- end
-
- local expandmacro = token.expand_macro
- -- local runlocal = tex.runlocal
- -- local setmacro = tokens.setters.macro
- -- local settoks = tex.settoks
- local toscaled = tex.toscaled
-
- local setattrs = nuts.setattrs
-
- local b_done = false
- local b_packed = false
-
- local b_amount = 0
- local b_stretch = 0
- local b_shrink = 0
- local b_category = false
- local b_penalty = false
- local b_order = false
- local b_fixed = false
- local b_grid = false
-
- local pattern = nil
-
- local packed = categories.packed
-
- local gluefactor = .25
-
- local ctx_ignoreparskip = context.core.ignoreparskip
-
- local function before()
- b_amount = 0
- b_stretch = 0
- b_shrink = 0
- b_category = 1
- b_penalty = false
- b_order = false
- b_fixed = b_grid
- end
-
- local function after()
- if fixed then
- b_stretch = 0
- b_shrink = 0
- else
- b_stretch = gluefactor * b_amount
- b_shrink = gluefactor * b_amount
- end
- end
-
- -- use a cache for predefined ones
-
- local function inject()
- local n = new_glue(b_amount,b_stretch,b_shrink)
- setattrs(n,false,a_skipcategory,b_category,a_skippenalty,b_penalty,a_skiporder,b_order or 1)
- write_node(n)
- end
-
- local function flush()
- after()
- if b_done then
- inject()
- b_done = false
- end
- before()
- end
-
- -- local cmd = token.create("vspacingfromtempstring")
- -- local cmd = token.create("vspacingpredefinedvalue") -- not yet known
-
- local function handler(multiplier, keyword, detail)
- if not keyword then
- report_vspacing("unknown directive %a",s)
- else
- local mk = map[keyword]
- if mk then
- lpegmatch(pattern,mk)
- elseif keyword == k_fixed then
- b_fixed = true
- elseif keyword == k_flexible then
- b_flexible = false
- elseif keyword == k_category then
- local category = tonumber(detail)
- if category == packed then
- b_packed = true
- elseif category then
- b_category = category
- b_done = true
- flush()
- end
- elseif keyword == k_order and detail then
- local order = tonumber(detail)
- if order then
- b_order = order
- end
- elseif keyword == k_penalty and detail then
- local penalty = tonumber(detail)
- if penalty then
- flush()
- b_done = true
- b_category = 3
- b_penalty = penalty
- flush()
- end
- else
- local amount, stretch, shrink
- multiplier = tonumber(multiplier) or 1
- local sk = skip[keyword]
- if sk then
- -- multiplier, keyword
- -- best, for now, todo: runlocal with arguments
- expandmacro("vspacingpredefinedvalue",true,keyword)
- -- expandmacro(cmd,true,keyword)
- -- setmacro("tempstring",keyword)
- -- runlocal(cmd)
- -- nicest
- -- runlocal(cache[keyword])
- -- fast
- -- settoks("scratchtoks",keyword)
- -- runlocal("vspacingfromscratchtoks")
- -- middleground
- -- setmacro("tempstring",keyword)
- -- runlocal(ctx_vspacingfromtempstring)
- --
- amount, stretch, shrink = texgetglue("scratchskip")
- if not stretch then
- stretch = 0
- end
- if not shrink then
- shrink = 0
- end
- if stretch == 0 and shrink == 0 then
- stretch = gluefactor * amount -- always unless grid
- shrink = stretch -- always unless grid
- end
- else -- no check, todo: parse plus and minus
- amount = toscaled(keyword)
- stretch = gluefactor * amount -- always unless grid
- shrink = stretch -- always unless grid
- end
- -- we look at fixed later
- b_amount = b_amount + multiplier * amount
- b_stretch = b_stretch + multiplier * stretch
- b_shrink = b_shrink + multiplier * shrink
- b_done = true
- end
- end
- end
-
- -- alternatively we can make a table and have a keyword -> split cache but this is probably
- -- not really a bottleneck
-
- local splitter = ((multiplier + singlefier) * keyword * (category + Cc(false))) / handler
- pattern = (splitter + separator^1)^0
-
- function vspacing.inject(grid,str)
- if trace_vspacing then
- -- ctx_pushlogger(report_vspacing)
- end
- b_done = false
- b_packed = false
- b_grid = gridsnapping
- before()
- lpegmatch(pattern,str)
- after()
- if b_done then
- inject()
- end
- if b_packed then
- ctx_ignoreparskip()
- end
- if trace_vspacing then
- -- ctx_poplogger()
- end
- end
-
- function vspacing.injectpenalty(penalty)
- local n = new_glue()
- setattrs(n,false,a_skipcategory,categories.penalty,a_skippenalty,penalty,a_skiporder,1)
- write_node(n)
- end
-
- function vspacing.injectskip(amount)
- local n = new_glue(amount)
- setattrs(n,false,a_skipcategory,categories.largest,a_skippenalty,false,a_skiporder,1)
- write_node(n)
- end
-
- function vspacing.injectdisable(amount)
- local n = new_glue()
- setattrs(n,false,a_skipcategory,categories.disable,a_skippenalty,false,a_skiporder,1)
- write_node(n)
- end
-
-end
-
--- implementation
-
--- alignment box begin_of_par vmode_par hmode_par insert penalty before_display after_display
-
-function vspacing.snapbox(n,how)
- local sv = snapmethods[how]
- if sv then
- local box = getbox(n)
- local list = getlist(box)
- if list then
- local s = getattr(list,a_snapmethod)
- if s == 0 then
- if trace_vsnapping then
- -- report_snapper("box list not snapped, already done")
- end
- else
- local wd, ht, dp = getwhd(box)
- if false then -- todo: already_done
- -- assume that the box is already snapped
- if trace_vsnapping then
- report_snapper("box list already snapped at (%p,%p): %s",
- ht,dp,listtoutf(list))
- end
- else
- local h, d, ch, cd, lines, extra = snap_hlist("box",box,sv,ht,dp)
- setprop(box,"snapper",{
- ht = h,
- dp = d,
- ch = ch,
- cd = cd,
- extra = extra,
- current = current,
- })
- setwhd(box,wd,ch,cd)
- if trace_vsnapping then
- report_snapper("box list snapped from (%p,%p) to (%p,%p) using method %a (%s) for %a (%s lines): %s",
- h,d,ch,cd,sv.name,sv.specification,"direct",lines,listtoutf(list))
- end
- setattr(box,a_snapmethod,0) --
- setattr(list,a_snapmethod,0) -- yes or no
- end
- end
- end
- end
-end
-
--- I need to figure out how to deal with the prevdepth that crosses pages. In fact,
--- prevdepth is often quite interfering (even over a next paragraph) so I need to
--- figure out a trick. Maybe use something other than a rule. If we visualize we'll
--- see the baselineskip in action:
---
--- \blank[force,5*big] { \baselineskip1cm xxxxxxxxx \par } \page
--- \blank[force,5*big] { \baselineskip1cm xxxxxxxxx \par } \page
--- \blank[force,5*big] { \baselineskip5cm xxxxxxxxx \par } \page
-
--- We can register and copy the rule instead.
-
-do
-
- local insert_node_after = nuts.insert_after
- local insert_node_before = nuts.insert_before
-
- local abovedisplayskip_code = gluecodes.abovedisplayskip
- local belowdisplayskip_code = gluecodes.belowdisplayskip
- local abovedisplayshortskip_code = gluecodes.abovedisplayshortskip
- local belowdisplayshortskip_code = gluecodes.belowdisplayshortskip
-
- local w, h, d = 0, 0, 0
- ----- w, h, d = 100*65536, 65536, 65536
-
- local trace_list = { }
- local tracing_info = { }
- local before = ""
- local after = ""
-
- local function nodes_to_string(head)
- local current = head
- local t = { }
- while current do
- local id = getid(current)
- local ty = nodecodes[id]
- if id == penalty_code then
- t[#t+1] = formatters["%s:%s"](ty,getpenalty(current))
- elseif id == glue_code then
- t[#t+1] = formatters["%s:%s:%p"](ty,gluecodes[getsubtype(current)],getwidth(current))
- elseif id == kern_code then
- t[#t+1] = formatters["%s:%p"](ty,getkern(current))
- else
- t[#t+1] = ty
- end
- current = getnext(current)
- end
- return concat(t," + ")
- end
-
- local function reset_tracing(head)
- trace_list, tracing_info, before, after = { }, false, nodes_to_string(head), ""
- end
-
- local function trace_skip(str,sc,so,sp,data)
- trace_list[#trace_list+1] = { "skip", formatters["%s | %p | category %s | order %s | penalty %s"](str, getwidth(data), sc or "-", so or "-", sp or "-") }
- tracing_info = true
- end
-
- local function trace_natural(str,data)
- trace_list[#trace_list+1] = { "skip", formatters["%s | %p"](str, getwidth(data)) }
- tracing_info = true
- end
-
- local function trace_info(message, where, what)
- trace_list[#trace_list+1] = { "info", formatters["%s: %s/%s"](message,where,what) }
- end
-
- local function trace_node(what)
- local nt = nodecodes[getid(what)]
- local tl = trace_list[#trace_list]
- if tl and tl[1] == "node" then
- trace_list[#trace_list] = { "node", formatters["%s + %s"](tl[2],nt) }
- else
- trace_list[#trace_list+1] = { "node", nt }
- end
- end
-
- local function show_tracing(head)
- if tracing_info then
- after = nodes_to_string(head)
- for i=1,#trace_list do
- local tag, text = unpack(trace_list[i])
- if tag == "info" then
- report_collapser(text)
- else
- report_collapser(" %s: %s",tag,text)
- end
- end
- report_collapser("before: %s",before)
- report_collapser("after : %s",after)
- end
- end
-
- local function trace_done(str,data)
- if getid(data) == penalty_code then
- trace_list[#trace_list+1] = { "penalty", formatters["%s | %s"](str,getpenalty(data)) }
- else
- trace_list[#trace_list+1] = { "glue", formatters["%s | %p"](str,getwidth(data)) }
- end
- tracing_info = true
- end
-
- local function forced_skip(head,current,width,where,trace) -- looks old ... we have other tricks now
- if head == current then
- if getsubtype(head) == baselineskip_code then
- width = width - getwidth(head)
- end
- end
- if width == 0 then
- -- do nothing
- elseif where == "after" then
- head, current = insert_node_after(head,current,new_rule(w,h,d))
- head, current = insert_node_after(head,current,new_kern(width))
- head, current = insert_node_after(head,current,new_rule(w,h,d))
- else
- local c = current
- head, current = insert_node_before(head,current,new_rule(w,h,d))
- head, current = insert_node_before(head,current,new_kern(width))
- head, current = insert_node_before(head,current,new_rule(w,h,d))
- current = c
- end
- if trace then
- report_vspacing("inserting forced skip of %p",width)
- end
- return head, current
- end
-
- -- penalty only works well when before skip
-
- local discard = categories.discard
- local largest = categories.largest
- local force = categories.force
- local penalty = categories.penalty
- local add = categories.add
- local disable = categories.disable
- local nowhite = categories.nowhite
- local goback = categories.goback
- local packed = categories.packed
- local overlay = categories.overlay
- local enable = categories.enable
- local notopskip = categories.notopskip
-
- -- [whatsits][hlist][glue][glue][penalty]
-
- local special_penalty_min = 32250
- local special_penalty_max = 35000
- local special_penalty_xxx = 0
-
- -- this is rather messy and complex: we want to make sure that successive
- -- header don't break but also make sure that we have at least a decent
- -- break when we have succesive ones (often when testing)
-
- -- todo: mark headers as such so that we can recognize them
-
- local specialmethods = { }
- local specialmethod = 1
-
- specialmethods[1] = function(pagehead,pagetail,start,penalty)
- --
- if not pagehead or penalty < special_penalty_min or penalty > special_penalty_max then
- return
- end
- local current = pagetail
- --
- -- nodes.showsimplelist(pagehead,0)
- --
- if trace_specials then
- report_specials("checking penalty %a",penalty)
- end
- while current do
- local id = getid(current)
- if id == penalty_code then
- local p = properties[current]
- if p then
- local p = p.special_penalty
- if not p then
- if trace_specials then
- report_specials(" regular penalty, continue")
- end
- elseif p == penalty then
- if trace_specials then
- report_specials(" context penalty %a, same level, overloading",p)
- end
- return special_penalty_xxx
- elseif p > special_penalty_min and p < special_penalty_max then
- if penalty < p then
- if trace_specials then
- report_specials(" context penalty %a, lower level, overloading",p)
- end
- return special_penalty_xxx
- else
- if trace_specials then
- report_specials(" context penalty %a, higher level, quitting",p)
- end
- return
- end
- elseif trace_specials then
- report_specials(" context penalty %a, higher level, continue",p)
- end
- else
- local p = getpenalty(current)
- if p < 10000 then
- -- assume some other mechanism kicks in so we seem to have content
- if trace_specials then
- report_specials(" regular penalty %a, quitting",p)
- end
- break
- else
- if trace_specials then
- report_specials(" regular penalty %a, continue",p)
- end
- end
- end
- end
- current = getprev(current)
- end
- -- none found, so no reson to be special
- if trace_specials then
- if pagetail then
- report_specials(" context penalty, discarding, nothing special")
- else
- report_specials(" context penalty, discarding, nothing preceding")
- end
- end
- return special_penalty_xxx
- end
-
- -- This will be replaced after 0.80+ when we have a more robust look-back and
- -- can look at the bigger picture.
-
- -- todo: look back and when a special is there before a list is seen penalty keep ut
-
- -- we now look back a lot, way too often
-
- -- userskip
- -- lineskip
- -- baselineskip
- -- parskip
- -- abovedisplayskip
- -- belowdisplayskip
- -- abovedisplayshortskip
- -- belowdisplayshortskip
- -- topskip
- -- splittopskip
-
- -- we could inject a vadjust to force a recalculation .. a mess
- --
- -- So, the next is far from robust and okay but for the moment this overlaying
- -- has to do. Always test this with the examples in spac-ver.mkvi!
-
- local function snap_topskip(current,method)
- local w = getwidth(current)
- setwidth(current,0)
- return w, 0
- end
-
- local function check_experimental_overlay(head,current)
- local p = nil
- local c = current
- local n = nil
- local function overlay(p,n,mvl)
- local p_wd, p_ht, p_dp = getwhd(p)
- local n_wd, n_ht, n_dp = getwhd(n)
- local skips = 0
- --
- -- We deal with this at the tex end .. we don't see spacing .. enabling this code
- -- is probably harmless but then we need to test it.
- --
- -- we could calculate this before we call
- --
- -- problem: prev list and next list can be unconnected
- --
- local c = getnext(p)
- local l = c
- while c and c ~= n do
- local id = getid(c)
- if id == glue_code then
- skips = skips + getwidth(c)
- elseif id == kern_code then
- skips = skips + getkern(c)
- end
- l = c
- c = getnext(c)
- end
- local c = getprev(n)
- while c and c ~= n and c ~= l do
- local id = getid(c)
- if id == glue_code then
- skips = skips + getwidth(c)
- elseif id == kern_code then
- skips = skips + getkern(c)
- end
- c = getprev(c)
- end
- --
- local delta = n_ht + skips + p_dp
- texsetdimen("global","d_spac_overlay",-delta) -- for tracing
- -- we should adapt pagetotal ! (need a hook for that) .. now we have the wrong pagebreak
- local k = new_kern(-delta)
- head = insert_node_before(head,n,k)
- if n_ht > p_ht then
- local k = new_kern(n_ht-p_ht)
- head = insert_node_before(head,p,k)
- end
- if trace_vspacing then
- 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)
- end
- return remove_node(head,current,true)
- end
-
- -- goto next line
- while c do
- local id = getid(c)
- if id == glue_code or id == penalty_code or id == kern_code then
- -- skip (actually, remove)
- c = getnext(c)
- elseif id == hlist_code then
- n = c
- break
- else
- break
- end
- end
- if n then
- -- we have a next line, goto prev line
- c = current
- while c do
- local id = getid(c)
- if id == glue_code or id == penalty_code then -- kern ?
- c = getprev(c)
- elseif id == hlist_code then
- p = c
- break
- else
- break
- end
- end
- if not p then
- if a_snapmethod == a_snapvbox then
- -- quit, we're not on the mvl
- else
- -- inefficient when we're at the end of a page
- local c = tonut(texlists.page_head)
- while c and c ~= n do
- local id = getid(c)
- if id == hlist_code then
- p = c
- end
- c = getnext(c)
- end
- if p and p ~= n then
- return overlay(p,n,true)
- end
- end
- elseif p ~= n then
- return overlay(p,n,false)
- end
- end
- -- in fact, we could try again later ... so then no remove (a few tries)
- return remove_node(head,current,true)
- end
-
- local function collapser(head,where,what,trace,snap,a_snapmethod) -- maybe also pass tail
- if trace then
- reset_tracing(head)
- end
- local current = head
- local oldhead = head
- local glue_order = 0
- local glue_data
- local force_glue = false
- local penalty_order = 0
- local penalty_data
- local natural_penalty
- local special_penalty
- local parskip
- local ignore_parskip = false
- local ignore_following = false
- local ignore_whitespace = false
- local keep_together = false
- local lastsnap
- local pagehead
- local pagetail
- --
- -- todo: keep_together: between headers
- --
- local function getpagelist()
- if not pagehead then
- pagehead = texlists.page_head
- if pagehead then
- pagehead = tonut(pagehead)
- pagetail = find_node_tail(pagehead) -- no texlists.page_tail yet-- no texlists.page_tail yet
- end
- end
- end
- --
- local function compensate(n)
- local g = 0
- while n and getid(n) == glue_code do
- g = g + getwidth(n)
- n = getnext(n)
- end
- if n then
- local p = getprop(n,"snapper")
- if p then
- local extra = p.extra
- if extra and extra < 0 then -- hm, extra can be unset ... needs checking
- local h = p.ch -- getheight(n)
- -- maybe an extra check
- -- if h - extra < g then
- setheight(n,h-2*extra)
- p.extra = 0
- if trace_vsnapping then
- report_snapper("removed extra space at top: %p",extra)
- end
- -- end
- end
- end
- return n
- end
- end
- --
- local function removetopsnap()
- getpagelist()
- if pagehead then
- local n = pagehead and compensate(pagehead)
- if n and n ~= pagetail then
- local p = getprop(pagetail,"snapper")
- if p then
- local e = p.extra
- if e and e < 0 then
- local t = texget("pagetotal")
- if t > 0 then
- local g = texget("pagegoal") -- 1073741823 is signal
- local d = g - t
- if d < -e then
- local penalty = new_penalty(1000000)
- setlink(penalty,head)
- head = penalty
- report_snapper("force pagebreak due to extra space at bottom: %p",e)
- end
- end
- end
- end
- end
- elseif head then
- compensate(head)
- end
- end
- --
- local function getavailable()
- getpagelist()
- if pagehead then
- local t = texget("pagetotal")
- if t > 0 then
- local g = texget("pagegoal")
- return g - t
- end
- end
- return false
- end
- --
- local function flush(why)
- if penalty_data then
- local p = new_penalty(penalty_data)
- if trace then
- trace_done("flushed due to " .. why,p)
- end
- if penalty_data >= 10000 then -- or whatever threshold?
- local prev = getprev(current)
- if getid(prev) == glue_code then -- maybe go back more, or maybe even push back before any glue
- -- tricky case: spacing/grid-007.tex: glue penalty glue
- head = insert_node_before(head,prev,p)
- else
- head = insert_node_before(head,current,p)
- end
- else
- head = insert_node_before(head,current,p)
- end
- -- if penalty_data > special_penalty_min and penalty_data < special_penalty_max then
- local props = properties[p]
- if props then
- props.special_penalty = special_penalty or penalty_data
- else
- properties[p] = {
- special_penalty = special_penalty or penalty_data
- }
- end
- -- end
- end
- if glue_data then
- if force_glue then
- if trace then
- trace_done("flushed due to forced " .. why,glue_data)
- end
- head = forced_skip(head,current,getwidth(glue_data,width),"before",trace)
- flush_node(glue_data)
- else
- local width, stretch, shrink = getglue(glue_data)
- if width ~= 0 then
- if trace then
- trace_done("flushed due to non zero " .. why,glue_data)
- end
- head = insert_node_before(head,current,glue_data)
- elseif stretch ~= 0 or shrink ~= 0 then
- if trace then
- trace_done("flushed due to stretch/shrink in" .. why,glue_data)
- end
- head = insert_node_before(head,current,glue_data)
- else
- -- report_vspacing("needs checking (%s): %p",gluecodes[getsubtype(glue_data)],w)
- flush_node(glue_data)
- end
- end
- end
-
- if trace then
- trace_node(current)
- end
- glue_order, glue_data, force_glue = 0, nil, false
- penalty_order, penalty_data, natural_penalty = 0, nil, nil
- parskip, ignore_parskip, ignore_following, ignore_whitespace = nil, false, false, false
- end
- --
- if trace_vsnapping then
- report_snapper("global ht/dp = %p/%p, local ht/dp = %p/%p",
- texgetdimen("globalbodyfontstrutheight"),
- texgetdimen("globalbodyfontstrutdepth"),
- texgetdimen("bodyfontstrutheight"),
- texgetdimen("bodyfontstrutdepth")
- )
- end
- if trace then
- trace_info("start analyzing",where,what)
- end
- if snap and where == "page" then
- removetopsnap()
- end
- while current do
- local id = getid(current)
- if id == hlist_code or id == vlist_code then
- -- needs checking, why so many calls
- if snap then
- lastsnap = nil
- local list = getlist(current)
- local s = getattr(current,a_snapmethod)
- if not s then
- -- if trace_vsnapping then
- -- report_snapper("mvl list not snapped")
- -- end
- elseif s == 0 then
- if trace_vsnapping then
- report_snapper("mvl %a not snapped, already done: %s",nodecodes[id],listtoutf(list))
- end
- else
- local sv = snapmethods[s]
- if sv then
- -- check if already snapped
- local done = list and already_done(id,list,a_snapmethod)
- if done then
- -- assume that the box is already snapped
- if trace_vsnapping then
- local w, h, d = getwhd(current)
- report_snapper("mvl list already snapped at (%p,%p): %s",h,d,listtoutf(list))
- end
- else
- local h, d, ch, cd, lines, extra = snap_hlist("mvl",current,sv,false,false)
- lastsnap = {
- ht = h,
- dp = d,
- ch = ch,
- cd = cd,
- extra = extra,
- current = current,
- }
- setprop(current,"snapper",lastsnap)
- if trace_vsnapping then
- report_snapper("mvl %a snapped from (%p,%p) to (%p,%p) using method %a (%s) for %a (%s lines): %s",
- nodecodes[id],h,d,ch,cd,sv.name,sv.specification,where,lines,listtoutf(list))
- end
- end
- elseif trace_vsnapping then
- report_snapper("mvl %a not snapped due to unknown snap specification: %s",nodecodes[id],listtoutf(list))
- end
- setattr(current,a_snapmethod,0)
- end
- else
- --
- end
- -- tex.prevdepth = 0
- flush("list")
- current = getnext(current)
- elseif id == penalty_code then
- -- natural_penalty = getpenalty(current)
- -- if trace then
- -- trace_done("removed penalty",current)
- -- end
- -- head, current = remove_node(head,current,true)
- current = getnext(current)
- elseif id == kern_code then
- if snap and trace_vsnapping and getkern(current) ~= 0 then
- report_snapper("kern of %p kept",getkern(current))
- end
- flush("kern")
- current = getnext(current)
- elseif id == glue_code then
- local subtype = getsubtype(current)
- if subtype == userskip_code then
- local sc, so, sp = getattrs(current,a_skipcategory,a_skiporder,a_skippenalty)
- if not so then
- so = 1 -- the others have no default value
- end
- if sp and sc == penalty then
- if where == "page" then
- getpagelist()
- local p = specialmethods[specialmethod](pagehead,pagetail,current,sp)
- if p then
- -- todo: other tracer
- --
- -- if trace then
- -- trace_skip("previous special penalty %a is changed to %a using method %a",sp,p,specialmethod)
- -- end
- special_penalty = sp
- sp = p
- end
- end
- if not penalty_data then
- penalty_data = sp
- elseif penalty_order < so then
- penalty_order, penalty_data = so, sp
- elseif penalty_order == so and sp > penalty_data then
- penalty_data = sp
- end
- if trace then
- trace_skip("penalty in skip",sc,so,sp,current)
- end
- head, current = remove_node(head,current,true)
- elseif not sc then -- if not sc then
- if glue_data then
- if trace then
- trace_done("flush",glue_data)
- end
- head = insert_node_before(head,current,glue_data)
- if trace then
- trace_natural("natural",current)
- end
- current = getnext(current)
- glue_data = nil
- else
- -- not look back across head
- -- todo: prev can be whatsit (latelua)
- local previous = getprev(current)
- if previous and getid(previous) == glue_code and getsubtype(previous) == userskip_code then
- local pwidth, pstretch, pshrink, pstretch_order, pshrink_order = getglue(previous)
- local cwidth, cstretch, cshrink, cstretch_order, cshrink_order = getglue(current)
- if pstretch_order == 0 and pshrink_order == 0 and cstretch_order == 0 and cshrink_order == 0 then
- setglue(previous,pwidth + cwidth, pstretch + cstretch, pshrink + cshrink)
- if trace then
- trace_natural("removed",current)
- end
- head, current = remove_node(head,current,true)
- if trace then
- trace_natural("collapsed",previous)
- end
- else
- if trace then
- trace_natural("filler",current)
- end
- current = getnext(current)
- end
- else
- if trace then
- trace_natural("natural (no prev)",current)
- end
- current = getnext(current)
- end
- end
- glue_order = 0
- elseif sc == disable or sc == enable then
- local next = getnext(current)
- if next then
- ignore_following = sc == disable
- if trace then
- trace_skip(sc == disable and "disable" or "enable",sc,so,sp,current)
- end
- head, current = remove_node(head,current,true)
- else
- current = next
- end
- elseif sc == packed then
- if trace then
- trace_skip("packed",sc,so,sp,current)
- end
- -- can't happen !
- head, current = remove_node(head,current,true)
- elseif sc == nowhite then
- local next = getnext(current)
- if next then
- ignore_whitespace = true
- head, current = remove_node(head,current,true)
- else
- current = next
- end
- elseif sc == discard then
- if trace then
- trace_skip("discard",sc,so,sp,current)
- end
- head, current = remove_node(head,current,true)
- elseif sc == overlay then
- -- todo (overlay following line over previous
- if trace then
- trace_skip("overlay",sc,so,sp,current)
- end
- -- beware: head can actually be after the affected nodes as
- -- we look back ... some day head will the real head
- head, current = check_experimental_overlay(head,current,a_snapmethod)
- elseif ignore_following then
- if trace then
- trace_skip("disabled",sc,so,sp,current)
- end
- head, current = remove_node(head,current,true)
- elseif not glue_data then
- if trace then
- trace_skip("assign",sc,so,sp,current)
- end
- glue_order = so
- head, current, glue_data = remove_node(head,current)
- elseif glue_order < so then
- if trace then
- trace_skip("force",sc,so,sp,current)
- end
- glue_order = so
- flush_node(glue_data)
- head, current, glue_data = remove_node(head,current)
- elseif glue_order == so then
- -- is now exclusive, maybe support goback as combi, else why a set
- if sc == largest then
- local cw = getwidth(current)
- local gw = getwidth(glue_data)
- if cw > gw then
- if trace then
- trace_skip("largest",sc,so,sp,current)
- end
- flush_node(glue_data)
- head, current, glue_data = remove_node(head,current)
- else
- if trace then
- trace_skip("remove smallest",sc,so,sp,current)
- end
- head, current = remove_node(head,current,true)
- end
- elseif sc == goback then
- if trace then
- trace_skip("goback",sc,so,sp,current)
- end
- flush_node(glue_data)
- head, current, glue_data = remove_node(head,current)
- elseif sc == force then
- -- last one counts, some day we can provide an accumulator and largest etc
- -- but not now
- if trace then
- trace_skip("force",sc,so,sp,current)
- end
- flush_node(glue_data)
- head, current, glue_data = remove_node(head,current)
- elseif sc == penalty then
- if trace then
- trace_skip("penalty",sc,so,sp,current)
- end
- flush_node(glue_data)
- glue_data = nil
- head, current = remove_node(head,current,true)
- elseif sc == add then
- if trace then
- trace_skip("add",sc,so,sp,current)
- end
- local cwidth, cstretch, cshrink = getglue(current)
- local gwidth, gstretch, gshrink = getglue(glue_data)
- setglue(glue_data,gwidth + cwidth, gstretch + cstretch,gshrink + cshrink)
- -- toto: order
- head, current = remove_node(head,current,true)
- else
- if trace then
- trace_skip("unknown",sc,so,sp,current)
- end
- head, current = remove_node(head,current,true)
- end
- else
- if trace then
- trace_skip("unknown",sc,so,sp,current)
- end
- head, current = remove_node(head,current,true)
- end
- if sc == force then
- force_glue = true
- end
- elseif subtype == lineskip_code then
- if snap then
- local s = getattr(current,a_snapmethod)
- if s and s ~= 0 then
- setattr(current,a_snapmethod,0)
- setwidth(current,0)
- if trace_vsnapping then
- report_snapper("lineskip set to zero")
- end
- else
- if trace then
- trace_skip("lineskip",sc,so,sp,current)
- end
- flush("lineskip")
- end
- else
- if trace then
- trace_skip("lineskip",sc,so,sp,current)
- end
- flush("lineskip")
- end
- current = getnext(current)
- elseif subtype == baselineskip_code then
- if snap then
- local s = getattr(current,a_snapmethod)
- if s and s ~= 0 then
- setattr(current,a_snapmethod,0)
- setwidth(current,0)
- if trace_vsnapping then
- report_snapper("baselineskip set to zero")
- end
- else
- if trace then
- trace_skip("baselineskip",sc,so,sp,current)
- end
- flush("baselineskip")
- end
- else
- if trace then
- trace_skip("baselineskip",sc,so,sp,current)
- end
- flush("baselineskip")
- end
- current = getnext(current)
- elseif subtype == parskip_code then
- -- parskip always comes later
- if ignore_whitespace then
- if trace then
- trace_natural("ignored parskip",current)
- end
- head, current = remove_node(head,current,true)
- elseif glue_data then
- local w = getwidth(current)
- if w ~= 0 and w > getwidth(glue_data) then
-flush_node(glue_data)
- glue_data = current
- if trace then
- trace_natural("taking parskip",current)
- end
- head, current = remove_node(head,current)
- else
- if trace then
- trace_natural("removed parskip",current)
- end
- head, current = remove_node(head,current,true)
- end
- else
- if trace then
- trace_natural("honored parskip",current)
- end
- head, current, glue_data = remove_node(head,current)
- end
- elseif subtype == topskip_code or subtype == splittopskip_code then
- local next = getnext(current)
- if next and getattr(next,a_skipcategory) == notopskip then
- nuts.setglue(current) -- zero
- end
- if snap then
- local s = getattr(current,a_snapmethod)
- if s and s ~= 0 then
- setattr(current,a_snapmethod,0)
- local sv = snapmethods[s]
- local w, cw = snap_topskip(current,sv)
- if trace_vsnapping then
- report_snapper("topskip snapped from %p to %p for %a",w,cw,where)
- end
- else
- if trace then
- trace_skip("topskip",sc,so,sp,current)
- end
- flush("topskip")
- end
- else
- if trace then
- trace_skip("topskip",sc,so,sp,current)
- end
- flush("topskip")
- end
- current = getnext(current)
- elseif subtype == abovedisplayskip_code and remove_math_skips then
- --
- if trace then
- trace_skip("above display skip (normal)",sc,so,sp,current)
- end
- flush("above display skip (normal)")
- current = getnext(current)
- --
- elseif subtype == belowdisplayskip_code and remove_math_skips then
- --
- if trace then
- trace_skip("below display skip (normal)",sc,so,sp,current)
- end
- flush("below display skip (normal)")
- current = getnext(current)
- --
- elseif subtype == abovedisplayshortskip_code and remove_math_skips then
- --
- if trace then
- trace_skip("above display skip (short)",sc,so,sp,current)
- end
- flush("above display skip (short)")
- current = getnext(current)
- --
- elseif subtype == belowdisplayshortskip_code and remove_math_skips then
- --
- if trace then
- trace_skip("below display skip (short)",sc,so,sp,current)
- end
- flush("below display skip (short)")
- current = getnext(current)
- --
- else -- other glue
- if snap and trace_vsnapping then
- local w = getwidth(current)
- if w ~= 0 then
- report_snapper("glue %p of type %a kept",w,gluecodes[subtype])
- end
- end
- if trace then
- trace_skip(formatters["glue of type %a"](subtype),sc,so,sp,current)
- end
- flush("some glue")
- current = getnext(current)
- end
- else
- flush(trace and formatters["node with id %a"](id) or "other node")
- current = getnext(current)
- end
- end
- if trace then
- trace_info("stop analyzing",where,what)
- end
- -- if natural_penalty and (not penalty_data or natural_penalty > penalty_data) then
- -- penalty_data = natural_penalty
- -- end
- if trace and (glue_data or penalty_data) then
- trace_info("start flushing",where,what)
- end
- local tail
- if penalty_data then
- tail = find_node_tail(head)
- local p = new_penalty(penalty_data)
- if trace then
- trace_done("result",p)
- end
- setlink(tail,p)
- -- if penalty_data > special_penalty_min and penalty_data < special_penalty_max then
- local props = properties[p]
- if props then
- props.special_penalty = special_penalty or penalty_data
- else
- properties[p] = {
- special_penalty = special_penalty or penalty_data
- }
- end
- -- end
- end
- if glue_data then
- if not tail then tail = find_node_tail(head) end
- if trace then
- trace_done("result",glue_data)
- end
- if force_glue then
- head, tail = forced_skip(head,tail,getwidth(glue_data),"after",trace)
- flush_node(glue_data)
- glue_data = nil
- elseif tail then
- setlink(tail,glue_data)
-setnext(glue_data)
- else
- head = glue_data
- end
- texnest[texnest.ptr].prevdepth = 0 -- appending to the list bypasses tex's prevdepth handler
- end
- if trace then
- if glue_data or penalty_data then
- trace_info("stop flushing",where,what)
- end
- show_tracing(head)
- if oldhead ~= head then
- trace_info("head has been changed from %a to %a",nodecodes[getid(oldhead)],nodecodes[getid(head)])
- end
- end
- return head
- end
-
- local stackhead, stacktail, stackhack = nil, nil, false
-
- local function report(message,where,lst)
- if lst and where then
- report_vspacing(message,where,count_nodes(lst,true),nodeidstostring(lst))
- else
- report_vspacing(message,count_nodes(lst,true),nodeidstostring(lst))
- end
- end
-
- -- ugly code: we get partial lists (check if this stack is still okay) ... and we run
- -- into temp nodes (sigh)
-
- local forceflush = false
-
- function vspacing.pagehandler(newhead,where)
- if newhead then
- local newtail = find_node_tail(newhead) -- best pass that tail, known anyway
- local flush = false
- stackhack = true -- todo: only when grid snapping once enabled
- -- todo: fast check if head = tail
- for n, id, subtype in nextnode, newhead do -- we could just look for glue nodes
- if id ~= glue_code then
- flush = true
- elseif subtype == userskip_code then
- if getattr(n,a_skipcategory) then
- stackhack = true
- else
- flush = true
- end
- elseif subtype == parskip_code then
- -- if where == new_graf then ... end
- if texgetcount("c_spac_vspacing_ignore_parskip") > 0 then
- -- texsetcount("c_spac_vspacing_ignore_parskip",0)
- setglue(n)
- -- maybe removenode
- end
- end
- end
- texsetcount("c_spac_vspacing_ignore_parskip",0)
-
- if forceflush then
- forceflush = false
- flush = true
- end
-
- if flush then
- if stackhead then
- if trace_collect_vspacing then report("%s > appending %s nodes to stack (final): %s",where,newhead) end
- setlink(stacktail,newhead)
- newhead = stackhead
- stackhead = nil
- stacktail = nil
- end
- if stackhack then
- stackhack = false
- if trace_collect_vspacing then report("%s > processing %s nodes: %s",where,newhead) end
- newhead = collapser(newhead,"page",where,trace_page_vspacing,true,a_snapmethod)
- else
- if trace_collect_vspacing then report("%s > flushing %s nodes: %s",where,newhead) end
- end
- return newhead
- else
- if stackhead then
- if trace_collect_vspacing then report("%s > appending %s nodes to stack (intermediate): %s",where,newhead) end
- setlink(stacktail,newhead)
- else
- if trace_collect_vspacing then report("%s > storing %s nodes in stack (initial): %s",where,newhead) end
- stackhead = newhead
- end
- stacktail = newtail
- end
- end
- return nil
- end
-
- function vspacing.pageoverflow()
- local h = 0
- if stackhead then
- for n, id in nextnode, stackhead do
- if id == glue_code then
- h = h + getwidth(n)
- elseif id == kern_code then
- h = h + getkern(n)
- end
- end
- end
- return h
- end
-
- function vspacing.forcepageflush()
- forceflush = true
- end
-
- local ignore = table.tohash {
- "split_keep",
- "split_off",
- -- "vbox",
- }
-
- function vspacing.vboxhandler(head,where)
- if head and not ignore[where] and getnext(head) then
- if getnext(head) then -- what if a one liner and snapping?
- head = collapser(head,"vbox",where,trace_vbox_vspacing,true,a_snapvbox) -- todo: local snapper
- return head
- end
- end
- return head
- end
-
- function vspacing.collapsevbox(n,aslist) -- for boxes but using global a_snapmethod
- local box = getbox(n)
- if box then
- local list = getlist(box)
- if list then
- list = collapser(list,"snapper","vbox",trace_vbox_vspacing,true,a_snapmethod)
- if aslist then
- setlist(box,list) -- beware, dimensions of box are wrong now
- else
- setlist(box,vpack_node(list))
- end
- end
- end
- end
-
-end
-
--- This one is needed to prevent bleeding of prevdepth to the next page
--- which doesn't work well with forced skips. I'm not that sure if the
--- following is a good way out.
-
-do
-
- local outer = texnest[0]
-
- local enabled = true
- local trace = false
- local report = logs.reporter("vspacing")
-
- trackers.register("vspacing.synchronizepage",function(v)
- trace = v
- end)
-
- directives.register("vspacing.synchronizepage",function(v)
- enabled = v
- end)
-
- local ignoredepth = -65536000
-
- -- A previous version analyzed the number of lines moved to the next page in
- -- synchronizepage because prevgraf is unreliable in that case. However, we cannot
- -- tweak that parameter because it is also used in postlinebreak and hangafter, so
- -- there is a danger for interference. Therefore we now do it dynamically.
-
- -- We can also support other lists but there prevgraf probably is ok.
-
- function vspacing.getnofpreviouslines(head)
- if enabled then
- if not thead then
- head = texlists.page_head
- end
- local noflines = 0
- if head then
- local tail = find_node_tail(tonut(head))
- while tail do
- local id = getid(tail)
- if id == hlist_code then
- if getsubtype(tail) == linelist_code then
- noflines = noflines + 1
- else
- break
- end
- elseif id == vlist_code then
- break
- elseif id == glue_code then
- local subtype = getsubtype(tail)
- if subtype == baselineskip_code or subtype == lineskip_code then
- -- we're ok
- elseif subtype == parskip_code then
- if getwidth(tail) > 0 then
- break
- else
- -- we assume we're ok
- end
- end
- elseif id == penalty_code then
- -- we're probably ok
- elseif id == rule_code or id == kern_code then
- break
- else
- -- ins, mark, boundary, whatsit
- end
- tail = getprev(tail)
- end
- end
- return noflines
- end
- end
-
- interfaces.implement {
- name = "getnofpreviouslines",
- public = true,
- actions = vspacing.getnofpreviouslines,
- }
-
- function vspacing.synchronizepage()
- if enabled then
- if trace then
- local newdepth = outer.prevdepth
- local olddepth = newdepth
- if not texlists.page_head then
- newdepth = ignoredepth
- texset("prevdepth",ignoredepth)
- outer.prevdepth = ignoredepth
- end
- report("page %i, prevdepth %p => %p",texgetcount("realpageno"),olddepth,newdepth)
- -- report("list %s",nodes.idsandsubtypes(head))
- else
- if not texlists.page_head then
- texset("prevdepth",ignoredepth)
- outer.prevdepth = ignoredepth
- end
- end
- end
- end
-
- local trace = false
- local last = nil
- local vmode_code = tex.modelevels.vertical
- local temp_code = nodecodes.temp
- local getnest = tex.getnest
- local getlist = tex.getlist
-
- trackers.register("vspacing.forcestrutdepth",function(v) trace = v end)
-
- -- abs : negative is inner
-
- function vspacing.checkstrutdepth(depth)
- local nest = getnest()
- if abs(nest.mode) == vmode_code and nest.head then
- local tail = nest.tail
- local id = tail.id
- if id == hlist_code then
- if tail.depth < depth then
- tail.depth = depth
- end
- nest.prevdepth = depth
- elseif id == temp_code and getnest("ptr") == 0 then
- local head = getlist("page_head")
- if head then
- tail = nodes.tail(head)
- if tail and tail.id == hlist_code then
- if tail.depth < depth then
- tail.depth = depth
- end
- nest.prevdepth = depth
- -- only works in lmtx
- texset("pagedepth",depth)
- end
- end
- end
- end
- end
-
- interfaces.implement {
- name = "checkstrutdepth",
- arguments = "dimension",
- actions = vspacing.checkstrutdepth,
- }
-
- function vspacing.forcestrutdepth(n,depth,trace_mode,plus)
- local box = texgetbox(n)
- if box then
- box = tonut(box)
- local head = getlist(box)
- if head then
- local tail = find_node_tail(head)
- if tail then
- if getid(tail) == hlist_code then
- local dp = getdepth(tail)
- if dp < depth then
- setdepth(tail,depth)
- outer.prevdepth = depth
- if trace or trace_mode > 0 then
- nuts.setvisual(tail,"depth")
- end
- end
- end
- last = nil
- if plus then
- -- penalty / skip ...
- local height = 0
- local sofar = 0
- local same = false
- local seen = false
- local list = { }
- last = nil
- while tail do
- local id = getid(tail)
- if id == hlist_code or id == vlist_code then
- local w, h, d = getwhd(tail)
- height = height + h + d + sofar
- sofar = 0
- last = tail
- elseif id == kern_code then
- sofar = sofar + getkern(tail)
- elseif id == glue_code then
- if seen then
- sofar = sofar + getwidth(tail)
- seen = false
- else
- break
- end
- elseif id == penalty_code then
- local p = getpenalty(tail)
- if p >= 10000 then
- same = true
- seen = true
- else
- break
- end
- else
- break
- end
- tail = getprev(tail)
- end
- texsetdimen("global","d_spac_prevcontent",same and height or 0)
- end
- end
- end
- end
- end
-
- function vspacing.pushatsame()
- -- needs better checking !
- if last then -- setsplit
- nuts.setnext(getprev(last))
- nuts.setprev(last)
- end
- end
-
- function vspacing.popatsame()
- -- needs better checking !
- nuts.write(last)
- end
-
-end
-
--- interface
-
-do
-
- -- old variant
-
--- implement {
--- name = "vspacing",
--- actions = vspacing.analyze,
--- scope = "private",
--- arguments = "string"
--- }
-
- -- new variant
-
- interfaces.implement {
- name = "injectvspacing",
- actions = vspacing.inject,
- arguments = { "integer", "string" },
- }
-
- interfaces.implement {
- name = "injectvpenalty",
- actions = vspacing.injectpenalty,
- arguments = "integer",
- }
-
- interfaces.implement {
- name = "injectvskip",
- actions = vspacing.injectskip,
- arguments = "dimension",
- }
-
- interfaces.implement {
- name = "injectdisable",
- actions = vspacing.injectdisable,
- }
-
- --
-
- implement {
- name = "synchronizepage",
- actions = vspacing.synchronizepage,
- scope = "private"
- }
-
- implement {
- name = "forcestrutdepth",
- arguments = { "integer", "dimension", "integer" },
- actions = vspacing.forcestrutdepth,
- scope = "private"
- }
-
- implement {
- name = "forcestrutdepthplus",
- arguments = { "integer", "dimension", "integer", true },
- actions = vspacing.forcestrutdepth,
- scope = "private"
- }
-
- implement {
- name = "pushatsame",
- actions = vspacing.pushatsame,
- scope = "private"
- }
-
- implement {
- name = "popatsame",
- actions = vspacing.popatsame,
- scope = "private"
- }
-
- implement {
- name = "vspacingsetamount",
- actions = vspacing.setskip,
- scope = "private",
- arguments = "string",
- }
-
- implement {
- name = "vspacingdefine",
- actions = vspacing.setmap,
- scope = "private",
- arguments = "2 strings",
- }
-
- implement {
- name = "vspacingcollapse",
- actions = vspacing.collapsevbox,
- scope = "private",
- arguments = "integer"
- }
-
- implement {
- name = "vspacingcollapseonly",
- actions = vspacing.collapsevbox,
- scope = "private",
- arguments = { "integer", true }
- }
-
- implement {
- name = "vspacingsnap",
- actions = vspacing.snapbox,
- scope = "private",
- arguments = { "integer", "integer" }
- }
-
- implement {
- name = "definesnapmethod",
- actions = vspacing.definesnapmethod,
- scope = "private",
- arguments = "2 strings",
- }
-
- -- local remove_node = nodes.remove
- -- local find_node_tail = nodes.tail
- --
- -- interfaces.implement {
- -- name = "fakenextstrutline",
- -- actions = function()
- -- local head = texlists.page_head
- -- if head then
- -- local head = remove_node(head,find_node_tail(head),true)
- -- texlists.page_head = head
- -- buildpage()
- -- end
- -- end
- -- }
-
- implement {
- name = "removelastline",
- actions = function()
- local head = texlists.page_head
- if head then
- local tail = find_node_tail(head)
- if tail then
- -- maybe check for hlist subtype 1
- local head = remove_node(head,tail,true)
- texlists.page_head = head
- buildpage()
- end
- end
- end
- }
-
- implement {
- name = "showpagelist", -- will improve
- actions = function()
- local head = texlists.page_head
- if head then
- print("start")
- while head do
- print(" " .. tostring(head))
- head = head.next
- end
- end
- end
- }
-
- implement {
- name = "pageoverflow",
- actions = { vspacing.pageoverflow, context }
- }
-
- implement {
- name = "forcepageflush",
- actions = vspacing.forcepageflush
- }
-
-end