diff options
Diffstat (limited to 'Master')
11 files changed, 902 insertions, 268 deletions
diff --git a/Master/texmf-dist/doc/luatex/lua-widow-control/README.md b/Master/texmf-dist/doc/luatex/lua-widow-control/README.md index d9ccf7b4549..920873c5b06 100644 --- a/Master/texmf-dist/doc/luatex/lua-widow-control/README.md +++ b/Master/texmf-dist/doc/luatex/lua-widow-control/README.md @@ -35,6 +35,11 @@ To load the package, add the relevant line to your preamble: |OpTeX |`\load[lua-widow-control]` | +Contributing +------------ + +Please see [`contributing.md`](https://github.com/gucci-on-fleek/lua-widow-control/blob/master/.github/contributing.md). + Licence ------- @@ -43,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. --- -_v1.1.6 (2022-02-22)_ <!--%%version %%date--> +_v2.0.0 (2022-03-07)_ <!--%%version %%dashdate--> diff --git a/Master/texmf-dist/doc/luatex/lua-widow-control/lua-widow-control.pdf b/Master/texmf-dist/doc/luatex/lua-widow-control/lua-widow-control.pdf Binary files differindex 4560ff7c560..9243a3a07c9 100644 --- a/Master/texmf-dist/doc/luatex/lua-widow-control/lua-widow-control.pdf +++ b/Master/texmf-dist/doc/luatex/lua-widow-control/lua-widow-control.pdf diff --git a/Master/texmf-dist/source/luatex/lua-widow-control/lwc-documentation.mkxl b/Master/texmf-dist/source/luatex/lua-widow-control/lwc-documentation.mkxl index 0256bdd56a2..4edaa106941 100644 --- a/Master/texmf-dist/source/luatex/lua-widow-control/lwc-documentation.mkxl +++ b/Master/texmf-dist/source/luatex/lua-widow-control/lwc-documentation.mkxl @@ -138,6 +138,8 @@ setbounds currentpicture to boundingbox OverlayBox; after={\blank[medium]} ] +\define[1]\meta{\m{\langle}\italic{#1}\m{\rangle}} + %%%%%%%%%%%%%%%%%%%%%%%%%%%% %%%%% Section Commands %%%%% %%%%%%%%%%%%%%%%%%%%%%%%%%%% @@ -183,6 +185,7 @@ setbounds currentpicture to boundingbox OverlayBox; \stopcurrentlistentrywrapper \par + \nobreak \setbox0=\vbox{\framedtext[ % Align the subsection titles under the section title frame=off, @@ -199,6 +202,8 @@ setbounds currentpicture to boundingbox OverlayBox; \else \box0 \fi + + \goodbreak \stoptexdefinition \newif\iffirstsubsection diff --git a/Master/texmf-dist/source/luatex/lua-widow-control/lwc-documentation.tex b/Master/texmf-dist/source/luatex/lua-widow-control/lwc-documentation.tex index bbee53cee14..1194acb44fa 100644 --- a/Master/texmf-dist/source/luatex/lua-widow-control/lwc-documentation.tex +++ b/Master/texmf-dist/source/luatex/lua-widow-control/lwc-documentation.tex @@ -24,6 +24,7 @@ \def\waos/{widows and orphans} \def\wao/{widow and orphan} \def\woo/{widow or orphan} +\def\plainop/{Plain~\TeX{}/Op\TeX{}} \let\q=\quotation \def\titlecite[#1]{\cite[title][#1]\cite[#1]} @@ -37,7 +38,7 @@ \startdocument[ title=lua-widow-control, author=Max Chernoff, - version=1.1.6, %%version + version=2.0.0, %%version github=https://github.com/gucci-on-fleek/lua-widow-control, ctan=https://www.ctan.org/pkg/lua-widow-control, ] @@ -45,7 +46,7 @@ \Lwc/ is a Plain~\TeX/\LaTeX/\ConTeXt{}/Op\TeX{} package that removes \waos/ without any user intervention. Using the power of \LuaTeX{}, it does so \emph{without} stretching any glue or shortening any pages or columns. Instead, \lwc/ automatically lengthens a paragraph on a page or column where a \woo/ would otherwise occur. \section{Quick Start} -Ensure that your \TeX~Live/Mik\TeX{} distribution is up-to-date. Then, \LaTeX{} users just need to place \inlineTEX{\usepackage{lua-widow-control}} in the preamble of your document. For more details, see the \goto{Installation and Usage sections}[sec:install]. +Ensure that your \TeX~Live/Mik\TeX{} distribution is up-to-date. Then, \LaTeX{} users just need to place \inlineTEX{\usepackage{lua-widow-control}} in the preamble of your document. For more details, see the \goto{Usage sections}[sec:usage]. \subject{Contents} \placecontent[criterium=all] @@ -157,7 +158,7 @@ To do so, \lwc/ lengthened the second paragraph by one line. If you look closely \Lwc/ automatically finds the \q{best} paragraph to stretch, so the increase in interword spaces should almost always be minimal. -\section[sec:install]{Installation} +\section{Installation} Most up-to-date \TeX~Live and Mik\TeX{} systems should already have \lwc/ installed. However, a manual installation may occasionally be required. \subsection{\TeX~Live} @@ -190,7 +191,11 @@ Currently, \ConTeXt{} \acronym{MKXL} (\LuaMetaTeX{}) users must manually install \Lwc/ requires \LuaTeX{} ($\ge$ 0.85) and the most recent version of \sans{luatexbase} (2015/10/04). Any version of \TeX~Live $\ge$ 2016 will meet these requirements. \subsection{\LaTeX{}} -\Lwc/ requires \LuaTeX{} ($\ge$ 0.85), \LaTeX{} ($\ge$ 2015/01/01), \sans{microtype} (any version), and \sans{etoolbox} (any version). Any version of \TeX~Live $\ge$ 2016 will meet these requirements. +\Lwc/ requires \LuaTeX{} ($\ge$ 0.85), \LaTeX{} ($\ge$ 2020/10/01), and \sans{microtype} (any version). Any version of \TeX~Live $\ge$ 2021 will meet these requirements. + +\Lwc/ also supports a \q{legacy} mode for older \LaTeX{} kernels. This uses an older version of the \LaTeX{} code while still using the most recent Lua code. This mode requires \LuaTeX{} ($\ge$ 0.85), \LaTeX{} ($\ge$ 2015/01/01), \sans{microtype} (any version), and \sans{etoolbox} (any version). Any version of \TeX~Live $\ge$ 2016 will meet these requirements. + +Please note that when running in legacy mode, you cannot use the key--value interface. Instead, you should follow the \q{Plain~\TeX{}} interface. \subsection{\ConTeXt{}} \Lwc/ supports both \ConTeXt{} \acronym{MKXL} (\LuaMetaTeX{}) and \ConTeXt{} \acronym{MKIV} (\LuaTeX{}). @@ -199,88 +204,221 @@ Currently, \ConTeXt{} \acronym{MKXL} (\LuaMetaTeX{}) users must manually install \Lwc/ works with any version of Op\TeX{} and has no dependencies. -\section{Loading the Package} - -\subsection{Plain \TeX} - -\inlineTEX{\input lua-widow-control} +\section[sec:usage]{Loading the Package} -\subsection{\LaTeX} +\setupTABLE[frame=off] +\setupTABLE[column][first][roffset=1.5em] +\setupTABLE[row][first][toffset=0.25ex] -\inlineTEX{\usepackage{lua-widow-control}} +\startTABLE + \NC Plain~\TeX{} + \NC\inlineTEX{\input lua-widow-control} + \NC\NR + \NC \LaTeX{} + \NC\inlineTEX{\usepackage{lua-widow-control}} + \NC\NR + \NC \ConTeXt{} + \NC\inlineTEX{\usemodule[lua-widow-control]} + \NC\NR + \NC Op\TeX{} + \NC\inlineTEX{\load[lua-widow-control]} + \NC\NR +\stopTABLE -\subsection{\ConTeXt} +\section{Options} -\inlineTEX{\usemodule[lua-widow-control]} +\Lwc/ is automatically enabled with the default settings as soon as you load it. Most users should not need to configure \lwc/; however, the packages provides a few commands. -\subsection{Op\TeX{}} +\subsection{Overview} -\inlineTEX{\load[lua-widow-control]} +\LaTeX{} users can set the options either when loading the package (\tex{usepackage[\meta{options}]\{lua-widow-control\}}) or at any point using \tex{lwcsetup\{\meta{options}\}}. -\section{Columns} +\ConTeXt{} users should always use the \tex{setuplwc[\meta{options}]} command. -Since \TeX{} implements column breaking and page breaking through the same internal mechanisms, \lwc/ should remove \waos/ between columns just as well as it does with \waos/ between pages. This has been tested with the standard \LaTeX{} class option \type{twocolumn} and the two-column output routine from Chapter~23 of \cite[title][texbook].\cite[texbook] \Lwc/ should presumably work with any other multi-column implementation; however, due to the diversity and complexity of output~routines, this cannot be guaranteed. +Plain~\TeX{} and Op\TeX{} are a little different. Some options have commands provided (i.e., \tex{lwcemergencystretch=\meta{dimension}}), while others must be set manually (i.e., \tex{directlua\{lwc.debug = true\}}). -\section{Advanced Usage} +Also, please note that not all commands are provided for all formats. -\Lwc/ is automatically enabled with the default settings as soon as you load it. Most users should not need to configure \lwc/; however, the packages does provide a few commands. +\subsection{Enabling} -\setupTABLE[frame=off] +\Lwc/ is enabled by default as soon as you load it. Nevertheless, you may need to explicitly reenable it if you have previously disabled it. -\subsection{Plain \TeX{}} \startTABLE -\NC Enable (default) \NC \inlineTEX{\lwcenable} \NC\NR -\NC Disable \NC \inlineTEX{\lwcdisable} \NC\NR -\NC \estretch/ \NC \inlineTEX{\lwcemergencystretch=3em} \NC\NR -\NC Selectively Disable \NC \inlineTEX{\lwcdisablecmd{\cmd}} \NC\NR + \NC \plainop/ + \NC\inlineTEX{\lwcenable} + \NC\NR + \NC \LaTeX{} + \NC\inlineTEX{\lwcsetup{enable}} + \NC\NR + \NC \ConTeXt{} + \NC\inlineTEX{\setuplwc[state = start]} + \NC\NR \stopTABLE -\subsection{\LaTeX{}} -\startTABLE -\NC Enable (default) \NC \inlineTEX{\lwcenable} \NC\NR -\NC Disable \NC \inlineTEX{\lwcdisable} \NC\NR -\NC \estretch/ \NC \inlineTEX{\setlength{\lwcemergencystretch}{3em}} \NC\NR -\NC Selectively Disable \NC \inlineTEX{\lwcdisablecmd{\cmd}} \NC\NR -\stopTABLE +\subsection{Disabling} -\subsection{\ConTeXt{}} \startTABLE -\NC Enable (default) \NC \inlineTEX{\setuplwc[state = start]} \NC\NR -\NC Disable \NC \inlineTEX{\setuplwc[state = stop]} \NC\NR -\NC \estretch/ \NC \inlineTEX{\setuplwc[emergencystretch = 3em]} \NC\NR -\NC Selectively Disable \NC \inlineTEX{\prependtoks\lwc@patch@pre \to\everybeforefoo} \NC\NR -\NC \NC \inlineTEX{\prependtoks\lwc@patch@post\to\everyafterfoo} \NC\NR + \NC \plainop/ + \NC\inlineTEX{\lwcdisable} + \NC\NR + \NC \LaTeX{} + \NC\inlineTEX{\lwcsetup{disable}} + \NC\NR + \NC \ConTeXt{} + \NC\inlineTEX{\setuplwc[state = stop]} + \NC\NR \stopTABLE -\subsection{Op\TeX{}} +\subsection{Strict Mode} + +By default, \lwc/ takes all possible measures to remove \waos/. This normally works out pretty well; however, sometimes these measures may be a little more aggressive than certain users want. + +\Lwc/ offers a \q{strict} mode that will only make modification to the page that are near-imperceptible to remove \waos/. + \startTABLE -\NC Enable (default) \NC \inlineTEX{\lwcenable} \NC\NR -\NC Disable \NC \inlineTEX{\lwcdisable} \NC\NR -\NC \estretch/ \NC \inlineTEX{\lwcemergencystretch=3em} \NC\NR -\NC Selectively Disable \NC \emph{Not Implemented} \NC\NR + \NC \LaTeX{} + \NC\inlineTEX{\lwcsetup{strict}} + \NC\NR \stopTABLE +Internally, this sets {\tt emergencystretch = 0pt}, {\tt max-cost = 2500}, and {\tt nobreak = warn}. + \subsection{\estretch/} You can configure the \estretch/ used when stretching a paragraph. The default value is 3~em. -\Lwc/ will only use use the \estretch/ when it cannot lengthen a paragraph in any other way, so it is fairly safe to set this to a large value. \TeX{} still accumulates badness when \estretch/ is used, so its pretty rare that a paragraph that requires any \estretch/ will actually be used on the page. +\Lwc/ will only use the \estretch/ when it cannot lengthen a paragraph in any other way, so it is fairly safe to set this to a large value. \TeX{} still accumulates badness when \estretch/ is used, so it's pretty rare that a paragraph that requires any \estretch/ will actually be used on the page. + +\startTABLE + \NC \plainop/ + \NC\tex{lwcemergencystretch=\meta{dimension}} + \NC\NR + \NC \LaTeX{} + \NC\tex{lwcsetup\{emergencystretch = \meta{dimension}\}} + \NC\NR + \NC \ConTeXt{} + \NC\tex{setuplwc[emergencystretch = \meta{dimension}]} + \NC\NR +\stopTABLE \subsection{Selectively Disabling} Sometimes, you may want to disable \lwc/ for certain commands where stretching is undesirable. For example, you typically wouldn't want section headings to be stretched. -You could just disable then reenable \lwc/ every time that you use the command; however, \lwc/ provides a convenience macro that will do this automatically for you. Place \inlineTEX{\lwcdisablecmd{\cmd}} in the preamble, and \lwc/ will not expand any arguments of \tex{cmd} in the document. +You could just disable then reenable \lwc/ every time that you use the command; however, \lwc/ provides a convenience macro that will do this automatically for you. + +\Lwc/ automatically patches the default \LaTeX{}, \ConTeXt{}, and Plain~\TeX{} section commands, so you shouldn't need to patch these yourself; however, \lwc/ does \bold{not} patch the non-standard section commands provided by \sans{memoir}, \sans{\acronym{KOMA}-script}, \sans{titlesec}, Op\TeX{}, and others. You'll need to add these yourself. + +\startTABLE + \NC Plain \TeX{} + \NC \tex{lwcdisablecmd\{\meta{\backslash macro}\}} + \NC\NR + \NC \LaTeX{} + \NC \tex{lwcsetup\{disablecmds = \{\meta{\backslash macroone}, \meta{\backslash macrotwo}\}\}} + \NC\NR + \NC \ConTeXt{} + \NC \tex{prependtoks\backslash lwc@patch@pre\backslash to\backslash everybefore\meta{hook}} + \NC\NR\NC\NC \tex{prependtoks\backslash lwc@patch@post\backslash to\backslash everyafter\meta{hook}} + \NC\NR +\stopTABLE + +\subsection{Widow and Orphan Penalties} + +You can also manually adjust the penalties that \TeX{} assigns to \waos/. Usually, the defaults are fine, but advanced users may want to change them. + +\startTABLE + \NC \plainop/ + \NC\tex{widowpenalty=\meta{integer}} + \NC\NR\NC\NC\tex{clubpenalty=\meta{integer}} + \NC\NR + \NC \LaTeX{} + \NC\tex{lwcsetup\{widowpenalty = \meta{integer}\}} + \NC\NR\NC\NC\tex{lwcsetup\{orphanpenalty = \meta{integer}\}} + \NC\NR + \NC \ConTeXt{} + \NC\tex{setuplwc[widowpenalty = \meta{integer}]} + \NC\NR\NC\NC\tex{setuplwc[orphanpenalty = \meta{integer}]} + \NC\NR +\stopTABLE + +Some suitable integers: + +\startTABLE + \NC Ignore widows/orphans \NC 0 \NC\NR + \NC Default \NC 1 \NC\NR + \NC Disable \lwc/ \NC 10\,000 \NC\NR +\stopTABLE + +\subsection{\tex{nobreak} Behaviour} + +When \lwc/ encounters an orphan, it removes it by removing the orphaned line to the next page. However, sometimes, an orphan is immediately preceded by a section heading or a \tex{nobreak} command. By moving the orphan to the next page, you would naïvely separate a section (or other such material) from the line that follows. This really ought to be avoided, so \lwc/ provides some options to avoid this. + +\startTABLE + \NC \plainop/ + \NC\tex{directlua\{lwc.nobreak_behaviour = "\meta{value}"\}} + \NC\NR + \NC \LaTeX{} + \NC\tex{lwcsetup\{nobreak = \meta{value}\}} + \NC\NR + \NC \ConTeXt{} + \NC\tex{setuplwc[nobreak = \meta{value}]} + \NC\NR +\stopTABLE + +The default value, \type{keep}, \emph{keep}s the section heading with the orphan by moving both to the next page. + +The value \type{split} \emph{split}s up the section heading and the orphan by moving the orphan to the next page while leaving the heading behind. This is usually a bad idea. -\Lwc/ automatically patches the default \LaTeX{}, \ConTeXt{}, and Plain~\TeX{} section commands, so you shouldn't need to patch these yourself; however, \lwc/ does \bold{not} patch the non-standard section commands provided by \sans{memoir}, \sans{\acronym{KOMA}-script}, \sans{titlesec}, and others. You'll need to patch these yourself. +The value \type{warn} causes \lwc/ to give up on the page and do nothing, leaving an orphaned line. \Lwc/ \emph{warn}s the user so that they can manually remove the orphan. -Under \ConTeXt{}, you need to use a different method to selectively disable \lwc/. Instead of using \tex{lwcdisablecmd}, you should use one of the preexisting \tex{everyfoo} hooks as shown in the table above since patching commands is rarely advisable in \ConTeXt{} +\subsection{Maximum Cost} -No commands are provided for selectively disabling \lwc/ with Op\TeX{}. Users must implement this functionality themselves. +When \TeX{} breaks a paragraph, it scores it by the number of \q{demerits}. The demerits for a paragraph is the sum of the squared badnesses for each line, plus any \q{additional demerits} added for any other reason. The badness for a line is proportional the cube of the glue stretch ratio, so demerits grow with the sixth power of glue stretch. + +To choose the \q{best} paragraph on the page, \lwc/ uses a \q{cost function} $C$ that is initially defined as \startformula + C = \frac{d}{2\sqrt{l}} +\stopformula where $d$ is the total demerits of the paragraph, and $l$ is the number of lines in the paragraph. + +By default, \lwc/ just selects the paragraph on the page with the lowest cost; however, you can configure it to only select paragraphs below a selected cost. If there aren't any paragraphs below the set threshold, then \lwc/ won't remove the \woo/ and will instead issue a warning. + +\startTABLE + \NC \plainop/ + \NC\tex{lwcmaxcost=\meta{integer}} + \NC\NR + \NC \LaTeX{} + \NC\tex{lwcsetup\{max-cost = \meta{integer}\}} + \NC\NR + \NC \ConTeXt{} + \NC\tex{setuplwc[maxcost = \meta{integer}]} + \NC\NR +\stopTABLE + +Very advanced users may also set a custom cost function by redefining the \inlineLUA{lwc.paragraph_cost(demerits, lines)} function. + +\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. + +\startTABLE + \NC \plainop/ + \NC\inlineTEX{\directlua{lwc.debug = true}} + \NC\NR\NC\NC\inlineTEX{\directlua{lwc.debug = false}} + \NC\NR + \NC \LaTeX{} + \NC\inlineTEX{\lwcsetup{debug = true}} + \NC\NR\NC\NC\inlineTEX{\lwcsetup{debug = false}} + \NC\NR + \NC \ConTeXt{} + \NC\inlineTEX{\setuplwc[state = start]} + \NC\NR\NC\NC\inlineTEX{\setuplwc[state = stop]} + \NC\NR +\stopTABLE + +\section{Columns} + +Since \TeX{} implements column breaking and page breaking through the same internal mechanisms, \lwc/ should remove \waos/ between columns just as well as it does with \waos/ between pages. This has been tested with the standard \LaTeX{} class option \type{twocolumn} and the two-column output routine from Chapter~23 of \cite[title][texbook].\cite[texbook] \Lwc/ should presumably work with any other multi-column implementation; however, due to the diversity and complexity of output~routines, this cannot be guaranteed. \section[sec:issues]{Known Issues} \startitemize - \item \Lwc/ will not expand the first paragraph of a document. - \item \Lwc/ will rarely fail to correctly move the last line on an expanded page to the next page in documents with \emph{very} small paper sizes. \item When a 3-line paragraph is at the end of a page forming a widow, \lwc/ will remove the widow; however, it will leave an orphan. This issue is inherent to any process that removes widows through paragraph expansion and is thus unavoidable. Orphans are better than widows, so this is still an improvement. @@ -289,7 +427,9 @@ No commands are provided for selectively disabling \lwc/ with Op\TeX{}. Users mu \item When running under \LuaMetaTeX, the log may be filled with lines like \q{\tt luatex warning > tex: left parfill skip is gone}. This is harmless and can be ignored. % I have no idea how to fix this. The LMTX manual has "\parfillleftskip" in the indices, but that's all that I can find about it. - \item \Lwc/ will rarely raise a \q{\tt Circular node list detected!} warning. This occurs when the replacement paragraph node list loops back on itself. Since there is no \q{end} to the paragraph, \lwc/ cannot splice the paragraph into the page. The only reasonable option in this scenario is to stop processing the page, without removing the \woo/. + \item \Lwc/ will rarely raise a \q{\tt Circular node list detected!} warning. This occurs when the replacement paragraph node list loops back on itself. This is usually harmless, but can rarely cause the entire compile to completely fail. + + \item \TeX{} may warn you about over or underfull vboxes on pages where \lwc/ removed a \woo/. This is a false alarm and can be ignored. \stopitemize \section{The Algorithm} @@ -307,7 +447,7 @@ After \TeX{} has broken its paragraph into its natural length, \lwc/ appears onc First, \lwc/ analyzes the \openalty/ of the page or column. If the page was broken at a \woo/, the \openalty/ will equal either \tex{widow\allowbreak penalty} or \tex{orphan\allowbreak penalty}. If the \openalty/ is not indicative of a \woo/, \lwc/ will stop and return \tex{box255} unmodified. -At this point, we know that we have a \woo/ on the page, so we must lengthen the page by 1~line. We iterate through the list of saved paragraphs to find the lengthened paragraph with the least demerits. Once we've selected a paragraph to replace, we can now traverse through the page to find the original version of this paragraph that \TeX{} originally typeset. Once we find the original paragraph, we \q{splice} the lengthened paragraph in the place of the original. +At this point, we know that we have a \woo/ on the page, so we must lengthen the page by 1~line. We iterate through the list of saved paragraphs to find the lengthened paragraph with the least cost. Once we've selected a paragraph to replace, we can now traverse through the page to find the original version of this paragraph that \TeX{} originally typeset. Once we find the original paragraph, we \q{splice} the lengthened paragraph in the place of the original. Since the page is now 1~line longer than it was before, we pull the last line off of the page to bring it back to its original length. We place the line onto the top of the \emph{recent contributions} list so that it is added to the start of the next page. Now, we can return the new, widow-free page to the output routine. @@ -353,14 +493,10 @@ Please note that a compiled document is \bold{not} considered to be an \q{Execut \typeTEXfile{../source/lua-widow-control.sty} \def\module{\tex{module}} -\subsection{t-lua-widow-control.mkxl} +\subsection{t-lua-widow-control.mkxl/mkiv} \typeTEXfile{../source/t-lua-widow-control.mkxl} -\subsection{t-lua-widow-control.mkiv} - -\typeTEXfile{../source/t-lua-widow-control.mkiv} - \subsection{lua-widow-control.opm} \typeTEXfile{../source/lua-widow-control.opm} diff --git a/Master/texmf-dist/tex/context/third/lua-widow-control/t-lua-widow-control.mkiv b/Master/texmf-dist/tex/context/third/lua-widow-control/t-lua-widow-control.mkiv index 6eff5c9b00c..e24f972d577 100644 --- a/Master/texmf-dist/tex/context/third/lua-widow-control/t-lua-widow-control.mkiv +++ b/Master/texmf-dist/tex/context/third/lua-widow-control/t-lua-widow-control.mkiv @@ -1,10 +1,10 @@ %D \module %D [ file=t-lua-widow-control, -%D version=1.1.6, %%version +%D version=2.0.0, %%version %D title=lua-widow-control, %D subtitle=\ConTeXt module for lua-widow-control, %D author=Max Chernoff, -%D date=2022-02-22, %%date +%D date=2022-03-07, %%dashdate %D copyright=Max Chernoff, %D license=MPL-2.0+, %D url=https://github.com/gucci-on-fleek/lua-widow-control] @@ -15,9 +15,9 @@ \installcommandhandler \????lwc {lwc} \????lwc -\newdimen\lwcemergencystretch +\newdimen\lwc_emergency_stretch \appendtoks - \lwcemergencystretch=\lwcparameter{emergencystretch} + \lwc_emergency_stretch=\lwcparameter{emergencystretch} \to\everysetuplwc \appendtoks @@ -28,23 +28,49 @@ } \to\everysetuplwc +\appendtoks + \doifelse{\lwcparameter{debug}}\v!start{ + \ctxlua{lwc.debug = true} + }{ + \ctxlua{lwc.debug = false} + } +\to\everysetuplwc + +\appendtoks + \ctxlua{lwc.nobreak_behaviour = "\lwcparameter{nobreak}"} +\to\everysetuplwc + +\newcount\lwc_max_cost +\appendtoks + \lwc_max_cost=\lwcparameter{maxcost} +\to\everysetuplwc + +\appendtoks + % We can't just set the penalties because they will be reset automatically + % at \\starttext. + \startsetups[*default] + \clubpenalty=\lwcparameter{orphanpenalty} + \widowpenalty=\lwcparameter{widowpenalty} + \displaywidowpenalty=\lwcparameter{widowpenalty} + \stopsetups + + \setups[*default] +\to\everysetuplwc + \define\iflwc{\ctxlua{lwc.if_lwc_enabled()}} \ctxloadluafile{lua-widow-control} -\setuplwc[emergencystretch=3em, \c!state=\v!start] - -% We can't just set the penalties because they will be reset automatically -% at \\starttext. -\startsetups[*default] - \clubpenalty=1 - \widowpenalty=1 - \displaywidowpenalty=0 - \interlinepenalty=0 - \brokenpenalty=0 -\stopsetups +\setuplwc[ + emergencystretch=3em, + \c!state=\v!start, + debug=\v!stop, + orphanpenalty=1, + widowpenalty=1, + nobreak=keep, + maxcost=2147483647, +] -\setups[*default] % Here, we enable font expansion/contraction. It isn't strictly necessary for % \lwc/'s functionality; however, it is required for the @@ -55,23 +81,23 @@ % Expansion of some parts of the document, such as section headings, is quite % undesirable, so we'll disable \lwc/ for certain commands. % We should only reenable \lwc/ at the end if it was already enabled. -\newif\iflwc@should@reenable +\newif\iflwc_should_reenable -\define\lwc@patch@pre{% +\define\lwc_patch_pre{% \iflwc% - \lwc@should@reenabletrue% + \lwc_should_reenabletrue% \setuplwc[state=stop]% \else% - \lwc@should@reenablefalse + \lwc_should_reenablefalse \fi% } -\define\lwc@patch@post{\iflwc@should@reenable% +\define\lwc_patch_post{\iflwc_should_reenable% \setuplwc[state=start]% \fi} -\prependtoks\lwc@patch@pre\to\everybeforesectionheadhandle % Sectioning -\prependtoks\lwc@patch@post\to\everyaftersectionheadhandle +\prependtoks\lwc_patch_pre\to\everybeforesectionheadhandle % Sectioning +\prependtoks\lwc_patch_post\to\everyaftersectionheadhandle \protect \stopmodule 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 6eff5c9b00c..e24f972d577 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=1.1.6, %%version +%D version=2.0.0, %%version %D title=lua-widow-control, %D subtitle=\ConTeXt module for lua-widow-control, %D author=Max Chernoff, -%D date=2022-02-22, %%date +%D date=2022-03-07, %%dashdate %D copyright=Max Chernoff, %D license=MPL-2.0+, %D url=https://github.com/gucci-on-fleek/lua-widow-control] @@ -15,9 +15,9 @@ \installcommandhandler \????lwc {lwc} \????lwc -\newdimen\lwcemergencystretch +\newdimen\lwc_emergency_stretch \appendtoks - \lwcemergencystretch=\lwcparameter{emergencystretch} + \lwc_emergency_stretch=\lwcparameter{emergencystretch} \to\everysetuplwc \appendtoks @@ -28,23 +28,49 @@ } \to\everysetuplwc +\appendtoks + \doifelse{\lwcparameter{debug}}\v!start{ + \ctxlua{lwc.debug = true} + }{ + \ctxlua{lwc.debug = false} + } +\to\everysetuplwc + +\appendtoks + \ctxlua{lwc.nobreak_behaviour = "\lwcparameter{nobreak}"} +\to\everysetuplwc + +\newcount\lwc_max_cost +\appendtoks + \lwc_max_cost=\lwcparameter{maxcost} +\to\everysetuplwc + +\appendtoks + % We can't just set the penalties because they will be reset automatically + % at \\starttext. + \startsetups[*default] + \clubpenalty=\lwcparameter{orphanpenalty} + \widowpenalty=\lwcparameter{widowpenalty} + \displaywidowpenalty=\lwcparameter{widowpenalty} + \stopsetups + + \setups[*default] +\to\everysetuplwc + \define\iflwc{\ctxlua{lwc.if_lwc_enabled()}} \ctxloadluafile{lua-widow-control} -\setuplwc[emergencystretch=3em, \c!state=\v!start] - -% We can't just set the penalties because they will be reset automatically -% at \\starttext. -\startsetups[*default] - \clubpenalty=1 - \widowpenalty=1 - \displaywidowpenalty=0 - \interlinepenalty=0 - \brokenpenalty=0 -\stopsetups +\setuplwc[ + emergencystretch=3em, + \c!state=\v!start, + debug=\v!stop, + orphanpenalty=1, + widowpenalty=1, + nobreak=keep, + maxcost=2147483647, +] -\setups[*default] % Here, we enable font expansion/contraction. It isn't strictly necessary for % \lwc/'s functionality; however, it is required for the @@ -55,23 +81,23 @@ % Expansion of some parts of the document, such as section headings, is quite % undesirable, so we'll disable \lwc/ for certain commands. % We should only reenable \lwc/ at the end if it was already enabled. -\newif\iflwc@should@reenable +\newif\iflwc_should_reenable -\define\lwc@patch@pre{% +\define\lwc_patch_pre{% \iflwc% - \lwc@should@reenabletrue% + \lwc_should_reenabletrue% \setuplwc[state=stop]% \else% - \lwc@should@reenablefalse + \lwc_should_reenablefalse \fi% } -\define\lwc@patch@post{\iflwc@should@reenable% +\define\lwc_patch_post{\iflwc_should_reenable% \setuplwc[state=start]% \fi} -\prependtoks\lwc@patch@pre\to\everybeforesectionheadhandle % Sectioning -\prependtoks\lwc@patch@post\to\everyaftersectionheadhandle +\prependtoks\lwc_patch_pre\to\everybeforesectionheadhandle % Sectioning +\prependtoks\lwc_patch_post\to\everyaftersectionheadhandle \protect \stopmodule 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 new file mode 100644 index 00000000000..783db59ed22 --- /dev/null +++ b/Master/texmf-dist/tex/lualatex/lua-widow-control/lua-widow-control-2022-02-22.sty @@ -0,0 +1,94 @@ +% lua-widow-control +% https://github.com/gucci-on-fleek/lua-widow-control +% SPDX-License-Identifier: MPL-2.0+ +% SPDX-FileCopyrightText: 2022 Max Chernoff + +\NeedsTeXFormat{LaTeX2e}[2015/01/01] % Formats built after 2015 include \LuaTeX{}Base +\ProvidesPackage{lua-widow-control}% + [2022/02/22 v1.1.6] + +\PackageWarning{lua-widow-control}{% + Old LaTeX format detected!\MessageBreak\MessageBreak + Lua-widow-control prefers a LaTeX format\MessageBreak + newer than November 2020. I'll still run\MessageBreak + the latest Lua code, but I'm using an older\MessageBreak + version of the LaTeX code. This means that\MessageBreak + the key-value interface is *UNSUPPORTED*.\MessageBreak +} + +\ifdefined\directlua\else + \PackageError{lua-widow-control}{% + LuaTeX is required for this package.\MessageBreak + Make sure to compile with `lualatex'% + }{} +\fi + +\clubpenalty=1 +\widowpenalty=1 +\displaywidowpenalty=1 + +% We can't use \\newlength since that makes a \TeX{} "skip", not a "dimen" +\newdimen\lwcemergencystretch +\lwcemergencystretch=3em + +\newcount\lwcmaxcost +\lwcmaxcost=2147483647 + +\directlua{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 +% lengthened paragraphs to not have terrible spacing. +\RequirePackage{etoolbox} +\AtEndPreamble{ + \@ifpackageloaded{microtype}{}{ % Only load if not already loaded + \RequirePackage[ + final, + activate={true,nocompatibility} + ]{microtype} + } +} + +% Define \TeX{} wrappers for Lua functions +\newcommand{\lwcenable}{\directlua{lwc.enable_callbacks()}} +\newcommand{\lwcdisable}{\directlua{lwc.disable_callbacks()}} +\newcommand{\iflwc}{\directlua{lwc.if_lwc_enabled()}} + +% Enable \lwc/ by default when the package is loaded. +\lwcenable + +% Expansion of some parts of the document, such as section headings, is quite +% undesirable, so we'll disable \lwc/ for certain commands. +\newcommand{\lwc@patch@warning}[1]{\PackageWarning{lua-widow-control}{% + Patching the \protect#1 command failed% +}} + +% We should only reenable \lwc/ at the end if it was already enabled. +\newif\iflwc@should@reenable + +\newcommand{\lwc@patch@pre}{% + \iflwc% + \lwc@should@reenabletrue% + \lwcdisable% + \else% + \lwc@should@reenablefalse% + \fi% +} + +\newcommand{\lwc@patch@post}{% + \iflwc@should@reenable% + \lwcenable% + \fi% +} + +\newcommand{\lwcdisablecmd}[1]{% + \ifdefined#1 + \pretocmd{#1}{\lwc@patch@pre}{}{\lwc@patch@warning{#1}}% + \apptocmd{#1}{\lwc@patch@post}{}{\lwc@patch@warning{#1}}% + \fi +} + +\lwcdisablecmd{\@sect} % Sectioning + + +\endinput 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 9ef5a76de56..56462e40d87 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 @@ -3,82 +3,232 @@ % SPDX-License-Identifier: MPL-2.0+ % SPDX-FileCopyrightText: 2022 Max Chernoff -\NeedsTeXFormat{LaTeX2e}[2015/01/01] % Formats built after 2015 include \LuaTeX{}Base -\ProvidesPackage{lua-widow-control}% - [2022/02/22 v1.1.6] %%version %%date +% Formats built after 2015 include \LuaTeX{}Base, so this is the absolute +% minimum version that we will run under. +\NeedsTeXFormat{LaTeX2e}[2015/01/01] + +% For _really_ old formats +\providecommand\DeclareRelease[3]{} +\providecommand\DeclareCurrentRelease[2]{} + +\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.0.0}{2022-03-07} %%version %%dashdate + +% If this version of LaTeX doesn't support command hooks, then we load +% the last v1.1.X version of the package. +\providecommand\IfFormatAtLeastTF{\@ifl@t@r\fmtversion} +\IfFormatAtLeastTF{2020/10/01}{}{\input{lua-widow-control-2022-02-22.sty}} +\IfFormatAtLeastTF{2020/10/01}{}{\endinput} + +\ProvidesExplPackage + {lua-widow-control} + {2022/03/07} %%slashdate + {v2.0.0} %%version + {Use Lua to remove widows and orphans} + +% Unconditional Package Loads +\RequirePackage { l3keys2e } + +% Message and String Constants +\str_const:Nn \c__lwc_name_str { lua-widow-control } + +\msg_new:nnn + { \c__lwc_name_str } + { no-luatex } + { + LuaTeX~ is~ REQUIRED! \\ + Make~ sure~ to~ compile~ your~ document~ with~ `lualatex'. + } -\ifdefined\directlua\else - \PackageError{lua-widow-control}{% - LuaTeX is required for this package.\MessageBreak - Make sure to compile with `lualatex'% - }{} -\fi +\msg_new:nnn + { \c__lwc_name_str } + { patch-failed } + { + Patching~ \c_backslash_str #1~ failed. \\ + Please~ ensure~ that~ \c_backslash_str #1~ exists. + } -\clubpenalty=1 -\widowpenalty=1 -\displaywidowpenalty=0 -\interlinepenalty=0 -\brokenpenalty=0 +\msg_new:nnn + { \c__lwc_name_str } + { old-format-patch } + { + Patching~ not~ supported~ with~ old~ LaTeX. \\ + Please~ use~ a~ LaTeX~ format~ >=~ 2021/06/01. + } -% We can't use \\newlength since that makes a \TeX{} "skip", not a "dimen" -\newdimen\lwcemergencystretch -\lwcemergencystretch=3em +\msg_new:nnn + { \c__lwc_name_str } + { old-command } + { + \c_backslash_str #1~ has~ been~ REMOVED! \\ + Please~ use~ \c_backslash_str setuplwc \c_left_brace_str #2 + \c_right_brace_str\ instead. + } -\directlua{require "lua-widow-control"} +% Don't let the user proceed unless they are using \LuaTeX{}. +\sys_if_engine_luatex:F { + \msg_critical:nn { \c__lwc_name_str } { no-luatex } +} + +% Define (most of) the keys +\keys_define:nn { \c__lwc_name_str } { + emergencystretch .dim_gset:N = \g__lwc_emergencystretch_dim, + emergencystretch .value_required:n = true, + emergencystretch .initial:x = \dim_max:nn { 3em } { 30pt }, + + max-cost .int_gset:N = \g__lwc_maxcost_int, + max-cost .value_required:n = true, + max-cost .initial:x = \c_max_int, + + widowpenalty .code:n = \int_gset:Nn \tex_widowpenalty:D { #1 } + \int_gset:Nn \tex_displaywidowpenalty:D { #1 }, + widowpenalty .value_required:n = true, + widowpenalty .initial:n = 1, + + orphanpenalty .code:n = \int_gset:Nn \tex_clubpenalty:D { #1 } + \int_gset:Nn \@clubpenalty { #1 }, + orphanpenalty .value_required:n = true, + orphanpenalty .initial:n = 1, + + microtype .bool_gset:N = \g__lwc_use_microtype_bool, + microtype .value_required:n = true, + microtype .initial:n = true, + microtype .usage:n = preamble, + + disablecmds .clist_gset:N = \g__lwc_disablecmds_cl, + disablecmds .value_required:n = false, + disablecmds .initial:n = { \@sect }, + disablecmds .usage:n = preamble, + + nobreak .choice:, + nobreak / keep .code:n = \lua_now:n { lwc.nobreak_behaviour = "keep" }, + nobreak / split .code:n = \lua_now:n { lwc.nobreak_behaviour = "split" }, + nobreak / warn .code:n = \lua_now:n { lwc.nobreak_behaviour = "warn" }, +} + +% Load the Lua code +\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 -% lengthened paragraphs to not have terrible spacing. -\RequirePackage{etoolbox} -\AtEndPreamble{ - \@ifpackageloaded{microtype}{}{ % Only load if not already loaded - \RequirePackage[ - final, - activate={true,nocompatibility} - ]{microtype} +% lengthened paragraphs to not have terrible spacing. +\bool_if:NT \g__lwc_use_microtype_bool { + \hook_gput_code:nnn { begindocument / before } { \c__lwc_name_str } { + \@ifpackageloaded { microtype } {} { + \RequirePackage[ + final, + activate = { true, nocompatibility } + ] + { microtype } + } } } -% Define \TeX{} wrappers for Lua functions -\newcommand{\lwcenable}{\directlua{lwc.enable_callbacks()}} -\newcommand{\lwcdisable}{\directlua{lwc.disable_callbacks()}} -\newcommand{\iflwc}{\directlua{lwc.if_lwc_enabled()}} +% Core Function Definitions +\cs_new:Npn \__lwc_enable: { + \lua_now:n { lwc.enable_callbacks() } +} + +\cs_new:Npn \__lwc_disable: { + \lua_now:n { lwc.disable_callbacks() } +} -% Enable \lwc/ by default when the package is loaded. -\lwcenable +\prg_set_conditional:Nnn \__lwc_if_enabled: { T, F, TF } { + \lua_now:n { lwc.if_lwc_enabled() } + \prg_return_true: + \else + \prg_return_false: + \fi +} % Expansion of some parts of the document, such as section headings, is quite % undesirable, so we'll disable \lwc/ for certain commands. -\newcommand{\lwc@patch@warning}[1]{\PackageWarning{lua-widow-control}{% - Patching the \protect#1 command failed% -}} - -% We should only reenable \lwc/ at the end if it was already enabled. -\newif\iflwc@should@reenable - -\newcommand{\lwc@patch@pre}{% - \iflwc% - \lwc@should@reenabletrue% - \lwcdisable% - \else% - \lwc@should@reenablefalse% - \fi% +\bool_new:N \g__lwc_should_reenable_bool + +\cs_new:Npn \__lwc_patch_cmd:c #1 { + \IfFormatAtLeastTF {2021/06/01 } { + \hook_gput_code:nnn { cmd / #1 / before } { \c__lwc_name_str } { + % We should only reenable \lwc/ at the end if it was already enabled. + \__lwc_if_enabled:TF { + \bool_gset_true:N \g__lwc_should_reenable_bool + \__lwc_disable: + } { + \bool_gset_false:N \g__lwc_should_reenable_bool + } + } + \hook_gput_code:nnn { cmd / #1 / after } { \c__lwc_name_str } { + \bool_if:NT { \g__lwc_should_reenable_bool } { + \__lwc_enable: + } + } + } { + \msg_warning:nn + { \c__lwc_name_str } + { old-format-patch } + } } -\newcommand{\lwc@patch@post}{% - \iflwc@should@reenable% - \lwcenable% - \fi% +\cs_new:Npn \__lwc_patch_cmd:N #1 { + \__lwc_patch_cmd:c { \cs_to_str:N #1 } } -\newcommand{\lwcdisablecmd}[1]{% - \ifdefined#1 - \pretocmd{#1}{\lwc@patch@pre}{}{\lwc@patch@warning{#1}}% - \apptocmd{#1}{\lwc@patch@post}{}{\lwc@patch@warning{#1}}% - \fi +\hook_gput_code:nnn { begindocument / before } { \c__lwc_name_str } { + \clist_map_function:NN \g__lwc_disablecmds_cl \__lwc_patch_cmd:N +} + +% Define some final keys +\keys_define:nn { \c__lwc_name_str } { + enable .choice:, + enable / true .code:n = \__lwc_enable:, + enable / false .code:n = \__lwc_disable:, + enable .initial:n = true, + enable .default:n = true, + enable .value_required:n = false, + + disable .code:n = \__lwc_disable:, + disable .value_forbidden:n = true, + + debug .choice:, + debug / true .code:n = \lua_now:n { lwc.debug = true }, + debug / false .code:n = \lua_now:n { lwc.debug = false }, + + strict .meta:n = { emergencystretch = 0pt, + max-cost = 2500, + nobreak = warn, + }, + strict .value_forbidden:n = true, + + default .meta:n = { emergencystretch = 3em, + max-cost = \c_max_int, + nobreak = keep, + }, + default .value_forbidden:n = true, } -\lwcdisablecmd{\@sect} % Sectioning +% Add the user interface for the keys +\ProcessKeysPackageOptions{ \c__lwc_name_str } + +\NewDocumentCommand \lwcsetup {m} { \keys_set:nn{\c__lwc_name_str}{#1} } + +% Legacy Commands +\NewDocumentCommand \lwcemergencystretch { } { + \msg_error:nnnnn + { \c__lwc_name_str } + { old-command } + { lwcemergencystretch } + { emergencystretch=XXXpt } +} + +\NewDocumentCommand \lwcdisablecmd { m } { + \msg_error:nnxx + { \c__lwc_name_str } + { old-command } + { lwcdisablecmd } + { disablecmds={\c_backslash_str aaa,~ \c_backslash_str bbb} } +} +\cs_new_eq:NN \lwcenable \__lwc_enable: +\cs_new_eq:NN \lwcdisable \__lwc_disable: \endinput 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 f1ecb54c65f..5801feef129 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 @@ -5,8 +5,24 @@ SPDX-FileCopyrightText: 2022 Max Chernoff ]] -lwc = {} +lwc = lwc or {} lwc.name = "lua-widow-control" +lwc.nobreak_behaviour = "keep" + +local write_nl = texio.write_nl +local string_rep = string.rep +local function debug_print(title, text) + if not lwc.debug then return end + + local filler = 15 - #title + + if text then + write_nl("log", "LWC (" .. title .. string_rep(" ", filler) .. "): " .. text) + else + write_nl("log", "LWC: " .. string_rep(" ", 18) .. title) + end +end + --[[ \lwc/ is intended to be format-agonistic. It only runs on Lua\TeX{}, @@ -37,70 +53,90 @@ local last = node.slide local copy = node.copy_list 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 +local glyph_id = node.id("glyph") +local penalty_id = node.id("penalty") +local hlist_id = node.id("hlist") +local traverse = node.traverse +local set_attribute = node.set_attribute or node.setattribute +local find_attribute = node.find_attribute or node.findattribute local flush_list = node.flush_list or node.flushlist local free = node.free -local min_col_width = tex.sp("25em") -local maxdimen = 1073741823 -- \\maxdimen in sp +local min_col_width = tex.sp("250pt") --[[ Package/module initialization ]] -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{} +local warning, + info, + attribute, + contrib_head, + stretch_order, + pagenum, + emergencystretch, + max_cost + +if lmtx then + debug_print("LMTX") + contrib_head = 'contributehead' stretch_order = "stretchorder" - pagenum = function() return tex.count["realpageno"] end -elseif context and not lmtx then +else + contrib_head = 'contrib_head' + stretch_order = "stretch_order" +end + +if context then + debug_print("ConTeXt") 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/22", --%%date - version = "1.1.6", --%%version - description = [[ - - This module provides a LuaTeX-based solution to prevent - widows and orphans from appearing in a document. It does - so by increasing or decreasing the lengths of previous - paragraphs. - ]], - } - 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" + emergencystretch = "lwc_emergency_stretch" + max_cost = "lwc_max_cost" +elseif plain or latex or optex then pagenum = function() return tex.count[0] end + + if tex.isdimen("g__lwc_emergencystretch_dim") then + emergencystretch = "g__lwc_emergencystretch_dim" + max_cost = "g__lwc_maxcost_int" + else + emergencystretch = "lwcemergencystretch" + max_cost = "lwcmaxcost" + end + + if plain or latex then + debug_print("Plain/LaTeX") + luatexbase.provides_module { + name = lwc.name, + date = "2022/03/07", --%%slashdate + version = "2.0.0", --%%version + description = [[ + +This module provides a LuaTeX-based solution to prevent +widows and orphans from appearing in a document. It does +so by increasing or decreasing the lengths of previous +paragraphs.]], + } + 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) + elseif optex then + debug_print("OpTeX") + 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) + end else -- uh oh - error [[ - Unsupported format. + error [[Unsupported format. - Please use (Lua)LaTeX, Plain (Lua)TeX, ConTeXt (MKXL/LMTX), - or OpTeX. - ]] +Please use LaTeX, Plain TeX, ConTeXt or OpTeX.]] end local paragraphs = {} -- List to hold the alternate paragraph versions +local function get_location() + return "At " .. pagenum() .. "/" .. #paragraphs +end + --[[ Function definitions @@ -165,11 +201,39 @@ local function register_callback(t) end end +local function get_chars(head) + if not lwc.debug then return end + + local chars = "" + for n in traverse(head) do + if n.id == glyph_id then + if n.char < 127 then + chars = chars .. string.char(n.char) + else + chars = chars .. "#" + end + elseif n.id == glue_id then + chars = chars .. " " + end + if #chars > 25 then + break + end + end + + debug_print(get_location(), chars) +end + + +function lwc.paragraph_cost(demerits, lines) + return demerits / (2 * math.sqrt(lines)) +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 local renable_box_warnings - if (latex or plain) and + if (context or optex) or #luatexbase.callback_descriptions("hpack_quality") == 0 then -- See #18 and michal-h21/linebreaker#3 renable_box_warnings = true @@ -194,7 +258,7 @@ function lwc.save_paragraphs(head) -- Break the paragraph 1 line longer than natural local long_node, long_info = tex.linebreak(new_head, { looseness = 1, - emergencystretch = tex.dimen.lwcemergencystretch, + emergencystretch = tex.getdimen(emergencystretch), }) -- Break the natural paragraph so we know how long it was @@ -205,20 +269,31 @@ function lwc.save_paragraphs(head) lwc.callbacks.disable_box_warnings.disable() end - -- If we can't lengthen the paragraph, assign a \emph{very} large demerit value - local long_demerits - if long_info.prevgraf == natural_info.prevgraf then - long_demerits = maxdimen - else - long_demerits = long_info.demerits - end - -- Offset the accumulated \\prevdepth local prevdepth = node.new("glue") prevdepth.width = natural_info.prevdepth - long_info.prevdepth last(long_node).next = prevdepth - table.insert(paragraphs, { demerits = long_demerits, node = long_node }) + if long_info.prevgraf == natural_info.prevgraf + 1 then + table.insert(paragraphs, { + cost = lwc.paragraph_cost(long_info.demerits, long_info.prevgraf), + node = long_node + }) + end + + get_chars(head) + debug_print(get_location(), "nat lines " .. natural_info.prevgraf) + debug_print( + get_location(), + "nat cost " .. + lwc.paragraph_cost(natural_info.demerits, natural_info.prevgraf) + ) + debug_print(get_location(), "long lines " .. long_info.prevgraf) + debug_print( + get_location(), + "long cost " .. + lwc.paragraph_cost(long_info.demerits, long_info.prevgraf) + ) -- \LuaMetaTeX{} crashes if we return `true` return head @@ -251,11 +326,12 @@ local function safe_last(head) if ids[id] then 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.]] - +This should never happen. I'll try and +recover, but your output may be corrupted. +(Internal Error)]] prev.next = nil + debug_print("safe_last", node.type(head.id) .. " " .. node.type(prev.id)) + return prev end @@ -276,12 +352,16 @@ 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 head_save = head -- Save the start of the `head` linked-list + local penalty = tex.outputpenalty - tex.interlinepenalty local widowpenalty = tex.widowpenalty local clubpenalty = tex.clubpenalty local displaywidowpenalty = tex.displaywidowpenalty local brokenpenalty = tex.brokenpenalty + debug_print("outputpenalty", tex.outputpenalty .. " " .. #paragraphs) + --[[ We only need to process pages that have orphans or widows. If `paragraphs` is empty, then there is nothing that we can do. @@ -289,7 +369,9 @@ function lwc.remove_widows(head) The list of penalties is from: https://tug.org/TUGboat/tb39-3/tb123mitt-widows-code.pdf#subsection.0.2.1 ]] - if (penalty == widowpenalty or + if penalty ~= 0 and + penalty < 10000 and + (penalty == widowpenalty or penalty == displaywidowpenalty or penalty == clubpenalty or penalty == clubpenalty + widowpenalty or @@ -302,28 +384,26 @@ function lwc.remove_widows(head) penalty == brokenpenalty + clubpenalty + displaywidowpenalty) and #paragraphs >= 1 then else + paragraphs = {} return head end info("Widow/orphan detected. Attempting to remove.") - local head_save = head -- Save the start of the `head` linked-list - --[[ - Find the paragraph on the page with the minimum penalty. - - This would be a 1-liner in Python or JavaScript, but Lua is pretty low-level, - so there's quite a bit of code here. + Find the paragraph on the page with the least cost. ]] local paragraph_index = 1 - local minimum_demerits = paragraphs[paragraph_index].demerits + local best_cost = paragraphs[paragraph_index].cost -- We find the current "best" replacement, then free the unused ones for i, paragraph in pairs(paragraphs) do - if paragraph.demerits < minimum_demerits and i <= #paragraphs - 1 then + if paragraph.cost < best_cost and i <= #paragraphs - 1 then + -- Clear the old best paragraph flush_list(paragraphs[paragraph_index].node) paragraphs[paragraph_index].node = nil - paragraph_index, minimum_demerits = i, paragraph.demerits + -- Set the new best paragraph + paragraph_index, best_cost = i, paragraph.cost elseif i > 1 then -- Not sure why `i > 1` is required? flush_list(paragraph.node) @@ -331,50 +411,113 @@ function lwc.remove_widows(head) end end + debug_print( + "selected para", + pagenum() .. + "/" .. + paragraph_index .. + " (" .. + best_cost .. + ")" + ) + + if best_cost > tex.getcount(max_cost) then + -- If the best replacement is too bad, we can't do anything + warning("Widow/Orphan NOT removed on page " .. pagenum()) + paragraphs = {} + return head_save + end + local target_node = paragraphs[paragraph_index].node - local clear_flag = false - -- Loop through all of the nodes on the page + -- Start of final paragraph + debug_print("remove_widows", "moving last line") + + head = last(head_save).prev + local big_penalty_found, last_line, hlist_head + while head do + if head.id == glue_id then + -- Ignore any glue nodes + elseif head.id == penalty_id and head.penalty >= 10000 then + -- Infinite break penalty + big_penalty_found = true + elseif big_penalty_found and head.id == hlist_id then + -- Line before the penalty + if lwc.nobreak_behaviour == "keep" then + hlist_head = head + big_penalty_found = false + elseif lwc.nobreak_behaviour == "split" then + head = last(head_save) + break + elseif lwc.nobreak_behaviour == "warn" then + warning("Widow/Orphan NOT removed on page " .. pagenum()) + paragraphs = {} + return head_save + end + else + -- Not found + if hlist_head then + head = hlist_head + else + head = last(head_save) + end + break + end + head = head.prev + end + + last_line = copy(head) + last(last_line).next = copy(tex.lists[contrib_head]) + + head.prev.prev.next = nil + -- Move the last line to the next page + tex.lists[contrib_head] = last_line + + local free_next_nodes = false + + -- Loop through all of the nodes on the page with the lwc attribute + head = head_save while head do + local value + value, head = find_attribute(head, attribute) + + if not head then + break + end + + debug_print("remove_widows", "found " .. value) + -- Insert the start of the replacement paragraph - if has_attribute(head, attribute, paragraph_index) then + if value == paragraph_index then + debug_print("remove_widows", "replacement start") safe_last(target_node) -- Remove any loops head.prev.next = target_node - clear_flag = true + free_next_nodes = true end -- Insert the end of the replacement paragraph - if has_attribute(head, attribute, -1 * paragraph_index) then + if value == -1 * paragraph_index then + debug_print("remove_widows", "replacement end") safe_last(target_node).next = head.next - clear_flag = false + break end - -- Start of final paragraph - if has_attribute(head, attribute, #paragraphs) then - local last_line = copy(last(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[contrib_head] = last_line - info( - "Widow/orphan successfully removed at paragraph " - .. paragraph_index - .. " on page " - .. pagenum() - .. "." - ) - end - - if clear_flag then + if free_next_nodes then head = free(head) else head = head.next end end + info( + "Widow/orphan successfully removed at paragraph " + .. paragraph_index + .. " on page " + .. pagenum() + .. "." + ) + paragraphs = {} -- Clear paragraphs array at the end of the page return head_save @@ -413,18 +556,19 @@ lwc.callbacks = { local enabled = false function lwc.enable_callbacks() + debug_print("callbacks", "enabling") if not enabled then - lwc.callbacks.remove_widows.enable() lwc.callbacks.save_paragraphs.enable() lwc.callbacks.mark_paragraphs.enable() enabled = true else - warning("Already enabled") + info("Already enabled") end end function lwc.disable_callbacks() + debug_print("callbacks", "disabling") if enabled then lwc.callbacks.save_paragraphs.disable() lwc.callbacks.mark_paragraphs.disable() @@ -436,11 +580,12 @@ function lwc.disable_callbacks() enabled = false else - warning("Already disabled") + info("Already disabled") end end function lwc.if_lwc_enabled() + debug_print("iflwc") if enabled then tex.sprint("\\iftrue") else @@ -448,4 +593,49 @@ function lwc.if_lwc_enabled() end end +--- Silence the luatexbase "Enabling/Removing <callback>" info messages +--- +--- Every time that a paragraph is typeset, \lwc/ hooks in +--- and typesets the paragraph 1 line longer. Some of these longer paragraphs +--- will have pretty bad badness values, so TeX will issue an over/underfull +--- hbox warning. To block these warnings, we hook into the `hpack_quality` +--- callback and disable it so that no warning is generated. +--- +--- However, each time that we enable/disable the null `hpack_quality` callback, +--- luatexbase puts an info message in the log. This completely fills the log file +--- with useless error messages, so we disable it here. +--- +--- This uses the Lua `debug` library to internally modify the log upvalue in the +--- `add_to_callback` function. This is almost certainly a terrible idea, but I don't +--- know of a better way. +local function silence_luatexbase() + local nups = debug.getinfo(luatexbase.add_to_callback).nups + + for x = 1, nups do + local name, func = debug.getupvalue(luatexbase.add_to_callback, x) + if name == "luatexbase_log" then + debug.setupvalue( + luatexbase.add_to_callback, + x, + function(text) + if text:match("^Inserting") or text:match("^Removing") then + return + else + func(text) + end + end + ) + return + end + end +end + + +-- Activate \lwc/ +if plain or latex then + silence_luatexbase() +end + +lwc.callbacks.remove_widows.enable() + return lwc 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 d83e6114f4f..7a75e52132f 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 v1.1.6} %%version +\wlog{lua-widow-control v2.0.0} %%version \ifx\directlua\undefined \errmessage{% @@ -16,13 +16,14 @@ \clubpenalty=1 \widowpenalty=1 -\displaywidowpenalty=0 -\interlinepenalty=0 -\brokenpenalty=0 +\displaywidowpenalty=1 \newdimen\lwcemergencystretch \lwcemergencystretch=3em +\newcount\lwcmaxcost +\lwcmaxcost=2147483647 + \directlua{require "lua-widow-control"} % Here, we enable font expansion/contraction. It isn't strictly necessary for 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 4b8837cfec9..992d817a5c8 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,17 +3,18 @@ % SPDX-License-Identifier: MPL-2.0+ % SPDX-FileCopyrightText: 2022 Max Chernoff -\_codedecl\lwcenable{lua-widow-control <v1.1.6>} %%version +\_codedecl\lwcenable{lua-widow-control <v2.0.0>} %%version \_clubpenalty=1 \_widowpenalty=1 -\_displaywidowpenalty=0 -\_interlinepenalty=0 -\_brokenpenalty=0 +\_displaywidowpenalty=1 \_newdimen\lwcemergencystretch \lwcemergencystretch=3em +\_newcount\lwcmaxcost +\lwcmaxcost=2147483647 + \_directlua{require "lua-widow-control"} % Define \TeX{} wrappers for Lua functions |