local pagebuilder = pl.class()
pagebuilder.type = "pagebuilder"
pagebuilder._name = "base"
function pagebuilder:_init ()
self.awful_bad = 1073741823
self.inf_bad = 10000
self.eject_penalty = -self.inf_bad
self.deplorable = 100000
end
function pagebuilder.collateVboxes (_, vboxlist)
local output = SILE.types.node.vbox()
output:append(vboxlist)
return output
end
function pagebuilder:findBestBreak (options)
local vboxlist = SU.required(options, "vboxlist", "in findBestBreak")
local target = SU.required(options, "target", "in findBestBreak", "length")
local restart = options.restart or false
local force = options.force or false
local i = 0
local totalHeight = SILE.types.length()
local bestBreak = nil
local started = false
if restart and restart.target == target then
totalHeight = restart.totalHeight
i = restart.i
started = restart.started
end
local leastC = self.inf_bad
SU.debug("pagebuilder", function ()
return "Page builder for frame "
.. SILE.typesetter.frame.id
.. " called with "
.. #vboxlist
.. " nodes, "
.. tostring(target)
end)
if SU.debugging("vboxes") then
for j, box in ipairs(vboxlist) do
SU.debug("vboxes", function ()
return (j == i and " >" or " ") .. j .. ": " .. box
end)
end
end
while not started and i < #vboxlist do
i = i + 1
if not vboxlist[i].is_vglue then
started = true
i = i - 1
break
end
end
local pi
while i < #vboxlist do
i = i + 1
local vbox = vboxlist[i]
SU.debug("pagebuilder", "Dealing with VBox", vbox)
if vbox.is_vbox then
totalHeight:___add(vbox.height)
totalHeight:___add(vbox.depth)
elseif vbox.is_vglue then
totalHeight:___add(vbox.height)
elseif vbox.is_insertion then
target = SILE.insertions.processInsertion(vboxlist, i, totalHeight, target)
vbox = vboxlist[i]
end
local left = target - totalHeight
SU.debug("pagebuilder", "I have", left, "left")
pi = 0
if vbox.is_penalty then
pi = vbox.penalty
end
if
vbox.is_penalty and vbox.penalty < self.inf_bad
or (vbox.is_vglue and i > 1 and not vboxlist[i - 1].discardable)
then
local badness
SU.debug("pagebuilder", "totalHeight", totalHeight, "with target", target)
if totalHeight.length.amount < target.length.amount then badness = SU.rateBadness(self.inf_bad, left.length.amount, totalHeight.stretch.amount)
elseif left.length.amount < totalHeight.shrink.amount then
badness = self.awful_bad
else
badness = SU.rateBadness(self.inf_bad, -left.length.amount, totalHeight.shrink.amount)
end
local c
if badness < self.awful_bad then
if pi <= self.eject_penalty then
c = pi
elseif badness < self.inf_bad then
c = badness + pi else
c = self.deplorable
end
else
c = badness
end
if c < leastC then
leastC = c
bestBreak = i
else
restart = { totalHeight = totalHeight, i = i, started = started, target = target }
end
SU.debug("pagebuilder", "Badness:", c)
if c == self.awful_bad or pi <= self.eject_penalty then
SU.debug("pagebuilder", "outputting")
local onepage = {}
if not bestBreak then
bestBreak = i
end
for j = 1, bestBreak do
onepage[j] = table.remove(vboxlist, 1)
end
while #onepage > 1 and onepage[#onepage].discardable do
onepage[#onepage] = nil
end
return onepage, pi
end
end
end
SU.debug("pagebuilder", "No page break here")
if force and bestBreak then
local onepage = {}
for j = 1, bestBreak do
onepage[j] = table.remove(vboxlist, 1)
end
return onepage, pi
end
return false, restart
end
return pagebuilder