From 15103df278e40e78061cca62bd5d956644f3352a Mon Sep 17 00:00:00 2001 From: Norbert Preining Date: Sun, 19 Jun 2022 03:01:19 +0000 Subject: CTAN sync 202206190301 --- .../luatex/generic/lua-widow-control/CHANGELOG.md | 153 ++++++++++++ macros/luatex/generic/lua-widow-control/README.md | 2 +- .../lua-widow-control-2022-02-22.sty | 4 +- .../lua-widow-control/lua-widow-control.lua | 277 +++++++++++++++++---- .../lua-widow-control/lua-widow-control.opm | 3 +- .../lua-widow-control/lua-widow-control.pdf | Bin 459643 -> 509126 bytes .../lua-widow-control/lua-widow-control.sty | 24 +- .../lua-widow-control/lua-widow-control.tex | 3 +- .../generic/lua-widow-control/lwc-manual.tex | 58 ++++- .../lua-widow-control/t-lua-widow-control.mkxl | 7 +- .../lua-widow-control/tb133chernoff-widows.ltx | 20 +- .../lua-widow-control/tb133chernoff-widows.pdf | Bin 333173 -> 391529 bytes 12 files changed, 484 insertions(+), 67 deletions(-) create mode 100644 macros/luatex/generic/lua-widow-control/CHANGELOG.md (limited to 'macros/luatex/generic') diff --git a/macros/luatex/generic/lua-widow-control/CHANGELOG.md b/macros/luatex/generic/lua-widow-control/CHANGELOG.md new file mode 100644 index 0000000000..0314b88e6b --- /dev/null +++ b/macros/luatex/generic/lua-widow-control/CHANGELOG.md @@ -0,0 +1,153 @@ + + +Changelog +========= + +All notable changes to lua-widow-control will be listed here, in reverse chronological order. **Changes listed in bold** are important changes: they either remove options or commands, or may change the location of page breaks. + +## v2.2.0 (2022-06-17) + +- Fix paragraphs not being properly saved for potential expansion. **May affect page breaks.** +- Add a new `draft` option ([#36](https://github.com/gucci-on-fleek/lua-widow-control/issues/36)). +- Fix a node memory leak ([#29](https://github.com/gucci-on-fleek/lua-widow-control/issues/29)). You should now be able to use lua-widow-control on documents with > 10 000 pages. +- Use `\lua_load_module:n` when available. +- Add additional metadata to the documentation. + + +## [v2.1.2 (2022-05-27)](https://github.com/gucci-on-fleek/lua-widow-control/releases/tag/release-3744f3e78bdf02fc63d508a0f80595260191607c) + +- Fully-support footnotes/inserts: lua-widow-control now moves the "footnote text" with the "footnote mark" when it moves a line to the next page. +- No longer attempt to expand paragraphs in `\vbox`es +- Minor documentation updates + + +## [v2.1.1 (2022-05-20)](https://github.com/gucci-on-fleek/lua-widow-control/releases/tag/release-980f24ac64816bd0d453754f8f1af676f0f7ee99) + +- Prevent spurious `under/overfull \vbox` warnings when widows/orphans are removed +- Add TUGboat article to the distributed documentation +- Rewrite many portions of the manual +- Add support for `luahbtex` and `mmoptex` ([#35](https://github.com/gucci-on-fleek/lua-widow-control/pull/35) [@vlasakm](https://github.com/vlasakm)) +- Fix the (undocumented) `microtype` LaTeX option + + +## [v2.1.0 (2022-05-14)](https://github.com/gucci-on-fleek/lua-widow-control/releases/tag/release-82563aa0396805008059e3a96c2bf30b59c58026) + +- Fully support grid snapping in ConTeXt +- New warnings when a new widow/orphan is inadvertently created +- Significant internal rewrites +- Add Plain and OpTeX interfaces to `\nobreak` behaviour and debug mode + + +## [v2.0.6 (2022-04-23)](https://github.com/gucci-on-fleek/lua-widow-control/releases/tag/release-2aa9269b33a03f66d2ece634c3dcba6b258fddf0) + +- Emergency fix for renamed LMTX engine Lua functions +- Internal LaTeX refactoring + + +## [v2.0.5 (2022-04-13)](https://github.com/gucci-on-fleek/lua-widow-control/releases/tag/release-e3234ac7dfb31118d08fcb5ed0fe03f394df2b57) + +- Support nested `\lwcdisablecmd` macros +- Fix `\lwcdisablecmd` in Plain TeX +- Support command patching in OpTeX +- Patch memoir to prevent spurious asterisks at broken two-line paragraphs ([#32](https://github.com/gucci-on-fleek/lua-widow-control/issues/32)) + + +## [v2.0.4 (2022-04-07)](https://github.com/gucci-on-fleek/lua-widow-control/releases/tag/release-8a0e97e448976280a38d41f92c2781320b1a91f0) + +- Don't expand paragraphs typeset during output routines ([#31](https://github.com/gucci-on-fleek/lua-widow-control/issues/31)) + + +## [v2.0.3 (2022-03-28)](https://github.com/gucci-on-fleek/lua-widow-control/releases/tag/release-d6622dd9fd04a4bc7678ff18420c1b4bdf077844) + +- Automatically patch section commands provided by memoir, KOMA-Script, and titlesec. + + +## [v2.0.2 (2022-03-20)](https://github.com/gucci-on-fleek/lua-widow-control/releases/tag/release-7e79189406a2318c33dcaceb85d9d1021b357a3f) + +_Final release present in TeX Live 2021_ + +- Add `balanced` mode preset. + + +## [v2.0.1 (2022-03-18)](https://github.com/gucci-on-fleek/lua-widow-control/releases/tag/release-f3048dbcbfaf4d7d6f6a57e236cdb9684ff5d18d) + +- Documentation updates ([#25](https://github.com/gucci-on-fleek/lua-widow-control/issues/25)) +- Bug fixes ([#28](https://github.com/gucci-on-fleek/lua-widow-control/issues/28)) + + +## [v2.0.0 (2022-03-07)](https://github.com/gucci-on-fleek/lua-widow-control/releases/tag/release-cea06ddad8dfcf15fa9ba2a86c6640648b9df523) + +- **Page breaks may be slightly different** +- **Removed `\lwcemergencystretch` and `\lwcdisablecmd` in LaTeX. Please use the new key–value interface** +- Use expl3 for the LaTeX files ([#20](https://github.com/gucci-on-fleek/lua-widow-control/pull/20)) +- Use a key–value interface for configuration with LaTeX ([#11](https://github.com/gucci-on-fleek/lua-widow-control/issues/11)) +- Silence some extraneous `luatexbase` info messages +- Add a "debug mode" to print extra information ([#12](https://github.com/gucci-on-fleek/lua-widow-control/issues/12)) +- Fix error message line wrapping +- Don't reset `\interlinepenalty` and `\brokenpenalty` +- Set and analyze `\displaywidowpenalty` +- Keep section headings with moved orphans ([#17](https://github.com/gucci-on-fleek/lua-widow-control/issues/17)) +- Add the ability to configure the maximum paragraph cost ([#22](https://github.com/gucci-on-fleek/lua-widow-control/issues/22)) +- Add a "strict" mode +- Use an improved cost function to select the best paragraph to lengthen ([#23](https://github.com/gucci-on-fleek/lua-widow-control/issues/23)) +- Dozens of bug fixes +- Miscellaneous documentation updates + + +## [v1.1.6 (2022-02-22)](https://github.com/gucci-on-fleek/lua-widow-control/releases/tag/release-2c7201854d89535ef7c02f6c38486205677f1aa1) + +- Add support for the OpTeX format/macro package. +- Add support for the LuaTeX/MKIV version of ConTeXt. +- Ensure compatibility with the new `linebreaker` package. +- Fix a small bug with `\lwcdisablecmd`. +- Test the `\outputpenalty` for the specific values that indicate a widow or orphan. + + +## [v1.1.5 (2022-02-15)](https://github.com/gucci-on-fleek/lua-widow-control/releases/tag/release-5cc95212c8141006ae3a600d26a4e0cd63b368c0) + +- Improve the appearance of the demo table in the documentation ([#4](https://github.com/gucci-on-fleek/lua-widow-control/issues/4)) +- Improve compatibility with microtype +- Block loading the package without LuaTeX +- Improve logging +- Bug fix to prevent crashing + + +## [v1.1.4 (2022-02-04)](https://github.com/gucci-on-fleek/lua-widow-control/releases/tag/release-a8caba8e689ce5c743a88dcf1dcd8e4a0db67fb2) + +- Enable protrusion/expansion in the demo table in the documentation ([#3](https://github.com/gucci-on-fleek/lua-widow-control/issues/3)) +- Fix `\prevdepth` bug + + +## [v1.1.3 (2022-01-30)](https://github.com/gucci-on-fleek/lua-widow-control/releases/tag/release-8d1228bf1697e2720062b0c2a40f306005da72e8) + +- Fix bug when used with the LaTeX `calc` package. ([#2](https://github.com/gucci-on-fleek/lua-widow-control/issues/2)) + + +## [v1.1.2 (2021-12-14)](https://github.com/gucci-on-fleek/lua-widow-control/releases/tag/release-61a684d92f1a38ecf0ff53c6da22b2a973fae530) + +- Fix crash under ConTeXt LMTX + + +## [v1.1.1 (2021-11-26)](https://github.com/gucci-on-fleek/lua-widow-control/releases/tag/v1.1.1) + +- Minor documentation updates + + +## [v1.1.0 (2021-11-08)](https://github.com/gucci-on-fleek/lua-widow-control/releases/tag/release-8c958011bb4bd7f6e4ad843321c0d2643645a08f) + +- Extensive documentation updates +- Clarify that lua-widow-control *does* in fact support columns +- Add `\lwcdisablecmd` macro to disable lua-widow-control for certain commands +- Automatically disable lua-widow-control inside section headings (uses `\lwcdisablecmd`) +- Add automated tests to prevent regressions +- Fix a rare issue that would cause indefinite hangs + + + +## [v1.0.0 (2021-10-09)](https://github.com/gucci-on-fleek/lua-widow-control/releases/tag/release-bae44a6858432e095597521bf1ef7df2104b6b9c) + +Initial release diff --git a/macros/luatex/generic/lua-widow-control/README.md b/macros/luatex/generic/lua-widow-control/README.md index c1ce5258f0..61e62cb59a 100644 --- a/macros/luatex/generic/lua-widow-control/README.md +++ b/macros/luatex/generic/lua-widow-control/README.md @@ -48,4 +48,4 @@ Lua-widow-control is licensed under the [_Mozilla Public License_, version 2.0]( Please note that a compiled document is absolutely **not** considered to be an "Executable Form" as defined by the MPL. The use of lua-widow-control in a document does not place **any** obligations on the document's author or distributors. The MPL and CC-BY-SA licenses **only** apply to you if you distribute the lua-widow-control source code or documentation. --- -_v2.1.2 (2022-05-26)_ +_v2.2.0 (2022-06-17)_ diff --git a/macros/luatex/generic/lua-widow-control/lua-widow-control-2022-02-22.sty b/macros/luatex/generic/lua-widow-control/lua-widow-control-2022-02-22.sty index b7b2ab44c0..7464563560 100644 --- a/macros/luatex/generic/lua-widow-control/lua-widow-control-2022-02-22.sty +++ b/macros/luatex/generic/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/macros/luatex/generic/lua-widow-control/lua-widow-control.lua b/macros/luatex/generic/lua-widow-control/lua-widow-control.lua index 4bd109e483..800faf5624 100644 --- a/macros/luatex/generic/lua-widow-control/lua-widow-control.lua +++ b/macros/luatex/generic/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 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 " info messages --- diff --git a/macros/luatex/generic/lua-widow-control/lua-widow-control.opm b/macros/luatex/generic/lua-widow-control/lua-widow-control.opm index f1261e6c24..ff26f856c5 100644 --- a/macros/luatex/generic/lua-widow-control/lua-widow-control.opm +++ b/macros/luatex/generic/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 } %%version +\_codedecl\lwcenable{lua-widow-control } %%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 diff --git a/macros/luatex/generic/lua-widow-control/lua-widow-control.pdf b/macros/luatex/generic/lua-widow-control/lua-widow-control.pdf index f2d8337ee8..36b8db2908 100644 Binary files a/macros/luatex/generic/lua-widow-control/lua-widow-control.pdf and b/macros/luatex/generic/lua-widow-control/lua-widow-control.pdf differ diff --git a/macros/luatex/generic/lua-widow-control/lua-widow-control.sty b/macros/luatex/generic/lua-widow-control/lua-widow-control.sty index 89f417a457..7d1be850b9 100644 --- a/macros/luatex/generic/lua-widow-control/lua-widow-control.sty +++ b/macros/luatex/generic/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/macros/luatex/generic/lua-widow-control/lua-widow-control.tex b/macros/luatex/generic/lua-widow-control/lua-widow-control.tex index b8ae064c04..2e1dcef902 100644 --- a/macros/luatex/generic/lua-widow-control/lua-widow-control.tex +++ b/macros/luatex/generic/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/macros/luatex/generic/lua-widow-control/lwc-manual.tex b/macros/luatex/generic/lua-widow-control/lwc-manual.tex index d2eb12c82e..c890238371 100644 --- a/macros/luatex/generic/lua-widow-control/lwc-manual.tex +++ b/macros/luatex/generic/lua-widow-control/lwc-manual.tex @@ -39,10 +39,32 @@ \input lwc-manual-samples +\definecolor[expanded][r=0.00, g=0.70, b=0.25] +\definecolor[failure] [r=0.90, g=0.00, b=0.25] +\definecolor[moved] [r=0.25, g=0.25, b=1.00] + +\usemodule[markdown] +\define[4]\markdownRendererLinkPrototype{\goto{#1}[url(#3)]} +\def\markdownOptionHtml{true} +\def\markdownOptionShiftHeadings{1} +\startluacode + interfaces.implement { + name = "markdownRendererHeadingThreePrototype", + public = true, + arguments = { "string" }, + actions = function (str) + context.subsection { + title = str, + list = str:match "v%d+%.%d+%.%d+" + } + end + } +\stopluacode + \startdocument[ title=lua-widow-control, author=Max Chernoff, - version=2.1.2, %%version + version=2.2.0, %%version github=https://github.com/gucci-on-fleek/lua-widow-control, ctan=https://www.ctan.org/pkg/lua-widow-control, ] @@ -501,9 +523,27 @@ sets the values to~5\,000 and 10\,000 respectively. C = \frac{d}{\sqrt{l}} \stopformula where $d$ is the total demerits of the paragraph, and $l$ is the number of lines in the paragraph; however, advanced users may also set a custom cost function by redefining the \inlineLUA{lwc.paragraph_cost(demerits, lines)} function. +\subsection{Draft Mode} + +You can use the draft mode to visualize how \lwc/ processes pages. Any \color[failure]{remaining widows and orphans will be coloured red}, any \color[expanded]{expanded paragraphs will be coloured green}, and any \color[moved]{lines moved to the next page will be coloured blue}. In addition, the cost of each paragraph is shown in the right margin. + +\startTABLE[setups=commandtable] + \NC \plainop/ + \NC\inlineTEX{\lwcdraft 1} + \NC\NR + \NC \LaTeX{} + \NC\inlineTEX{\lwcsetup{draft}} + \NC\NR + \NC \ConTeXt{} + \NC\inlineTEX{\setuplwc[draft = start]} + \NC\NR +\stopTABLE + +Advanced users may also customize the colours used by modifying the \inlineLUA{lwc.colours} table. The table keys are currently \type{expanded}, \type{failure}, and \type{moved}. The table values are \acronym{RGB} 3-tuples, where each element is a float between zero and one. + \subsection{Debug Mode} -\Lwc/ offers a \q{debug} mode that prints extra information in the log files. This may be helpful to understand how \lwc/ is processing paragraphs and pages. If you are reporting an issue with \lwc/ make sure to compile your document with debug mode enabled! +\Lwc/ offers a \q{debug} mode that prints extra information in the log files. This may be helpful to understand how \lwc/ is processing paragraphs and pages, although the information is likely inscrutable unless you are the package's author. If you are reporting an issue with \lwc/ make sure to compile your document with debug mode enabled! \startTABLE[setups=commandtable] \NC \plainop/ @@ -712,6 +752,8 @@ ultra-short~lines. system remove widows or orphans that \lwc/ cannot. \item \Lwc/ does not move footnotes in \mkxl/ due to limitations with the \LuaMetaTeX{} engine. + + \item Draft mode does not colour any text in \mkxl/. Draft mode will not colour the first line of a page in \mkiv/ and \OpTeX{}. \stopitemize \section{Contributions} @@ -729,6 +771,16 @@ Please note that a compiled document is \bold{not} considered to be an \q{Execut \placelistofpublications \page + +\setuphead[subsection][ + alternative=normal, + style=\ssitb, + after={\blank[disable, penalty:10000]} +] +\markdownInput{../../CHANGELOG.md} + +\page + \setuplayout[ width=middle, backspace=1in, @@ -745,8 +797,6 @@ If want to offer any improvements to the code below, please open an issue or a \ \usemodule[scite] \setupbodyfont[10pt] \setuphead[subsection][ - alternative=normal, - style=\ssita, page=yes, continue=yes, ] diff --git a/macros/luatex/generic/lua-widow-control/t-lua-widow-control.mkxl b/macros/luatex/generic/lua-widow-control/t-lua-widow-control.mkxl index 682e8d23c5..5982533202 100644 --- a/macros/luatex/generic/lua-widow-control/t-lua-widow-control.mkxl +++ b/macros/luatex/generic/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/macros/luatex/generic/lua-widow-control/tb133chernoff-widows.ltx b/macros/luatex/generic/lua-widow-control/tb133chernoff-widows.ltx index af35ab0f4d..7cd18ab68c 100644 --- a/macros/luatex/generic/lua-widow-control/tb133chernoff-widows.ltx +++ b/macros/luatex/generic/lua-widow-control/tb133chernoff-widows.ltx @@ -13,6 +13,7 @@ % journal={TUGboat}, % volume={43}, % number={1}, +% pages={28--39}, % year={2022}, % month=may, % DOI={10.47397/tb/43-1/tb133chernoff-widows}, @@ -156,9 +157,26 @@ }}% } -\let\nameref=\undefined % latex3/hyperref#234 \usepackage[hidelinks,pdfa]{hyperref} +\usepackage{hyperxmp} +\hypersetup{ + pdfdisplaydoctitle=true, + pdftitle={Automatically removing widows and orphans with lua-widow-control}, + pdfauthor={Max Chernoff}, + pdflang={en}, + pdfcontacturl={https://github.com/gucci-on-fleek/lua-widow-control}, + pdfcopyright={SPDX-License-Identifier: MPL-2.0+ OR CC-BY-SA-4.0+}, + pdflicenseurl={https://creativecommons.org/licenses/by-sa/4.0/}, + pdfdoi={10.47397/tb/43-1/tb133chernoff-widows}, + pdfpublication={TUGboat}, + pdfpubtype={journal}, + pdfvolumenum={43}, + pdfissuenum={1}, + pdfpagerange={28-39}, + pdfdate={2022-05} +} + \begin{document} \maketitle diff --git a/macros/luatex/generic/lua-widow-control/tb133chernoff-widows.pdf b/macros/luatex/generic/lua-widow-control/tb133chernoff-widows.pdf index 9c9de8bd02..b506109496 100644 Binary files a/macros/luatex/generic/lua-widow-control/tb133chernoff-widows.pdf and b/macros/luatex/generic/lua-widow-control/tb133chernoff-widows.pdf differ -- cgit v1.2.3