diff options
Diffstat (limited to 'Master/texmf-dist/tex/luatex/lua-widow-control/lua-widow-control.lua')
-rw-r--r-- | Master/texmf-dist/tex/luatex/lua-widow-control/lua-widow-control.lua | 225 |
1 files changed, 125 insertions, 100 deletions
diff --git a/Master/texmf-dist/tex/luatex/lua-widow-control/lua-widow-control.lua b/Master/texmf-dist/tex/luatex/lua-widow-control/lua-widow-control.lua index 9304bf9a4d1..f1ecb54c65f 100644 --- a/Master/texmf-dist/tex/luatex/lua-widow-control/lua-widow-control.lua +++ b/Master/texmf-dist/tex/luatex/lua-widow-control/lua-widow-control.lua @@ -2,7 +2,7 @@ lua-widow-control https://github.com/gucci-on-fleek/lua-widow-control SPDX-License-Identifier: MPL-2.0+ - SPDX-FileCopyrightText: 2021 Max Chernoff + SPDX-FileCopyrightText: 2022 Max Chernoff ]] lwc = {} @@ -14,13 +14,19 @@ lwc.name = "lua-widow-control" detect the format name then set some flags for later processing. ]] local format = tex.formatname +local context, latex, plain, optex, lmtx if format:find('cont') then -- cont-en, cont-fr, cont-nl, ... - lwc.context = true + context = true + if status.luatex_engine == "luametatex" then + lmtx = true + end elseif format:find('latex') then -- lualatex, lualatex-dev, ... - lwc.latex = true + latex = true elseif format == 'luatex' then -- Plain - lwc.plain = true + plain = true +elseif format == 'optex' then -- OpTeX + optex = true end --[[ @@ -29,7 +35,7 @@ end ]] local last = node.slide local copy = node.copy_list -local par_id = node.id("par") +local par_id = node.id("par") or node.id("local_par") local glue_id = node.id("glue") local set_attribute = node.set_attribute local has_attribute = node.has_attribute @@ -39,36 +45,29 @@ local min_col_width = tex.sp("25em") local maxdimen = 1073741823 -- \\maxdimen in sp --[[ - This error is raised in the following circumstances: - - When the user manually loads the Lua module without loading Lua\TeX{}Base - - When the package is used with an unsupported format - Both of these are pretty unlikely, but it can't hurt to check. - ]] -assert(lwc.context or luatexbase, [[ - - This module requires a supported callback library. Please - follow the following format-dependant instructions: - - LaTeX: Use a version built after 2015-01-01, or include - `\usepackage{luatexbase}' before loading this module. - - Plain: Include `\input ltluatex' before loading this module. - - ConTeXt: Use the LMTX version. -]]) - ---[[ Package/module initialization ]] -if lwc.context then - lwc.warning = logs.reporter("module", lwc.name) - lwc.info = logs.reporter("module", lwc.name) - lwc.attribute = attributes.public(lwc.name) - lwc.contrib_head = 'contributehead' -- For \LuaMetaTeX{} - lwc.stretch_order = "stretchorder" - lwc.pagenum = function () return tex.count["realpageno"] end -elseif lwc.plain or lwc.latex then +local warning, info, attribute, contrib_head, stretch_order, pagenum + +if context and lmtx then + warning = logs.reporter("module", lwc.name) + info = logs.reporter("module", lwc.name) + attribute = attributes.public(lwc.name) + contrib_head = 'contributehead' -- For \LuaMetaTeX{} + stretch_order = "stretchorder" + pagenum = function() return tex.count["realpageno"] end +elseif context and not lmtx then + warning = logs.reporter("module", lwc.name) + info = logs.reporter("module", lwc.name) + attribute = attributes.public(lwc.name) + contrib_head = 'contrib_head' + stretch_order = "stretch_order" + pagenum = function() return tex.count["realpageno"] end +elseif plain or latex then luatexbase.provides_module { name = lwc.name, - date = "2022/02/15", --%%date - version = "1.1.5", --%%version + date = "2022/02/22", --%%date + version = "1.1.6", --%%version description = [[ This module provides a LuaTeX-based solution to prevent @@ -77,29 +76,30 @@ elseif lwc.plain or lwc.latex then paragraphs. ]], } - lwc.warning = function(str) luatexbase.module_warning(lwc.name, str) end - lwc.info = function(str) luatexbase.module_info(lwc.name, str) end - lwc.attribute = luatexbase.new_attribute(lwc.name) - lwc.contrib_head = 'contrib_head' -- For \LuaTeX{} - lwc.stretch_order = "stretch_order" - lwc.pagenum = function () return tex.count[0] end + warning = function(str) luatexbase.module_warning(lwc.name, str) end + info = function(str) luatexbase.module_info(lwc.name, str) end + attribute = luatexbase.new_attribute(lwc.name) + contrib_head = 'contrib_head' -- For \LuaTeX{} + stretch_order = "stretch_order" + pagenum = function() return tex.count[0] end +elseif optex then + local write_nl = texio.write_nl + warning = function(str) write_nl(lwc.name .. " Warning: " .. str) end + info = function(str) write_nl("log", lwc.name .. " Info: " .. str) end + attribute = alloc.new_attribute(lwc.name) + contrib_head = 'contrib_head' + stretch_order = "stretch_order" + pagenum = function() return tex.count[0] end else -- uh oh error [[ Unsupported format. - Please use (Lua)LaTeX, Plain (Lua)TeX, or ConTeXt (MKXL/LMTX) + Please use (Lua)LaTeX, Plain (Lua)TeX, ConTeXt (MKXL/LMTX), + or OpTeX. ]] end -lwc.paragraphs = {} -- List to hold the alternate paragraph versions - -if tex.interlinepenalty ~= 0 then - lwc.warning [[ -\interlinepenalty is set to a non-zero value. -This may prevent lua-widow-control from -properly functioning. -]] -end +local paragraphs = {} -- List to hold the alternate paragraph versions --[[ @@ -118,8 +118,8 @@ end --- @return table t Enablers/Disablers for the callback --- enable: function = Enable the callback --- disable: function = Disable the callback -function lwc.register_callback(t) - if lwc.plain or lwc.latex then -- Both use \LuaTeX{}Base for callbacks +local function register_callback(t) + if plain or latex then -- Both use \LuaTeX{}Base for callbacks return { enable = function() luatexbase.add_to_callback(t.callback, t.func, t.name) @@ -128,19 +128,19 @@ function lwc.register_callback(t) luatexbase.remove_from_callback(t.callback, t.name) end, } - elseif lwc.context and not t.lowlevel then + elseif context and not t.lowlevel then return { -- Register the callback when the table is created, -- but activate it when `enable()` is called. enable = nodes.tasks.appendaction(t.category, t.position, "lwc." .. t.name) - or function() + or function() nodes.tasks.enableaction(t.category, "lwc." .. t.name) - end, + end, disable = function() nodes.tasks.disableaction(t.category, "lwc." .. t.name) end, } - elseif lwc.context and t.lowlevel then + elseif context and t.lowlevel then --[[ Some of the callbacks in \ConTeXt{} have no associated "actions". Unlike with \LuaTeX{}base, \ConTeXt{} leaves some \LuaTeX{} callbacks unregistered @@ -153,17 +153,31 @@ function lwc.register_callback(t) enable = function() callback.register(t.callback, t.func) end, disable = function() callback.register(t.callback, nil) end, } + elseif optex then + return { + enable = function() + callback.add_to_callback(t.callback, t.func, t.name) + end, + disable = function() + callback.remove_from_callback(t.callback, t.name) + end, + } end end - --- Saves each paragraph, but lengthened by 1 line function lwc.save_paragraphs(head) -- Prevent the "underfull hbox" warnings when we store a potential paragraph - lwc.callbacks.disable_box_warnings.enable() + local renable_box_warnings + if (latex or plain) and + #luatexbase.callback_descriptions("hpack_quality") == 0 + then -- See #18 and michal-h21/linebreaker#3 + renable_box_warnings = true + lwc.callbacks.disable_box_warnings.enable() + end -- Ensure that we were actually given a par (only under \ConTeXt{} for some reason) - if head.id ~= par_id and lwc.context then + if head.id ~= par_id and context then return head end @@ -173,8 +187,8 @@ function lwc.save_paragraphs(head) -- Prevent ultra-short last lines (\TeX{}Book p. 104), except with narrow columns local parfillskip = last(new_head) if parfillskip.id == glue_id and tex.hsize > min_col_width then - parfillskip[lwc.stretch_order] = 0 - parfillskip.stretch = 0.8 * tex.hsize -- Last line must be at least 20% long + parfillskip[stretch_order] = 0 + parfillskip.stretch = 0.8 * tex.hsize -- Last line must be at least 20% long end -- Break the paragraph 1 line longer than natural @@ -187,7 +201,9 @@ function lwc.save_paragraphs(head) local natural_node, natural_info = tex.linebreak(copy(head)) flush_list(natural_node) - lwc.callbacks.disable_box_warnings.disable() + if renable_box_warnings then + lwc.callbacks.disable_box_warnings.disable() + end -- If we can't lengthen the paragraph, assign a \emph{very} large demerit value local long_demerits @@ -202,26 +218,24 @@ function lwc.save_paragraphs(head) prevdepth.width = natural_info.prevdepth - long_info.prevdepth last(long_node).next = prevdepth - table.insert(lwc.paragraphs, {demerits = long_demerits, node = long_node}) + table.insert(paragraphs, { demerits = long_demerits, node = long_node }) -- \LuaMetaTeX{} crashes if we return `true` return head end - --- Tags the beginning and the end of each paragraph as it is added to the page. --- --- We add an attribute to the first and last node of each paragraph. The ID is --- some arbitrary number for \lwc/, and the value corresponds to the --- paragraphs index, which is negated for the end of the paragraph. function lwc.mark_paragraphs(head) - set_attribute(head, lwc.attribute, #lwc.paragraphs) - set_attribute(last(head), lwc.attribute, -1 * #lwc.paragraphs) + set_attribute(head, attribute, #paragraphs) + set_attribute(last(head), attribute, -1 * #paragraphs) return head end - --- A "safe" version of the last/slide function. --- --- Sometimes the node list can form a loop. Since there is no last element @@ -236,7 +250,7 @@ local function safe_last(head) local id = node.is_node(head) -- Returns the internal node id if ids[id] then - lwc.warning [[Circular node list detected! + warning [[Circular node list detected! This should never happen. I'll try and recover, but your output may be corrupted. As a workaround, disable lua-widow-control for the affected paragraph or change the page breaks in your document.]] @@ -254,7 +268,6 @@ affected paragraph or change the page breaks in your document.]] return head end - --- Remove the widows and orphans from the page, just after the output routine. --- --- This function holds the "meat" of the module. It is called just after the @@ -263,21 +276,36 @@ end --- replace one paragraph with the same paragraph, but lengthened by one line. --- Then, we can push the bottom line of the page to the next page. function lwc.remove_widows(head) - local penalty = tex.outputpenalty - local paragraphs = lwc.paragraphs + local penalty = tex.outputpenalty - tex.interlinepenalty + local widowpenalty = tex.widowpenalty + local clubpenalty = tex.clubpenalty + local displaywidowpenalty = tex.displaywidowpenalty + local brokenpenalty = tex.brokenpenalty --[[ We only need to process pages that have orphans or widows. If `paragraphs` is empty, then there is nothing that we can do. + + The list of penalties is from: + https://tug.org/TUGboat/tb39-3/tb123mitt-widows-code.pdf#subsection.0.2.1 ]] - if penalty >= 10000 or - penalty <= -10000 or - penalty == 0 or - #paragraphs == 0 then - return head + if (penalty == widowpenalty or + penalty == displaywidowpenalty or + penalty == clubpenalty or + penalty == clubpenalty + widowpenalty or + penalty == clubpenalty + displaywidowpenalty or + penalty == brokenpenalty or + penalty == brokenpenalty + widowpenalty or + penalty == brokenpenalty + displaywidowpenalty or + penalty == brokenpenalty + clubpenalty or + penalty == brokenpenalty + clubpenalty + widowpenalty or + penalty == brokenpenalty + clubpenalty + displaywidowpenalty) and + #paragraphs >= 1 then + else + return head end - lwc.info("Widow/orphan detected. Attempting to remove.") + info("Widow/orphan detected. Attempting to remove.") local head_save = head -- Save the start of the `head` linked-list @@ -309,7 +337,7 @@ function lwc.remove_widows(head) -- Loop through all of the nodes on the page while head do -- Insert the start of the replacement paragraph - if has_attribute(head, lwc.attribute, paragraph_index) then + if has_attribute(head, attribute, paragraph_index) then safe_last(target_node) -- Remove any loops head.prev.next = target_node @@ -317,25 +345,25 @@ function lwc.remove_widows(head) end -- Insert the end of the replacement paragraph - if has_attribute(head, lwc.attribute, -1 * paragraph_index) then + if has_attribute(head, attribute, -1 * paragraph_index) then safe_last(target_node).next = head.next clear_flag = false end -- Start of final paragraph - if has_attribute(head, lwc.attribute, #paragraphs) then + if has_attribute(head, attribute, #paragraphs) then local last_line = copy(last(head)) - last(last_line).next = copy(tex.lists[lwc.contrib_head]) + last(last_line).next = copy(tex.lists[contrib_head]) last(head).prev.prev.next = nil -- Move the last line to the next page - tex.lists[lwc.contrib_head] = last_line - lwc.info( - "Widow/orphan successfully removed at paragraph " + tex.lists[contrib_head] = last_line + info( + "Widow/orphan successfully removed at paragraph " .. paragraph_index .. " on page " - .. lwc.pagenum() + .. pagenum() .. "." ) end @@ -347,37 +375,36 @@ function lwc.remove_widows(head) end end - lwc.paragraphs = {} -- Clear paragraphs array at the end of the page + paragraphs = {} -- Clear paragraphs array at the end of the page return head_save end - -- Add all of the callbacks lwc.callbacks = { - disable_box_warnings = lwc.register_callback({ + disable_box_warnings = register_callback({ callback = "hpack_quality", - func = function() end, - name = "disable_box_warnings", + func = function() end, + name = "disable_box_warnings", lowlevel = true, }), - remove_widows = lwc.register_callback({ + remove_widows = register_callback({ callback = "pre_output_filter", - func = lwc.remove_widows, - name = "remove_widows", + func = lwc.remove_widows, + name = "remove_widows", lowlevel = true, }), - save_paragraphs = lwc.register_callback({ + save_paragraphs = register_callback({ callback = "pre_linebreak_filter", - func = lwc.save_paragraphs, - name = "save_paragraphs", + func = lwc.save_paragraphs, + name = "save_paragraphs", category = "processors", position = "after", }), - mark_paragraphs = lwc.register_callback({ + mark_paragraphs = register_callback({ callback = "post_linebreak_filter", - func = lwc.mark_paragraphs, - name = "mark_paragraphs", + func = lwc.mark_paragraphs, + name = "mark_paragraphs", category = "finalizers", position = "after", }), @@ -393,11 +420,10 @@ function lwc.enable_callbacks() enabled = true else - lwc.warning("Already enabled") + warning("Already enabled") end end - function lwc.disable_callbacks() if enabled then lwc.callbacks.save_paragraphs.disable() @@ -410,7 +436,7 @@ function lwc.disable_callbacks() enabled = false else - lwc.warning("Already disabled") + warning("Already disabled") end end @@ -422,5 +448,4 @@ function lwc.if_lwc_enabled() end end - return lwc |