diff options
author | Norbert Preining <norbert@preining.info> | 2019-09-02 13:46:59 +0900 |
---|---|---|
committer | Norbert Preining <norbert@preining.info> | 2019-09-02 13:46:59 +0900 |
commit | e0c6872cf40896c7be36b11dcc744620f10adf1d (patch) | |
tree | 60335e10d2f4354b0674ec22d7b53f0f8abee672 /macros/latex/contrib/widows-and-orphans |
Initial commit
Diffstat (limited to 'macros/latex/contrib/widows-and-orphans')
-rw-r--r-- | macros/latex/contrib/widows-and-orphans/README.md | 28 | ||||
-rw-r--r-- | macros/latex/contrib/widows-and-orphans/changes.txt | 5 | ||||
-rw-r--r-- | macros/latex/contrib/widows-and-orphans/widows-and-orphans-code.pdf | bin | 0 -> 535372 bytes | |||
-rw-r--r-- | macros/latex/contrib/widows-and-orphans/widows-and-orphans-code.tex | 4 | ||||
-rw-r--r-- | macros/latex/contrib/widows-and-orphans/widows-and-orphans-doc.pdf | bin | 0 -> 264666 bytes | |||
-rw-r--r-- | macros/latex/contrib/widows-and-orphans/widows-and-orphans-doc.tex | 5 | ||||
-rw-r--r-- | macros/latex/contrib/widows-and-orphans/widows-and-orphans.dtx | 1049 | ||||
-rw-r--r-- | macros/latex/contrib/widows-and-orphans/widows-and-orphans.ins | 61 |
8 files changed, 1152 insertions, 0 deletions
diff --git a/macros/latex/contrib/widows-and-orphans/README.md b/macros/latex/contrib/widows-and-orphans/README.md new file mode 100644 index 0000000000..147333ff6b --- /dev/null +++ b/macros/latex/contrib/widows-and-orphans/README.md @@ -0,0 +1,28 @@ +# widows-and-orphans + +----- + +Automatically identifying widows and orphans in a document + +This package identifies all widows and orphans in a document to help a user to get rid of them. The act of resolving still needs to be done manually: By rewriting text, running some paragraph long or short or or explicitly breaking in some strategic place. + +It will also identify and warn about words broken across columns or pages and display formulas separated from their introductory paragraph. + + The initial idea was born way back in 2012 when somebody on stackexchange asked if it would be possible to [Find all widows and orphans](https://tex.stackexchange.com/questions/45528/finding-all-widows-and-orphans). + +Package source can be found below: + +https://github.com/FrankMittelbach/fmitex/ + +The license is LPPL. + +A general discussion on how to manage widows and orphans can be found in a TugBoat article located at + +https://www.latex-project.org/publications/indexbytopic/2e-packages/ + +----- + +Copyright (C) 2018 Frank Mittelbach<br /> +<https://latex-project.org/> <br /> +All rights reserved. + diff --git a/macros/latex/contrib/widows-and-orphans/changes.txt b/macros/latex/contrib/widows-and-orphans/changes.txt new file mode 100644 index 0000000000..398f99f7d0 --- /dev/null +++ b/macros/latex/contrib/widows-and-orphans/changes.txt @@ -0,0 +1,5 @@ +2018-11-18 <Frank.Mittelbach@latex-project.org> + + * widows-and-orphans.dtx: Surplus open parenthese in message, + spotted by Barbara Beeton's eagle eyes. + diff --git a/macros/latex/contrib/widows-and-orphans/widows-and-orphans-code.pdf b/macros/latex/contrib/widows-and-orphans/widows-and-orphans-code.pdf Binary files differnew file mode 100644 index 0000000000..2c8da3b152 --- /dev/null +++ b/macros/latex/contrib/widows-and-orphans/widows-and-orphans-code.pdf diff --git a/macros/latex/contrib/widows-and-orphans/widows-and-orphans-code.tex b/macros/latex/contrib/widows-and-orphans/widows-and-orphans-code.tex new file mode 100644 index 0000000000..26831a6436 --- /dev/null +++ b/macros/latex/contrib/widows-and-orphans/widows-and-orphans-code.tex @@ -0,0 +1,4 @@ +% this will typeset documentation + code + +\AtBeginDocument{\AlsoImplementation} +\input{widows-and-orphans.dtx} diff --git a/macros/latex/contrib/widows-and-orphans/widows-and-orphans-doc.pdf b/macros/latex/contrib/widows-and-orphans/widows-and-orphans-doc.pdf Binary files differnew file mode 100644 index 0000000000..b432515a1e --- /dev/null +++ b/macros/latex/contrib/widows-and-orphans/widows-and-orphans-doc.pdf diff --git a/macros/latex/contrib/widows-and-orphans/widows-and-orphans-doc.tex b/macros/latex/contrib/widows-and-orphans/widows-and-orphans-doc.tex new file mode 100644 index 0000000000..9f8b1efbea --- /dev/null +++ b/macros/latex/contrib/widows-and-orphans/widows-and-orphans-doc.tex @@ -0,0 +1,5 @@ +% this will typeset only documentation but not the code + +\AtBeginDocument{\OnlyDescription + \let\tableofcontents\relax} +\input{widows-and-orphans.dtx} diff --git a/macros/latex/contrib/widows-and-orphans/widows-and-orphans.dtx b/macros/latex/contrib/widows-and-orphans/widows-and-orphans.dtx new file mode 100644 index 0000000000..bef8cd4a69 --- /dev/null +++ b/macros/latex/contrib/widows-and-orphans/widows-and-orphans.dtx @@ -0,0 +1,1049 @@ +% \iffalse meta-comment +%% +%% File: widows-and-orphans.dtx (C) Copyright 2017-2018 Frank Mittelbach +% +% It may be distributed and/or modified under the conditions of the +% LaTeX Project Public License (LPPL), either version 1.3c of this +% license or (at your option) any later version. The latest version +% of this license is in the file +% +% http://www.latex-project.org/lppl.txt +% +% +% The development version of the bundle can be found at +% +% https://github.com/FrankMittelbach/fmitex/widows-and-orphans +% +% for those people who are interested. +% \fi +% \iffalse +%<*driver> +\RequirePackage[nohyphen]{underscore} % no hyphen after undercore + % in csname +\documentclass + [final] + {l3doc-TUB} + +\setcounter{page}{1} + +% fix for _TF undefined, +% https://github.com/latex3/latex3/issues/477#issuecomment-419458783 +\ExplSyntaxOn +\cs_set_protected:Npn \__codedoc_typeset_TF: + { + \group_begin: + \exp_args:No \__codedoc_if_macro_internal:nT \l__codedoc_tmpa_tl + { \color[gray]{0.5} } + \itshape TF +\iffalse \makebox[0pt][r] + { + \color{red} + \underline{\phantom{\itshape TF} \kern-0.1em} + } \fi + \group_end: + } + +\NewDocumentCommand \TypesetImplementationTF { m m } + { + \bool_if:NTF \g__codedoc_typeset_implementation_bool + { #1 } + { #2 } + } + +\ExplSyntaxOff + +\makeatletter % fix for ltugboat issue with doc (if old doc is used) +\def\dotfill{\leaders\hbox to.6em{\hss .\hss}\hskip\z@ plus 1fill\kern\z@}% +\def\dotfil{\leaders\hbox to.6em{\hss .\hss}\hfil\kern\z@}% +\def\pfill{\unskip~\dotfill\penalty500\strut\nobreak + \dotfil~\ignorespaces}% +\makeatother + +\begin{document} + +\DocInput{widows-and-orphans.dtx} +\advance\signaturewidth by 42pt +\makesignature +\end{document} +%</driver> +% \fi +% +% \DoNotIndex{\cs_new:Npn,\cs_new_nopar:Npn,\csname,\endcsname,\fi:,\else:} +% \DoNotIndex{\tl_new:N,\bool_new:N} +% \DoNotIndex{\if_meaning:w,\iftrue,\int_new:N,\int_set:Nn} +% \DoNotIndex{\prop_new:N,\tl_put_left:Nn} +% \DoNotIndex{\bool_do_until:Nn,\bool_gset_false:N,\bool_gset_true:N} +% \DoNotIndex{\bool_if:NTF,\bool_set_false:N,\bool_set_true:N} +% \DoNotIndex{\clist_map_break:n,\clist_map_inline:nn} +% \DoNotIndex{\cs_if_eq:NNTF,\exp_args:Nc} +% \DoNotIndex{\int_case:nnTF,\int_case:nnF,\int_case:nnF,\int_incr:N} +% \DoNotIndex{\int_to_arabic:n,\int_to_roman:n} +% \DoNotIndex{\keys_define:nn,\keys_set:nn} +% \DoNotIndex{\msg_err:nn,\msg_new:nnnn,\msg_redirect_module:nnn} +% \DoNotIndex{\msg_warning:nn,\msg_warning:nnn,\msg_error:nn} +% \DoNotIndex{\prop_clear:N,\prop_get:NnNTF,\prop_put:Nnn,\prop_show:N} +% \DoNotIndex{\prg_new_conditional:Npnn} +% \DoNotIndex{\prg_return_false:,\prg_return_true:} +% \DoNotIndex{\prop_get:NVNT,\prop_put:NVn,\bool_if:NF,\bool_if:NT} +% \DoNotIndex{\marginpar,\space,\ignorespaces,\thepage} +% ^^A\DoNotIndex{\RequirePackage,\NeedsTeXFormat,\NewDocumentCommand} +% +% \title{The \pkg{widows-and-orphans} package} +% \author{Frank Mittelbach} +% \date{Printed \today\thanks{For the package version +% see the \texttt{\string\ProvidesExplPackage} line in the code.}} +% \address{Mainz, Germany} +% \netaddress{https://www.latex-project.org} +% \personalURL{https://ctan.org/pkg/widows-and-orphans} +% +% \maketitle +% +% \begin{abstract} +% The \pkg{widows-and-orphans} package checks page or column breaks +% for issues with widow or orphan lines and issues warnings if such +% problems are detected. In addition, it checks and complains +% about breaks involving hyphenated words and warns about display +% formulas directly after a page break\Dash if they are allowed by the +% document parameter settings, which by default isn't the case. +% +% A general discussion of the problem of widows and orphans and +% suggestions for resolution is given in~\cite{tub-wao}. +% \end{abstract} +% +% \tableofcontents +% +% \newcommand\option[1]{\texttt{#1}} +% \newcommand\ovalue[1]{\texttt{#1}} +% +% \section{Overview} +% +% To determine if a widow or orphan has occurred at a column or page +% break, the package analyzes the \cs{outputpenalty} that triggered the +% break. As \TeX{} adds special penalties to widow and orphan lines +% (\cs{widowpenalty} and \cs{clubpenalty}), we can hope to identify +% them, provided the penalties have unique values so that we don't end +% up with false positives. +% +% \TypesetImplementationTF {\enlargethispage*{2\baselineskip}}{} +% +% The package therefore analyzes the different parameter values and if +% necessary adjusts the settings slightly so that all possible +% combinations that can appear in documents have unique values and can +% thus be identified. +% +% All that remains is to hook into the output routine check; +% if \cs{outputpenalty} has a value that matches one of the +% problematic cases, issue a warning. +% +% Besides widows and orphans it is possible to detect other cases +% controlled through penalty parameters, e.g., \cs{brokenpenalty} that is +% added if a line ends in a hyphen. So by including this parameter +% into the checks, we can identify when that happens at the end of a +% column and issue a warning there too. +% +% We also do this for \cs{predisplaypenalty}, which controls a break just +% in front of a math display. This is normally set to \texttt{10000} +% so such breaks don't happen in standard \LaTeX{}, but if the value +% is lowered it becomes possible, and thus a possible issue. +% +% \subsection{Options} +% +% The package has a number of key/value options to adjust its +% behavior. +% The option \option{check} defines what happens +% when an issue is found: default is \ovalue{warning}, other +% possibilities are \ovalue{error}, \ovalue{info} and \ovalue{none}. +% +% The options \option{orphans} and \option{widows} set +% reasonable parameter values; the default is to use whatever the class +% defines. Possible values are \ovalue{prevent}, \ovalue{avoid} or +% \ovalue{default}, the latter meaning use standard \LaTeX{} defaults. +% +% To set all parameters in one go you can use \option{prevent-all}, +% \option{avoid-all} or \option{default-all}. These options also assign +% values to \cs{brokenpenalty} and \cs{predisplaypenalty}. +% +% \TypesetImplementationTF {\pagebreak}{} +% +% +% \subsection{User commands} +% +% The package provides three user-level commands. +% +% \vspace*{-4pt} +% \begin{function}{\WaOsetup} +% \begin{syntax} +% \cs{WaOsetup} \meta{comma list} +% \end{syntax} +% This command accepts any of the package options and allows +% adjusting the package behavior in mid-document if necessary. +% \end{function} + +% \vspace*{-4pt} +% \begin{function}{\WaOparameters} +% \begin{syntax} +% \cs{WaOparameters} +% \end{syntax} +% This command produces a listing of the parameter combinations and +% their values. +% \end{function} + +% \vspace*{-4pt} +% \begin{function}{\WaOignorenext} +% \begin{syntax} +% \cs{WaOignorenext} +% \end{syntax} +% This command directs the package to not generate warnings for +% the current page or columns (if we know that they can't be corrected). +% \end{function} +% +% +% +% \subsection{Related packages} +% +% Package \pkg{nowidow}: This package offers some commands to help +% pushing lines from one page to another by locally +% requesting no widows or orphans\Dash possibly for several lines. In that +% respect it implements one of the possibilities discussed in the +% \TUB\ article~\cite{tub-wao}. This is, however, in many cases not the +% best solution to get rid of a widow or orphan when the interest is +% to achieve high typographical quality. +% +% +% +% \StopEventually{ +% \begin{thebibliography}{1} +% \bibitem{tub-wao} +% Frank Mittelbach. +% \newblock Managing forlorn paragraph lines (a.k.a.\ widows and orphans) +% in \LaTeX{}. +% \newblock \textsl{TUGboat} 39:3, +% \ifx\thisissuepageref\undefined \else \thisissuepageref{mitt-widows} ,\fi +% 2018. +% \bibitem{expl3} +% \LaTeX3 Project Team. +% \newblock A collection of articles on \pkg{expl3}.\\ +% \url{https://latex-project.org/publications/indexbytopic/l3-expl3/} +% \end{thebibliography} +% \ifx\thisissuepageref\undefined ^^A is this TUB production ??? if not gen index +% \setlength\IndexMin{200pt} \PrintIndex +% \fi +% } +% +% +% \section{The implementation} +% +% The package is implemented in \pkg{expl3}, so the first thing we do +% is to define a prefix for our internal commands, so that we can +% write \texttt{@@} in the source when we mean the prefix \texttt{__fmwao}, +% indicating that something is internal.\footnote{\texttt{l3docstrip} +% expands that for us, so in the \texttt{.sty} file we get the +% longer names with double underscores even though the real source +% just contains \texttt{@@} all over the place. The same is done by +% the \texttt{l3doc} class for the printed documentation you are +% currently reading.\raggedright} +% +% \begin{macrocode} +%<@@=fmwao> +% \end{macrocode} +% +% Then we check that we are running on top of \LaTeXe{} and load the +% two packages we want to use: \pkg{xparse} for the user interface and +% \pkg{l3keys2e} for key/value option syntax. They load the needed +% \pkg{expl3} code so we are ready to roll afterwards. +% +% \begin{macrocode} +\NeedsTeXFormat{LaTeX2e} \RequirePackage{xparse,l3keys2e} +% \end{macrocode} +% +% Then we announce the package to the world at large. This declaration +% will also tell \LaTeX{} that this is an \pkg{expl3} package and that +% from now on the \pkg{expl3} conventions are to be obeyed, e.g., +% \texttt{_} and \texttt{:} can appear in command names and whitespace +% is automatically ignored (so no need for \texttt{\%} all over +% the place).\footnote{Figure~\ref{fig:expl3} gives a short +% introduction to the naming conventions of \pkg{expl3} for those +% readers who haven't seen any code written in \pkg{expl3}. +% For more details refer to~\cite{expl3}.} +% +% \begin{figure} +% \centering +% \setlength\fboxsep{7pt} +% \noindent\hspace{-\marginparwidth}\fbox{\begin{minipage}{1.21\textwidth} +% Commands in \pkg{expl3} use the following naming convention: +% \begin{tabbing} +% \qquad \texttt{\space\space\bslash\meta{module}\_\meta{action}:\meta{arg-spec}} +% \quad +% \= \% externally available command \\ +% \qquad \texttt{\bslash\_\_\meta{module}\_\meta{action}:\meta{arg-spec}} +% \> \% internal command local to the package +% \end{tabbing} +% +% \vspace*{2pt} +% +% \begin{description} \raggedright +% \item[\ttfamily\meta{module}] describes the (main) area to which +% the command belongs, +% \item[\ttfamily\meta{action}] describes the (main) action of the +% command, and +% \item[\ttfamily\meta{arg-spec}] shows the expected command +% arguments and their preprocessing:\\ +% \texttt{N} means expect a +% single token; \\ +% \texttt{n} means expect a (normal) braced +% argument;\\ +% \texttt{T} and \texttt{F} also represent braced +% arguments, indicating ``true'' and ``false'' branches in a +% conditional; \\ +% \texttt{V} means expect a single token, interpret it as a +% variable and pass its value on to the command;\\ +% Finally, \texttt{p} stands for a (possibly empty) parameter spec, e.g., +% \texttt{\#1\#2...}\ in a definition. +% \\There are a number of other +% argument types, but they aren't used in the code described here. +% \end{description} +% +% \vspace*{2pt} +% +% Examples: +% \begin{description} \raggedright +% \item[\cs{cs_if_eq:NNTF}] is a conditional from the module +% \texttt{cs} (command names) implementing the action \\ +% \texttt{if\_eq} (if equal) and expecting two single tokens +% (commands to compare) and a true and a false branch (one of +% which is executed). +% +% \item[\cs{tl_put_left:Nn}] is a function from the module +% \texttt{tl} (token lists) implementing \texttt{put\_left} and \\ +% expecting a single token (a token list variable) and a braced +% argument (the data to insert at \\ +% the front/left in the variable). +% \end{description} +% +% \medskip +% +% Variables start with \cs{l_} (for local) or \cs{g_} (for global) +% and have the data type as the last part of the name. Variables +% internal to the package use two underscores, e.g., +% \cs{l__@@_gen_warn_bool}. +% +% \end{minipage}} +% +% \caption{Crash course in \pkg{expl3} command name conventions} +% \label{fig:expl3} +% \end{figure} +% +% \begin{macrocode} +\ProvidesExplPackage{widows-and-orphans}{2018/11/18}{v1.0b} + {Detecting widows and orphans (FMi)} +% \end{macrocode} +% +% +% \begin{macro}[int]{\@makecol} +% +% As mentioned in the introduction we want to check the value of +% \cs{outputpenalty} inside the output routine, and so we need +% to add some code to the output routine. +% +% We add it to the front of \cs{@makecol}, the macro that +% assembles the actual column. This way it only gets executed if we +% make a column or a page but not when the output routine is +% triggered because of a float or \cs[no-index]{marginpar}. +% \begin{macrocode} +\tl_put_left:Nn \@makecol { \@@_test_for_widows_etc: } +% \end{macrocode} +% \end{macro} +% +% +% \subsection{Checking \cs{outputpenalty}} +% +% +% \begin{macro}{\g_@@_gen_warn_bool} +% To be able to suppress checking we define a global boolean +% variable which by default is set to \texttt{true} (warnings enabled). +% \begin{macrocode} +\bool_new:N \g_@@_gen_warn_bool +\bool_gset_true:N \g_@@_gen_warn_bool +% \end{macrocode} +% \end{macro} +% +% +% +% \begin{macro}{\@@_test_for_widows_etc:} +% +% What are the different values related to orphans and widows and +% the like that can appear in \cs{outputpenalty}? Here is the +% basic list: +% \begin{description} +% +% \item[$\cs{widowpenalty} + \cs{interlinepenalty} \to$] if the +% break happens on the second-last line of a paragraph and the +% paragraph is not followed by a math display. +% +% \item[$\cs{displaywidowpenalty} + \cs{interlinepenalty} \to$] if +% the break happens on the second-last line of a paragraph and the +% paragraph is followed by a math display. +% +% \item[$\cs{clubpenalty} + \cs{interlinepenalty} \to$] if the +% break happens after the first line of a paragraph and the +% paragraph has more than two lines. +% +% \item[$\cs{clubpenalty}+\cs{widowpenalty} + \cs{interlinepenalty} +% \to$] if the break happens after the first line of a paragraph in +% a two-line paragraph (thus this line is also the second-last +% line). +% +% \item[$\cs{clubpenalty}+\cs{displaywidowpenalty} + +% \cs{interlinepenalty} \to $] if the +% break happens after the first line of a paragraph in a two-line +% paragraph and a math display follows. +% +% \end{description} +% That's it for widows and orphans. If we also consider hyphenated +% breaks then we get a further set of cases, namely all of the +% above with \cs{brokenpenalty} added in and the case of +% \cs{brokenpenalty} on its own (with just \cs{interlinepenalty} added). +% +% \begin{macrocode} +\cs_new:Npn \@@_test_for_widows_etc: { +% \end{macrocode} +% So here is the main test. We compare \cs{outputpenalty} with each +% case until we have a hit or run out of cases. Instead of adding +% \cs{interlinepenalty} to each case we subtract it once from +% \cs{outputpenalty} to make the comparison a little more +% efficient. +% +% If we get a hit, we execute the corresponding code: either +% \cs{@@_problem_identified:n} or \cs{@@_problem_identified:nn} +% (i.e., one or two arguments) to issue the warning. The arguments +% are to select the correct warning text and customize it further +% if necessary. For example, in the first case it is a ``widow'' +% problem and the text in the message should start with +% ``\texttt{Widow}'' while in the second case it is also a +% ``widow'' problem but the text will say ``\texttt{Display\string~ +% widow}''.\footnote{If you haven't seen much \pkg{expl3} code you +% may wonder about the \texttt{\string~}. As the code ignores +% spaces we have to mark up real spaces and for this the tilde is +% used. In \pkg{expl3} code this does not act as a tie, but simply +% as a catcode 10 space character (while normal spaces are +% ignored).} This just saves a bit of space when different cases +% share more or less the same message text. +% \begin{macrocode} + \int_case:nnF { \outputpenalty - \interlinepenalty } + { + { \widowpenalty } + { \@@_problem_identified:nn{widow}{Widow} } + { \displaywidowpenalty } + { \@@_problem_identified:nn{widow}{Display~ widow} } + { \clubpenalty } + { \@@_problem_identified:n{orphan} } + { \clubpenalty + \widowpenalty } + { \@@_problem_identified:nn{orphan-widow}{} } + { \clubpenalty + \displaywidowpenalty } + { \@@_problem_identified:nn{orphan-widow}{display} } +% \end{macrocode} +% +% A similar issue comes from a hyphen at the end of a column or +% page in which case \TeX{} adds \cs{brokenpenalty} so we can test +% against that: +% \begin{macrocode} + { \brokenpenalty } + { \@@_problem_identified:n{hyphen} } +% \end{macrocode} +% However, I said ``\TeX{} adds'', which means if a widow line also +% ends in a hyphen then the penalty will be the sum of both +% individual penalties. So all the cases above need to be repeated +% with \cs{brokenpenalty} added to the value. +% We generate the same warnings, though\Dash e.g., we will say ``Widow +% detected'' and not ``Hyphenated widow line detected'' as the +% latter seems to be overkill. +% \begin{macrocode} + { \brokenpenalty + \widowpenalty } + { \@@_problem_identified:nn{widow}{Widow} } + { \brokenpenalty + \displaywidowpenalty } + { \@@_problem_identified:nn{widow}{Display~ widow} } + { \brokenpenalty + \clubpenalty } + { \@@_problem_identified:n{orphan} } + { \brokenpenalty + \clubpenalty + \widowpenalty } + { \@@_problem_identified:nn{orphan-widow}{} } + { \brokenpenalty + \clubpenalty + \displaywidowpenalty } + { \@@_problem_identified:nn{orphan-widow}{display} } +% \end{macrocode} +% Finally there is \cs{predisplaypenalty} that we may as well check also +% (in case it was set to a value lower than \texttt{10000}). If it +% appears it means we have a display at the very top of the page. +% We reuse the ``widow'' warning but this time say +% ``\texttt{Lonely\string~ display}'' in the second argument. +% This case does not have \cs{interlinepenalty} added by +% \TeX{}, so we have to undo the optimization above. +% \begin{macrocode} + { \predisplaypenalty - \interlinepenalty } + { \@@_problem_identified:nn{widow}{Lonely~ display} } + } +% \end{macrocode} +% The last argument of \cs[no-index]{int_case:nnF} is executed in +% the ``false'' case, i.e., when no match has been found. In that +% case we check the status of \cs{g_@@_gen_warn_bool} and +% if that is also ``false'', i.e., we have been asked not to +% generate warnings, we issue an error message. Why? Because +% the user asked us explicitly to ignore problems on the +% current page, but we found nothing wrong. This either means a +% problem got corrected or the request was intended for a +% different page. Either way it is probably worth checking. +% +% \begin{macrocode} + { \bool_if:NF \g_@@_gen_warn_bool + { \msg_error:nn{widows-and-orphans}{no-problem} } } +% \end{macrocode} +% Finally, we make sure that the next page or column is again +% checked. +% \begin{macrocode} + \bool_gset_true:N \g_@@_gen_warn_bool +} +% \end{macrocode} +% \end{macro} + + + +% \begin{macro}{\@@_problem_identified:n} +% \begin{macro}{\@@_problem_identified:nn} +% These commands prepare for generating a warning, but only if we +% are supposed to, i.e., if \cs{g_@@_gen_warn_bool} is true. +% \begin{macrocode} +\cs_new:Npn \@@_problem_identified:n #1 { + \bool_if:NT \g_@@_gen_warn_bool + { \msg_warning:nn{widows-and-orphans}{#1} } +} +\cs_new:Npn \@@_problem_identified:nn #1 #2 { + \bool_if:NT \g_@@_gen_warn_bool + { \msg_warning:nnn{widows-and-orphans}{#1}{#2} } +} +% \end{macrocode} +% \end{macro} +% \end{macro} + + +% \subsection{Messages to the user} +% +% +% \begin{macro}{\@@_this_page:} +% \begin{macro}{\@@_next_page:} +% For displaying nice messages to the user we need a few helper +% commands. The two here show the page number of the current or +% next page. They are semi-smart, that is they will recognize +% if the document uses roman numerals and if so display the number +% as a roman numeral (but in all other cases it uses arabic numerals). +% \begin{macrocode} +\cs_new:Npn \@@_this_page: { \@@_some_page:n \c@page } +\cs_new:Npn \@@_next_page: { \@@_some_page:n { \c@page + 1 } } +% \end{macrocode} +% \end{macro} +% \end{macro} + + +% \begin{macro}{\@@_some_page:n} +% \begin{macro}{\@@_roman_thepage:} +% This macro first compares \cs[no-index]{thepage} against the code that +% would be used in the case of a roman numeral representation, and then +% displays its argument using either arabic numbers or roman numerals. +% \begin{macrocode} +\cs_new:Npn \@@_some_page:n #1 { + \cs_if_eq:NNTF \thepage \@@_roman_thepage: + { \int_to_roman:n } { \int_to_arabic:n } + { #1 } +} +% \end{macrocode} +% \cs{@@_roman_thepage:} just stores the default definition of +% \cs[no-index]{thepage} if page numbers are represented by roman numerals +% for use in the comparison above. +% \begin{macrocode} +\cs_new_nopar:Npn \@@_roman_thepage: {\csname @roman\endcsname \c@page} +% \end{macrocode} +% \end{macro} +% \end{macro} + + + + +% \begin{macro}[TF, int]{\legacy_switch_if:n} +% To evaluate \LaTeXe{} boolean switches in a nice way, we need a +% conditional. Eventually this will probably make it into the \pkg{expl3} +% code in this or a similar form, but right now it is missing. +% \begin{macrocode} +\prg_new_conditional:Npnn \legacy_switch_if:n #1 {p, T , F , TF } + { \exp_args:Nc\if_meaning:w { if#1 } \iftrue \prg_return_true: + \else: \prg_return_false: \fi: } +% \end{macrocode} +% \end{macro} +% +% +% +% +% The first message is issued if we have been directed to ignore a +% problem and there wasn't one:{\hfuzz=14.1pt +% \begin{macrocode} +\msg_new:nnnn {widows-and-orphans} {no-problem} + { No~ problem~ to~ suppress~ on~ this~ page! } + { Suppression~ of~ a~ widow~ or~ orphan~ problem~ was~ requested~ + but~ on~ the~ current~ page~ there~ doesn't~ seem~ to~ be~ any.~ + Maybe~ the~ text~ was~ changed~ and~ the~ request~ should~ get~ + (re)moved?} +% \end{macrocode} +% } +% +% \bigskip +% +% +% +% +% The next message is about orphans. They can appear at the bottom of +% the first or the second column of the current page, if we are in +% two-column mode. So we check for this and adjust the message accordingly. +% \begin{macrocode} +\msg_new:nnnn {widows-and-orphans} {orphan} + { Orphan~ on~ page~ \@@_this_page: + \legacy_switch_if:nT {@twocolumn} + { \space ( \legacy_switch_if:nTF {@firstcolumn} + { first~ } { second~ } column) } + } + { Check~ out~ the~ page~ and~ see~ if~ you~ can~ avoid~ the~ orphan.} +% \end{macrocode} +% +% +% \bigskip +% +% +% A hyphen at the end of a page or column requires more or less the +% same message, so this could have been combined with the previous one. +% \begin{macrocode} +\msg_new:nnnn {widows-and-orphans} {hyphen} + { Hyphen~ in~ last~ line~ of~ page~ \@@_this_page: + \legacy_switch_if:nT {@twocolumn} + { \space ( \legacy_switch_if:nTF {@firstcolumn} + { first~ } { second~ } column) } + } + { Check~ out~ the~ page~ and~ see~ if~ you~ can~ get~ + a~ better~ line~ break. } +% \end{macrocode} +% +% +% \bigskip +% +% +% Widows need a different logic since we detect them when cutting a +% previous page or column but the widow is on the following one. +% This message works for ``widows'', ``display widows'' as well as +% math displays by just changing the first word (or words), so here +% we use an additional argument: +% \begin{macrocode} +\msg_new:nnnn {widows-and-orphans} {widow} + { #1~ on~ page~ + \legacy_switch_if:nTF {@twocolumn} + { \legacy_switch_if:nTF {@firstcolumn} + { \@@_this_page: \space (second~ } + { \@@_next_page: \space (first~ } + column) + } + { \@@_next_page: } + } + { Check~ out~ the~ page~ and~ see~ if~ you~ can~ avoid~ the~ widow.} +% \end{macrocode} +% +% +% \bigskip +% +% +% The case of both widow and orphan is similar, but we obviously +% need different text so we made it its own message. +% \begin{macrocode} +\msg_new:nnnn {widows-and-orphans} {orphan-widow} + { Orphan~ + \legacy_switch_if:nTF {@twocolumn} + { \legacy_switch_if:nTF {@firstcolumn} + { and~ #1 widow~ on~ page~ \@@_this_page: \space + (first~ and~ second~ } + { on~ page~ \@@_this_page: \space (second~ column)~ + and~ #1 widow~ on~ page~ \@@_next_page: \space (first~ } + } + { on~ page~ \@@_this_page: \space (second~ column)~ + and~ #1 widow~ on~ page~ \@@_next_page: \space (first~ } + column) + } + { Check~ out~ the~ page~ and~ see~ if~ you~ can~ avoid~ both~ + orphan~ and~ widow.} +% \end{macrocode} +% +% +% +% +% +% +% \subsection{Adjusting parameter values} +% +% To avoid (a lot of) false positives during checking it is important +% that the parameter values are chosen in a way that all possible +% combinations lead to unique \cs{outputpenalty} values. At the same +% time, we want them to be as close as possible to the values that +% have been initially requested by the user (or in the document class) +% and if we deviate too much then this will likely alter the +% page breaks \TeX{} finds. +% So here is an outline of how we handle the parameters: +% \begin{itemize} +% \item We set up a property list to hold penalty values that +% can appear in \cs{outputpenalty} inside the output routine. The +% penalties are the ``keys'' and the corresponding property list value is +% the source of how they got produced. For example, the key might be +% \texttt{150} and the value \cs{widowpenalty}. +% +% \item Initially the property list is empty. So adding the first item +% simply means taking the value of one parameter, say \texttt{150} +% from $\cs{widowpenalty}+\cs{interlinepenalty}$, as the key and +% this formula as the property list value. +% +% \item For the next parameter, say \cs{clubpenalty}, we check if its +% value (or more precisely its value plus \cs{interlinepenalty}) is +% already a key in the property list. If that is the case, then we +% have failed and must modify the parameter value somehow. +% +% \item If not, we also have to check any combination of the current +% parameter with any parameter processed earlier. If that +% combination is possible, e.g., \cs{clubpenalty} (new) and +% \cs{widowpenalty} (already processed) then we also have to check +% the sum. If that sum is already a key in the property list then +% we have failed as well. +% +% \item If we have failed, we iterate by incrementing the current parameter +% value and try again. Eventually we will get to a value where all +% combinations we test work, that is, are not yet in the property list. +% +% \item We then change the parameter to this value and add all the +% combinations we tried before to the property list (that is +% $\cs{clubpenalty}+\cs{interlinepenalty}$ both alone and together with +% \cs{widowpenalty} in our example). Thus from now on those are +% also forbidden values. +% +% \item We do all this with a helper command that takes the new +% parameter as the first argument and the list of different cases +% to try as a comma-separated list as a second argument, e.g., +%\begin{verbatim} +% \__fmwao_decide_penalty:Nn \clubpenalty +% { \clubpenalty + \interlinepenalty , +% \clubpenalty + \widowpenalty + \interlinepenalty } +%\end{verbatim} +% +% \item This way we are adding all relevant parameters to the property +% list and at the same time adjusting their values if needed. +% \item Once all parameters are handled the property list is no longer +% needed as the parameters got changed along the way, but we keep it +% around as it allows for a simple display of all settings in one go. +% \end{itemize} +% +% + +% \begin{macro}{\l_@@_penalties_prop} +% Here is the property list for our process. +% \begin{macrocode} +\prop_new:N \l_@@_penalties_prop +% \end{macrocode} +% \end{macro} +% +% +% \begin{macro}{\@@_initialize:} +% Now we are ready to go. The first action is to clear the property +% list as the initialization may happen several times. +% \begin{macrocode} +\cs_new:Npn \@@_initialize: { + \prop_clear:N \l_@@_penalties_prop +% \end{macrocode} +% When \TeX{} breaks a page at a glue item with no explicit penalty involved +% it sets \cs{outputpenalty} to \texttt{10000} in the output +% routine to distinguish it from a case where an explicit penalty of +% \texttt{0} was in the document. That means none of our parameters +% or parameter combinations can be allowed to have that particular +% value, because otherwise we would get a false match for each break +% at glue and report an issue. So we enter that value first (by +% hand) so that it will not be used by a parameter or parameter +% combination. +% \begin{macrocode} + \prop_put:Nnn \l_@@_penalties_prop {10000} {break~ at~ glue} +% \end{macrocode} +% The next thing is to add the values for \cs{@lowpenalty}, +% \cs{@medpenalty}, \cs{@highpenalty} to the property list as +% they also may show up in \cs{outputpenalty} if a user says, for +% example, \verb=\nopagebreak[2]=. +% +% Such a penalty from an explicit page break request does not get +% \cs{interlinepenalty} added in. +% \begin{macrocode} + \@@_decide_penalty:Nn \@lowpenalty { \@lowpenalty} + \@@_decide_penalty:Nn \@medpenalty { \@medpenalty} + \@@_decide_penalty:Nn \@highpenalty { \@highpenalty} +% \end{macrocode} +% Then comes the first real parameter for the orphans: +% \begin{macrocode} + \@@_decide_penalty:Nn \clubpenalty + { \clubpenalty + \interlinepenalty } +% \end{macrocode} +% followed by the one for the widows and the one for the display widows: +% \begin{macrocode} + \@@_decide_penalty:Nn \widowpenalty + { \widowpenalty + \interlinepenalty , + \widowpenalty + \clubpenalty + \interlinepenalty } + + \@@_decide_penalty:Nn \displaywidowpenalty + { \displaywidowpenalty + \interlinepenalty , + \displaywidowpenalty + \clubpenalty + \interlinepenalty } +% \end{macrocode} +% \cs{brokenpenalty} can appear on its own, and also with each and +% every combination we have seen so far: +% \begin{macrocode} + \@@_decide_penalty:Nn \brokenpenalty + { \brokenpenalty + \interlinepenalty , + \brokenpenalty + \clubpenalty + \interlinepenalty , + \brokenpenalty + \widowpenalty + \interlinepenalty , + \brokenpenalty + \widowpenalty + \clubpenalty + \interlinepenalty , + \brokenpenalty + \displaywidowpenalty + \clubpenalty + + \interlinepenalty } +% \end{macrocode} +% Finally we have the parameter for lonely displays (again without +% \cs{interlinepenalty} being added): +% \begin{macrocode} + \@@_decide_penalty:Nn \predisplaypenalty { \predisplaypenalty } +} +% \end{macrocode} +% If we run the above code with \LaTeX's default parameter settings +% in force it will make a few adjustments and the property list +% will afterwards contain +% the following entries: +%\begin{small} +%\begin{verbatim} +% The property list \l__fmwao_penalties_prop contains the pairs (without +% outer braces): +% > {10000} => {break at glue} +% > {51} => {\@lowpenalty } +% > {151} => {\@medpenalty } +% > {301} => {\@highpenalty } +% > {150} => {\clubpenalty +\interlinepenalty } +% > {152} => {\widowpenalty +\interlinepenalty } +% > {302} => {\widowpenalty +\clubpenalty +\interlinepenalty } +% > {50} => {\displaywidowpenalty +\interlinepenalty } +% > {200} => {\displaywidowpenalty +\clubpenalty +\interlinepenalty } +% > {100} => {\brokenpenalty +\interlinepenalty } +% > {250} => {\brokenpenalty +\clubpenalty +\interlinepenalty } +% > {252} => {\brokenpenalty +\widowpenalty +\interlinepenalty } +% > {402} => {\brokenpenalty +\widowpenalty +\clubpenalty +\interlinepenalty } +% > {300} => {\brokenpenalty +\displaywidowpenalty +\clubpenalty +% +\interlinepenalty } +% > {10001} => {\predisplaypenalty }. +%\end{verbatim} +%\end{small} +% \end{macro} +% +% +% \begin{macro}{\l_@@_tmp_int} +% \begin{macro}{\l_@@_tmp_tl} +% \begin{macro}{\l_@@_success_bool} +% For doing the calculations and insertions into the +% property list, we will also need an integer register, a token list +% variable and another boolean variable. +% \begin{macrocode} +\int_new:N \l_@@_tmp_int +\tl_new:N \l_@@_tmp_tl +\bool_new:N \l_@@_success_bool +% \end{macrocode} +% \end{macro} +% \end{macro} +% \end{macro} +% +% +% \begin{macro}{\@@_decide_penalty:Nn} +% This is the core command that does the real work of choosing values. +% Let's recall that its first +% argument is the parameter we are currently handling and the +% second argument is a comma-separated list of cases for which we +% need to ensure that their results are not yet in the property list. +% \begin{macrocode} +\cs_new:Npn \@@_decide_penalty:Nn #1 #2 { +% \end{macrocode} +% We start by setting the boolean to \texttt{false} and then run a +% loop until we have found a suitable parameter value that meets +% our criteria. +% \begin{macrocode} + \bool_set_false:N \l_@@_success_bool + \bool_do_until:Nn \l_@@_success_bool +% \end{macrocode} +% Inside the loop we start with the assumption that the current +% value of the parameter is fine and then check if that assumption +% is true. If yes, we can exit the loop, otherwise we will have to +% try with a different value. +% \begin{macrocode} + { \bool_set_true:N \l_@@_success_bool +% \end{macrocode} +% For the verification we try each item in the second parameter to +% see if that is already in the property list. +% This means evaluating the expression to get the penalty value +% and then looking it up in the property list. If it is there, we +% have failed. In this case we set the boolean back to false and +% break out of the loop over the second argument since there is +% no point in testing further. +% \begin{macrocode} + \clist_map_inline:nn { #2 } + { \int_set:Nn \l_@@_tmp_int {##1} + \prop_get:NVNT + \l_@@_penalties_prop \l_@@_tmp_int \l_@@_tmp_tl + { \clist_map_break:n {\bool_set_false:N\l_@@_success_bool} } + } +% \end{macrocode} +% Once we have finished, the boolean will tell us if we are +% successful so far. If yes, it means there was no conflict. We +% therefore add all combinations with this parameter to the +% property list, as from now on they are forbidden as well. +% +% +% So we +% map once more over the second argument and enter them: +% \begin{macrocode} + \bool_if:NTF \l_@@_success_bool + { \clist_map_inline:nn { #2 } + { \int_set:Nn \l_@@_tmp_int {##1} + \prop_put:NVn \l_@@_penalties_prop \l_@@_tmp_int {##1} + } } +% \end{macrocode} +% If we failed we increment the parameter value and retry: +% \begin{macrocode} + { \int_incr:N #1 } + } +} +% \end{macrocode} +% +% +% One place where we will run this code is at the beginning of the +% document (so that changes to the parameters in the document class or +% the preamble are picked up). The other place is when the +% user changes any of the parameters in the middle of the document +% via \cs{WaOsetup}. +% \begin{macrocode} +\AtBeginDocument { \@@_initialize: } +% \end{macrocode} +% +% \end{macro} +% +% \subsection{The option setup} +% +% The options are fairly straightforward: +% +% \begin{macrocode} +\keys_define:nn {fmwao} { +% \end{macrocode} +% By default messages are given as warnings above. If anything else +% is wanted the option \option{check} can be used which simply +% changes the message class used internally: +% \begin{macrocode} + ,check .choice: + ,check / error + .code:n = \msg_redirect_module:nnn {widows-and-orphans}{warning}{error} + ,check / info + .code:n = \msg_redirect_module:nnn {widows-and-orphans}{warning}{info} + ,check / none + .code:n = \msg_redirect_module:nnn {widows-and-orphans}{warning}{none} + ,check / warning + .code:n = \msg_redirect_module:nnn {widows-and-orphans}{warning}{ } +% \end{macrocode} +% The other options set parameters to some hopefully ``reasonable'' +% values\Dash no real surprises here. \LaTeX{} internally uses +% \cs{@clubpenalty} so we need to set this too, if we change +% \cs{clubpenalty}. +% \begin{macrocode} + ,orphans .choice: + ,orphans / prevent .code:n = \int_set:Nn \clubpenalty { 10000 } + \int_set:Nn \@clubpenalty { \clubpenalty } + ,orphans / avoid .code:n = \int_set:Nn \clubpenalty { 5000 } + \int_set:Nn \@clubpenalty { \clubpenalty } + ,orphans / default .code:n = \int_set:Nn \clubpenalty { 150 } + \int_set:Nn \@clubpenalty { \clubpenalty } +% \end{macrocode} +% +% \begin{macrocode} + ,widows .choice: + ,widows / prevent .code:n = \int_set:Nn \widowpenalty { 10000 } + ,widows / avoid .code:n = \int_set:Nn \widowpenalty { 5000 } + ,widows / default .code:n = \int_set:Nn \widowpenalty { 150 } +% \end{macrocode} +% +% \begin{macrocode} + ,hyphens .choice: + ,hyphens / prevent .code:n = \int_set:Nn \brokenpenalty { 10000 } + ,hyphens / avoid .code:n = \int_set:Nn \brokenpenalty { 2000 } + ,hyphens / default .code:n = \int_set:Nn \brokenpenalty { 50 } + ,prevent-all .code:n = \int_set:Nn \clubpenalty { 10000 } + \int_set:Nn \widowpenalty { 10000 } + \int_set:Nn \displaywidowpenalty{ 10000 } + \int_set:Nn \brokenpenalty { 10000 } + \int_set:Nn \predisplaypenalty { 10000 } + \int_set:Nn \@clubpenalty { \clubpenalty } +% \end{macrocode} +% As an exception, \option{avoid-all} doesn't set +% \cs{predisplaypenalty}; maybe it should. +% \begin{macrocode} + ,avoid-all .code:n = \int_set:Nn \clubpenalty { 5000 } + \int_set:Nn \widowpenalty { 5000 } + \int_set:Nn \displaywidowpenalty { 2000 } + \int_set:Nn \brokenpenalty { 2000 } +% \int_set:Nn \predisplaypenalty { 9999 } + \int_set:Nn \@clubpenalty { \clubpenalty } +% \end{macrocode} +% \option{default-all} reverts back to the standard \LaTeX{} +% default values: +% \begin{macrocode} + ,default-all .code:n = \int_set:Nn \clubpenalty { 150 } + \int_set:Nn \widowpenalty { 150 } + \int_set:Nn \displaywidowpenalty { 50 } + \int_set:Nn \brokenpenalty { 100 } + \int_set:Nn \predisplaypenalty { 10000 } + \int_set:Nn \@clubpenalty { \clubpenalty } +} +% \end{macrocode} +% Once declared we evaluate the options given to the package: +% \begin{macrocode} +\ProcessKeysPackageOptions{fmwao} +% \end{macrocode} +% +% +% \subsection{Document-level commands} +% +% Finally we declare the user-level commands: +% + +% \begin{macro}{\WaOsetup} +% This runs the key setup on the first argument and then +% reinitializes the parameter setup: +% \begin{macrocode} +\NewDocumentCommand\WaOsetup{m} + { \keys_set:nn{fmwao}{#1} \@@_initialize: \ignorespaces } +% \end{macrocode} +% \end{macro} +% +% + +% \begin{macro}{\WaOparameters} +% This parameterless command outputs a display of the +% current parameter settings. +% \begin{macrocode} +\NewDocumentCommand\WaOparameters{}{\prop_show:N \l_@@_penalties_prop} +% \end{macrocode} +% \end{macro} +% +% +% \begin{macro}{\WaOignorenext} +% And here is the command that suppresses any warning on the +% current page or column: +% \begin{macrocode} +\NewDocumentCommand\WaOignorenext{} + { \bool_gset_false:N \g_@@_gen_warn_bool } +% \end{macrocode} +% \end{macro} +% +% \small\Finale +% + +\endinput diff --git a/macros/latex/contrib/widows-and-orphans/widows-and-orphans.ins b/macros/latex/contrib/widows-and-orphans/widows-and-orphans.ins new file mode 100644 index 0000000000..916cab227c --- /dev/null +++ b/macros/latex/contrib/widows-and-orphans/widows-and-orphans.ins @@ -0,0 +1,61 @@ +%% +%% This file will generate fast loadable files and documentation +%% driver files from the dtx file(s) in this package when run through +%% LaTeX or TeX. +%% +%% Copyright 2017-2018 Frank Mittelbach +%% +%% +%% This file is part of the LaTeX windows-and-orphans Bundle. +%% ---------------------------------------------------------- +%% +%% It may be distributed and/or modified under the +%% conditions of the LaTeX Project Public License, either version 1.3c +%% of this license or (at your option) any later version. +%% The latest version of this license is in +%% http://www.latex-project.org/lppl.txt +%% and version 1.3c or later is part of all distributions of LaTeX +%% version 2005/12/01 or later. +%% +%% In particular, NO PERMISSION is granted to modify the contents of this +%% file since it contains the legal notices that are placed in the files +%% it generates. +%% +%% +%% +%% --------------- start of docstrip commands ------------------ +%% +\input l3docstrip + +\keepsilent + +\usedir{tex/latex/widows-and-orphans} + +\preamble + +This is a generated file. + +Copyright 2017-2018 Frank Mittelbach + + +This file was generated from file(s) of the LaTeX windows-and-orphans Bundle. +----------------------------------------------------------------------------- + +It may be distributed and/or modified under the +conditions of the LaTeX Project Public License, either version 1.3c +of this license or (at your option) any later version. +The latest version of this license is in + https://www.latex-project.org/lppl/ +and version 1.3c or later is part of all distributions of LaTeX +version 2005/12/01 or later. + +This file may only be distributed together with a copy of the LaTeX +windows-and-orphans Bundle. You may however distribute the Bundle +without such generated files. + +\endpreamble + + +\generate{\file{widows-and-orphans.sty}{\from{widows-and-orphans.dtx}{}}} + +\endbatchfile |