summaryrefslogtreecommitdiff
path: root/Master/texmf-dist/tex
diff options
context:
space:
mode:
authorKarl Berry <karl@freefriends.org>2022-06-18 20:06:28 +0000
committerKarl Berry <karl@freefriends.org>2022-06-18 20:06:28 +0000
commit6aef67d9f5fa3aa0eff4adc1196a7cf4e36c81a9 (patch)
treef96c841c384afd9888443815fff064c92cedea40 /Master/texmf-dist/tex
parentebf61fc815a3b70fb28f94ecffbcc7243dcf833c (diff)
lua-widow-control (18jun22)
git-svn-id: svn://tug.org/texlive/trunk@63642 c570f23f-e606-0410-a88d-b1316a301751
Diffstat (limited to 'Master/texmf-dist/tex')
-rw-r--r--Master/texmf-dist/tex/context/third/lua-widow-control/t-lua-widow-control.mkxl7
-rw-r--r--Master/texmf-dist/tex/lualatex/lua-widow-control/lua-widow-control-2022-02-22.sty4
-rw-r--r--Master/texmf-dist/tex/lualatex/lua-widow-control/lua-widow-control.sty24
-rw-r--r--Master/texmf-dist/tex/luatex/lua-widow-control/lua-widow-control.lua277
-rw-r--r--Master/texmf-dist/tex/luatex/lua-widow-control/lua-widow-control.tex3
-rw-r--r--Master/texmf-dist/tex/optex/lua-widow-control/lua-widow-control.opm3
6 files changed, 257 insertions, 61 deletions
diff --git a/Master/texmf-dist/tex/context/third/lua-widow-control/t-lua-widow-control.mkxl b/Master/texmf-dist/tex/context/third/lua-widow-control/t-lua-widow-control.mkxl
index 682e8d23c5d..59825332020 100644
--- a/Master/texmf-dist/tex/context/third/lua-widow-control/t-lua-widow-control.mkxl
+++ b/Master/texmf-dist/tex/context/third/lua-widow-control/t-lua-widow-control.mkxl
@@ -1,10 +1,10 @@
%D \module
%D [ file=t-lua-widow-control,
-%D version=2.1.2, %%version
+%D version=2.2.0, %%version
%D title=lua-widow-control,
%D subtitle=\ConTeXt module for lua-widow-control,
%D author=Max Chernoff,
-%D date=2022-05-26, %%dashdate
+%D date=2022-06-17, %%dashdate
%D copyright=Max Chernoff,
%D license=MPL-2.0+,
%D url=https://github.com/gucci-on-fleek/lua-widow-control]
@@ -28,6 +28,8 @@
\lwc_debug{\lwcparameter{debug}}
+ \lwc_draft{\lwcparameter{draft}}
+
\lwc_nobreak{\lwcparameter{nobreak}}
\lwc_max_cost=\lwcparameter{maxcost}
@@ -61,6 +63,7 @@
emergencystretch=3em,
\c!state=\v!start,
debug=\v!stop,
+ draft=\v!stop,
orphanpenalty=1,
widowpenalty=1,
brokenpenalty=1,
diff --git a/Master/texmf-dist/tex/lualatex/lua-widow-control/lua-widow-control-2022-02-22.sty b/Master/texmf-dist/tex/lualatex/lua-widow-control/lua-widow-control-2022-02-22.sty
index b7b2ab44c04..74645635600 100644
--- a/Master/texmf-dist/tex/lualatex/lua-widow-control/lua-widow-control-2022-02-22.sty
+++ b/Master/texmf-dist/tex/lualatex/lua-widow-control/lua-widow-control-2022-02-22.sty
@@ -12,8 +12,8 @@
% report a real version number here for debugging.
\PackageInfo{lua-widow-control}{%
Real version:
- 2022/05/26 %%slashdate
- v2.1.2 %%version
+ 2022/06/17 %%slashdate
+ v2.2.0 %%version
}
\PackageWarning{lua-widow-control}{%
diff --git a/Master/texmf-dist/tex/lualatex/lua-widow-control/lua-widow-control.sty b/Master/texmf-dist/tex/lualatex/lua-widow-control/lua-widow-control.sty
index 89f417a4576..7d1be850b91 100644
--- a/Master/texmf-dist/tex/lualatex/lua-widow-control/lua-widow-control.sty
+++ b/Master/texmf-dist/tex/lualatex/lua-widow-control/lua-widow-control.sty
@@ -13,7 +13,7 @@
\DeclareRelease{}{0000-00-00}{lua-widow-control-2022-02-22.sty}
\DeclareRelease{v1.1.6}{2022-02-22}{lua-widow-control-2022-02-22.sty}
-\DeclareCurrentRelease{v2.1.2}{2022-05-26} %%version %%dashdate
+\DeclareCurrentRelease{v2.2.0}{2022-06-17} %%version %%dashdate
% If this version of LaTeX doesn't support command hooks, then we load
% the last v1.1.X version of the package.
@@ -23,8 +23,8 @@
\ProvidesExplPackage
{lua-widow-control}
- {2022/05/26} %%slashdate
- {v2.1.2} %%version
+ {2022/06/17} %%slashdate
+ {v2.2.0} %%version
{Use Lua to remove widows and orphans}
% Unconditional Package Loads
@@ -116,7 +116,11 @@
}
% Load the Lua code
-\lua_now:n { require "lua-widow-control" }
+\cs_if_exist:NTF \lua_load_module:n {
+ \lua_load_module:n { lua-widow-control }
+} {
+ \lua_now:n { require "lua-widow-control" }
+}
% Here, we enable font expansion/contraction. It isn't strictly necessary for
% \lwc/'s functionality; however, it is required for the
@@ -220,8 +224,16 @@
disable .value_forbidden:n = true,
debug .choice:,
- debug / true .code:n = \__lwc_debug:n { true },
- debug / false .code:n = \__lwc_debug:n { false },
+ debug / true .code:n = \__lwc_debug:n { true },
+ debug / false .code:n = \__lwc_debug:n { false },
+ debug .default:n = true,
+ debug .value_required:n = false,
+
+ draft .choice:,
+ draft / true .code:n = \__lwc_draft:n { true },
+ draft / false .code:n = \__lwc_draft:n { false },
+ draft .default:n = true,
+ draft .value_required:n = false,
nobreak .code:n = \__lwc_nobreak:n { #1 },
nobreak .value_required:n = true,
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 4bd109e4839..800faf56242 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
@@ -118,10 +118,17 @@ local iftrue = token.create("iftrue")
local INFINITY = 10000
local INSERT_CLASS_MULTIPLE = 1000 * 1000
local INSERT_FIRST_MULTIPLE = 1000
+local llap_offset = math.max(tex.dimen.parindent, tex.sp("12pt"))
local min_col_width = tex.sp("250pt")
local PAGE_MULTIPLE = 100
local SINGLE_LINE = 50
+lwc.colours = {
+ expanded = {0.00, 0.70, 0.25},
+ failure = {0.90, 0.00, 0.25},
+ moved = {0.25, 0.25, 1.00},
+}
+
--[[ Package/module initialization.
Here, we replace any format/engine-specific variables/functions with some
@@ -135,6 +142,7 @@ local contrib_head,
max_cost,
pagenum,
paragraph_attribute,
+ shrink_order,
stretch_order,
warning
@@ -142,9 +150,11 @@ if lmtx then
-- LMTX has removed underscores from most of the Lua parts
debug("LMTX")
contrib_head = "contributehead"
+ shrink_order = "shrinkorder"
stretch_order = "stretchorder"
else
contrib_head = "contrib_head"
+ shrink_order = "shrink_order"
stretch_order = "stretch_order"
end
@@ -184,8 +194,8 @@ elseif plain or latex or optex then
debug("Plain/LaTeX")
luatexbase.provides_module {
name = lwc.name,
- date = "2022/05/26", --%%slashdate
- version = "2.1.2", --%%version
+ date = "2022/06/17", --%%slashdate
+ version = "2.2.0", --%%version
description = [[
This module provides a LuaTeX-based solution to prevent
@@ -211,6 +221,23 @@ else -- This shouldn't ever happen
Please use LaTeX, Plain TeX, ConTeXt or OpTeX.]]
end
+--[[ Select the fonts
+
+ We want to use cmr7 for the draft mode cost displays, and the easiest
+ way to do so is to just hardcode the font id's. This relies on some
+ implementation details; however, it is very unlikely to ever be an issue
+ ]]
+local SMALL_FONT
+if plain then
+ SMALL_FONT = 4
+elseif latex then
+ SMALL_FONT = 7
+elseif optex then
+ SMALL_FONT = 7
+elseif context then
+ SMALL_FONT = 3
+end
+
--[[ Table to hold the alternate paragraph versions.
This is global(ish) mutable state, which isn't ideal, but any other way of
@@ -219,8 +246,7 @@ end
local paragraphs = {}
local inserts = {}
---[[
- Function definitions
+--[[ Function definitions
]]
--- Gets the current paragraph and page locations
@@ -268,6 +294,7 @@ end
--- Checks if the ConTeXt "grid snapping" is active
+---
--- @return boolean
local function grid_mode_enabled()
-- Compare the token "mode" to see if `\\ifgridsnapping` is `\\iftrue`
@@ -371,6 +398,91 @@ local function natural_paragraph(head)
end
+lwc.draft_mode = false
+--- Changes the text colour in a node list if draft mode is active
+---
+--- @param head node The first node to colour
+--- @param colour table<number> A 3-tuple of RGB values
+--- @return node head The coloured node
+local function colour_list(head, colour)
+ if not lwc.draft_mode then
+ return head
+ end
+
+ -- Adapted from https://tex.stackexchange.com/a/372437
+ -- \\pdfextension colorstack is ignored in LMTX
+ local start_colour = new_node("whatsit", "pdf_colorstack")
+ start_colour.stack = 0
+ start_colour.command = 1
+ start_colour.data = string.format("%.2f %.2f %.2f rg", table.unpack(colour))
+
+ local end_colour = new_node("whatsit", "pdf_colorstack")
+ end_colour.stack = 0
+ end_colour.command = 2
+
+ start_colour.next = head
+ last(head).next = end_colour
+
+ return start_colour
+end
+
+
+--- Generate an \\llap'ed box containing the provided string
+---
+--- @param str string The string to typeset
+--- @return node head The box node
+local function llap_string(str)
+ local first = new_node("glue")
+ first.width = llap_offset
+
+ local m = first
+ for letter in str:gmatch(".") do
+ local n = new_node("glyph")
+ n.font = SMALL_FONT
+ n.char = string.byte(letter)
+
+ m.next = n
+ m = n
+ end
+
+ local hss = new_node("glue")
+ hss.stretch = 1
+ hss[stretch_order] = 1
+ hss.shrink = 1
+ hss[shrink_order] = 1
+ m.next = hss
+
+ return node.hpack(first, 0, "exactly")
+end
+
+
+--- Typesets the cost of a paragraph beside it in draft mode
+---
+--- @param paragraph node
+--- @param cost number
+--- @return nil
+local function show_cost(paragraph, cost)
+ if not lwc.draft_mode then
+ return
+ end
+
+ local last_hlist_end = last(next_of_type(
+ last(paragraph),
+ hlist_id,
+ { subtype = line_subid, reverse = true }
+ ).list)
+
+ local cost_str
+ if cost < math.maxinteger then
+ cost_str = string.format("%.0f", cost)
+ else
+ cost_str = "infinite"
+ end
+
+ last_hlist_end.next = llap_string(cost_str)
+end
+
+
--- Saves each paragraph, but lengthened by 1 line
---
--- Called by the `pre_linebreak_filter` callback
@@ -411,15 +523,27 @@ function lwc.save_paragraphs(head)
local long_cost = lwc.paragraph_cost(long_info.demerits, long_info.prevgraf)
- if long_info.prevgraf == natural_info.prevgraf + 1 and
- long_cost > 10 -- Any paragraph that is "free" to expand is suspicious
+ if long_info.prevgraf ~= natural_info.prevgraf + 1 or
+ long_cost < 10 -- Any paragraph that is "free" to expand is suspicious
then
- table.insert(paragraphs, {
- cost = long_cost,
- node = next_of_type(long_node, hlist_id, { subtype = line_subid })
- })
+ -- This paragraph is infinitely bad
+ long_cost = math.maxinteger
end
+ local saved_node = next_of_type(long_node, hlist_id, { subtype = line_subid })
+
+ show_cost(saved_node, long_cost)
+ for n in traverse_id(hlist_id, saved_node) do
+ n.list = colour_list(n.list, lwc.colours.expanded)
+ end
+
+ table.insert(paragraphs, {
+ cost = long_cost,
+ node = copy_list(saved_node)
+ })
+
+ free_list(long_node)
+
-- Print some debugging information
get_chars(head)
debug(get_location(), "nat lines " .. natural_info.prevgraf)
@@ -452,21 +576,21 @@ local function mark_paragraphs(head)
-- Tag the paragraphs
if not status.output_active then -- Don't run during the output routine
-- Get the start and end of the paragraph
- local top_para = next_of_type(head, hlist_id, { subtype = line_subid })
- local bottom_para = last(head)
+ local top = next_of_type(head, hlist_id, { subtype = line_subid })
+ local bottom = last(head)
- while bottom_para.id == insert_id do
- bottom_para = bottom_para.prev
+ while bottom.id == insert_id do
+ bottom = bottom.prev
end
- if top_para ~= bottom_para then
+ if top ~= bottom then
set_attribute(
- top_para,
+ top,
paragraph_attribute,
#paragraphs + (PAGE_MULTIPLE * pagenum())
)
set_attribute(
- bottom_para,
+ bottom,
paragraph_attribute,
-1 * (#paragraphs + (PAGE_MULTIPLE * pagenum()))
)
@@ -474,11 +598,15 @@ local function mark_paragraphs(head)
-- We need a special tag for a 1-line paragraph since the node can only
-- have a single attribute value
set_attribute(
- top_para,
+ top,
paragraph_attribute,
#paragraphs + (PAGE_MULTIPLE * pagenum()) + SINGLE_LINE
)
end
+
+ if #paragraphs > 0 then
+ show_cost(head, paragraphs[#paragraphs].cost)
+ end
end
end
@@ -582,12 +710,14 @@ end
---
--- @return nil
local function reset_state()
+ for _, paragraph in ipairs(paragraphs) do
+ free_list(paragraph.node)
+ end
paragraphs = {}
for _, insert in ipairs(inserts) do
free(insert)
end
-
inserts = {}
end
@@ -597,6 +727,25 @@ end
--- @return nil
local function remove_widows_fail()
warning("Widow/Orphan/broken hyphen NOT removed on page " .. pagenum())
+
+ local last_line = next_of_type(
+ last(tex_lists.page_head),
+ hlist_id,
+ { subtype = line_subid, reverse = true }
+ )
+ if last_line then
+ last_line.list = colour_list(last_line.list, lwc.colours.failure)
+ end
+
+ local next_first_line = next_of_type(
+ tex_lists[contrib_head],
+ hlist_id,
+ { subtype = line_subid }
+ )
+ if next_first_line then
+ next_first_line.list = colour_list(next_first_line.list, lwc.colours.failure)
+ end
+
reset_state()
end
@@ -625,18 +774,24 @@ local function first_last_paragraphs(head)
-- Find the first paragraph on the page, from the top
local first_val, first_head = find_attribute(head, paragraph_attribute)
- if first_val // PAGE_MULTIPLE == pagenum() - 1 then
+ while abs(first_val) // PAGE_MULTIPLE == pagenum() - 1 do
--[[ If the first complete paragraph on the page was initially broken on the
previous page, then we can't expand it here so we need to skip it.
]]
- first_index = find_attribute(
+ first_val, first_head = find_attribute(
first_head.next,
paragraph_attribute
- ) % PAGE_MULTIPLE
- else
- first_index = first_val % PAGE_MULTIPLE
+ )
end
+ first_index = first_val % PAGE_MULTIPLE
+
+ if first_index >= SINGLE_LINE then
+ first_index = first_index - SINGLE_LINE
+ end
+
+ debug("first/last", first_index .. "/" .. last_index)
+
return first_index, last_index
end
@@ -653,21 +808,13 @@ local function best_paragraph(head)
local best_index = 1
local best_cost = paragraphs[best_index].cost
- -- We find the current "best" replacement, then free the unused ones
+ -- We find the current "best" replacement
for index, paragraph in pairs(paragraphs) do
if paragraph.cost < best_cost and
index < last_paragraph_index and
index >= first_paragraph_index
then
- -- Free the old best paragraph
- free_list(paragraphs[best_index].node)
- paragraphs[best_index].node = nil
- -- Set the new best paragraph
best_index, best_cost = index, paragraph.cost
- elseif index > 1 then
- -- Not sure why `i > 1` is required?
- free_list(paragraph.node)
- paragraph.node = nil
end
end
@@ -677,7 +824,8 @@ local function best_paragraph(head)
)
if best_cost > tex_count[max_cost] or
- best_index == last_paragraph_index
+ best_index == last_paragraph_index or
+ best_cost == math.maxinteger
then
return nil
else
@@ -737,9 +885,11 @@ local function get_inserts(last_line)
if box_value > 0 then
selected_inserts[#selected_inserts + 1] = copy(inserts[box_value])
end
- end
- m = m.next
+ m = free(m)
+ else
+ m = m.next
+ end
end
if not insert_box.list then
@@ -811,10 +961,19 @@ local function move_last_line(head)
is_matching_penalty(potential_penalty.penalty)
then
warning("Making a new widow/orphan/broken hyphen on page " .. pagenum())
+
+ local second_last_line = next_of_type(
+ potential_penalty,
+ hlist_id,
+ { subtype = line_subid, reverse = true }
+ )
+ second_last_line.list = colour_list(second_last_line.list, lwc.colours.failure)
end
last_line = copy_list(n)
+ last_line.list = colour_list(last_line.list, lwc.colours.moved)
+
-- Reinsert any inserts originally present in this moved line
local selected_inserts = get_inserts(last_line)
for _, insert in ipairs(selected_inserts) do
@@ -824,10 +983,11 @@ local function move_last_line(head)
-- Add back in the content from the next page
last(last_line).next = copy_list(tex_lists[contrib_head])
+ free_list(n.prev.prev.next)
n.prev.prev.next = nil
-- Set the content of the next page
- last(last_line)
+ free_list(tex_lists[contrib_head])
tex_lists[contrib_head] = last_line
return true
@@ -842,11 +1002,11 @@ end
--- @param head node
--- @param paragraph_index number
local function replace_paragraph(head, paragraph_index)
- local target_node = paragraphs[paragraph_index].node
- local free_next_nodes = false
+ local target_node = copy_list(paragraphs[paragraph_index].node)
local start_found = false
local end_found = false
+ local free_nodes_begin
-- Loop through all of the nodes on the page with the lwc attribute
local n = head
@@ -884,7 +1044,7 @@ local function replace_paragraph(head, paragraph_index)
end
n.prev.next = target_node
- free_next_nodes = true
+ free_nodes_begin = n
end
-- Insert the end of the replacement paragraph
@@ -907,17 +1067,17 @@ local function replace_paragraph(head, paragraph_index)
target_node_last.next = n.next
end
+ n.next = nil
+
break
end
- if free_next_nodes then
- n = free(n)
- else
- n = n.next
- end
+ n = n.next
end
- if not (start_found and end_found) then
+ if start_found and end_found then
+ free_list(free_nodes_begin)
+ else
warning("Paragraph NOT expanded on page " .. pagenum())
end
end
@@ -946,24 +1106,30 @@ function lwc.remove_widows(head)
-- Nothing that we can do if there aren't any paragraphs available to expand
if #paragraphs == 0 then
+ debug("failure", "no paragraphs to expand")
remove_widows_fail()
return head
end
-- Check the original height of \\box255
local vsize = tex_dimen.vsize
- local orig_height_diff = vpack(head).height - vsize
+ local orig_vpack = vpack(head)
+ local orig_height_diff = orig_vpack.height - vsize
+ orig_vpack.list = nil
+ free(orig_vpack)
-- Find the paragraph to expand
local paragraph_index = best_paragraph(head)
if not paragraph_index then
+ debug("failure", "no good paragraph")
remove_widows_fail()
return head
end
-- Move the last line of the page to the next page
if not move_last_line(head) then
+ debug("failure", "can't move last line")
remove_widows_fail()
return head
end
@@ -975,13 +1141,19 @@ function lwc.remove_widows(head)
over/underfull box warnings, so we correct any discrepancies
here.
]]
- local new_height_diff = vpack(head).height - vsize
+ local new_vpack = vpack(head)
+ local new_height_diff = new_vpack.height - vsize
+ new_vpack.list = nil
+ free(new_vpack)
-- We need the original height discrepancy in case there are \\vfill's
local net_height_diff = orig_height_diff - new_height_diff
+ local bls = tex.skip.baselineskip
+ local bls_width = bls.width
+ free(bls)
if abs(net_height_diff) > 0 and
-- A difference larger than 0.25\\baselineskip is probably not from \lwc/
- abs(net_height_diff) < tex.skip.baselineskip.width / 4
+ abs(net_height_diff) < bls_width / 4
then
local bottom_glue = new_node("glue")
bottom_glue.width = net_height_diff
@@ -1214,6 +1386,13 @@ register_tex_cmd(
end,
{ "string" }
)
+register_tex_cmd(
+ "draft",
+ function(str)
+ lwc.draft_mode = str ~= "0" and str ~= "false" and str ~= "stop"
+ end,
+ { "string" }
+)
--- Silence the luatexbase "Enabling/Removing <callback>" info messages
---
diff --git a/Master/texmf-dist/tex/luatex/lua-widow-control/lua-widow-control.tex b/Master/texmf-dist/tex/luatex/lua-widow-control/lua-widow-control.tex
index b8ae064c044..2e1dcef902e 100644
--- a/Master/texmf-dist/tex/luatex/lua-widow-control/lua-widow-control.tex
+++ b/Master/texmf-dist/tex/luatex/lua-widow-control/lua-widow-control.tex
@@ -3,7 +3,7 @@
% SPDX-License-Identifier: MPL-2.0+
% SPDX-FileCopyrightText: 2022 Max Chernoff
-\wlog{lua-widow-control v2.1.2} %%version
+\wlog{lua-widow-control v2.2.0} %%version
\ifx\directlua\undefined
\errmessage{%
@@ -88,6 +88,7 @@
\let\lwcenable=\lwc@enable
\let\lwcdisable=\lwc@disable
\let\lwcdebug=\lwc@debug
+\let\lwcdraft=\lwc@draft
\let\iflwc=\lwc@if@enabled
\let\lwcnobreak=\lwc@nobreak
diff --git a/Master/texmf-dist/tex/optex/lua-widow-control/lua-widow-control.opm b/Master/texmf-dist/tex/optex/lua-widow-control/lua-widow-control.opm
index f1261e6c249..ff26f856c5f 100644
--- a/Master/texmf-dist/tex/optex/lua-widow-control/lua-widow-control.opm
+++ b/Master/texmf-dist/tex/optex/lua-widow-control/lua-widow-control.opm
@@ -3,7 +3,7 @@
% SPDX-License-Identifier: MPL-2.0+
% SPDX-FileCopyrightText: 2022 Max Chernoff
-\_codedecl\lwcenable{lua-widow-control <v2.1.2>} %%version
+\_codedecl\lwcenable{lua-widow-control <v2.2.0>} %%version
\_namespace{lwc}
\_clubpenalty=1
@@ -72,6 +72,7 @@
\_let\lwcdisable=\.disable
\_let\lwcdisablecmd=\.disable_cmd
\_let\lwcdebug=\.debug
+\_let\lwcdraft=\.draft
\_let\iflwc=\.if_enabled
\_let\lwcnobreak=\.nobreak