diff options
Diffstat (limited to 'tex/context/base/typo-mar.lua')
-rw-r--r-- | tex/context/base/typo-mar.lua | 1758 |
1 files changed, 879 insertions, 879 deletions
diff --git a/tex/context/base/typo-mar.lua b/tex/context/base/typo-mar.lua index ec827883d..65b205098 100644 --- a/tex/context/base/typo-mar.lua +++ b/tex/context/base/typo-mar.lua @@ -1,879 +1,879 @@ -if not modules then modules = { } end modules ['typo-mar'] = { - version = 1.001, - comment = "companion to typo-mar.mkiv", - author = "Hans Hagen, PRAGMA-ADE, Hasselt NL", - copyright = "PRAGMA ADE / ConTeXt Development Team", - license = "see context related readme files" -} - --- todo: --- --- * autoleft/right depending on available space (or distance to margin) --- * stack across paragraphs, but that is messy and one should reconsider --- using margin data then as also vertical spacing kicks in --- * floating margin data, with close-to-call anchoring - --- -- experiment (does not work, too much interference) --- --- local pdfprint = pdf.print --- local format = string.format --- --- anchors = anchors or { } --- --- local whatever = { } --- local factor = (7200/7227)/65536 --- --- function anchors.set(tag) --- whatever[tag] = { pdf.h, pdf.v } --- end --- --- function anchors.reset(tag) --- whatever[tag] = nil --- end --- --- function anchors.startmove(tag,how) -- save/restore nodes but they don't support moves --- local w = whatever[tag] --- if not w then --- -- error --- elseif how == "horizontal" or how == "h" then --- pdfprint("page",format(" q 1 0 0 1 %f 0 cm ", (w[1] - pdf.h) * factor)) --- elseif how == "vertical" or how == "v" then --- pdfprint("page",format(" q 1 0 0 1 0 %f cm ", (w[2] - pdf.v) * factor)) --- else --- pdfprint("page",format(" q 1 0 0 1 %f %f cm ", (w[1] - pdf.h) * factor, (w[2] - pdf.v) * factor)) --- end --- end --- --- function anchors.stopmove(tag) --- local w = whatever[tag] --- if not w then --- -- error --- else --- pdfprint("page"," Q ") --- end --- end --- --- local latelua = nodes.pool.latelua --- --- function anchors.node_set(tag) --- return latelua(formatters["anchors.set(%q)"](tag)) --- end --- --- function anchors.node_reset(tag) --- return latelua(formatters["anchors.reset(%q)"](tag)) --- end --- --- function anchors.node_start_move(tag,how) --- return latelua(formatters["anchors.startmove(%q,%q)](tag,how)) --- end --- --- function anchors.node_stop_move(tag) --- return latelua(formatters["anchors.stopmove(%q)"](tag)) --- end - --- so far - -local format, validstring = string.format, string.valid -local insert, remove = table.insert, table.remove -local setmetatable, next = setmetatable, next - -local attributes, nodes, node, variables = attributes, nodes, node, variables - -local trace_margindata = false trackers.register("typesetters.margindata", function(v) trace_margindata = v end) -local trace_marginstack = false trackers.register("typesetters.margindata.stack", function(v) trace_marginstack = v end) -local trace_margingroup = false trackers.register("typesetters.margindata.group", function(v) trace_margingroup = v end) - -local report_margindata = logs.reporter("typesetters","margindata") - -local tasks = nodes.tasks -local prependaction = tasks.prependaction -local disableaction = tasks.disableaction -local enableaction = tasks.enableaction - -local variables = interfaces.variables - -local conditionals = tex.conditionals -local systemmodes = tex.systemmodes - -local v_top = variables.top -local v_depth = variables.depth -local v_local = variables["local"] -local v_global = variables["global"] -local v_left = variables.left -local v_right = variables.right -local v_flushleft = variables.flushleft -local v_flushright = variables.flushright -local v_inner = variables.inner -local v_outer = variables.outer -local v_margin = variables.margin -local v_edge = variables.edge -local v_default = variables.default -local v_normal = variables.normal -local v_yes = variables.yes -local v_continue = variables.continue -local v_first = variables.first -local v_text = variables.text -local v_column = variables.column - -local copy_node_list = node.copy_list -local slide_nodes = node.slide -local hpack_nodes = node.hpack -- nodes.fasthpack not really faster here -local traverse_id = node.traverse_id -local free_node_list = node.flush_list -local insert_node_after = node.insert_after -local insert_node_before = node.insert_before - -local concat_nodes = nodes.concat - -local nodecodes = nodes.nodecodes -local listcodes = nodes.listcodes -local gluecodes = nodes.gluecodes -local whatsitcodes = nodes.whatsitcodes - -local hlist_code = nodecodes.hlist -local vlist_code = nodecodes.vlist -local glue_code = nodecodes.glue -local kern_code = nodecodes.kern -local penalty_code = nodecodes.penalty -local whatsit_code = nodecodes.whatsit -local line_code = listcodes.line -local cell_code = listcodes.cell -local alignment_code = listcodes.alignment -local leftskip_code = gluecodes.leftskip -local rightskip_code = gluecodes.rightskip -local userdefined_code = whatsitcodes.userdefined - -local dir_code = whatsitcodes.dir -local localpar_code = whatsitcodes.localpar - -local nodepool = nodes.pool - -local new_kern = nodepool.kern -local new_glue = nodepool.glue -local new_penalty = nodepool.penalty -local new_stretch = nodepool.stretch -local new_usernumber = nodepool.usernumber -local new_latelua = nodepool.latelua - -local texcount = tex.count -local texdimen = tex.dimen -local texbox = tex.box - -local points = number.points - -local isleftpage = layouts.status.isleftpage -local registertogether = builders.paragraphs.registertogether - -local jobpositions = job.positions -local getposition = jobpositions.position - -local a_margindata = attributes.private("margindata") - -local inline_mark = nodepool.userids["margins.inline"] - -local margins = { } -typesetters.margins = margins - -local locations = { v_left, v_right, v_inner, v_outer } -- order might change -local categories = { } -local displaystore = { } -- [category][location][scope] -local inlinestore = { } -- [number] -local nofsaved = 0 -local nofstored = 0 -local nofinlined = 0 -local nofdelayed = 0 -local h_anchors = 0 -local v_anchors = 0 - -local mt1 = { - __index = function(t,location) - local v = { [v_local] = { }, [v_global] = { } } - t[location] = v - return v - end -} - -local mt2 = { - __index = function(stores,category) - categories[#categories+1] = category - local v = { } - setmetatable(v,mt1) - stores[category] = v - return v - end -} - -setmetatable(displaystore,mt2) - -local defaults = { - __index = { - location = v_left, - align = v_normal, - method = "", - name = "", - threshold = 0, -- .25ex - margin = v_normal, - scope = v_global, - distance = 0, - hoffset = 0, - voffset = 0, - category = v_default, - line = 0, - vstack = 0, - dy = 0, - baseline = false, - inline = false, - leftskip = 0, - rightskip = 0, - } -} - -local enablelocal, enableglobal -- forward reference (delayed initialization) - -local function showstore(store,banner,location) - if next(store) then - for i, si in table.sortedpairs(store) do - local si =store[i] - report_margindata("%s: stored in %a at %s: %a => %s",banner,location,i,validstring(si.name,"no name"),nodes.toutf(si.box.list)) - end - else - report_margindata("%s: nothing stored in location %a",banner,location) - end -end - -function margins.save(t) - setmetatable(t,defaults) - local content = texbox[t.number] - local location = t.location - local category = t.category - local inline = t.inline - local scope = t.scope or v_global - if not content then - report_margindata("ignoring empty margin data %a",location or "unknown") - return - end - local store - if inline then - store = inlinestore - else - store = displaystore[category][location] - if not store then - report_margindata("invalid location %a",location) - return - end - store = store[scope] - end - if not store then - report_margindata("invalid scope %a",scope) - return - end - if enablelocal and scope == v_local then - enablelocal() - if enableglobal then - enableglobal() -- is the fallback - end - elseif enableglobal and scope == v_global then - enableglobal() - end - nofsaved = nofsaved + 1 - nofstored = nofstored + 1 - local name = t.name - if trace_marginstack then - showstore(store,"before",location) - end - if name and name ~= "" then - if inlinestore then -- todo: inline store has to be done differently (not sparse) - local t = table.sortedkeys(store) for j=#t,1,-1 do local i = t[j] - local si = store[i] - if si.name == name then - local s = remove(store,i) - free_node_list(s.box) - end - end - else - for i=#store,1,-1 do - local si = store[i] - if si.name == name then - local s = remove(store,i) - free_node_list(s.box) - end - end - end - if trace_marginstack then - showstore(store,"between",location) - end - end - if t.number then - -- better make a new table and make t entry in t - t.box = copy_node_list(content) - t.n = nofsaved - -- used later (we will clean up this natural mess later) - -- nice is to make a special status table mechanism - local leftmargindistance = texdimen.naturalleftmargindistance - local rightmargindistance = texdimen.naturalrightmargindistance - t.strutdepth = texbox.strutbox.depth - t.strutheight = texbox.strutbox.height - t.leftskip = tex.leftskip.width -- we're not in forgetall - t.rightskip = tex.rightskip.width -- we're not in forgetall - t.leftmargindistance = leftmargindistance -- todo:layoutstatus table - t.rightmargindistance = rightmargindistance - t.leftedgedistance = texdimen.naturalleftedgedistance - + texdimen.leftmarginwidth - + leftmargindistance - t.rightedgedistance = texdimen.naturalrightedgedistance - + texdimen.rightmarginwidth - + rightmargindistance - t.lineheight = texdimen.lineheight - -- - -- t.realpageno = texcount.realpageno - if inline then - context(new_usernumber(inline_mark,nofsaved)) - store[nofsaved] = t -- no insert - nofinlined = nofinlined + 1 - else - insert(store,t) - end - end - if trace_marginstack then - showstore(store,"after",location) - end - if trace_margindata then - report_margindata("saved %a, location %a, scope %a, inline %a",nofsaved,location,scope,inline) - end -end - --- Actually it's an advantage to have them all anchored left (tags and such) --- we could keep them in store and flush in stage two but we might want to --- do more before that so we need the content to be there unless we can be --- sure that we flush this first which might not be the case in the future. --- --- When the prototype inner/outer code that was part of this proved to be --- okay it was moved elsewhere. - -local status, nofstatus = { }, 0 - -local function realign(current,candidate) - local location = candidate.location - local margin = candidate.margin - local hoffset = candidate.hoffset - local distance = candidate.distance - local hsize = candidate.hsize - local width = candidate.width - local align = candidate.align - -- local realpageno = candidate.realpageno - local leftpage = isleftpage(false,true) - local delta = 0 - local leftdelta = 0 - local rightdelta = 0 - local leftdistance = distance - local rightdistance = distance - if margin == v_normal then - -- - elseif margin == v_local then - leftdelta = - candidate.leftskip - rightdelta = candidate.rightskip - elseif margin == v_margin then - leftdistance = candidate.leftmargindistance - rightdistance = candidate.rightmargindistance - elseif margin == v_edge then - leftdistance = candidate.leftedgedistance - rightdistance = candidate.rightedgedistance - end - if leftpage then - leftdistance, rightdistance = rightdistance, leftdistance - end - - if location == v_left then - delta = hoffset + width + leftdistance + leftdelta - elseif location == v_right then - delta = -hoffset - hsize - rightdistance + rightdelta - elseif location == v_inner then - if leftpage then - delta = -hoffset - hsize - rightdistance + rightdelta - else - delta = hoffset + width + leftdistance + leftdelta - end - elseif location == v_outer then - if leftpage then - delta = hoffset + width + leftdistance + leftdelta - else - delta = -hoffset - hsize - rightdistance + rightdelta - end - end - - -- we assume that list is a hbox, otherwise we had to take the whole current - -- in order to get it right - - current.width = 0 - local anchornode, move_x - - -- this mess is needed for alignments (combinations) so we use that - -- oportunity to add arbitrary anchoring - - -- always increment anchor is nicer for multipass when we add new .. - - local inline = candidate.inline - local anchor = candidate.anchor - if not anchor or anchor == "" then - anchor = v_text - end - if inline or anchor ~= v_text or candidate.psubtype == alignment_code then - -- the alignment_code check catches margintexts ste before a tabulate - h_anchors = h_anchors + 1 - anchornode = new_latelua(format("_plib_.set('md:h',%i,{x=true,c=true})",h_anchors)) - local blob = jobpositions.get('md:h', h_anchors) - if blob then - local reference = jobpositions.getreserved(anchor,blob.c) - if reference then - if location == v_left then - move_x = (reference.x or 0) - (blob.x or 0) - elseif location == v_right then - move_x = (reference.x or 0) - (blob.x or 0) + (reference.w or 0) - hsize - else - -- not yet done - end - end - end - end - - if move_x then - delta = delta - move_x - if trace_margindata then - report_margindata("realigned %a, location %a, margin %a, move %p",candidate.n,location,margin,move_x) - end - else - if trace_margindata then - report_margindata("realigned %a, location %a, margin %a",candidate.n,location,margin) - end - end - - current.list = hpack_nodes(concat_nodes{anchornode,new_kern(-delta),current.list,new_kern(delta)}) - current.width = 0 -end - -local function realigned(current,a) - local candidate = status[a] - realign(current,candidate) - nofdelayed = nofdelayed - 1 - status[a] = nil - return true -end - --- Stacking is done in two ways: the v_yes option stacks per paragraph (or line, --- depending on what gets by) and mostly concerns margin data dat got set at more or --- less the same time. The v_continue option uses position tracking and works on --- larger range. However, crossing pages is not part of it. Anyway, when you have --- such messed up margin data you'd better think twice. --- --- The stacked table keeps track (per location) of the offsets (the v_yes case). This --- table gets saved when the v_continue case is active. We use a special variant --- of position tracking, after all we only need the page number and vertical position. - -local stacked = { } -- left/right keys depending on location -local cache = { } - -local function resetstacked() - stacked = { } -end - --- resetstacked() - -function margins.ha(tag) -- maybe l/r keys ipv left/right keys - local p = cache[tag] - p.p = true - p.y = true - jobpositions.set('md:v',tag,p) - cache[tag] = nil -end - -local function markovershoot(current) - v_anchors = v_anchors + 1 - cache[v_anchors] = stacked - local anchor = new_latelua(format("typesetters.margins.ha(%s)",v_anchors)) -- todo: alleen als offset > line - current.list = hpack_nodes(concat_nodes{anchor,current.list}) -end - -local function getovershoot(location) - local p = jobpositions.get("md:v",v_anchors) - local c = jobpositions.get("md:v",v_anchors+1) - if p and c and p.p and p.p == c.p then - local distance = p.y - c.y - local offset = p[location] or 0 - local overshoot = offset - distance - if trace_marginstack then - report_margindata("location %a, distance %p, offset %p, overshoot %p",location,distance,offset,overshoot) - end - if overshoot > 0 then - return overshoot - end - end - return 0 -end - -local function inject(parent,head,candidate) - local box = candidate.box - local width = box.width - local height = box.height - local depth = box.depth - local shift = box.shift - local stack = candidate.stack - local location = candidate.location - local method = candidate.method - local voffset = candidate.voffset - local line = candidate.line - local baseline = candidate.baseline - local strutheight = candidate.strutheight - local strutdepth = candidate.strutdepth - local psubtype = parent.subtype - local offset = stacked[location] - local firstonstack = offset == false or offset == nil - nofstatus = nofstatus + 1 - nofdelayed = nofdelayed + 1 - status[nofstatus] = candidate - -- yet untested - if baseline == true then - baseline = false - -- hbox vtop ---~ for h in traverse_id(hlist_code,box.list.list) do ---~ baseline = h.height ---~ break ---~ end - else - baseline = tonumber(baseline) - if not baseline or baseline <= 0 then - -- in case we have a box of width 0 that is not analyzed - baseline = false -- strutheight -- actually a hack - end - end - candidate.width = width - candidate.hsize = parent.width -- we can also pass textwidth - candidate.psubtype = psubtype - if trace_margindata then - report_margindata("processing, index %s, height %p, depth %p, parent %s",candidate.n,height,depth,listcodes[psubtype]) - end - if firstonstack then - offset = 0 - else --- offset = offset + height - end - if stack == v_yes then - offset = offset + candidate.dy - shift = shift + offset - elseif stack == v_continue then - offset = offset + candidate.dy - if firstonstack then - offset = offset + getovershoot(location) - end - shift = shift + offset - end - -- -- -- - -- Maybe we also need to patch offset when we apply methods, but how ... - -- This needs a bit of playing as it depends on the stack setting of the - -- following which we don't know yet ... so, consider stacking partially - -- experimental. - -- -- -- - if method == v_top then - local delta = height - parent.height - if trace_margindata then - report_margindata("top aligned by %p",delta) - end - if delta < candidate.threshold then - shift = shift + voffset + delta - end - elseif method == v_first then - if baseline then - shift = shift + voffset + height - baseline -- option - else - shift = shift + voffset -- normal - end - if trace_margindata then - report_margindata("first aligned") - end - elseif method == v_depth then - local delta = strutdepth - if trace_margindata then - report_margindata("depth aligned by %p",delta) - end - shift = shift + voffset + delta - elseif method == v_height then - local delta = - strutheight - if trace_margindata then - report_margindata("height aligned by %p",delta) - end - shift = shift + voffset + delta - elseif voffset ~= 0 then - if trace_margindata then - report_margindata("voffset %p applied",voffset) - end - shift = shift + voffset - end - -- -- -- - if line ~= 0 then - local delta = line * candidate.lineheight - if trace_margindata then - report_margindata("offset %p applied to line %s",delta,line) - end - shift = shift + delta - offset = offset + delta - end - box.shift = shift - box.width = 0 - if not head then - head = box - elseif head.id == whatsit_code and head.subtype == localpar_code then - -- experimental - if head.dir == "TRT" then - box.list = hpack_nodes(concat_nodes{new_kern(candidate.hsize),box.list,new_kern(-candidate.hsize)}) - end - insert_node_after(head,head,box) - else - head.prev = box - box.next = head - head = box - end - box[a_margindata] = nofstatus - if trace_margindata then - report_margindata("injected, location %a, shift %p",location,shift) - end - -- we need to add line etc to offset as well - offset = offset + depth - local room = { - height = height, - depth = offset, - slack = candidate.bottomspace, -- todo: 'depth' => strutdepth - lineheight = candidate.lineheight, -- only for tracing - } - offset = offset + height - stacked[location] = offset -- weird, no table ? - -- todo: if no real depth then zero - if trace_margindata then - report_margindata("status, offset %s",offset) - end - return head, room, stack == v_continue -end - -local function flushinline(parent,head) - local current = head - local done = false - local continue = false - local room, don, con - while current and nofinlined > 0 do - local id = current.id - if id == whatsit_code then - if current.subtype == userdefined_code and current.user_id == inline_mark then - local n = current.value - local candidate = inlinestore[n] - if candidate then -- no vpack, as we want to realign - inlinestore[n] = nil - nofinlined = nofinlined - 1 - head, room, con = inject(parent,head,candidate) -- maybe return applied offset - continue = continue or con - done = true - nofstored = nofstored - 1 - end - end - elseif id == hlist_code or id == vlist_code then - -- optional (but sometimes needed) - current.list, don, con = flushinline(current,current.list) - continue = continue or con - done = done or don - end - current = current.next - end - return head, done, continue -end - -local a_linenumber = attributes.private('linenumber') - -local function flushed(scope,parent) -- current is hlist - local head = parent.list - local done = false - local continue = false - local room, con, don - for c=1,#categories do - local category = categories[c] - for l=1,#locations do - local location = locations[l] - local store = displaystore[category][location][scope] - while true do - local candidate = remove(store,1) -- brr, local stores are sparse - if candidate then -- no vpack, as we want to realign - head, room, con = inject(parent,head,candidate) - done = true - continue = continue or con - nofstored = nofstored - 1 - registertogether(parent,room) - else - break - end - end - end - end - if nofinlined > 0 then - if done then - parent.list = head - end - head, don, con = flushinline(parent,head) - continue = continue or con - done = done or don - end - if done then - local a = head[a_linenumber] -- hack .. we need a more decent critical attribute inheritance mechanism - parent.list = hpack_nodes(head,parent.width,"exactly") - if a then - parent.list[a_linenumber] = a - end - -- resetstacked() - end - return done, continue -end - --- only when group : vbox|vmode_par --- only when subtype : line, box (no indent alignment cell) - -local function handler(scope,head,group) - if nofstored > 0 then - if trace_margindata then - report_margindata("flushing stage one, stored %s, scope %s, delayed %s, group %a",nofstored,scope,nofdelayed,group) - end - local current = head - local done = false - while current do - local id = current.id - if (id == vlist_code or id == hlist_code) and not current[a_margindata] then - local don, continue = flushed(scope,current) - if don then - current[a_margindata] = 0 -- signal to prevent duplicate processing - if continue then - markovershoot(current) - end - if nofstored <= 0 then - break - end - done = true - end - end - current = current.next - end - -- if done then - resetstacked() -- why doesn't done work ok here? - -- end - return head, done - else - return head, false - end -end - -function margins.localhandler(head,group) -- sometimes group is "" which is weird - local inhibit = conditionals.inhibitmargindata - if inhibit then - if trace_margingroup then - report_margindata("ignored 3, group %a, stored %s, inhibit %a",group,nofstored,inhibit) - end - return head, false - elseif nofstored > 0 then - return handler(v_local,head,group) - else - if trace_margingroup then - report_margindata("ignored 4, group %a, stored %s, inhibit %a",group,nofstored,inhibit) - end - return head, false - end -end - -function margins.globalhandler(head,group) -- check group - local inhibit = conditionals.inhibitmargindata - if inhibit or nofstored == 0 then - if trace_margingroup then - report_margindata("ignored 1, group %a, stored %s, inhibit %a",group,nofstored,inhibit) - end - return head, false - elseif group == "hmode_par" then - return handler("global",head,group) - elseif group == "vmode_par" then -- experiment (for alignments) - return handler("global",head,group) - -- this needs checking as we then get quite some one liners to process and - -- we cannot look ahead then: - elseif group == "box" then -- experiment (for alignments) - return handler("global",head,group) - elseif group == "alignment" then -- experiment (for alignments) - return handler("global",head,group) - else - if trace_margingroup then - report_margindata("ignored 2, group %a, stored %s, inhibit %a",group,nofstored,inhibit) - end - return head, false - end -end - -local function finalhandler(head) - if nofdelayed > 0 then - local current = head - local done = false - while current do - local id = current.id - if id == hlist_code then - local a = current[a_margindata] - if not a or a == 0 then - finalhandler(current.list) - elseif realigned(current,a) then - done = true - if nofdelayed == 0 then - return head, true - end - end - elseif id == vlist_code then - finalhandler(current.list) - end - current = current.next - end - return head, done - else - return head, false - end -end - -function margins.finalhandler(head) - if nofdelayed > 0 then - -- if trace_margindata then - -- report_margindata("flushing stage two, instore: %s, delayed: %s",nofstored,nofdelayed) - -- end - return finalhandler(head) - else - return head, false - end -end - --- Somehow the vbox builder (in combinations) gets pretty confused and decides to --- go horizontal. So this needs more testing. - -prependaction("finalizers", "lists", "typesetters.margins.localhandler") --- ("vboxbuilders", "normalizers", "typesetters.margins.localhandler") -prependaction("mvlbuilders", "normalizers", "typesetters.margins.globalhandler") -prependaction("shipouts", "normalizers", "typesetters.margins.finalhandler") - -disableaction("finalizers", "typesetters.margins.localhandler") --- ("vboxbuilders", "typesetters.margins.localhandler") -disableaction("mvlbuilders", "typesetters.margins.globalhandler") -disableaction("shipouts", "typesetters.margins.finalhandler") - -enablelocal = function() - enableaction("finalizers", "typesetters.margins.localhandler") - -- enableaction("vboxbuilders", "typesetters.margins.localhandler") - enableaction("shipouts", "typesetters.margins.finalhandler") - enablelocal = nil -end - -enableglobal = function() - enableaction("mvlbuilders", "typesetters.margins.globalhandler") - enableaction("shipouts", "typesetters.margins.finalhandler") - enableglobal = nil -end - -statistics.register("margin data", function() - if nofsaved > 0 then - return format("%s entries, %s pending",nofsaved,nofdelayed) - else - return nil - end -end) +if not modules then modules = { } end modules ['typo-mar'] = {
+ version = 1.001,
+ comment = "companion to typo-mar.mkiv",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+-- todo:
+--
+-- * autoleft/right depending on available space (or distance to margin)
+-- * stack across paragraphs, but that is messy and one should reconsider
+-- using margin data then as also vertical spacing kicks in
+-- * floating margin data, with close-to-call anchoring
+
+-- -- experiment (does not work, too much interference)
+--
+-- local pdfprint = pdf.print
+-- local format = string.format
+--
+-- anchors = anchors or { }
+--
+-- local whatever = { }
+-- local factor = (7200/7227)/65536
+--
+-- function anchors.set(tag)
+-- whatever[tag] = { pdf.h, pdf.v }
+-- end
+--
+-- function anchors.reset(tag)
+-- whatever[tag] = nil
+-- end
+--
+-- function anchors.startmove(tag,how) -- save/restore nodes but they don't support moves
+-- local w = whatever[tag]
+-- if not w then
+-- -- error
+-- elseif how == "horizontal" or how == "h" then
+-- pdfprint("page",format(" q 1 0 0 1 %f 0 cm ", (w[1] - pdf.h) * factor))
+-- elseif how == "vertical" or how == "v" then
+-- pdfprint("page",format(" q 1 0 0 1 0 %f cm ", (w[2] - pdf.v) * factor))
+-- else
+-- pdfprint("page",format(" q 1 0 0 1 %f %f cm ", (w[1] - pdf.h) * factor, (w[2] - pdf.v) * factor))
+-- end
+-- end
+--
+-- function anchors.stopmove(tag)
+-- local w = whatever[tag]
+-- if not w then
+-- -- error
+-- else
+-- pdfprint("page"," Q ")
+-- end
+-- end
+--
+-- local latelua = nodes.pool.latelua
+--
+-- function anchors.node_set(tag)
+-- return latelua(formatters["anchors.set(%q)"](tag))
+-- end
+--
+-- function anchors.node_reset(tag)
+-- return latelua(formatters["anchors.reset(%q)"](tag))
+-- end
+--
+-- function anchors.node_start_move(tag,how)
+-- return latelua(formatters["anchors.startmove(%q,%q)](tag,how))
+-- end
+--
+-- function anchors.node_stop_move(tag)
+-- return latelua(formatters["anchors.stopmove(%q)"](tag))
+-- end
+
+-- so far
+
+local format, validstring = string.format, string.valid
+local insert, remove = table.insert, table.remove
+local setmetatable, next = setmetatable, next
+
+local attributes, nodes, node, variables = attributes, nodes, node, variables
+
+local trace_margindata = false trackers.register("typesetters.margindata", function(v) trace_margindata = v end)
+local trace_marginstack = false trackers.register("typesetters.margindata.stack", function(v) trace_marginstack = v end)
+local trace_margingroup = false trackers.register("typesetters.margindata.group", function(v) trace_margingroup = v end)
+
+local report_margindata = logs.reporter("typesetters","margindata")
+
+local tasks = nodes.tasks
+local prependaction = tasks.prependaction
+local disableaction = tasks.disableaction
+local enableaction = tasks.enableaction
+
+local variables = interfaces.variables
+
+local conditionals = tex.conditionals
+local systemmodes = tex.systemmodes
+
+local v_top = variables.top
+local v_depth = variables.depth
+local v_local = variables["local"]
+local v_global = variables["global"]
+local v_left = variables.left
+local v_right = variables.right
+local v_flushleft = variables.flushleft
+local v_flushright = variables.flushright
+local v_inner = variables.inner
+local v_outer = variables.outer
+local v_margin = variables.margin
+local v_edge = variables.edge
+local v_default = variables.default
+local v_normal = variables.normal
+local v_yes = variables.yes
+local v_continue = variables.continue
+local v_first = variables.first
+local v_text = variables.text
+local v_column = variables.column
+
+local copy_node_list = node.copy_list
+local slide_nodes = node.slide
+local hpack_nodes = node.hpack -- nodes.fasthpack not really faster here
+local traverse_id = node.traverse_id
+local free_node_list = node.flush_list
+local insert_node_after = node.insert_after
+local insert_node_before = node.insert_before
+
+local concat_nodes = nodes.concat
+
+local nodecodes = nodes.nodecodes
+local listcodes = nodes.listcodes
+local gluecodes = nodes.gluecodes
+local whatsitcodes = nodes.whatsitcodes
+
+local hlist_code = nodecodes.hlist
+local vlist_code = nodecodes.vlist
+local glue_code = nodecodes.glue
+local kern_code = nodecodes.kern
+local penalty_code = nodecodes.penalty
+local whatsit_code = nodecodes.whatsit
+local line_code = listcodes.line
+local cell_code = listcodes.cell
+local alignment_code = listcodes.alignment
+local leftskip_code = gluecodes.leftskip
+local rightskip_code = gluecodes.rightskip
+local userdefined_code = whatsitcodes.userdefined
+
+local dir_code = whatsitcodes.dir
+local localpar_code = whatsitcodes.localpar
+
+local nodepool = nodes.pool
+
+local new_kern = nodepool.kern
+local new_glue = nodepool.glue
+local new_penalty = nodepool.penalty
+local new_stretch = nodepool.stretch
+local new_usernumber = nodepool.usernumber
+local new_latelua = nodepool.latelua
+
+local texcount = tex.count
+local texdimen = tex.dimen
+local texbox = tex.box
+
+local points = number.points
+
+local isleftpage = layouts.status.isleftpage
+local registertogether = builders.paragraphs.registertogether
+
+local jobpositions = job.positions
+local getposition = jobpositions.position
+
+local a_margindata = attributes.private("margindata")
+
+local inline_mark = nodepool.userids["margins.inline"]
+
+local margins = { }
+typesetters.margins = margins
+
+local locations = { v_left, v_right, v_inner, v_outer } -- order might change
+local categories = { }
+local displaystore = { } -- [category][location][scope]
+local inlinestore = { } -- [number]
+local nofsaved = 0
+local nofstored = 0
+local nofinlined = 0
+local nofdelayed = 0
+local h_anchors = 0
+local v_anchors = 0
+
+local mt1 = {
+ __index = function(t,location)
+ local v = { [v_local] = { }, [v_global] = { } }
+ t[location] = v
+ return v
+ end
+}
+
+local mt2 = {
+ __index = function(stores,category)
+ categories[#categories+1] = category
+ local v = { }
+ setmetatable(v,mt1)
+ stores[category] = v
+ return v
+ end
+}
+
+setmetatable(displaystore,mt2)
+
+local defaults = {
+ __index = {
+ location = v_left,
+ align = v_normal,
+ method = "",
+ name = "",
+ threshold = 0, -- .25ex
+ margin = v_normal,
+ scope = v_global,
+ distance = 0,
+ hoffset = 0,
+ voffset = 0,
+ category = v_default,
+ line = 0,
+ vstack = 0,
+ dy = 0,
+ baseline = false,
+ inline = false,
+ leftskip = 0,
+ rightskip = 0,
+ }
+}
+
+local enablelocal, enableglobal -- forward reference (delayed initialization)
+
+local function showstore(store,banner,location)
+ if next(store) then
+ for i, si in table.sortedpairs(store) do
+ local si =store[i]
+ report_margindata("%s: stored in %a at %s: %a => %s",banner,location,i,validstring(si.name,"no name"),nodes.toutf(si.box.list))
+ end
+ else
+ report_margindata("%s: nothing stored in location %a",banner,location)
+ end
+end
+
+function margins.save(t)
+ setmetatable(t,defaults)
+ local content = texbox[t.number]
+ local location = t.location
+ local category = t.category
+ local inline = t.inline
+ local scope = t.scope or v_global
+ if not content then
+ report_margindata("ignoring empty margin data %a",location or "unknown")
+ return
+ end
+ local store
+ if inline then
+ store = inlinestore
+ else
+ store = displaystore[category][location]
+ if not store then
+ report_margindata("invalid location %a",location)
+ return
+ end
+ store = store[scope]
+ end
+ if not store then
+ report_margindata("invalid scope %a",scope)
+ return
+ end
+ if enablelocal and scope == v_local then
+ enablelocal()
+ if enableglobal then
+ enableglobal() -- is the fallback
+ end
+ elseif enableglobal and scope == v_global then
+ enableglobal()
+ end
+ nofsaved = nofsaved + 1
+ nofstored = nofstored + 1
+ local name = t.name
+ if trace_marginstack then
+ showstore(store,"before",location)
+ end
+ if name and name ~= "" then
+ if inlinestore then -- todo: inline store has to be done differently (not sparse)
+ local t = table.sortedkeys(store) for j=#t,1,-1 do local i = t[j]
+ local si = store[i]
+ if si.name == name then
+ local s = remove(store,i)
+ free_node_list(s.box)
+ end
+ end
+ else
+ for i=#store,1,-1 do
+ local si = store[i]
+ if si.name == name then
+ local s = remove(store,i)
+ free_node_list(s.box)
+ end
+ end
+ end
+ if trace_marginstack then
+ showstore(store,"between",location)
+ end
+ end
+ if t.number then
+ -- better make a new table and make t entry in t
+ t.box = copy_node_list(content)
+ t.n = nofsaved
+ -- used later (we will clean up this natural mess later)
+ -- nice is to make a special status table mechanism
+ local leftmargindistance = texdimen.naturalleftmargindistance
+ local rightmargindistance = texdimen.naturalrightmargindistance
+ t.strutdepth = texbox.strutbox.depth
+ t.strutheight = texbox.strutbox.height
+ t.leftskip = tex.leftskip.width -- we're not in forgetall
+ t.rightskip = tex.rightskip.width -- we're not in forgetall
+ t.leftmargindistance = leftmargindistance -- todo:layoutstatus table
+ t.rightmargindistance = rightmargindistance
+ t.leftedgedistance = texdimen.naturalleftedgedistance
+ + texdimen.leftmarginwidth
+ + leftmargindistance
+ t.rightedgedistance = texdimen.naturalrightedgedistance
+ + texdimen.rightmarginwidth
+ + rightmargindistance
+ t.lineheight = texdimen.lineheight
+ --
+ -- t.realpageno = texcount.realpageno
+ if inline then
+ context(new_usernumber(inline_mark,nofsaved))
+ store[nofsaved] = t -- no insert
+ nofinlined = nofinlined + 1
+ else
+ insert(store,t)
+ end
+ end
+ if trace_marginstack then
+ showstore(store,"after",location)
+ end
+ if trace_margindata then
+ report_margindata("saved %a, location %a, scope %a, inline %a",nofsaved,location,scope,inline)
+ end
+end
+
+-- Actually it's an advantage to have them all anchored left (tags and such)
+-- we could keep them in store and flush in stage two but we might want to
+-- do more before that so we need the content to be there unless we can be
+-- sure that we flush this first which might not be the case in the future.
+--
+-- When the prototype inner/outer code that was part of this proved to be
+-- okay it was moved elsewhere.
+
+local status, nofstatus = { }, 0
+
+local function realign(current,candidate)
+ local location = candidate.location
+ local margin = candidate.margin
+ local hoffset = candidate.hoffset
+ local distance = candidate.distance
+ local hsize = candidate.hsize
+ local width = candidate.width
+ local align = candidate.align
+ -- local realpageno = candidate.realpageno
+ local leftpage = isleftpage(false,true)
+ local delta = 0
+ local leftdelta = 0
+ local rightdelta = 0
+ local leftdistance = distance
+ local rightdistance = distance
+ if margin == v_normal then
+ --
+ elseif margin == v_local then
+ leftdelta = - candidate.leftskip
+ rightdelta = candidate.rightskip
+ elseif margin == v_margin then
+ leftdistance = candidate.leftmargindistance
+ rightdistance = candidate.rightmargindistance
+ elseif margin == v_edge then
+ leftdistance = candidate.leftedgedistance
+ rightdistance = candidate.rightedgedistance
+ end
+ if leftpage then
+ leftdistance, rightdistance = rightdistance, leftdistance
+ end
+
+ if location == v_left then
+ delta = hoffset + width + leftdistance + leftdelta
+ elseif location == v_right then
+ delta = -hoffset - hsize - rightdistance + rightdelta
+ elseif location == v_inner then
+ if leftpage then
+ delta = -hoffset - hsize - rightdistance + rightdelta
+ else
+ delta = hoffset + width + leftdistance + leftdelta
+ end
+ elseif location == v_outer then
+ if leftpage then
+ delta = hoffset + width + leftdistance + leftdelta
+ else
+ delta = -hoffset - hsize - rightdistance + rightdelta
+ end
+ end
+
+ -- we assume that list is a hbox, otherwise we had to take the whole current
+ -- in order to get it right
+
+ current.width = 0
+ local anchornode, move_x
+
+ -- this mess is needed for alignments (combinations) so we use that
+ -- oportunity to add arbitrary anchoring
+
+ -- always increment anchor is nicer for multipass when we add new ..
+
+ local inline = candidate.inline
+ local anchor = candidate.anchor
+ if not anchor or anchor == "" then
+ anchor = v_text
+ end
+ if inline or anchor ~= v_text or candidate.psubtype == alignment_code then
+ -- the alignment_code check catches margintexts ste before a tabulate
+ h_anchors = h_anchors + 1
+ anchornode = new_latelua(format("_plib_.set('md:h',%i,{x=true,c=true})",h_anchors))
+ local blob = jobpositions.get('md:h', h_anchors)
+ if blob then
+ local reference = jobpositions.getreserved(anchor,blob.c)
+ if reference then
+ if location == v_left then
+ move_x = (reference.x or 0) - (blob.x or 0)
+ elseif location == v_right then
+ move_x = (reference.x or 0) - (blob.x or 0) + (reference.w or 0) - hsize
+ else
+ -- not yet done
+ end
+ end
+ end
+ end
+
+ if move_x then
+ delta = delta - move_x
+ if trace_margindata then
+ report_margindata("realigned %a, location %a, margin %a, move %p",candidate.n,location,margin,move_x)
+ end
+ else
+ if trace_margindata then
+ report_margindata("realigned %a, location %a, margin %a",candidate.n,location,margin)
+ end
+ end
+
+ current.list = hpack_nodes(concat_nodes{anchornode,new_kern(-delta),current.list,new_kern(delta)})
+ current.width = 0
+end
+
+local function realigned(current,a)
+ local candidate = status[a]
+ realign(current,candidate)
+ nofdelayed = nofdelayed - 1
+ status[a] = nil
+ return true
+end
+
+-- Stacking is done in two ways: the v_yes option stacks per paragraph (or line,
+-- depending on what gets by) and mostly concerns margin data dat got set at more or
+-- less the same time. The v_continue option uses position tracking and works on
+-- larger range. However, crossing pages is not part of it. Anyway, when you have
+-- such messed up margin data you'd better think twice.
+--
+-- The stacked table keeps track (per location) of the offsets (the v_yes case). This
+-- table gets saved when the v_continue case is active. We use a special variant
+-- of position tracking, after all we only need the page number and vertical position.
+
+local stacked = { } -- left/right keys depending on location
+local cache = { }
+
+local function resetstacked()
+ stacked = { }
+end
+
+-- resetstacked()
+
+function margins.ha(tag) -- maybe l/r keys ipv left/right keys
+ local p = cache[tag]
+ p.p = true
+ p.y = true
+ jobpositions.set('md:v',tag,p)
+ cache[tag] = nil
+end
+
+local function markovershoot(current)
+ v_anchors = v_anchors + 1
+ cache[v_anchors] = stacked
+ local anchor = new_latelua(format("typesetters.margins.ha(%s)",v_anchors)) -- todo: alleen als offset > line
+ current.list = hpack_nodes(concat_nodes{anchor,current.list})
+end
+
+local function getovershoot(location)
+ local p = jobpositions.get("md:v",v_anchors)
+ local c = jobpositions.get("md:v",v_anchors+1)
+ if p and c and p.p and p.p == c.p then
+ local distance = p.y - c.y
+ local offset = p[location] or 0
+ local overshoot = offset - distance
+ if trace_marginstack then
+ report_margindata("location %a, distance %p, offset %p, overshoot %p",location,distance,offset,overshoot)
+ end
+ if overshoot > 0 then
+ return overshoot
+ end
+ end
+ return 0
+end
+
+local function inject(parent,head,candidate)
+ local box = candidate.box
+ local width = box.width
+ local height = box.height
+ local depth = box.depth
+ local shift = box.shift
+ local stack = candidate.stack
+ local location = candidate.location
+ local method = candidate.method
+ local voffset = candidate.voffset
+ local line = candidate.line
+ local baseline = candidate.baseline
+ local strutheight = candidate.strutheight
+ local strutdepth = candidate.strutdepth
+ local psubtype = parent.subtype
+ local offset = stacked[location]
+ local firstonstack = offset == false or offset == nil
+ nofstatus = nofstatus + 1
+ nofdelayed = nofdelayed + 1
+ status[nofstatus] = candidate
+ -- yet untested
+ if baseline == true then
+ baseline = false
+ -- hbox vtop
+--~ for h in traverse_id(hlist_code,box.list.list) do
+--~ baseline = h.height
+--~ break
+--~ end
+ else
+ baseline = tonumber(baseline)
+ if not baseline or baseline <= 0 then
+ -- in case we have a box of width 0 that is not analyzed
+ baseline = false -- strutheight -- actually a hack
+ end
+ end
+ candidate.width = width
+ candidate.hsize = parent.width -- we can also pass textwidth
+ candidate.psubtype = psubtype
+ if trace_margindata then
+ report_margindata("processing, index %s, height %p, depth %p, parent %s",candidate.n,height,depth,listcodes[psubtype])
+ end
+ if firstonstack then
+ offset = 0
+ else
+-- offset = offset + height
+ end
+ if stack == v_yes then
+ offset = offset + candidate.dy
+ shift = shift + offset
+ elseif stack == v_continue then
+ offset = offset + candidate.dy
+ if firstonstack then
+ offset = offset + getovershoot(location)
+ end
+ shift = shift + offset
+ end
+ -- -- --
+ -- Maybe we also need to patch offset when we apply methods, but how ...
+ -- This needs a bit of playing as it depends on the stack setting of the
+ -- following which we don't know yet ... so, consider stacking partially
+ -- experimental.
+ -- -- --
+ if method == v_top then
+ local delta = height - parent.height
+ if trace_margindata then
+ report_margindata("top aligned by %p",delta)
+ end
+ if delta < candidate.threshold then
+ shift = shift + voffset + delta
+ end
+ elseif method == v_first then
+ if baseline then
+ shift = shift + voffset + height - baseline -- option
+ else
+ shift = shift + voffset -- normal
+ end
+ if trace_margindata then
+ report_margindata("first aligned")
+ end
+ elseif method == v_depth then
+ local delta = strutdepth
+ if trace_margindata then
+ report_margindata("depth aligned by %p",delta)
+ end
+ shift = shift + voffset + delta
+ elseif method == v_height then
+ local delta = - strutheight
+ if trace_margindata then
+ report_margindata("height aligned by %p",delta)
+ end
+ shift = shift + voffset + delta
+ elseif voffset ~= 0 then
+ if trace_margindata then
+ report_margindata("voffset %p applied",voffset)
+ end
+ shift = shift + voffset
+ end
+ -- -- --
+ if line ~= 0 then
+ local delta = line * candidate.lineheight
+ if trace_margindata then
+ report_margindata("offset %p applied to line %s",delta,line)
+ end
+ shift = shift + delta
+ offset = offset + delta
+ end
+ box.shift = shift
+ box.width = 0
+ if not head then
+ head = box
+ elseif head.id == whatsit_code and head.subtype == localpar_code then
+ -- experimental
+ if head.dir == "TRT" then
+ box.list = hpack_nodes(concat_nodes{new_kern(candidate.hsize),box.list,new_kern(-candidate.hsize)})
+ end
+ insert_node_after(head,head,box)
+ else
+ head.prev = box
+ box.next = head
+ head = box
+ end
+ box[a_margindata] = nofstatus
+ if trace_margindata then
+ report_margindata("injected, location %a, shift %p",location,shift)
+ end
+ -- we need to add line etc to offset as well
+ offset = offset + depth
+ local room = {
+ height = height,
+ depth = offset,
+ slack = candidate.bottomspace, -- todo: 'depth' => strutdepth
+ lineheight = candidate.lineheight, -- only for tracing
+ }
+ offset = offset + height
+ stacked[location] = offset -- weird, no table ?
+ -- todo: if no real depth then zero
+ if trace_margindata then
+ report_margindata("status, offset %s",offset)
+ end
+ return head, room, stack == v_continue
+end
+
+local function flushinline(parent,head)
+ local current = head
+ local done = false
+ local continue = false
+ local room, don, con
+ while current and nofinlined > 0 do
+ local id = current.id
+ if id == whatsit_code then
+ if current.subtype == userdefined_code and current.user_id == inline_mark then
+ local n = current.value
+ local candidate = inlinestore[n]
+ if candidate then -- no vpack, as we want to realign
+ inlinestore[n] = nil
+ nofinlined = nofinlined - 1
+ head, room, con = inject(parent,head,candidate) -- maybe return applied offset
+ continue = continue or con
+ done = true
+ nofstored = nofstored - 1
+ end
+ end
+ elseif id == hlist_code or id == vlist_code then
+ -- optional (but sometimes needed)
+ current.list, don, con = flushinline(current,current.list)
+ continue = continue or con
+ done = done or don
+ end
+ current = current.next
+ end
+ return head, done, continue
+end
+
+local a_linenumber = attributes.private('linenumber')
+
+local function flushed(scope,parent) -- current is hlist
+ local head = parent.list
+ local done = false
+ local continue = false
+ local room, con, don
+ for c=1,#categories do
+ local category = categories[c]
+ for l=1,#locations do
+ local location = locations[l]
+ local store = displaystore[category][location][scope]
+ while true do
+ local candidate = remove(store,1) -- brr, local stores are sparse
+ if candidate then -- no vpack, as we want to realign
+ head, room, con = inject(parent,head,candidate)
+ done = true
+ continue = continue or con
+ nofstored = nofstored - 1
+ registertogether(parent,room)
+ else
+ break
+ end
+ end
+ end
+ end
+ if nofinlined > 0 then
+ if done then
+ parent.list = head
+ end
+ head, don, con = flushinline(parent,head)
+ continue = continue or con
+ done = done or don
+ end
+ if done then
+ local a = head[a_linenumber] -- hack .. we need a more decent critical attribute inheritance mechanism
+ parent.list = hpack_nodes(head,parent.width,"exactly")
+ if a then
+ parent.list[a_linenumber] = a
+ end
+ -- resetstacked()
+ end
+ return done, continue
+end
+
+-- only when group : vbox|vmode_par
+-- only when subtype : line, box (no indent alignment cell)
+
+local function handler(scope,head,group)
+ if nofstored > 0 then
+ if trace_margindata then
+ report_margindata("flushing stage one, stored %s, scope %s, delayed %s, group %a",nofstored,scope,nofdelayed,group)
+ end
+ local current = head
+ local done = false
+ while current do
+ local id = current.id
+ if (id == vlist_code or id == hlist_code) and not current[a_margindata] then
+ local don, continue = flushed(scope,current)
+ if don then
+ current[a_margindata] = 0 -- signal to prevent duplicate processing
+ if continue then
+ markovershoot(current)
+ end
+ if nofstored <= 0 then
+ break
+ end
+ done = true
+ end
+ end
+ current = current.next
+ end
+ -- if done then
+ resetstacked() -- why doesn't done work ok here?
+ -- end
+ return head, done
+ else
+ return head, false
+ end
+end
+
+function margins.localhandler(head,group) -- sometimes group is "" which is weird
+ local inhibit = conditionals.inhibitmargindata
+ if inhibit then
+ if trace_margingroup then
+ report_margindata("ignored 3, group %a, stored %s, inhibit %a",group,nofstored,inhibit)
+ end
+ return head, false
+ elseif nofstored > 0 then
+ return handler(v_local,head,group)
+ else
+ if trace_margingroup then
+ report_margindata("ignored 4, group %a, stored %s, inhibit %a",group,nofstored,inhibit)
+ end
+ return head, false
+ end
+end
+
+function margins.globalhandler(head,group) -- check group
+ local inhibit = conditionals.inhibitmargindata
+ if inhibit or nofstored == 0 then
+ if trace_margingroup then
+ report_margindata("ignored 1, group %a, stored %s, inhibit %a",group,nofstored,inhibit)
+ end
+ return head, false
+ elseif group == "hmode_par" then
+ return handler("global",head,group)
+ elseif group == "vmode_par" then -- experiment (for alignments)
+ return handler("global",head,group)
+ -- this needs checking as we then get quite some one liners to process and
+ -- we cannot look ahead then:
+ elseif group == "box" then -- experiment (for alignments)
+ return handler("global",head,group)
+ elseif group == "alignment" then -- experiment (for alignments)
+ return handler("global",head,group)
+ else
+ if trace_margingroup then
+ report_margindata("ignored 2, group %a, stored %s, inhibit %a",group,nofstored,inhibit)
+ end
+ return head, false
+ end
+end
+
+local function finalhandler(head)
+ if nofdelayed > 0 then
+ local current = head
+ local done = false
+ while current do
+ local id = current.id
+ if id == hlist_code then
+ local a = current[a_margindata]
+ if not a or a == 0 then
+ finalhandler(current.list)
+ elseif realigned(current,a) then
+ done = true
+ if nofdelayed == 0 then
+ return head, true
+ end
+ end
+ elseif id == vlist_code then
+ finalhandler(current.list)
+ end
+ current = current.next
+ end
+ return head, done
+ else
+ return head, false
+ end
+end
+
+function margins.finalhandler(head)
+ if nofdelayed > 0 then
+ -- if trace_margindata then
+ -- report_margindata("flushing stage two, instore: %s, delayed: %s",nofstored,nofdelayed)
+ -- end
+ return finalhandler(head)
+ else
+ return head, false
+ end
+end
+
+-- Somehow the vbox builder (in combinations) gets pretty confused and decides to
+-- go horizontal. So this needs more testing.
+
+prependaction("finalizers", "lists", "typesetters.margins.localhandler")
+-- ("vboxbuilders", "normalizers", "typesetters.margins.localhandler")
+prependaction("mvlbuilders", "normalizers", "typesetters.margins.globalhandler")
+prependaction("shipouts", "normalizers", "typesetters.margins.finalhandler")
+
+disableaction("finalizers", "typesetters.margins.localhandler")
+-- ("vboxbuilders", "typesetters.margins.localhandler")
+disableaction("mvlbuilders", "typesetters.margins.globalhandler")
+disableaction("shipouts", "typesetters.margins.finalhandler")
+
+enablelocal = function()
+ enableaction("finalizers", "typesetters.margins.localhandler")
+ -- enableaction("vboxbuilders", "typesetters.margins.localhandler")
+ enableaction("shipouts", "typesetters.margins.finalhandler")
+ enablelocal = nil
+end
+
+enableglobal = function()
+ enableaction("mvlbuilders", "typesetters.margins.globalhandler")
+ enableaction("shipouts", "typesetters.margins.finalhandler")
+ enableglobal = nil
+end
+
+statistics.register("margin data", function()
+ if nofsaved > 0 then
+ return format("%s entries, %s pending",nofsaved,nofdelayed)
+ else
+ return nil
+ end
+end)
|