diff options
Diffstat (limited to 'tex/context/base/node-ltp.lua')
-rw-r--r-- | tex/context/base/node-ltp.lua | 3207 |
1 files changed, 0 insertions, 3207 deletions
diff --git a/tex/context/base/node-ltp.lua b/tex/context/base/node-ltp.lua deleted file mode 100644 index 97e61cf18..000000000 --- a/tex/context/base/node-ltp.lua +++ /dev/null @@ -1,3207 +0,0 @@ -if not modules then modules = { } end modules ['node-par'] = { - version = 1.001, - comment = "companion to node-par.mkiv", - author = "Hans Hagen", - copyright = "ConTeXt Development Team", - license = "see context related readme files", - comment = "a translation of the built in parbuilder, initial convertsin by Taco Hoekwater", -} - --- todo: remove nest_stack from linebreak.w --- todo: use ex field as signal (index in ?) --- todo: attr driven unknown/on/off --- todo: permit global steps i.e. using an attribute that sets min/max/step and overloads the font parameters --- todo: split the three passes into three functions --- todo: simplify the direction stack, no copy needed --- todo: add more mkiv like tracing --- todo: add a couple of plugin hooks --- todo: maybe split expansion code paths --- todo: fix line numbers (cur_list.pg_field needed) --- todo: make kerns stretch an option and disable it by default (definitely not shrink) --- todo: check and improve protrusion --- todo: arabic etc (we could use pretty large scales there) .. marks and cursive - ---[[ - - This code is derived from traditional TeX and has bits of pdfTeX, Aleph (Omega), and of course LuaTeX. So, - the basic algorithm for sure is not our work. On the other hand, the directional model in LuaTeX is cleaned - up as is other code. And of course there are hooks for callbacks. - - The first version of the code below was a conversion of the C code that in turn was a conversion from the - original Pascal code. Around September 2008 we experimented with cq. discussed possible approaches to improved - typesetting of Arabic and as our policy is that extensions happen in Lua this means that we need a parbuilder - in Lua. Taco's first conversion still looked quite C-ish and in the process of cleaning up we uncovered some odd - bits and pieces in the original code as well. I did some first cleanup to get rid of C-artefacts, and Taco and I - spent the usual amount of Skyping to sort out problems. At that point we diverted to other LuaTeX issues. - - A while later I decided to pick up this thread and decided to look into better ways to deal with font expansion - (aka hz). I got it running using a simpler method. One reason why the built-in mechanims is slow is that there is - lots of redudancy in calculations. Expanded widths are recalculated each time and because the hpakc routine does - it again that gives some overhead. In the process extra fonts are created with different dimensions so that the - backend can deal with it. The alternative method doesn't create fonts but passes an expansion factor to the - pdf generator. The small patch needed for the backend code worked more or less okay but was never intergated into - LuaTeX due to lack of time. - - This all happened in 2010 while listening to Peter Gabriels "Scratch My Back" and Camels "Rayaz" so it was a - rather relaxed job. - - In 2012 I picked up this thread. Because both languages are similar but also quite different it took some time - to get compatible output. Because the C code uses macros, careful checking was needed. Of course Lua's table model - and local variables brought some work as well. And still the code looks a bit C-ish. We could not divert too much - from the original model simply because it's well documented but future versions (or variants) might as well look - different. - - Eventually I'll split this code into passes so that we can better see what happens, but first we need to reach - a decent level of stability. The current expansion results are not the same as the built-in but that was never - the objective. It all has to do with slightly different calculations. - - The original C-code related to protrusion and expansion is not that efficient as many (redundant) function - calls take place in the linebreaker and packer. As most work related to fonts is done in the backend, we - can simply stick to width calculations here. Also, it is no problem at all that we use floating point - calculations (as Lua has only floats). The final result will look ok as the hpack will nicely compensate - for rounding errors as it will normally distribute the content well enough. And let's admit: most texies - won't see it anyway. As long as we're cross platform compatible it's fine. - - We use the table checked_expansion to keep track of font related parameters (per paragraph). The table is - also the signal that we have adjustments > 1. In retrospect one might wonder if adjusting kerns is such a - good idea because other spacing is also not treated. If we would stick to the regular hpack routine - we do have to follow the same logic, but I decided to use a Lua hpacker so that constraint went away. And - anyway, instead of doing a lookup in the kern table (that we don't have in node mode) the set kern value - is used. Disabling kern scaling will become an option in Luatex some day. You can blame me for all errors - that crept in and I know that there are some. - - To be honest, I slowly start to grasp the magic here as normally I start from scratch when implementing - something (as it's the only way I can understand things). This time I had a recently acquired stack of - Porcupine Tree disks to get me through. - - Picking up this effort was inspired by discussions between Luigi Scarso and me about efficiency of Lua - code and we needed some stress tests to compare regular LuaTeX and LuajitTeX. One of the tests was - processing tufte.tex as that one has lots of hyphenations and is a tough one to get right. - - tufte: boxed 1000 times, no flushing in backend: - - \testfeatureonce{1000}{\setbox0\hbox{\tufte}} - \testfeatureonce{1000}{\setbox0\vbox{\tufte}} - \startparbuilder[basic]\testfeatureonce{1000}{\setbox0\vbox{\tufte}}\stopparbuilder - - method normal hz comment - - luatex tex hbox 9.64 9.64 baseline font feature processing, hyphenation etc: 9.74 - tex vbox 9.84 10.16 0.20 linebreak / 0.52 with hz -> 0.32 hz overhead (150pct more) - lua vbox 17.28 18.43 7.64 linebreak / 8.79 with hz -> 1.33 hz overhead ( 20pct more) - - luajittex tex hbox 6.33 6.33 baseline font feature processing, hyphenation etc: 6.33 - tex vbox 6.53 6.81 0.20 linebreak / 0.48 with hz -> 0.28 hz overhead (expected 0.32) - lua vbox 11.06 11.81 4.53 linebreak / 5.28 with hz -> 0.75 hz overhead - - Interesting is that the runtime for the built-in parbuilder indeed increases much when expansion - is enabled, but in the Lua variant the extra overhead is way less significant. This means that when we - retrofit the same approach into the core, the overhead of expansion can be sort of nilled. - -]]-- - -local utfchar = utf.char -local write, write_nl = texio.write, texio.write_nl -local sub, format = string.sub, string.format -local round = math.round -local insert, remove = table.insert, table.remove - -local fonts, nodes, node = fonts, nodes, node - -local trace_basic = false trackers.register("builders.paragraphs.basic", function(v) trace_basic = v end) -local trace_lastlinefit = false trackers.register("builders.paragraphs.lastlinefit", function(v) trace_lastlinefit = v end) -local trace_adjusting = false trackers.register("builders.paragraphs.adjusting", function(v) trace_adjusting = v end) -local trace_protruding = false trackers.register("builders.paragraphs.protruding", function(v) trace_protruding = v end) -local trace_expansion = false trackers.register("builders.paragraphs.expansion", function(v) trace_expansion = v end) -local trace_quality = false trackers.register("builders.paragraphs.quality", function(v) trace_quality = v end) - -local report_parbuilders = logs.reporter("nodes","parbuilders") -local report_hpackers = logs.reporter("nodes","hpackers") - -local calculate_badness = tex.badness -local texnest = tex.nest -local texlists = tex.lists - --- (t == 0 and 0) or (s <= 0 and 10000) or calculate_badness(t,s) - --- local function calculate_badness(t,s) --- if t == 0 then --- return 0 --- elseif s <= 0 then --- return 10000 -- infinite_badness --- else --- local r --- if t <= 7230584 then --- r = (t * 297) / s --- elseif s >= 1663497 then --- r = t / (s / 297) --- else --- r = t --- end --- if r > 1290 then --- return 10000 -- infinite_badness --- else --- return (r * r * r + 0x20000) / 0x40000 --- end --- end --- end - -local parbuilders = builders.paragraphs -local constructors = parbuilders.constructors - -local setmetatableindex = table.setmetatableindex - -local fonthashes = fonts.hashes -local fontdata = fonthashes.identifiers -local chardata = fonthashes.characters -local quaddata = fonthashes.quads -local parameters = fonthashes.parameters - -local slide_nodes = node.slide -local new_node = node.new -local copy_node = node.copy -local copy_node_list = node.copy_list -local flush_node = node.free -local flush_node_list = node.flush_list -local hpack_nodes = node.hpack -local xpack_nodes = node.hpack -local replace_node = nodes.replace -local insert_node_after = node.insert_after -local insert_node_before = node.insert_before -local traverse_by_id = node.traverse_id - -local setnodecolor = nodes.tracers.colors.set - -local nodepool = nodes.pool - -local nodecodes = nodes.nodecodes -local whatcodes = nodes.whatcodes -local kerncodes = nodes.kerncodes -local glyphcodes = nodes.glyphcodes -local gluecodes = nodes.gluecodes -local margincodes = nodes.margincodes -local disccodes = nodes.disccodes -local mathcodes = nodes.mathcodes -local fillcodes = nodes.fillcodes - -local temp_code = nodecodes.temp -local glyph_code = nodecodes.glyph -local ins_code = nodecodes.ins -local mark_code = nodecodes.mark -local adjust_code = nodecodes.adjust -local penalty_code = nodecodes.penalty -local whatsit_code = nodecodes.whatsit -local disc_code = nodecodes.disc -local math_code = nodecodes.math -local kern_code = nodecodes.kern -local glue_code = nodecodes.glue -local hlist_code = nodecodes.hlist -local vlist_code = nodecodes.vlist -local unset_code = nodecodes.unset -local marginkern_code = nodecodes.marginkern - -local leaders_code = gluecodes.leaders - -local localpar_code = whatcodes.localpar -local dir_code = whatcodes.dir -local pdfrefximage_code = whatcodes.pdfrefximage -local pdfrefxform_code = whatcodes.pdfrefxform - -local kerning_code = kerncodes.kerning -- font kern -local userkern_code = kerncodes.userkern - -local ligature_code = glyphcodes.ligature - -local stretch_orders = nodes.fillcodes - -local leftmargin_code = margincodes.left -local rightmargin_code = margincodes.right - -local automatic_disc_code = disccodes.automatic -local regular_disc_code = disccodes.regular -local first_disc_code = disccodes.first -local second_disc_code = disccodes.second - -local endmath_code = mathcodes.endmath - -local nosubtype_code = 0 - -local unhyphenated_code = nodecodes.unhyphenated or 1 -local hyphenated_code = nodecodes.hyphenated or 2 -local delta_code = nodecodes.delta or 3 -local passive_code = nodecodes.passive or 4 - -local maxdimen = number.maxdimen - -local max_halfword = 0x7FFFFFFF -local infinite_penalty = 10000 -local eject_penalty = -10000 -local infinite_badness = 10000 -local awful_badness = 0x3FFFFFFF - -local fit_very_loose_class = 0 -- fitness for lines stretching more than their stretchability -local fit_loose_class = 1 -- fitness for lines stretching 0.5 to 1.0 of their stretchability -local fit_decent_class = 2 -- fitness for all other lines -local fit_tight_class = 3 -- fitness for lines shrinking 0.5 to 1.0 of their shrinkability - -local new_penalty = nodepool.penalty -local new_dir = nodepool.textdir -local new_leftmarginkern = nodepool.leftmarginkern -local new_rightmarginkern = nodepool.rightmarginkern -local new_leftskip = nodepool.leftskip -local new_rightskip = nodepool.rightskip -local new_lineskip = nodepool.lineskip -local new_baselineskip = nodepool.baselineskip -local new_temp = nodepool.temp -local new_rule = nodepool.rule - -local is_rotated = nodes.is_rotated -local is_parallel = nodes.textdir_is_parallel -local is_opposite = nodes.textdir_is_opposite -local textdir_is_equal = nodes.textdir_is_equal -local pardir_is_equal = nodes.pardir_is_equal -local glyphdir_is_equal = nodes.glyphdir_is_equal - -local dir_pops = nodes.dir_is_pop -local dir_negations = nodes.dir_negation -local is_skipable = node.protrusion_skippable - --- helpers -- - --- It makes more sense to move the somewhat messy dir state tracking --- out of the main functions. First we create a stack allocator. - -local function new_dir_stack(dir) -- also use elsewhere - return { n = 0, dir } -end - --- The next function checks a dir node and returns the new dir state. By --- using s static table we are quite efficient. This function is used --- in the parbuilder. - -local function checked_line_dir(stack,current) - if not dir_pops[current] then - local n = stack.n + 1 - stack.n = n - stack[n] = current - return current.dir - elseif n > 0 then - local n = stack.n - local dirnode = stack[n] - dirstack.n = n - 1 - return dirnode.dir - else - report_parbuilders("warning: missing pop node (%a)",1) -- in line ... - end -end - --- The next function checks a dir nodes in a list and appends the negations --- that are currently needed (some day LuaTeX will be more tolerant). We use --- the negations for the next line. - -local function inject_dirs_at_end_of_line(stack,current,start,stop) - local e = start - local n = stack.n - local h = nil - while start and start ~= stop do - if start.id == whatsit_code and start.subtype == dir_code then - if not dir_pops[start.dir] then - n = n + 1 - stack[n] = start - elseif n > 0 then - n = n - 1 - else - report_parbuilders("warning: missing pop node (%a)",2) -- in line ... - end - end - start = start.next - end - for i=n,1,-1 do - h, current = insert_node_after(current,current,new_dir(dir_negations[stack[i].dir])) - end - stack.n = n - return current -end - -local function inject_dirs_at_begin_of_line(stack,current) - local h = nil - for i=stack.n,1,-1 do - h, current = insert_node_after(current,current,new_dir(stack[i])) - end - stack.n = 0 - return current -end - --- diagnostics -- - -local dummy = function() end - -local diagnostics = { - start = dummy, - stop = dummy, - current_pass = dummy, - break_node = dummy, - feasible_break = dummy, -} - --- statistics -- - -local nofpars, noflines, nofprotrudedlines, nofadjustedlines = 0, 0, 0, 0 - -local function register_statistics(par) - local statistics = par.statistics - nofpars = nofpars + 1 - noflines = noflines + statistics.noflines - nofprotrudedlines = nofprotrudedlines + statistics.nofprotrudedlines - nofadjustedlines = nofadjustedlines + statistics.nofadjustedlines -end - --- resolvers -- - -local whatsiters = { - get_width = { }, - get_dimensions = { }, -} - -local get_whatsit_width = whatsiters.get_width -local get_whatsit_dimensions = whatsiters.get_dimensions - -local function get_width (n) return n.width end -local function get_dimensions(n) return n.width, n.height, n.depth end - -get_whatsit_width[pdfrefximage_code] = get_width -get_whatsit_width[pdfrefxform_code ] = get_width - -get_whatsit_dimensions[pdfrefximage_code] = get_dimensions -get_whatsit_dimensions[pdfrefxform_code ] = get_dimensions - --- expansion etc -- - -local function calculate_fraction(x,n,d,max_answer) - local the_answer = x * n/d + 1/2 -- round ? - if the_answer > max_answer then - return max_answer - elseif the_answer < -max_answer then - return -max_answer - else - return the_answer - end -end - -local function check_shrinkage(par,n) - -- called often, so maybe move inline - if n.shrink_order ~= 0 and n.shrink ~= 0 then - if par.no_shrink_error_yet then - par.no_shrink_error_yet = false - report_parbuilders("infinite glue shrinkage found in a paragraph and removed") - end - n = copy_node(n) - n.shrink_order = 0 - end - return n -end - --- It doesn't really speed up much but the additional memory usage is --- rather small so it doesn't hurt too much. - -local expansions = { } -local nothing = { stretch = 0, shrink = 0 } - -setmetatableindex(expansions,function(t,font) - local expansion = parameters[font].expansion -- can be an extra hash - if expansion and expansion.auto then - local factors = { } - local c = chardata[font] - setmetatableindex(factors,function(t,char) - local fc = c[char] - local ef = fc.expansion_factor - if ef and ef > 0 then - local stretch = expansion.stretch - local shrink = expansion.shrink - if stretch ~= 0 or shrink ~= 0 then - local factor = ef / 1000 - local ef_quad = factor * quaddata[font] / 1000 - local v = { - glyphstretch = stretch * ef_quad, - glyphshrink = shrink * ef_quad, - factor = factor, - stretch = stretch, - shrink = shrink , - } - t[char] = v - return v - end - end - t[char] = nothing - return nothing - end) - t[font] = factors - return factors - else - t[font] = false - return false - end -end) - --- local function char_stretch_shrink(p) --- local data = expansions[p.font][p.char] --- if data then --- return data.glyphstretch, data.glyphshrink --- else --- return 0, 0 --- end --- end --- --- local cal_margin_kern_var = char_stretch_shrink - --- local function kern_stretch_shrink(p,d) --- local l = p.prev --- if l and l.id == glyph_code then -- how about disc nodes? --- local r = p.next --- if r and r.id == glyph_code then --- local lf, rf = l.font, r.font --- if lf == rf then --- local data = expansions[lf][l.char] --- if data then --- local stretch = data.stretch --- local shrink = data.shrink --- if stretch ~= 0 then --- -- stretch = data.factor * (d * stretch - d) --- stretch = data.factor * d * (stretch - 1) --- end --- if shrink ~= 0 then --- -- shrink = data.factor * (d * shrink - d) --- shrink = data.factor * d * (shrink - 1) --- end --- return stretch, shrink --- end --- end --- end --- end --- return 0, 0 --- end - -local function kern_stretch_shrink(p,d) - local left = p.prev - if left and left.id == glyph_code then -- how about disc nodes? - local data = expansions[left.font][left.char] - if data then - local stretch = data.stretch - local shrink = data.shrink - if stretch ~= 0 then - -- stretch = data.factor * (d * stretch - d) - stretch = data.factor * d * (stretch - 1) - end - if shrink ~= 0 then - -- shrink = data.factor * (d * shrink - d) - shrink = data.factor * d * (shrink - 1) - end - return stretch, shrink - end - end - return 0, 0 -end - -local function kern_stretch_shrink(p,d) - return 0, 0 -end - --- state: - -local function check_expand_pars(checked_expansion,f) - local expansion = parameters[f].expansion - if not expansion then - checked_expansion[f] = false - return false - end - local step = expansion.step or 0 - local stretch = expansion.stretch or 0 - local shrink = expansion.shrink or 0 - if step == 0 or (stretch == 0 and schrink == 0) then - checked_expansion[f] = false - return false - end - local par = checked_expansion.par - if par.cur_font_step < 0 then - par.cur_font_step = step - elseif par.cur_font_step ~= step then - report_parbuilders("using fonts with different step of expansion in one paragraph is not allowed") - checked_expansion[f] = false - return false - end - if stretch == 0 then - -- okay - elseif par.max_stretch_ratio < 0 then - par.max_stretch_ratio = stretch -- expansion_factor - elseif par.max_stretch_ratio ~= stretch then - report_parbuilders("using fonts with different stretch limit of expansion in one paragraph is not allowed") - checked_expansion[f] = false - return false - end - if shrink == 0 then - -- okay - elseif par.max_shrink_ratio < 0 then - par.max_shrink_ratio = shrink -- - expansion_factor - elseif par.max_shrink_ratio ~= shrink then - report_parbuilders("using fonts with different shrink limit of expansion in one paragraph is not allowed") - checked_expansion[f] = false - return false - end - if trace_adjusting then - report_parbuilders("expanding font %a using step %a, shrink %a and stretch %a",f,step,stretch,shrink) - end - local e = expansions[f] - checked_expansion[f] = e - return e -end - -local function check_expand_lines(checked_expansion,f) - local expansion = parameters[f].expansion - if not expansion then - checked_expansion[f] = false - return false - end - local step = expansion.step or 0 - local stretch = expansion.stretch or 0 - local shrink = expansion.shrink or 0 - if step == 0 or (stretch == 0 and schrink == 0) then - checked_expansion[f] = false - return false - end - if trace_adjusting then - report_parbuilders("expanding font %a using step %a, shrink %a and stretch %a",f,step,stretch,shrink) - end - local e = expansions[f] - checked_expansion[f] = e - return e -end - --- protrusion - -local function find(head) -- do we really want to recurse into an hlist? - while head do - local id = head.id - if id == glyph_code then - return head - elseif id == hlist_code then - local found = find(head.list) - if found then - return found - else - head = head.next - end - elseif is_skipable(head) then - head = head.next - else - return head - end - end - return nil -end - -local function find_protchar_left(l) -- weird function - local ln = l.next - if ln and ln.id == hlist_code and not ln.list and ln.width == 0 and ln.height == 0 and ln.depth == 0 then - l = l.next - else -- if d then -- was always true - local id = l.id - while ln and not (id == glyph_code or id < math_code) do -- is there always a glyph? - l = ln - ln = l.next - id = ln.id - end - end - -- if l.id == glyph_code then - -- return l - -- end - return find(l) or l -end - -local function find(head,tail) - local tail = tail or slide_nodes(head) - while tail do - local id = tail.id - if id == glyph_code then - return tail - elseif id == hlist_code then - local found = find(tail.list) - if found then - return found - else - tail = tail.prev - end - elseif is_skipable(tail) then - tail = tail.prev - else - return tail - end - end - return nil -end - -local function find_protchar_right(l,r) - return r and find(l,r) or r -end - -local function left_pw(p) - local font = p.font - local prot = chardata[font][p.char].left_protruding - if not prot or prot == 0 then - return 0 - end - return prot * quaddata[font] / 1000, p -end - -local function right_pw(p) - local font = p.font - local prot = chardata[font][p.char].right_protruding - if not prot or prot == 0 then - return 0 - end - return prot * quaddata[font] / 1000, p -end - --- par parameters - -local function reset_meta(par) - local active = { - id = hyphenated_code, - line_number = max_halfword, - } - active.next = par.active -- head of metalist - par.active = active - par.passive = nil -end - -local function add_to_width(line_break_dir,checked_expansion,s) -- split into two loops (normal and expansion) - local size = 0 - local adjust_stretch = 0 - local adjust_shrink = 0 - while s do - local id = s.id - if id == glyph_code then - if is_rotated[line_break_dir] then -- can be shared - size = size + s.height + s.depth - else - size = size + s.width - end - if checked_expansion then - local data = checked_expansion[s.font] - if data then - data = data[s.char] - if data then - adjust_stretch = adjust_stretch + data.glyphstretch - adjust_shrink = adjust_shrink + data.glyphshrink - end - end - end - elseif id == hlist_code or id == vlist_code then - if is_parallel[s.dir][line_break_dir] then - size = size + s.width - else - size = size + s.depth + s.height - end - elseif id == kern_code then - if checked_expansion and s.subtype == kerning_code then - local d = s.kern - if d ~= 0 then - local stretch, shrink = kern_stretch_shrink(s,d) - adjust_stretch = adjust_stretch + stretch - adjust_shrink = adjust_shrink + shrink - end - end - size = size + s.kern - elseif id == rule_code then - size = size + s.width - else - report_parbuilders("unsupported node at location %a",6) - end - s = s.next - end - return size, adjust_stretch, adjust_shrink -end - -local function compute_break_width(par,break_type,p) -- split in two - local break_width = par.break_width - if break_type > unhyphenated_code then - local disc_width = par.disc_width - local checked_expansion = par.checked_expansion - local line_break_dir = par.line_break_dir - local break_size = break_width.size + disc_width.size - local break_adjust_stretch = break_width.adjust_stretch + disc_width.adjust_stretch - local break_adjust_shrink = break_width.adjust_shrink + disc_width.adjust_shrink - local replace = p.replace - if replace then - local size, adjust_stretch, adjust_shrink = add_to_width(line_break_dir,checked_expansion,replace) - break_size = break_size - size - break_adjust_stretch = break_adjust_stretch - adjust_stretch - break_adjust_shrink = break_adjust_shrink - adjust_shrink - end - local post = p.post - if post then - local size, adjust_stretch, adjust_shrink = add_to_width(line_break_dir,checked_expansion,post) - break_size = break_size + size - break_adjust_stretch = break_adjust_stretch + adjust_stretch - break_adjust_shrink = break_adjust_shrink + adjust_shrink - end - break_width.size = break_size - break_width.adjust_stretch = break_adjust_stretch - break_width.adjust_shrink = break_adjust_shrink - if not post then - p = p.next - else - return - end - end - while p do -- skip spacing etc - local id = p.id - if id == glyph_code then - return -- happens often - elseif id == glue_code then - local spec = p.spec - local order = stretch_orders[spec.stretch_order] - break_width.size = break_width.size - spec.width - break_width[order] = break_width[order] - spec.stretch - break_width.shrink = break_width.shrink - spec.shrink - elseif id == penalty_code then - -- do nothing - elseif id == kern_code then - if p.subtype == userkern_code then - break_width.size = break_width.size - p.kern - else - return - end - elseif id == math_code then - break_width.size = break_width.size - p.surround - else - return - end - p = p.next - end -end - -local function append_to_vlist(par, b) - local prev_depth = par.prev_depth - if prev_depth > par.ignored_dimen then - if b.id == hlist_code then - local d = par.baseline_skip.width - prev_depth - b.height -- deficiency of space between baselines - local s = d < par.line_skip_limit and new_lineskip(tex.lineskip) or new_baselineskip(d) - -- local s = d < par.line_skip_limit - -- if s then - -- s = new_lineskip() - -- s.spec = tex.lineskip - -- else - -- s = new_baselineskip(d) - -- end - local head_field = par.head_field - if head_field then - local n = slide_nodes(head_field) - n.next, s.prev = s, n - else - par.head_field = s - end - end - end - local head_field = par.head_field - if head_field then - local n = slide_nodes(head_field) - n.next, b.prev = b, n - else - par.head_field = b - end - if b.id == hlist_code then - local pd = b.depth - par.prev_depth = pd - texnest[texnest.ptr].prevdepth = pd - end -end - -local function append_list(par, b) - local head_field = par.head_field - if head_field then - local n = slide_nodes(head_field) - n.next, b.prev = b, n - else - par.head_field = b - end -end - --- We can actually make par local to this module as we never break inside a break call and that way the --- array is reused. At some point the information will be part of the paragraph spec as passed. - -local function initialize_line_break(head,display) - - local hang_indent = tex.hangindent or 0 - local hsize = tex.hsize or 0 - local hang_after = tex.hangafter or 0 - local par_shape_ptr = tex.parshape - local left_skip = tex.leftskip -- nodes - local right_skip = tex.rightskip -- nodes - local pretolerance = tex.pretolerance - local tolerance = tex.tolerance - local adjust_spacing = tex.pdfadjustspacing - local protrude_chars = tex.pdfprotrudechars - local last_line_fit = tex.lastlinefit - - local newhead = new_temp() - newhead.next = head - - local adjust_spacing_status = adjust_spacing > 1 and -1 or 0 - - -- metatables - - local par = { - head = newhead, - head_field = nil, - display = display, - font_in_short_display = 0, - no_shrink_error_yet = true, -- have we complained about infinite shrinkage? - second_pass = false, -- is this our second attempt to break this paragraph? - final_pass = false, -- is this our final attempt to break this paragraph? - threshold = 0, -- maximum badness on feasible lines - - passive = nil, -- most recent node on passive list - printed_node = head, -- most recent node that has been printed - pass_number = 0, -- the number of passive nodes allocated on this pass - auto_breaking = 0, -- make auto_breaking accessible out of line_break - - active_width = { size = 0, stretch = 0, fi = 0, fil = 0, fill = 0, filll = 0, shrink = 0, adjust_stretch = 0, adjust_shrink = 0 }, - break_width = { size = 0, stretch = 0, fi = 0, fil = 0, fill = 0, filll = 0, shrink = 0, adjust_stretch = 0, adjust_shrink = 0 }, - disc_width = { size = 0, adjust_stretch = 0, adjust_shrink = 0 }, - fill_width = { stretch = 0, fi = 0, fil = 0, fill = 0, filll = 0, shrink = 0 }, - background = { size = 0, stretch = 0, fi = 0, fil = 0, fill = 0, filll = 0, shrink = 0 }, - - hang_indent = hang_indent, - hsize = hsize, - hang_after = hang_after, - par_shape_ptr = par_shape_ptr, - left_skip = left_skip, - right_skip = right_skip, - pretolerance = pretolerance, - tolerance = tolerance, - - protrude_chars = protrude_chars, - adjust_spacing = adjust_spacing, - max_stretch_ratio = adjust_spacing_status, - max_shrink_ratio = adjust_spacing_status, - cur_font_step = adjust_spacing_status, - checked_expansion = false, - tracing_paragraphs = tex.tracingparagraphs > 0, - - emergency_stretch = tex.emergencystretch or 0, - looseness = tex.looseness or 0, - line_penalty = tex.linepenalty or 0, - hyphen_penalty = tex.hyphenpenalty or 0, - broken_penalty = tex.brokenpenalty or 0, - inter_line_penalty = tex.interlinepenalty or 0, - club_penalty = tex.clubpenalty or 0, - widow_penalty = tex.widowpenalty or 0, - display_widow_penalty = tex.displaywidowpenalty or 0, - ex_hyphen_penalty = tex.exhyphenpenalty or 0, - - adj_demerits = tex.adjdemerits or 0, - double_hyphen_demerits = tex.doublehyphendemerits or 0, - final_hyphen_demerits = tex.finalhyphendemerits or 0, - - first_line = 0, -- tex.nest.modeline, -- 0, -- cur_list.pg_field - - each_line_height = tex.pdfeachlineheight or 0, -- this will go away - each_line_depth = tex.pdfeachlinedepth or 0, -- this will go away - first_line_height = tex.pdffirstlineheight or 0, -- this will go away - last_line_depth = tex.pdflastlinedepth or 0, -- this will go away - ignored_dimen = tex.pdfignoreddimen or 0, -- this will go away - - baseline_skip = tex.baselineskip or 0, - line_skip_limit = tex.lineskiplimit or 0, - - prev_depth = texnest[texnest.ptr].prevdepth, - - final_par_glue = slide_nodes(head), -- todo: we know tail already, slow - - par_break_dir = tex.pardir, - line_break_dir = tex.pardir, - - internal_pen_inter = 0, -- running localinterlinepenalty - internal_pen_broken = 0, -- running localbrokenpenalty - internal_left_box = nil, -- running localleftbox - internal_left_box_width = 0, -- running localleftbox width - init_internal_left_box = nil, -- running localleftbox - init_internal_left_box_width = 0, -- running localleftbox width - internal_right_box = nil, -- running localrightbox - internal_right_box_width = 0, -- running localrightbox width - - best_place = { }, -- how to achieve minimal_demerits - best_pl_line = { }, -- corresponding line number - easy_line = 0, -- line numbers easy_line are equivalent in break nodes - last_special_line = 0, -- line numbers last_special_line all have the same width - first_width = 0, -- the width of all lines last_special_line, if no parshape has been specified - second_width = 0, -- the width of all lines last_special_line - first_indent = 0, -- left margin to go with first_width - second_indent = 0, -- left margin to go with second_width - - best_bet = nil, -- use this passive node and its predecessors - fewest_demerits = 0, -- the demerits associated with best_bet - best_line = 0, -- line number following the last line of the new paragraph - line_diff = 0, -- the difference between the current line number and the optimum best_line - - -- not yet used - - best_pl_short = { }, -- shortfall corresponding to minimal_demerits - best_pl_glue = { }, -- corresponding glue stretch or shrink - do_last_line_fit = false, - last_line_fit = last_line_fit, - - minimum_demerits = awful_badness, - - minimal_demerits = { - - [fit_very_loose_class] = awful_badness, - [fit_loose_class] = awful_badness, - [fit_decent_class] = awful_badness, - [fit_tight_class] = awful_badness, - - }, - - prev_char_p = nil, - - font_steps = { }, -- mine - - statistics = { - - noflines = 0, - nofprotrudedlines = 0, - nofadjustedlines = 0, - - } - - } - - if adjust_spacing > 1 then - local checked_expansion = { par = par } - setmetatableindex(checked_expansion,check_expand_pars) - par.checked_expansion = checked_expansion - end - - -- we need par for the error message - - local background = par.background - - local l = check_shrinkage(par,left_skip) - local r = check_shrinkage(par,right_skip) - local l_order = stretch_orders[l.stretch_order] - local r_order = stretch_orders[r.stretch_order] - - background.size = l.width + r.width - background.shrink = l.shrink + r.shrink - background[l_order] = l.stretch - background[r_order] = r.stretch + background[r_order] - - -- this will move up so that we can assign the whole par table - - if not par_shape_ptr then - if hang_indent == 0 then - par.second_width = hsize - par.second_indent = 0 - else - local abs_hang_after = hang_after >0 and hang_after or -hang_after - local abs_hang_indent = hang_indent>0 and hang_indent or -hang_indent - par.last_special_line = abs_hang_after - if hang_after < 0 then - par.first_width = hsize - abs_hang_indent - if hang_indent >= 0 then - par.first_indent = hang_indent - else - par.first_indent = 0 - end - par.second_width = hsize - par.second_indent = 0 - else - par.first_width = hsize - par.first_indent = 0 - par.second_width = hsize - abs_hang_indent - if hang_indent >= 0 then - par.second_indent = hang_indent - else - par.second_indent = 0 - end - end - end - else - local last_special_line = #par_shape_ptr - par.last_special_line = last_special_line - local ptr = par_shape_ptr[last_special_line] - par.second_width = ptr[2] - par.second_indent = ptr[1] - end - - if par.looseness == 0 then - par.easy_line = par.last_special_line - else - par.easy_line = max_halfword - end - - if pretolerance >= 0 then - par.threshold = pretolerance - par.second_pass = false - par.final_pass = false - else - par.threshold = tolerance - par.second_pass = true - par.final_pass = par.emergency_stretch <= 0 - if trace_basic then - if par.final_pass then - report_parbuilders("enabling second and final pass") - else - report_parbuilders("enabling second pass") - end - end - end - - if last_line_fit > 0 then - local spec = par.final_par_glue.spec - local stretch = spec.stretch - local stretch_order = spec.stretch_order - if stretch > 0 and stretch_order > 0 and background.fi == 0 and background.fil == 0 and background.fill == 0 and background.filll == 0 then - par.do_last_line_fit = true - local si = stretch_orders[stretch_order] - if trace_lastlinefit or trace_basic then - report_parbuilders("enabling last line fit, stretch order %a set to %a, linefit is %a",si,stretch,last_line_fit) - end - par.fill_width[si] = stretch - end - end - - return par -end - -local function post_line_break(par) - - local prevgraf = texnest[texnest.ptr].prevgraf - local cur_line = prevgraf + 1 -- the current line number being justified - local cur_p = nil - - local adjust_spacing = par.adjust_spacing - local protrude_chars = par.protrude_chars - local statistics = par.statistics - - local p, s, k, w -- check when local - - local q = par.best_bet.break_node - repeat -- goto first breakpoint - local r = q - q = q.prev_break - r.prev_break = cur_p - cur_p = r - until not q - - local stack = new_dir_stack() - - repeat - - inject_dirs_at_begin_of_line(stack,par.head) - - local q = nil - local r = cur_p.cur_break - - local disc_break = false - local post_disc_break = false - local glue_break = false - - if not r then - r = slide_nodes(par.head) - if r == par.final_par_glue then - q = r -- q refers to the last node of the line (and paragraph) - r = r.prev -- r refers to the node after which the dir nodes should be closed - end - else - local id = r.id - if id == glue_code then - -- r is normal skip - r = replace_node(r,new_rightskip(par.right_skip)) - glue_break = true - q = r -- q refers to the last node of the line - r = r.prev -- r refers to the node after which the dir nodes should be closed - elseif id == disc_code then - -- todo: use insert_before/after - local prev_r = r.prev - local next_r = r.next - local subtype = r.subtype - local pre = r.pre - local post = r.post - local replace = r.replace - if subtype == second_disc_code then - if not (prev_r.id == disc_code and prev_r.subtype == first_disc_code) then - report_parbuilders('unsupported disc at location %a',3) - end - if pre then - flush_node_list(pre) - r.pre = nil - pre = nil -- signal - end - if replace then - local n = slide_nodes(replace) - prev_r.next = replace - replace.prev = prev_r - n.next = r - r.prev = n - r.replace = nil - replace = nil -- signal - end - local pre = prev_r.pre - local post = prev_r.post - local replace = prev_r.replace - if pre then - flush_node_list(pre) - prev_r.pre = nil - end - if replace then - flush_node_list(replace) - prev_r.replace = nil - end - if post then - flush_node_list(post) - prev_r.post = nil - end - elseif subtype == first_disc_code then - if not (v.id == disc_code and v.subtype == second_disc_code) then - report_parbuilders('unsupported disc at location %a',4) - end - next_r.subtype = regular_disc_code - next_r.replace = post - r.post = nil - end - if replace then - r.replace = nil -- free - flush_node_list(replace) - end - if pre then - local n = slide_nodes(pre) - prev_r.next = pre - pre.prev = prev_r - n.next = r - r.prev = n - r.pre = nil - end - if post then - local n = slide_nodes(post) - r.next = post - post.prev = r - n.next = next_r - next_r.prev = n - r.post = nil - post_disc_break = true - end - disc_break = true - elseif id == kern_code then - r.kern = 0 - elseif r.id == math_code then - r.surround = 0 - end - end - r = inject_dirs_at_end_of_line(stack,r,par.head.next,cur_p.cur_break) - local crb = cur_p.passive_right_box - if crb then - local s = copy_node(crb) - local e = r.next - r.next = s - s.prev = r - s.next = e - if e then - e.prev = s - end - r = s - end - if not q then - q = r - end - if q and q ~= par.head and protrude_chars > 0 then - local id = q.id - local c = (disc_break and (id == glyph_code or id ~= disc_code) and q) or q.prev - local p = find_protchar_right(par.head.next,c) - if p and p.id == glyph_code then - local w, last_rightmost_char = right_pw(p) - if last_rightmost_char and w ~= 0 then - -- so we inherit attributes, q is new pseudo head - q, c = insert_node_after(q,c,new_rightmarginkern(copy_node(last_rightmost_char),-w)) - end - end - end - if not glue_break then - local h - h, q = insert_node_after(q,q,new_rightskip(par.right_skip)) -- q moves on as pseudo head - end - r = q.next - q.next = nil - local phead = par.head - q = phead.next - phead.next = r - if r then - r.prev = phead - end - local clb = cur_p.passive_left_box - if clb then -- here we miss some prev links - local s = copy_node(cb) - s = q.next - r.next = q - q = r - if s and cur_line == (par.first_line + 1) and s.id == hlist_code and not s.list then - q = q.next - r.next = s.next - s.next = r - end - end - if protrude_chars > 0 then - local p = find_protchar_left(q) - if p and p.id == glyph_code then - local w, last_leftmost_char = left_pw(p) - if last_leftmost_char and w ~= 0 then - -- so we inherit attributes, q is pseudo head and moves back - q = insert_node_before(q,q,new_leftmarginkern(copy_node(last_leftmost_char),-w)) - end - end - end - local ls = par.left_skip - if ls and (ls.width ~= 0 or ls.stretch ~= 0 or ls.shrink ~= 0) then - q = insert_node_before(q,q,new_leftskip(ls)) - end - local curwidth, cur_indent - if cur_line > par.last_special_line then - cur_indent = par.second_indent - cur_width = par.second_width - else - local psp = par.par_shape_ptr - if psp then - cur_indent = psp[cur_line][1] - cur_width = psp[cur_line][2] - else - cur_indent = par.first_indent - cur_width = par.first_width - end - end - statistics.noflines = statistics.noflines + 1 - if adjust_spacing > 0 then - statistics.nofadjustedlines = statistics.nofadjustedlines + 1 - just_box = xpack_nodes(q,cur_width,"cal_expand_ratio",par.par_break_dir) -- ,cur_p.analysis) - else - just_box = xpack_nodes(q,cur_width,"exactly",par.par_break_dir) -- ,cur_p.analysis) - end - if protrude_chars > 0 then - statistics.nofprotrudedlines = statistics.nofprotrudedlines + 1 - end - -- wrong: - local adjust_head = texlists.adjust_head - local pre_adjust_head = texlists.pre_adjust_head - -- - just_box.shift = cur_indent - if par.each_line_height ~= par.ignored_dimen then - just_box.height = par.each_line_height - end - if par.each_line_depth ~= par.ignored_dimen then - just_box.depth = par.each_line_depth - end - if par.first_line_height ~= par.ignored_dimen and (cur_line == par.first_line + 1) then - just_box.height = par.first_line_height - end - if par.last_line_depth ~= par.ignored_dimen and cur_line + 1 == par.best_line then - just_box.depth = par.last_line_depth - end - if texlists.pre_adjust_head ~= pre_adjust_head then - append_list(par, texlists.pre_adjust_head) - texlists.pre_adjust_head = pre_adjust_head - end - append_to_vlist(par, just_box) - if texlists.adjust_head ~= adjust_head then - append_list(par, texlists.adjust_head) - texlists.adjust_head = adjust_head - end - local pen - if cur_line + 1 ~= par.best_line then - if cur_p.passive_pen_inter then - pen = cur_p.passive_pen_inter - else - pen = par.inter_line_penalty - end - if cur_line == prevgraf + 1 then - pen = pen + par.club_penalty - end - if cur_line + 2 == par.best_line then - if par.display then - pen = pen + par.display_widow_penalty - else - pen = pen + par.widow_penalty - end - end - if disc_break then - if cur_p.passive_pen_broken ~= 0 then - pen = pen + cur_p.passive_pen_broken - else - pen = pen + par.broken_penalty - end - end - if pen ~= 0 then - append_to_vlist(par,new_penalty(pen)) - end - end - cur_line = cur_line + 1 - cur_p = cur_p.prev_break - if cur_p and not post_disc_break then - local phead = par.head - local r = phead - while true do - q = r.next - if q == cur_p.cur_break or q.id == glyph_code then - break - end - local id = q.id - if not (id == whatsit_code and q.subtype == localpar_code) then - if id < math_code or (id == kern_code and q.subtype ~= userkern_code) then - break - end - end - r = q - end - if r ~= phead then - r.next = nil - flush_node_list(phead.next) - phead.next = q - if q then - q.prev = phead - end - end - end - until not cur_p - if cur_line ~= par.best_line then -- or not par.head.next then - report_parbuilders("line breaking") - end - if par.head then -- added --- flush_node(par.head) -- the localpar_code whatsit - par.head = nil - end - cur_line = cur_line - 1 - if trace_basic then - report_parbuilders("paragraph broken into %a lines",cur_line) - end - texnest[texnest.ptr].prevgraf = cur_line -end - -local function wrap_up(par) - if par.tracing_paragraphs then - diagnostics.stop() - end - if par.do_last_line_fit then - local best_bet = par.best_bet - local active_short = best_bet.active_short - local active_glue = best_bet.active_glue - if active_short == 0 then - if trace_lastlinefit then - report_parbuilders("disabling last line fit, no active_short") - end - par.do_last_line_fit = false - else - local glue = par.final_par_glue - local spec = copy_node(glue.spec) - spec.width = spec.width + active_short - active_glue - spec.stretch = 0 - -- flush_node(glue.spec) -- brrr, when we do this we can get an "invalid id stretch message", maybe dec refcount - glue.spec = spec - if trace_lastlinefit then - report_parbuilders("applying last line fit, short %a, glue %p",active_short,active_glue) - end - end - end - -- we have a bunch of glue and and temp nodes not freed - local head = par.head - if head.id == temp_code then - par.head = head.next - flush_node(head) - end - post_line_break(par) - reset_meta(par) - register_statistics(par) - return par.head_field -end - --- we could do active nodes differently ... table instead of linked list or a list --- with prev nodes - -local function deactivate_node(par,prev_prev_r,prev_r,r,cur_active_width,checked_expansion) -- no need for adjust if disabled - local active = par.active - local active_width = par.active_width - prev_r.next = r.next - -- removes r - -- r = nil - if prev_r == active then - r = active.next - if r.id == delta_code then - local aw = active_width.size + r.size active_width.size = aw cur_active_width.size = aw - local aw = active_width.stretch + r.stretch active_width.stretch = aw cur_active_width.stretch = aw - local aw = active_width.fi + r.fi active_width.fi = aw cur_active_width.fi = aw - local aw = active_width.fil + r.fil active_width.fil = aw cur_active_width.fil = aw - local aw = active_width.fill + r.fill active_width.fill = aw cur_active_width.fill = aw - local aw = active_width.filll + r.filll active_width.filll = aw cur_active_width.filll = aw - local aw = active_width.shrink + r.shrink active_width.shrink = aw cur_active_width.shrink = aw - if checked_expansion then - local aw = active_width.adjust_stretch + r.adjust_stretch active_width.adjust_stretch = aw cur_active_width.adjust_stretch = aw - local aw = active_width.adjust_shrink + r.adjust_shrink active_width.adjust_shrink = aw cur_active_width.adjust_shrink = aw - end - active.next = r.next - -- removes r - -- r = nil - end - elseif prev_r.id == delta_code then - r = prev_r.next - if r == active then - cur_active_width.size = cur_active_width.size - prev_r.size - cur_active_width.stretch = cur_active_width.stretch - prev_r.stretch - cur_active_width.fi = cur_active_width.fi - prev_r.fi - cur_active_width.fil = cur_active_width.fil - prev_r.fil - cur_active_width.fill = cur_active_width.fill - prev_r.fill - cur_active_width.filll = cur_active_width.filll - prev_r.filll - cur_active_width.shrink = cur_active_width.shrink - prev_r.shrink - if checked_expansion then - cur_active_width.adjust_stretch = cur_active_width.adjust_stretch - prev_r.adjust_stretch - cur_active_width.adjust_shrink = cur_active_width.adjust_shrink - prev_r.adjust_shrink - end - prev_prev_r.next = active - -- removes prev_r - -- prev_r = nil - prev_r = prev_prev_r - elseif r.id == delta_code then - local rn = r.size cur_active_width.size = cur_active_width.size + rn prev_r.size = prev_r.size + rn - local rn = r.stretch cur_active_width.stretch = cur_active_width.stretch + rn prev_r.stretch = prev_r.stretch + rn - local rn = r.fi cur_active_width.fi = cur_active_width.fi + rn prev_r.fi = prev_r.fi + rn - local rn = r.fil cur_active_width.fil = cur_active_width.fil + rn prev_r.fil = prev_r.fil + rn - local rn = r.fill cur_active_width.fill = cur_active_width.fill + rn prev_r.fill = prev_r.fill + rn - local rn = r.filll cur_active_width.filll = cur_active_width.filll + rn prev_r.filll = prev_r.fill + rn - local rn = r.shrink cur_active_width.shrink = cur_active_width.shrink + rn prev_r.shrink = prev_r.shrink + rn - if checked_expansion then - local rn = r.adjust_stretch cur_active_width.adjust_stretch = cur_active_width.adjust_stretch + rn prev_r.adjust_stretch = prev_r.adjust_stretch + rn - local rn = r.adjust_shrink cur_active_width.adjust_shrink = cur_active_width.adjust_shrink + rn prev_r.adjust_shrink = prev_r.adjust_shrink + rn - end - prev_r.next = r.next - -- removes r - -- r = nil - end - end - return prev_r, r -end - -local function lastlinecrap(shortfall,active_short,active_glue,cur_active_width,fill_width,last_line_fit) - if active_short == 0 or active_glue <= 0 then - return false, 0, fit_decent_class, 0, 0 - end - if cur_active_width.fi ~= fill_width.fi or cur_active_width.fil ~= fill_width.fil or cur_active_width.fill ~= fill_width.fill or cur_active_width.filll ~= fill_width.filll then - return false, 0, fit_decent_class, 0, 0 - end - local adjustment = active_short > 0 and cur_active_width.stretch or cur_active_width.shrink - if adjustment <= 0 then - return false, 0, fit_decent_class, adjustment, 0 - end - adjustment = calculate_fraction(adjustment,active_short,active_glue,maxdimen) - if last_line_fit < 1000 then - adjustment = calculate_fraction(adjustment,last_line_fit,1000,maxdimen) -- uses previous adjustment - end - local fit_class = fit_decent_class - if adjustment > 0 then - local stretch = cur_active_width.stretch - if adjustment > shortfall then - adjustment = shortfall - end - if adjustment > 7230584 and stretch < 1663497 then - return true, fit_very_loose_class, shortfall, adjustment, infinite_badness - end - -- if adjustment == 0 then -- badness = 0 - -- return true, shortfall, fit_decent_class, 0, 0 - -- elseif stretch <= 0 then -- badness = 10000 - -- return true, shortfall, fit_very_loose_class, adjustment, 10000 - -- end - -- local badness = (adjustment == 0 and 0) or (stretch <= 0 and 10000) or calculate_badness(adjustment,stretch) - local badness = calculate_badness(adjustment,stretch) - if badness > 99 then - return true, shortfall, fit_very_loose_class, adjustment, badness - elseif badness > 12 then - return true, shortfall, fit_loose_class, adjustment, badness - else - return true, shortfall, fit_decent_class, adjustment, badness - end - elseif adjustment < 0 then - local shrink = cur_active_width.shrink - if -adjustment > shrink then - adjustment = -shrink - end - local badness = calculate_badness(-adjustment,shrink) - if badness > 12 then - return true, shortfall, fit_tight_class, adjustment, badness - else - return true, shortfall, fit_decent_class, adjustment, badness - end - else - return false, 0, fit_decent_class, 0, 0 - end -end - -local function try_break(pi, break_type, par, first_p, cur_p, checked_expansion) - - if pi >= infinite_penalty then - return -- this breakpoint is inhibited by infinite penalty - elseif pi <= -infinite_penalty then - pi = eject_penalty -- this breakpoint will be forced - end - - local prev_prev_r = nil -- a step behind prev_r, if type(prev_r)=delta_code - local prev_r = par.active -- stays a step behind r - local r = nil -- runs through the active list - local no_break_yet = true -- have we found a feasible break at cur_p? - local node_r_stays_active = false -- should node r remain in the active list? - local line_width = 0 -- the current line will be justified to this width - local line_number = 0 -- line number of current active node - local old_line_number = 0 -- maximum line number in current equivalence class of lines - - local protrude_chars = par.protrude_chars - local checked_expansion = par.checked_expansion - local break_width = par.break_width - local active_width = par.active_width - local background = par.background - local minimal_demerits = par.minimal_demerits - local best_place = par.best_place - local best_pl_line = par.best_pl_line - local best_pl_short = par.best_pl_short - local best_pl_glue = par.best_pl_glue - local do_last_line_fit = par.do_last_line_fit - local final_pass = par.final_pass - local tracing_paragraphs = par.tracing_paragraphs - -- local par_active = par.active - - local cur_active_width = checked_expansion and { -- distance from current active node - size = active_width.size, - stretch = active_width.stretch, - fi = active_width.fi, - fil = active_width.fil, - fill = active_width.fill, - filll = active_width.filll, - shrink = active_width.shrink, - adjust_stretch = active_width.adjust_stretch, - adjust_shrink = active_width.adjust_shrink, - } or { - size = active_width.size, - stretch = active_width.stretch, - fi = active_width.fi, - fil = active_width.fil, - fill = active_width.fill, - filll = active_width.filll, - shrink = active_width.shrink, - } - - while true do - r = prev_r.next - if r.id == delta_code then - cur_active_width.size = cur_active_width.size + r.size - cur_active_width.stretch = cur_active_width.stretch + r.stretch - cur_active_width.fi = cur_active_width.fi + r.fi - cur_active_width.fil = cur_active_width.fil + r.fil - cur_active_width.fill = cur_active_width.fill + r.fill - cur_active_width.filll = cur_active_width.filll + r.filll - cur_active_width.shrink = cur_active_width.shrink + r.shrink - if checked_expansion then - cur_active_width.adjust_stretch = cur_active_width.adjust_stretch + r.adjust_stretch - cur_active_width.adjust_shrink = cur_active_width.adjust_shrink + r.adjust_shrink - end - prev_prev_r = prev_r - prev_r = r - else - line_number = r.line_number - if line_number > old_line_number then - local minimum_demerits = par.minimum_demerits - if minimum_demerits < awful_badness and (old_line_number ~= par.easy_line or r == par.active) then - if no_break_yet then - no_break_yet = false - break_width.size = background.size - break_width.stretch = background.stretch - break_width.fi = background.fi - break_width.fil = background.fil - break_width.fill = background.fill - break_width.filll = background.filll - break_width.shrink = background.shrink - if checked_expansion then - break_width.adjust_stretch = 0 - break_width.adjust_shrink = 0 - end - if cur_p then - compute_break_width(par,break_type,cur_p) - end - end - if prev_r.id == delta_code then - prev_r.size = prev_r.size - cur_active_width.size + break_width.size - prev_r.stretch = prev_r.stretch - cur_active_width.stretc + break_width.stretch - prev_r.fi = prev_r.fi - cur_active_width.fi + break_width.fi - prev_r.fil = prev_r.fil - cur_active_width.fil + break_width.fil - prev_r.fill = prev_r.fill - cur_active_width.fill + break_width.fill - prev_r.filll = prev_r.filll - cur_active_width.filll + break_width.filll - prev_r.shrink = prev_r.shrink - cur_active_width.shrink + break_width.shrink - if checked_expansion then - prev_r.adjust_stretch = prev_r.adjust_stretch - cur_active_width.adjust_stretch + break_width.adjust_stretch - prev_r.adjust_shrink = prev_r.adjust_shrink - cur_active_width.adjust_shrink + break_width.adjust_shrink - end - elseif prev_r == par.active then - active_width.size = break_width.size - active_width.stretch = break_width.stretch - active_width.fi = break_width.fi - active_width.fil = break_width.fil - active_width.fill = break_width.fill - active_width.filll = break_width.filll - active_width.shrink = break_width.shrink - if checked_expansion then - active_width.adjust_stretch = break_width.adjust_stretch - active_width.adjust_shrink = break_width.adjust_shrink - end - else - local q = checked_expansion and { - id = delta_code, - subtype = nosubtype_code, - next = r, - size = break_width.size - cur_active_width.size, - stretch = break_width.stretch - cur_active_width.stretch, - fi = break_width.fi - cur_active_width.fi, - fil = break_width.fil - cur_active_width.fil, - fill = break_width.fill - cur_active_width.fill, - filll = break_width.filll - cur_active_width.filll, - shrink = break_width.shrink - cur_active_width.shrink, - adjust_stretch = break_width.adjust_stretch - cur_active_width.adjust_stretch, - adjust_shrink = break_width.adjust_shrink - cur_active_width.adjust_shrink, - } or { - id = delta_code, - subtype = nosubtype_code, - next = r, - size = break_width.size - cur_active_width.size, - stretch = break_width.stretch - cur_active_width.stretch, - fi = break_width.fi - cur_active_width.fi, - fil = break_width.fil - cur_active_width.fil, - fill = break_width.fill - cur_active_width.fill, - filll = break_width.filll - cur_active_width.filll, - shrink = break_width.shrink - cur_active_width.shrink, - } - prev_r.next = q - prev_prev_r = prev_r - prev_r = q - end - local adj_demerits = par.adj_demerits - local abs_adj_demerits = adj_demerits > 0 and adj_demerits or -adj_demerits - if abs_adj_demerits >= awful_badness - minimum_demerits then - minimum_demerits = awful_badness - 1 - else - minimum_demerits = minimum_demerits + abs_adj_demerits - end - for fit_class = fit_very_loose_class, fit_tight_class do - if minimal_demerits[fit_class] <= minimum_demerits then - -- insert a new active node from best_place[fit_class] to cur_p - par.pass_number = par.pass_number + 1 - local prev_break = best_place[fit_class] - local passive = { - id = passive_code, - subtype = nosubtype_code, - next = par.passive, - cur_break = cur_p, - serial = par.pass_number, - prev_break = prev_break, - passive_pen_inter = par.internal_pen_inter, - passive_pen_broken = par.internal_pen_broken, - passive_last_left_box = par.internal_left_box, - passive_last_left_box_width = par.internal_left_box_width, - passive_left_box = prev_break and prev_break.passive_last_left_box or par.init_internal_left_box, - passive_left_box_width = prev_break and prev_break.passive_last_left_box_width or par.init_internal_left_box_width, - passive_right_box = par.internal_right_box, - passive_right_box_width = par.internal_right_box_width, --- analysis = table.fastcopy(cur_active_width), - } - par.passive = passive - local q = { - id = break_type, - subtype = fit_class, - break_node = passive, - line_number = best_pl_line[fit_class] + 1, - total_demerits = minimal_demerits[fit_class], -- or 0, - next = r, - } - if do_last_line_fit then - local active_short = best_pl_short[fit_class] - local active_glue = best_pl_glue[fit_class] - q.active_short = active_short - q.active_glue = active_glue - if trace_lastlinefit then - report_parbuilders("setting short to %i and glue to %p using class %a",active_short,active_glue,fit_class) - end - end - -- q.next = r -- already done - prev_r.next = q - prev_r = q - if tracing_paragraphs then - diagnostics.break_node(par,q,fit_class,break_type,cur_p) - end - end - minimal_demerits[fit_class] = awful_badness - end - par.minimum_demerits = awful_badness - if r ~= par.active then - local q = checked_expansion and { - id = delta_code, - subtype = nosubtype_code, - next = r, - size = cur_active_width.size - break_width.size, - stretch = cur_active_width.stretch - break_width.stretch, - fi = cur_active_width.fi - break_width.fi, - fil = cur_active_width.fil - break_width.fil, - fill = cur_active_width.fill - break_width.fill, - filll = cur_active_width.filll - break_width.filll, - shrink = cur_active_width.shrink - break_width.shrink, - adjust_stretch = cur_active_width.adjust_stretch - break_width.adjust_stretch, - adjust_shrink = cur_active_width.adjust_shrink - break_width.adjust_shrink, - } or { - id = delta_code, - subtype = nosubtype_code, - next = r, - size = cur_active_width.size - break_width.size, - stretch = cur_active_width.stretch - break_width.stretch, - fi = cur_active_width.fi - break_width.fi, - fil = cur_active_width.fil - break_width.fil, - fill = cur_active_width.fill - break_width.fill, - filll = cur_active_width.filll - break_width.filll, - shrink = cur_active_width.shrink - break_width.shrink, - } - -- q.next = r -- already done - prev_r.next = q - prev_prev_r = prev_r - prev_r = q - end - end - if r == par.active then - return - end - if line_number > par.easy_line then - old_line_number = max_halfword - 1 - line_width = par.second_width - else - old_line_number = line_number - if line_number > par.last_special_line then - line_width = par.second_width - elseif par.par_shape_ptr then - line_width = par.par_shape_ptr[line_number][2] - else - line_width = par.first_width - end - end - end - local artificial_demerits = false -- has d been forced to zero - local shortfall = line_width - cur_active_width.size - par.internal_right_box_width -- used in badness calculations - if not r.break_node then - shortfall = shortfall - par.init_internal_left_box_width - else - shortfall = shortfall - (r.break_node.passive_last_left_box_width or 0) - end - local pw, lp, rp -- used later on - if protrude_chars > 1 then - -- this is quite time consuming - local b = r.break_node - local l = b and b.cur_break or first_p - local o = cur_p and cur_p.prev - if cur_p and cur_p.id == disc_code and cur_p.pre then - o = slide_nodes(cur_p.pre) - else - o = find_protchar_right(l,o) - end - if o and o.id == glyph_code then - pw, rp = right_pw(o) - shortfall = shortfall + pw - end - local id = l.id - if id == glyph_code then - -- ok ? - elseif id == disc_code and l.post then - l = l.post -- TODO: first char could be a disc - else - l = find_protchar_left(l) - end - if l and l.id == glyph_code then - pw, lp = left_pw(l) - shortfall = shortfall + pw - end - end - if checked_expansion and shortfall ~= 0 then - local margin_kern_stretch = 0 - local margin_kern_shrink = 0 - if protrude_chars > 1 then - if lp then --- margin_kern_stretch, margin_kern_shrink = cal_margin_kern_var(lp) -local data = expansions[lp.font][lp.char] -if data then - margin_kern_stretch, margin_kern_shrink = data.glyphstretch, data.glyphshrink -end - end - if rp then --- local mka, mkb = cal_margin_kern_var(rp) --- margin_kern_stretch = margin_kern_stretch + mka --- margin_kern_shrink = margin_kern_shrink + mkb -local data = expansions[lp.font][lp.char] -if data then - margin_kern_stretch = margin_kern_stretch + data.glyphstretch - margin_kern_shrink = margin_kern_shrink + data.glyphshrink -end - end - end - local total = cur_active_width.adjust_stretch + margin_kern_stretch - if shortfall > 0 and total > 0 then - if total > shortfall then - shortfall = total / (par.max_stretch_ratio / par.cur_font_step) / 2 - else - shortfall = shortfall - total - end - else - total = cur_active_width.adjust_shrink + margin_kern_shrink - if shortfall < 0 and total > 0 then - if total > - shortfall then - shortfall = - total / (par.max_shrink_ratio / par.cur_font_step) / 2 - else - shortfall = shortfall + total - end - end - end - par.font_steps[line_number] = par.cur_font_step -- mine - else - par.font_steps[line_number] = 0 -- mine - end - local b = 0 - local g = 0 - local fit_class = fit_decent_class - local found = false - if shortfall > 0 then - if cur_active_width.fi ~= 0 or cur_active_width.fil ~= 0 or cur_active_width.fill ~= 0 or cur_active_width.filll ~= 0 then - if not do_last_line_fit then - -- okay - elseif not cur_p then - found, shortfall, fit_class, g, b = lastlinecrap(shortfall,r.active_short,r.active_glue,cur_active_width,par.fill_width,par.last_line_fit) - else - shortfall = 0 - end - else - local stretch = cur_active_width.stretch - if shortfall > 7230584 and stretch < 1663497 then - b = infinite_badness - fit_class = fit_very_loose_class - else - b = calculate_badness(shortfall,stretch) - if b > 99 then - fit_class = fit_very_loose_class - elseif b > 12 then - fit_class = fit_loose_class - else - fit_class = fit_decent_class - end - end - end - else - local shrink = cur_active_width.shrink - if -shortfall > shrink then - b = infinite_badness + 1 - else - b = calculate_badness(-shortfall,shrink) - end - if b > 12 then - fit_class = fit_tight_class - else - fit_class = fit_decent_class - end - end - if do_last_line_fit and not found then - if not cur_p then - -- g = 0 - shortfall = 0 - elseif shortfall > 0 then - g = cur_active_width.stretch - elseif shortfall < 0 then - g = cur_active_width.shrink - else - g = 0 - end - end - -- ::FOUND:: - local continue_only = false -- brrr - if b > infinite_badness or pi == eject_penalty then - if final_pass and par.minimum_demerits == awful_badness and r.next == par.active and prev_r == par.active then - artificial_demerits = true -- set demerits zero, this break is forced - node_r_stays_active = false - elseif b > par.threshold then - prev_r, r = deactivate_node(par,prev_prev_r,prev_r,r,cur_active_width,checked_expansion) - continue_only = true - else - node_r_stays_active = false - end - else - prev_r = r - if b > par.threshold then - continue_only = true - else - node_r_stays_active = true - end - end - if not continue_only then - local d = 0 - if not artificial_demerits then - d = par.line_penalty + b - if (d >= 0 and d or -d) >= 10000 then -- abs(d) - d = 100000000 - else - d = d * d - end - if pi == 0 then - -- nothing - elseif pi > 0 then - d = d + pi * pi - elseif pi > eject_penalty then - d = d - pi * pi - end - if break_type == hyphenated_code and r.id == hyphenated_code then - if cur_p then - d = d + par.double_hyphen_demerits - else - d = d + par.final_hyphen_demerits - end - end - local delta = fit_class - r.subtype - if (delta >= 0 and delta or -delta) > 1 then -- abs(delta) - d = d + par.adj_demerits - end - end - if tracing_paragraphs then - diagnostics.feasible_break(par,cur_p,r,b,pi,d,artificial_demerits) - end - d = d + r.total_demerits -- this is the minimum total demerits from the beginning to cur_p via r - if d <= minimal_demerits[fit_class] then - minimal_demerits[fit_class] = d - best_place [fit_class] = r.break_node - best_pl_line [fit_class] = line_number - if do_last_line_fit then - best_pl_short[fit_class] = shortfall - best_pl_glue [fit_class] = g - if trace_lastlinefit then - report_parbuilders("storing last line fit short %a and glue %p in class %a",shortfall,g,fit_class) - end - end - if d < par.minimum_demerits then - par.minimum_demerits = d - end - end - if not node_r_stays_active then - prev_r, r = deactivate_node(par,prev_prev_r,prev_r,r,cur_active_width,checked_expansion) - end - end - end - end -end - -local function kern_break(par, cur_p, first_p, checked_expansion) -- move inline if needed - local v = cur_p.next - if par.auto_breaking and v.id == glue_code then - try_break(0, unhyphenated_code, par, first_p, cur_p, checked_expansion) - end - local active_width = par.active_width - if cur_p.id ~= math_code then - active_width.size = active_width.size + cur_p.kern - else - active_width.size = active_width.size + cur_p.surround - end -end - --- we can call the normal one for simple box building in the otr so we need --- frequent enabling/disabling - -local temp_head = new_temp() - -function constructors.methods.basic(head,d) - if trace_basic then - report_parbuilders("starting at %a",head) - end - - local par = initialize_line_break(head,d) - - local checked_expansion = par.checked_expansion - local active_width = par.active_width - local disc_width = par.disc_width - local background = par.background - local tracing_paragraphs = par.tracing_paragraphs - - local dirstack = new_dir_stack() - - if tracing_paragraphs then - diagnostics.start() - if par.pretolerance >= 0 then - diagnostics.current_pass(par,"firstpass") - end - end - - while true do - reset_meta(par) - if par.threshold > infinite_badness then - par.threshold = infinite_badness - end - par.active.next = { - id = unhyphenated_code, - subtype = fit_decent_class, - next = par.active, - break_node = nil, - line_number = par.first_line + 1, - total_demerits = 0, - active_short = 0, - active_glue = 0, - } - active_width.size = background.size - active_width.stretch = background.stretch - active_width.fi = background.fi - active_width.fil = background.fil - active_width.fill = background.fill - active_width.filll = background.filll - active_width.shrink = background.shrink - - if checked_expansion then - active_width.adjust_stretch = 0 - active_width.adjust_shrink = 0 - end - - par.passive = nil -- = 0 - par.printed_node = temp_head -- only when tracing, shared - par.printed_node.next = head - par.pass_number = 0 - par.auto_breaking = true - - local cur_p = head - local first_p = cur_p - - par.font_in_short_display = 0 - - if cur_p and cur_p.id == whatsit_code and cur_p.subtype == localpar_code then - par.init_internal_left_box = cur_p.box_left - par.init_internal_left_box_width = cur_p.box_left_width - par.internal_pen_inter = cur_p.pen_inter - par.internal_pen_broken = cur_p.pen_broken - par.internal_left_box = par.init_internal_left_box - par.internal_left_box_width = par.init_internal_left_box_width - par.internal_right_box = cur_p.box_right - par.internal_right_box_width = cur_p.box_right_width - end - - -- all passes are combined in this loop so maybe we should split this into - -- three function calls; we then also need to do the wrap_up elsewhere - - -- split into normal and expansion loop - - -- use an active local - - local fontexp, lastfont -- we can pass fontexp to calculate width if needed - - while cur_p and par.active.next ~= par.active do - while cur_p and cur_p.id == glyph_code do - if is_rotated[par.line_break_dir] then - active_width.size = active_width.size + cur_p.height + cur_p.depth - else - active_width.size = active_width.size + cur_p.width - end - if checked_expansion then - local data= checked_expansion[cur_p.font] - if data then - local currentfont = cur_p.font - if currentfont ~= lastfont then - fontexps = checked_expansion[currentfont] -- a bit redundant for the par line packer - lastfont = currentfont - end - if fontexps then - local expansion = fontexps[cur_p.char] - if expansion then - active_width.adjust_stretch = active_width.adjust_stretch + expansion.glyphstretch - active_width.adjust_shrink = active_width.adjust_shrink + expansion.glyphshrink - end - end - end - end - cur_p = cur_p.next - end - if not cur_p then -- TODO - report_parbuilders("problems with linebreak_tail") - os.exit() - end - local id = cur_p.id - if id == hlist_code or id == vlist_code then - if is_parallel[cur_p.dir][par.line_break_dir] then - active_width.size = active_width.size + cur_p.width - else - active_width.size = active_width.size + cur_p.depth + cur_p.height - end - elseif id == glue_code then - if par.auto_breaking then - local prev_p = cur_p.prev - if prev_p and prev_p ~= temp_head then - local id = prev_p.id - if id == glyph_code or - (id < math_code and (id ~= whatsit_code or prev_p.subtype ~= dir_code)) or -- was: precedes_break(prev_p) - (id == kern_code and prev_p.subtype ~= userkern_code) then - try_break(0, unhyphenated_code, par, first_p, cur_p, checked_expansion) - end - end - end - local spec = check_shrinkage(par,cur_p.spec) - local order = stretch_orders[spec.stretch_order] - cur_p.spec = spec - active_width.size = active_width.size + spec.width - active_width[order] = active_width[order] + spec.stretch - active_width.shrink = active_width.shrink + spec.shrink - elseif id == disc_code then - local subtype = cur_p.subtype - if subtype ~= second_disc_code then -- are there still second_disc_code in luatex - local line_break_dir = par.line_break_dir - if par.second_pass then -- todo: make second pass local - local actual_pen = subtype == automatic_disc_code and par.ex_hyphen_penalty or par.hyphen_penalty - local pre = cur_p.pre - if not pre then -- trivial pre-break - disc_width.size = 0 - if checked_expansion then - disc_width.adjust_stretch = 0 - disc_width.adjust_shrink = 0 - end - try_break(actual_pen, hyphenated_code, par, first_p, cur_p, checked_expansion) - else - local size, adjust_stretch, adjust_shrink = add_to_width(line_break_dir,checked_expansion,pre) - disc_width.size = size - active_width.size = active_width.size + size - if checked_expansion then - disc_width.adjust_stretch = adjust_stretch - disc_width.adjust_shrink = adjust_shrink - active_width.adjust_stretch = active_width.adjust_stretch + adjust_stretch - active_width.adjust_shrink = active_width.adjust_shrink + adjust_shrink - else - -- disc_width.adjust_stretch = 0 - -- disc_width.adjust_shrink = 0 - end - try_break(actual_pen, hyphenated_code, par, first_p, cur_p, checked_expansion) - if subtype == first_disc_code then - local cur_p_next = cur_p.next - if cur_p_next.id ~= disc_code or cur_p_next.subtype ~= second_disc_code then - report_parbuilders("unsupported disc at location %a",1) - else - local pre = cur_p_next.pre - if pre then - local size, adjust_stretch, adjust_shrink = add_to_width(line_break_dir,checked_expansion,pre) - disc_width.size = disc_width.size + size - if checked_expansion then - disc_width.adjust_stretch = disc_width.adjust_stretch + adjust_stretch - disc_width.adjust_shrink = disc_width.adjust_shrink + adjust_shrink - end - try_break(actual_pen, hyphenated_code, par, first_p, cur_p_next, checked_expansion) - -- - -- I will look into this some day ... comment in linebreak.w says that this fails, - -- maybe this is what Taco means with his comment in the luatex manual. - -- - -- do_one_seven_eight(sub_disc_width_from_active_width); - -- do_one_seven_eight(reset_disc_width); - -- s = vlink_no_break(vlink(cur_p)); - -- add_to_widths(s, line_break_dir, pdf_adjust_spacing,disc_width); - -- ext_try_break(...,first_p,vlink(cur_p)); - -- - else - report_parbuilders("unsupported disc at location %a",2) - end - end - end - -- beware, we cannot restore to a saved value as the try_break adapts active_width - active_width.size = active_width.size - disc_width.size - if checked_expansion then - active_width.adjust_stretch = active_width.adjust_stretch - disc_width.adjust_stretch - active_width.adjust_shrink = active_width.adjust_shrink - disc_width.adjust_shrink - end - end - end - local replace = cur_p.replace - if replace then - local size, adjust_stretch, adjust_shrink = add_to_width(line_break_dir,checked_expansion,replace) - active_width.size = active_width.size + size - if checked_expansion then - active_width.adjust_stretch = active_width.adjust_stretch + adjust_stretch - active_width.adjust_shrink = active_width.adjust_shrink + adjust_shrink - end - end - end - elseif id == kern_code then - if cur_p.subtype == userkern_code then - kern_break(par,cur_p,first_p, checked_expansion) - else - active_width.size = active_width.size + cur_p.kern - if checked_expansion and cur_p.subtype == kerning_code then - local d = cur_p.kern - if d ~= 0 then - local stretch, shrink = kern_stretch_shrink(cur_p,d) - active_width.adjust_stretch = active_width.adjust_stretch + stretch - active_width.adjust_shrink = active_width.adjust_shrink + shrink - end - end - end - elseif id == math_code then - par.auto_breaking = cur_p.subtype == endmath_code - kern_break(par,cur_p, first_p, checked_expansion) - elseif id == rule_code then - active_width.size = active_width.size + cur_p.width - elseif id == penalty_code then - try_break(cur_p.penalty, unhyphenated_code, par, first_p, cur_p, checked_expansion) - elseif id == whatsit_code then - local subtype = cur_p.subtype - if subtype == localpar_code then - par.internal_pen_inter = cur_p.pen_inter - par.internal_pen_broken = cur_p.pen_broken - par.internal_left_box = cur_p.box_left - par.internal_left_box_width = cur_p.box_left_width - par.internal_right_box = cur_p.box_right - par.internal_right_box_width = cur_p.box_right_width - elseif subtype == dir_code then - par.line_break_dir = checked_line_dir(dirstack) or par.line_break_dir - else - local get_width = get_whatsit_width[subtype] - if get_width then - active_width.size = active_width.size + get_width(cur_p) - end - end - elseif id == mark_code or id == ins_code or id == adjust_code then - -- skip - else - report_parbuilders("node of type %a found in paragraph",type(id)) - end - cur_p = cur_p.next - end - if not cur_p then - try_break(eject_penalty, hyphenated_code, par, first_p, cur_p, checked_expansion) - local p_active = par.active - local n_active = p_active.next - if n_active ~= p_active then - local r = n_active - par.fewest_demerits = awful_badness - repeat -- use local d - if r.id ~= delta_code and r.total_demerits < par.fewest_demerits then - par.fewest_demerits = r.total_demerits - par.best_bet = r - end - r = r.next - until r == p_active - par.best_line = par.best_bet.line_number - local asked_looseness = par.looseness - if asked_looseness == 0 then - return wrap_up(par) - end - local r = n_active - local actual_looseness = 0 - -- minimize assignments to par but happens seldom - repeat - if r.id ~= delta_code then - local line_diff = r.line_number - par.best_line - par.line_diff = line_diff - if (line_diff < actual_looseness and asked_looseness <= line_diff) or - (line_diff > actual_looseness and asked_looseness >= line_diff) then - par.best_bet = r - actual_looseness = line_diff - par.fewest_demerits = r.total_demerits - elseif line_diff == actual_looseness and r.total_demerits < par.fewest_demerits then - par.best_bet = r - par.fewest_demerits = r.total_demerits - end - end - r = r.next - until r == p_active -- weird, loop list? - par.best_line = par.best_bet.line_number - if actual_looseness == asked_looseness or par.final_pass then - return wrap_up(par) - end - end - end - reset_meta(par) -- clean up the memory by removing the break nodes - if not par.second_pass then - if tracing_paragraphs then - diagnostics.current_pass(par,"secondpass") - end - par.threshold = par.tolerance - par.second_pass = true - par.final_pass = par.emergency_stretch <= 0 - else - if tracing_paragraphs then - diagnostics.current_pass(par,"emergencypass") - end - par.background.stretch = par.background.stretch + par.emergency_stretch - par.final_pass = true - end - end - return wrap_up(par) -end - --- standard tex logging .. will be adapted .. missing font names and to many [] - -local function write_esc(cs) - local esc = tex.escapechar - if esc then - write("log",utfchar(esc),cs) - else - write("log",cs) - end -end - -function diagnostics.start() -end - -function diagnostics.stop() - write_nl("log",'') -end - -function diagnostics.current_pass(par,what) - write_nl("log",format("@%s",what)) -end - -local function short_display(a,font_in_short_display) - while a do - local id = a.id - if id == glyph_code then - local font = a.font - if font ~= font_in_short_display then - write("log",tex.fontidentifier(font) .. ' ') - font_in_short_display = font - end - if a.subtype == ligature_code then - font_in_short_display = short_display(a.components,font_in_short_display) - else - write("log",utfchar(a.char)) - end --- elseif id == rule_code then --- write("log","|") --- elseif id == glue_code then --- if a.spec.writable then --- write("log"," ") --- end --- elseif id == math_code then --- write("log","$") - elseif id == disc_code then - font_in_short_display = short_display(a.pre,font_in_short_display) - font_in_short_display = short_display(a.post,font_in_short_display) - else -- no explicit checking - write("log",format("[%s]",nodecodes[id])) - end - a = a.next - end - return font_in_short_display -end - -diagnostics.short_display = short_display - -function diagnostics.break_node(par, q, fit_class, break_type, cur_p) -- %d ? - local passive = par.passive - local typ_ind = break_type == hyphenated_code and '-' or "" - if par.do_last_line_fit then - local s = number.toscaled(q.active_short) - local g = number.toscaled(q.active_glue) - if cur_p then - write_nl("log",format("@@%d: line %d.%d%s t=%s s=%s g=%s", - passive.serial or 0,q.line_number-1,fit_class,typ_ind,q.total_demerits,s,g)) - else - write_nl("log",format("@@%d: line %d.%d%s t=%s s=%s a=%s", - passive.serial or 0,q.line_number-1,fit_class,typ_ind,q.total_demerits,s,g)) - end - else - write_nl("log",format("@@%d: line %d.%d%s t=%s", - passive.serial or 0,q.line_number-1,fit_class,typ_ind,q.total_demerits)) - end - if not passive.prev_break then - write("log"," -> @0") - else - write("log",format(" -> @%d", passive.prev_break.serial or 0)) - end -end - -function diagnostics.feasible_break(par, cur_p, r, b, pi, d, artificial_demerits) - local printed_node = par.printed_node - if printed_node ~= cur_p then - write_nl("log","") - if not cur_p then - par.font_in_short_display = short_display(printed_node.next,par.font_in_short_display) - else - local save_link = cur_p.next - cur_p.next = nil - write_nl("log","") - par.font_in_short_display = short_display(printed_node.next,par.font_in_short_display) - cur_p.next = save_link - end - par.printed_node = cur_p - end - write_nl("log","@") - if not cur_p then - write_esc("par") - else - local id = cur_p.id - if id == glue_code then - -- print nothing - elseif id == penalty_code then - write_esc("penalty") - elseif id == disc_code then - write_esc("discretionary") - elseif id == kern_code then - write_esc("kern") - elseif id == math_code then - write_esc("math") - else - write_esc("unknown") - end - end - local via, badness, demerits = 0, '*', '*' - if r.break_node then - via = r.break_node.serial or 0 - end - if b <= infinite_badness then - badness = tonumber(d) -- format("%d", b) - end - if not artificial_demerits then - demerits = tonumber(d) -- format("%d", d) - end - write("log",format(" via @%d b=%s p=%s d=%s", via, badness, pi, demerits)) -end - --- reporting -- - -statistics.register("alternative parbuilders", function() - if nofpars > 0 then - return format("%s paragraphs, %s lines (%s protruded, %s adjusted)", nofpars, noflines, nofprotrudedlines, nofadjustedlines) - end -end) - --- actually scaling kerns is not such a good idea and it will become --- configureable - --- This is no way a replacement for the built in (fast) packer --- it's just an alternative for special (testing) purposes. --- --- We could use two hpacks: one to be used in the par builder --- and one to be used for other purposes. The one in the par --- builder is much more simple as it does not need the expansion --- code but only need to register the effective expansion factor --- with the glyph. - -local function glyph_width_height_depth(curdir,pdir,p) - if is_rotated[curdir] then - if is_parallel[curdir][pdir] then - local half = (p.height + p.depth) / 2 - return p.width, half, half - else - local half = p.width / 2 - return p.height + p.depth, half, half - end - elseif is_rotated[pdir] then - if is_parallel[curdir][pdir] then - local half = (p.height + p.depth) / 2 - return p.width, half, half - else - return p.height + p.depth, p.width, 0 -- weird - end - else - if glyphdir_is_equal[curdir][pdir] then - return p.width, p.height, p.depth - elseif is_opposite[curdir][pdir] then - return p.width, p.depth, p.height - else -- can this happen? - return p.height + p.depth, p.width, 0 -- weird - end - end -end - -local function pack_width_height_depth(curdir,pdir,p) - if is_rotated[curdir] then - if is_parallel[curdir][pdir] then - local half = (p.height + p.depth) / 2 - return p.width, half, half - else -- can this happen? - local half = p.width / 2 - return p.height + p.depth, half, half - end - else - if pardir_is_equal[curdir][pdir] then - return p.width, p.height, p.depth - elseif is_opposite[curdir][pdir] then - return p.width, p.depth, p.height - else -- weird dimensions, can this happen? - -- return p.width, p.depth, p.height - return p.height + p.depth, p.width, 0 - end - end -end - --- local function xpack(head,width,method,direction,analysis) --- --- -- inspect(analysis) --- --- local expansion = method == "cal_expand_ratio" --- local natural = analysis.size --- local font_stretch = analysis.adjust_stretch --- local font_shrink = analysis.adjust_shrink --- local font_expand_ratio = 0 --- local delta = width - natural --- --- local hlist = new_node("hlist") --- --- hlist.list = head --- hlist.dir = direction or tex.textdir --- hlist.width = width --- hlist.height = height --- hlist.depth = depth --- --- if delta == 0 then --- --- hlist.glue_sign = 0 --- hlist.glue_order = 0 --- hlist.glue_set = 0 --- --- else --- --- local order = analysis.filll ~= 0 and fillcodes.filll or --- analysis.fill ~= 0 and fillcodes.fill or --- analysis.fil ~= 0 and fillcodes.fil or --- analysis.fi ~= 0 and fillcodes.fi or 0 --- --- if delta > 0 then --- --- if expansion and order == 0 and font_stretch > 0 then --- font_expand_ratio = (delta/font_stretch) * 1000 --- else --- local stretch = analysis.stretch --- if stretch ~= 0 then --- hlist.glue_sign = 1 -- stretch --- hlist.glue_order = order --- hlist.glue_set = delta/stretch --- else --- hlist.glue_sign = 0 -- nothing --- hlist.glue_order = order --- hlist.glue_set = 0 --- end --- end --- print("stretch",hlist.glue_sign,hlist.glue_order,hlist.glue_set) --- --- else --- --- if expansion and order == 0 and font_shrink > 0 then --- font_expand_ratio = (delta/font_shrink) * 1000 --- else --- local shrink = analysis.shrink --- if shrink ~= 0 then --- hlist.glue_sign = 2 -- shrink --- hlist.glue_order = order --- hlist.glue_set = - delta/shrink --- else --- hlist.glue_sign = 0 -- nothing --- hlist.glue_order = order --- hlist.glue_set = 0 --- end --- end --- print("shrink",hlist.glue_sign,hlist.glue_order,hlist.glue_set) --- --- end --- --- end --- --- if not expansion or font_expand_ratio == 0 then --- -- nothing --- elseif font_expand_ratio > 0 then --- if font_expand_ratio > 1000 then --- font_expand_ratio = 1000 --- end --- local current = head --- while current do --- local id = current.id --- if id == glyph_code then --- local stretch, shrink = char_stretch_shrink(current) -- get only one --- if stretch then --- if trace_expansion then --- setnodecolor(g,"hz:positive") --- end --- current.expansion_factor = font_expand_ratio * stretch --- end --- elseif id == kern_code then --- local kern = current.kern --- if kern ~= 0 and current.subtype == kerning_code then --- current.kern = font_expand_ratio * current.kern --- end --- end --- current = current.next --- end --- elseif font_expand_ratio < 0 then --- if font_expand_ratio < -1000 then --- font_expand_ratio = -1000 --- end --- local current = head --- while current do --- local id = current.id --- if id == glyph_code then --- local stretch, shrink = char_stretch_shrink(current) -- get only one --- if shrink then --- if trace_expansion then --- setnodecolor(g,"hz:negative") --- end --- current.expansion_factor = font_expand_ratio * shrink --- end --- elseif id == kern_code then --- local kern = current.kern --- if kern ~= 0 and current.subtype == kerning_code then --- current.kern = font_expand_ratio * current.kern --- end --- end --- current = current.next --- end --- end --- return hlist, 0 --- end - --- local expansion_stack = { } -- no dealloc - -local function hpack(head,width,method,direction) -- fast version when head = nil - - -- we can pass the adjust_width and adjust_height so that we don't need to recalculate them but - -- with the glue mess it's less trivial as we lack detail - - local hlist = new_node("hlist") - - if head == nil then - return hlist, 0 - end - - local cal_expand_ratio = method == "cal_expand_ratio" -- "subst_ex_font" -- is gone - - direction = direction or tex.textdir - - local pack_begin_line = 0 - local line = 0 - - local height = 0 - local depth = 0 - local natural = 0 - local font_stretch = 0 - local font_shrink = 0 - local font_expand_ratio = 0 - local last_badness = 0 - local disc_stack = { } - local disc_level = 0 - local expansion_stack = cal_expand_ratio and { } -- todo: optionally pass this - local expansion_index = 0 - local total_stretch = { [0] = 0, 0, 0, 0, 0 } - local total_shrink = { [0] = 0, 0, 0, 0, 0 } - - local hpack_dir = direction - - local adjust_head = texlists.adjust_head - local pre_adjust_head = texlists.pre_adjust_head - local adjust_tail = adjust_head and slide_nodes(adjust_head) - local pre_adjust_tail = pre_adjust_head and slide_nodes(pre_adjust_head) - - hlist.list = head - hlist.dir = hpack_dir - - new_dir_stack(hpack_dir) - - local checked_expansion = false - - if cal_expand_ratio then - checked_expansion = { } - setmetatableindex(checked_expansion,check_expand_lines) - end - - -- this one also needs to check the font, so in the end indeed we might end up with two variants - - local fontexps, lastfont - - local current = head - - while current do - local id = current.id - if id == glyph_code then - if cal_expand_ratio then - local currentfont = current.font - if currentfont ~= lastfont then - fontexps = checked_expansion[currentfont] -- a bit redundant for the par line packer - lastfont = currentfont - end - if fontexps then - local expansion = fontexps[current.char] - if expansion then - font_stretch = font_stretch + expansion.glyphstretch - font_shrink = font_shrink + expansion.glyphshrink - expansion_index = expansion_index + 1 - expansion_stack[expansion_index] = current - end - end - end - -- use inline if no expansion - local wd, ht, dp = glyph_width_height_depth(hpack_dir,"TLT",current) -- was TRT ? - natural = natural + wd - if ht > height then - height = ht - end - if dp > depth then - depth = dp - end - current = current.next - elseif id == kern_code then - local kern = current.kern - if kern == 0 then - -- no kern - elseif current.subtype == kerning_code then -- check p.kern - if cal_expand_ratio then - local stretch, shrink = kern_stretch_shrink(current,kern) - font_stretch = font_stretch + stretch - font_shrink = font_shrink + shrink - expansion_index = expansion_index + 1 - expansion_stack[expansion_index] = current - end - natural = natural + kern - else - natural = natural + kern - end - current = current.next - elseif id == disc_code then - if current.subtype ~= second_disc_code then - -- todo : local stretch, shrink = char_stretch_shrink(s) - local replace = current.replace - if replace then - disc_level = disc_level + 1 - disc_stack[disc_level] = current.next - current = replace - else - current = current.next - end - else - -- -- pre post replace - -- - -- local stretch, shrink = char_stretch_shrink(current.pre) - -- font_stretch = font_stretch + stretch - -- font_shrink = font_shrink + shrink - -- expansion_index = expansion_index + 1 - -- expansion_stack[expansion_index] = current.pre - -- - current = current.next - end - elseif id == glue_code then - local spec = current.spec - natural = natural + spec.width - local op = spec.stretch_order - local om = spec.shrink_order - total_stretch[op] = total_stretch[op] + spec.stretch - total_shrink [om] = total_shrink [om] + spec.shrink - if current.subtype >= leaders_code then - local leader = current.leader - local ht = leader.height - local dp = leader.depth - if ht > height then - height = ht - end - if dp > depth then - depth = dp - end - end - current = current.next - elseif id == hlist_code or id == vlist_code then - local sh = current.shift - local wd, ht, dp = pack_width_height_depth(hpack_dir,current.dir or hpack_dir,current) -- added: or pack_dir - local hs, ds = ht - sh, dp + sh - natural = natural + wd - if hs > height then - height = hs - end - if ds > depth then - depth = ds - end - current = current.next - elseif id == rule_code then - local wd = current.width - local ht = current.height - local dp = current.depth - natural = natural + wd - if ht > height then - height = ht - end - if dp > depth then - depth = dp - end - current = current.next - elseif id == math_code then - natural = natural + current.surround - current = current.next - elseif id == unset_code then - local wd = current.width - local ht = current.height - local dp = current.depth - local sh = current.shift - local hs = ht - sh - local ds = dp + sh - natural = natural + wd - if hs > height then - height = hs - end - if ds > depth then - depth = ds - end - current = current.next - elseif id == ins_code or id == mark_code then - local prev = current.prev - local next = current.next - if adjust_tail then -- todo - if next then - next.prev = prev - end - if prev then - prev.next = next - end - current.prev = adjust_tail - current.next = nil - adjust_tail.next = current - adjust_tail = current - else - adjust_head = current - adjust_tail = current - current.prev = nil - current.next = nil - end - current = next - elseif id == adjust_code then - local list = current.list - if adjust_tail then - adjust_tail.next = list - adjust_tail = slide_nodes(list) - else - adjust_head = list - adjust_tail = slide_nodes(list) - end - current = current.next - elseif id == whatsit_code then - local subtype = current.subtype - if subtype == dir_code then - hpack_dir = checked_line_dir(stack,current) or hpack_dir - else - local get_dimensions = get_whatsit_dimensions[subtype] - if get_dimensions then - local wd, ht, dp = get_dimensions(current) - natural = natural + wd - if ht > height then - height = ht - end - if dp > depth then - depth = dp - end - end - end - current = current.next - elseif id == marginkern_code then - if cal_expand_ratio then - local glyph = current.glyph - local char_pw = current.subtype == leftmargin_code and left_pw or right_pw - font_stretch = font_stretch - current.width - char_pw(glyph) - font_shrink = font_shrink - current.width - char_pw(glyph) - expansion_index = expansion_index + 1 - expansion_stack[expansion_index] = glyph - end - natural = natural + current.width - current = current.next - else - current = current.next - end - if not current and disc_level > 0 then - current = disc_stack[disc_level] - disc_level = disc_level - 1 - end - end - if adjust_tail then - adjust_tail.next = nil -- todo - end - if pre_adjust_tail then - pre_adjust_tail.next = nil -- todo - end - if mode == "additional" then - width = width + natural - end - hlist.width = width - hlist.height = height - hlist.depth = depth - local delta = width - natural - if delta == 0 then - hlist.glue_sign = 0 - hlist.glue_order = 0 - hlist.glue_set = 0 - elseif delta > 0 then - local order = (total_stretch[4] ~= 0 and 4 or total_stretch[3] ~= 0 and 3) or - (total_stretch[2] ~= 0 and 2 or total_stretch[1] ~= 0 and 1) or 0 - if cal_expand_ratio and order == 0 and font_stretch > 0 then - font_expand_ratio = (delta/font_stretch) * 1000 -- round(delta/font_stretch * 1000) - else - local tso = total_stretch[order] - if tso ~= 0 then - hlist.glue_sign = 1 - hlist.glue_order = order - hlist.glue_set = delta/tso - else - hlist.glue_sign = 0 - hlist.glue_order = order - hlist.glue_set = 0 - end --- print("stretch",hlist.glue_sign,hlist.glue_order,hlist.glue_set) - if order == 0 and hlist.list then - last_badness = calculate_badness(delta,total_stretch[0]) - if last_badness > tex.hbadness then - if last_badness > 100 then - diagnostics.underfull_hbox(hlist,pack_begin_line,line,last_badness) - else - diagnostics.loose_hbox(hlist,pack_begin_line,line,last_badness) - end - end - end - end - else - local order = total_shrink[4] ~= 0 and 4 or total_shrink[3] ~= 0 and 3 - or total_shrink[2] ~= 0 and 2 or total_shrink[1] ~= 0 and 1 or 0 - if cal_expand_ratio and order == 0 and font_shrink > 0 then - font_expand_ratio = (delta/font_shrink) * 1000 -- round(delta/font_shrink * 1000) - else -- why was this else commented - local tso = total_shrink[order] - if tso ~= 0 then - hlist.glue_sign = 2 - hlist.glue_order = order - hlist.glue_set = -delta/tso - else - hlist.glue_sign = 0 - hlist.glue_order = order - hlist.glue_set = 0 - end --- print("shrink",hlist.glue_sign,hlist.glue_order,hlist.glue_set) - if total_shrink[order] < -delta and order == 0 and hlist.list then - last_badness = 1000000 - hlist.glue_set = 1 - local fuzz = - delta - total_shrink[0] - local hfuzz = tex.hfuzz - if fuzz > hfuzz or tex.hbadness < 100 then - local overfullrule = tex.overfullrule - if fuzz > hfuzz and overfullrule > 0 then - -- weird, is always called and no rules shows up - slide_nodes(list).next = new_rule(overfullrule,nil,nil,hlist.dir) - end - diagnostics.overfull_hbox(hlist,pack_begin_line,line,-delta) -- - added - end - elseif order == 0 and hlist.list and last_badness > tex.hbadness then - diagnostics.bad_hbox(hlist,pack_begin_line,line,last_badness) - end - end - end - if cal_expand_ratio and font_expand_ratio ~= 0 then - -- if font_expand_ratio > 1000 then - -- font_expand_ratio = 1000 - -- elseif font_expand_ratio < -1000 then - -- font_expand_ratio = -1000 - -- end - - local fontexps, lastfont - - if font_expand_ratio > 0 then - for i=1,expansion_index do - local g = expansion_stack[i] - if g.id == glyph_code then - local currentfont = g.font - if currentfont ~= lastfont then - fontexps = expansions[currentfont] - lastfont = currentfont - end - local data = fontexps[g.char] - if trace_expansion then - setnodecolor(g,"hz:positive") - end - g.expansion_factor = font_expand_ratio * data.glyphstretch - else - local stretch, shrink = kern_stretch_shrink(g,g.kern) - g.expansion_factor = font_expand_ratio * stretch - end - end - else - for i=1,expansion_index do - local g = expansion_stack[i] - if g.id == glyph_code then - local currentfont = g.font - if currentfont ~= lastfont then - fontexps = expansions[currentfont] - lastfont = currentfont - end - local data = fontexps[g.char] - if trace_expansion then - setnodecolor(g,"hz:negative") - end - g.expansion_factor = font_expand_ratio * data.glyphshrink - else - local stretch, shrink = kern_stretch_shrink(g,g.kern) - g.expansion_factor = font_expand_ratio * shrink - end - end - end - - end - return hlist, last_badness -end - -nodes.hpack = hpack -hpack_nodes = hpack -- comment this for old fashioned expansion -xpack_nodes = hpack -- comment this for old fashioned expansion - -local function common_message(hlist,pack_begin_line,line,str) - write_nl("") - if status.output_active then -- unset - write(str," has occurred while \\output is active") - end - if pack_begin_line > 0 then - write(str," in paragraph at lines ",pack_begin_line,"--",line) - elseif pack_begin_line < 0 then - write(str," in alignment at lines ",-pack_begin_line,"--",line) - else - write(str," detected at line ",line) - end - write_nl("") - diagnostics.short_display(hlist.list,false) - write_nl("") - -- diagnostics.start() - -- show_box(hlist.list) - -- diagnostics.stop() -end - -function diagnostics.overfull_hbox(hlist,pack_begin_line,line,d) - common_message(hlist,pack_begin_line,line,format("Overfull \\hbox (%spt too wide)",number.toscaled(d))) -end - -function diagnostics.bad_hbox(hlist,pack_begin_line,line,b) - common_message(hlist,pack_begin_line,line,format("Tight \\hbox (badness %i)",b)) -end - -function diagnostics.underfull_hbox(hlist,pack_begin_line,line,b) - common_message(hlist,pack_begin_line,line,format("Underfull \\hbox (badness %i)",b)) -end - -function diagnostics.loose_hbox(hlist,pack_begin_line,line,b) - common_message(hlist,pack_begin_line,line,format("Loose \\hbox (badness %i)",b)) -end - --- for the moment here: - -local utfchar = utf.char -local concat = table.concat - -local nodecodes = nodes.nodecodes -local hlist_code = nodecodes.hlist -local vlist_code = nodecodes.vlist -local glyph_code = nodecodes.glyph -local kern_code = nodecodes.kern -local setnodecolor = nodes.tracers.colors.set -local parameters = fonts.hashes.parameters -local basepoints = number.basepoints - --- definecolor[hz:positive] [r=0.6] --- definecolor[hz:negative] [g=0.6] --- definecolor[hz:zero] [b=0.6] - --- scale = multiplier + ef/multiplier - -local trace_verbose = false trackers.register("builders.paragraphs.expansion.verbose", function(v) trace_verbose = v end) - -local report_verbose = logs.reporter("fonts","expansion") - -local function colorize(n) - local size, font, ef, width, scale, list, flush, length - if trace_verbose then - width = 0 - length = 0 - list = { } - flush = function() - if length > 0 then - report_verbose("%0.3f : %10s %10s %s",scale,basepoints(width),basepoints(width*scale),concat(list,"",1,length)) - width = 0 - length = 0 - end - end - else - length = 0 - end - -- tricky: the built-in method creates dummy fonts and the last line normally has the - -- original font and that one then has ex.auto set - while n do - local id = n.id - if id == glyph_code then - local ne = n.expansion_factor - if ne == 0 then - if length > 0 then flush() end - else - local f = n.font - if f ~= font then - if length > 0 then - flush() - end - local pf = parameters[f] - local ex = pf.expansion - if ex and ex.auto then - size = pf.size - font = f -- save lookups - else - size = false - end - end - if size then - if ne ~= ef then - if length > 0 then - flush() - end - ef = ne - end - -- scale = 1.0 + ef / 1000 / 1000 / 1000 - scale = 1.0 + ef / 1000000000 - if scale > 1 then - setnodecolor(n,"hz:positive") - elseif scale < 1 then - setnodecolor(n,"hz:negative") - else - setnodecolor(n,"hz:zero") - end - if report_verbose then - length = length + 1 - list[length] = utfchar(n.char) - width = width + n.width -- no kerning yet - end - end - end - elseif id == hlist_code or id == vlist_code then - if length > 0 then - flush() - end - colorize(n.list,flush) - else -- nothing to show on kerns - if length > 0 then - flush() - end - end - n = n.next - end - if length > 0 then - flush() - end -end - -builders.paragraphs.expansion = builders.paragraphs.expansion or { } - -function builders.paragraphs.expansion.trace(head) - colorize(head,true) - return head -end - -local tasks = nodes.tasks - -tasks.prependaction("shipouts","normalizers","builders.paragraphs.expansion.trace") -tasks.disableaction("shipouts","builders.paragraphs.expansion.trace") - -trackers.register("builders.paragraphs.expansion.verbose", function(v) - if v then - tasks.enableaction("shipouts","builders.paragraphs.expansion.trace") - else - tasks.disableaction("shipouts","builders.paragraphs.expansion.trace") - end -end) |