diff options
author | Norbert Preining <norbert@preining.info> | 2022-05-28 03:01:14 +0000 |
---|---|---|
committer | Norbert Preining <norbert@preining.info> | 2022-05-28 03:01:14 +0000 |
commit | 98bbe9f6255283aebab5ce6151393e1ff9014577 (patch) | |
tree | 8f436a4696beaff708614cf9d97c28858e6476e4 /macros | |
parent | 84a5593d3fb9d03aac5677678bcc4c92e8c9a9c4 (diff) |
CTAN sync 202205280301
Diffstat (limited to 'macros')
29 files changed, 1191 insertions, 439 deletions
diff --git a/macros/latex/contrib/gamebooklib/Makefile b/macros/latex/contrib/gamebooklib/Makefile index 6925b402bd..00dd31cbb6 100644 --- a/macros/latex/contrib/gamebooklib/Makefile +++ b/macros/latex/contrib/gamebooklib/Makefile @@ -1,4 +1,4 @@ -default: gamebooklib_test.pdf gamebooklib.pdf tidy +default: gamebooklib_test.pdf gamebooklib.pdf gamebooklib.zip tidy gamebooklib_test.pdf : gamebooklib_test.tex gamebooklib.sty pdflatex \\nonstopmode\\input gamebooklib_test.tex && \ @@ -13,6 +13,14 @@ gamebooklib.pdf : gamebooklib.sty gamebooklib.dtx makeindex -s gind.ist -o gamebooklib.ind gamebooklib.idx && \ pdflatex \\nonstopmode\\input gamebooklib.dtx +gamebooklib.zip : gamebooklib_countpagesperseed.sh gamebooklib.dtx gamebooklib.ins gamebooklib.pdf gamebooklib_test.pdf gamebooklib_test.tex Makefile README + cd ..; zip gamebooklib/gamebooklib.zip \ + gamebooklib/gamebooklib_countpagesperseed.sh \ + gamebooklib/gamebooklib.dtx gamebooklib/gamebooklib.ins \ + gamebooklib/gamebooklib.pdf gamebooklib/gamebooklib_test.pdf \ + gamebooklib/gamebooklib_test.tex gamebooklib/Makefile \ + gamebooklib/README; cd gamebooklib + tidy: rm -f *.log *.ind *.idx *.glo *.gls *.aux *.ilg # TODO: should we really be deleting the .sty? Check CTAN rules. diff --git a/macros/latex/contrib/gamebooklib/gamebooklib.dtx b/macros/latex/contrib/gamebooklib/gamebooklib.dtx index 0251127fb0..deef74af4d 100644 --- a/macros/latex/contrib/gamebooklib/gamebooklib.dtx +++ b/macros/latex/contrib/gamebooklib/gamebooklib.dtx @@ -17,7 +17,7 @@ % \iffalse % <package>\NeedsTeXFormat{LaTeX2e}[2005/12/01] % <package>\ProvidesPackage{gamebooklib} -% <package> [2021/08/24 v1.1 gamebooklib typesetting] +% <package> [2022/05/26 v1.3 gamebooklib typesetting] % %<*driver> \documentclass{ltxdoc} @@ -51,10 +51,11 @@ % Grave accent \` Left brace \{ Vertical bar \| % Right brace \} Tilde \~} % -% \CheckSum{539} +% \CheckSum{708} % % \changes{v1.0}{2021/08/10}{Initial Release} % \changes{v1.1}{2021/08/24}{Bugfix: edge case with clashing fixed-index entries} +% \changes{v1.2}{2021/09/11}{Bugfix: footnotes set justified on last line} % % \GetFileInfo{gamebooklib.sty} % @@ -67,6 +68,7 @@ % \DoNotIndex{\endgroup, \equal, \expandafter, \fi} % \DoNotIndex{\gdef, \global, \head, \hspace, \Huge, \if} % \DoNotIndex{\ifcsname, \ifhmode, \ifnum, \ifthenelse, \ifvmode} +% \DoNotIndex{\numexpr} % \DoNotIndex{\interlinepenalty, \interlinefootnotepenalty} % \DoNotIndex{\large, \leftskip, \mark, \NeedsTeXFormat} % \DoNotIndex{\newcounter, \newenvironment, \newtoks, \noexpand} @@ -74,7 +76,7 @@ % \DoNotIndex{\par, \PassOptionsToClass, \penalty, \ProcessOptions} % \DoNotIndex{\protected@xdef, \ProvidesPackage, \rand, \requirecommand} % \DoNotIndex{\RequirePackage, \rightskip, \rule, \setbox} -% \DoNotIndex{\setcounter, \stepcounter, \textbf, \typeout, \value} +% \DoNotIndex{\setcounter, \stepcounter, \textbf, \value} % \DoNotIndex{\vbox, \unvbox, \vfill, \vspace, \vsize} % \DoNotIndex{\WarningFilter, \whiledo, \Collect@Body, \CurrentOption} % \DoNotIndex{\DeclareOption, \g@addto@macro, \the, \noindent} @@ -82,6 +84,7 @@ % \DoNotIndex{\addtocounter, \refstepcounter, \label} % \DoNotIndex{\iffalse, \iftrue, \outputpenalty, \renewcommand} % \DoNotIndex{\noshuffle, \verbose, \endpage, \footnote} +% \DoNotIndex{\if\dots} % % \title{The Gamebooklib Package\thanks{This document corresponds to \textsf{Gamebooklib}~\fileversion, dated \filedate.}} % \author{Robert J Lee \\ latex@rjlee.homelinux.org} @@ -104,7 +107,7 @@ % paragraph or longer), each with a sequentially numbered value. At % the end of each entry, a link to one or more other numbered % entries is given; the reader selects one and they follow through -% the story in this order. gamebooks traditionally start at the +% the story in this order. Gamebooks traditionally start at the % entry numbered~``1'' and some gamebooks have multiple endings, % with the final section representing a success or victory for the % reader. @@ -138,8 +141,8 @@ % % To load the class, use |\usepackage[|\textit{options}|]{gamebook}| % -% The class options are described below; |[footnotes]| is normally a -% good choice, although there are some limitations. +% The class options are described below; |[footnotes,jukebox]| is +% normally a good choice, although there are some limitations. % % The user simply writes entries like this inside the document: % \begin{verbatim} @@ -239,6 +242,26 @@ % to avoid broken links. % \end{macro} % +% +% \begin{macro}{jukebox} +% \changes{v1.3}{2022/05/26}{Feature: "jukebox" shuffle} +% Use the Jukebox Index shuffling +% algorithm\footnote{\texttt{https://github.com/robertjlee/jukeboxshuffle}}. +% This is slightly slower, but tends to reduce the number of times you +% get a ``turn to~'' instrution referencing the next (or previous) +% entry in the original order, by modifying the shuffle to ensure that +% adjacent gentries in the input have much less chance of being +% adjacent in the final document. |jukebox| requires a \LaTeXe that +% supports \cs{numexpr}, and a minimum of 6 gentries. If fewer than +% 6~|gentry| environments are supplied before \cs{thegentries}, this +% option will only log a warning in verbose mode. +% +% The |jukebox| option guarantees no more adjacent entries than +% without the option, for a given |seed| value; it may not eliminate +% them completely unless the number of entries is large. The +% performance is linear to the number of entries. +% \end{macro} +% % \begin{macro}{noshuffle} % \changes{v1.0}{2021/08/10}{Noshuffle option} % In general, it's easier to write gamebooks in a more linear fashion, @@ -269,7 +292,11 @@ % each way and see which works better for your text. % \end{macro} % -% +% \begin{macro}{seed} +% It's suggested that users specify a value for ``seed'' for stable +% builds, by adding the following before including this package: +% |\usepackage[seed=123]{lcg}| +% \end{macro} % \section*{Footnotes} % % When typesetting a gamebook with footnotes, it is confusing if they @@ -295,7 +322,7 @@ % page. \TeX\ is asked not to break the page there, but this influence % is limited. It may be possible for the author to avoid this, eg by % adding a rubber length to the interline spacing (it may be easier to -% simply choose a different seed value for the |lcg| package to +% simply choose a different |seed| value for the |lcg| package to % reshuffle). % \item If you have a lot of rubber space in the text, or % variably-sized items, \LaTeX\ may expand the footnote at the end of @@ -333,7 +360,7 @@ %\StopEventually{\PrintChanges \pagebreak[4] \PrintIndex} % % \begin{macrocode} -\ProvidesPackage{gamebooklib}[2021/08/24 Gamebook by R Lee latex@rjlee.homelinux.org] +\ProvidesPackage{gamebooklib}[2022/05/25 Gamebook by R Lee latex@rjlee.homelinux.org] % \end{macrocode} % We need \LaTeXe, for the extra token registers. % \begin{macrocode} @@ -353,7 +380,7 @@ % % \begin{macro}{endpage} % The |endpage| option puts the last entry on its own page. This can -% work better when the last entryraph is about a page long, and also +% work better when the last entry is about a page long, and also % the final ``winning'' entry of the gamebook. % \begin{macrocode} \newcommand\gamebook@beforelast{} @@ -363,6 +390,19 @@ % \end{macrocode} % \end{macro} % +% \begin{macro}{jukebox} +% The |jukebox| option defines the \cs{gamebox@jukebox} macro; +% while the macro does nothing, \cs{ifcsname} can then be used to +% determine if the option was set. +% \begin{macrocode} +\DeclareOption{jukebox}{% + \newcommand\gamebook@jukebox{}% + \gamebook@info{Gamebook Library to perform jukebox index reshuffle + pass}% +}% +% \end{macrocode} +% \end{macro} +% % \begin{macro}{footnote} % The |footnote| option enables our footnote processing, to throw out % footnotes at the end of each |gentry| so that they don't appear to be @@ -370,7 +410,7 @@ % % This is genearlly recommended, unless you have a reason to turn it % off (such as a conflicting package). It's disabled by default -% because it could cause unexpected errors. +% because it could cause unexpected faults. % \begin{macrocode} \def\if@gamebook@footnotes{\iffalse} \DeclareOption{footnote}{% @@ -390,18 +430,15 @@ % \end{macrocode} % \end{macro} % +% \begin{macro}{seed} % All unknown options are passed to |lcg|, as it's our only dependency with options. -% It's suggested that users specify a value for ``seed'' for stable -% builds, by adding the following before including this package: -% -% |\usepackage[seed=123]{lcg}| % \begin{macrocode} \DeclareOption*{% \PassOptionsToClass{\CurrentOption}{lcg}% }% \ProcessOptions\relax% % \end{macrocode} -% We need to capture environment contents +% \end{macro}% We need to capture environment contents % \begin{macrocode} \RequirePackage{environ}% % \end{macrocode} @@ -504,7 +541,7 @@ % Later, we'll change the values of the $N$ bit of \cs{gentry$N$} % macros when we shuffle (the underlying token registers stay the same). % -% This relies on \cs{refstepcounter}\marg{gentryctr} before expanded +% This relies on \cs{refstepcounter}\marg{gentryctr} being expanded % before this macro. The label |gentry:\gentrycode| is thus set to the % current index of the final output entry. % \begin{macrocode} @@ -546,7 +583,7 @@ % |footnote| option was given; it sets up the output routine while the % gamebook is running. % \begin{macrocode} -\begingroup% +\begingroup% \if@gamebook@footnotes\gentry@footnotespergentry\fi% % \end{macrocode} % To begin, let's record the number of entries. This may come in @@ -561,7 +598,7 @@ % than replacing the definition of \cs{p@stkeysr@nd}. % The warning is simply that this package doesn't waste another % counter every time it changes the random limits (which happens a lot -% during Knuth shuffle): +% during the Fisher-Yates shuffle): % \begin{macrocode} \WarningFilter{lcg}{Using an already existing counter rand}% % \end{macrocode} @@ -587,22 +624,29 @@ % bottom of \cs{box255}. % \begin{macrocode} \if@gamebook@footnotes\output={% + \def\gentry@deferoutput{\the\gentry@oldoutput}% % \end{macrocode} % The \cs{outputpenalty} tells us why the output routine was called; generally, it's invoked whenever % a new floatable environment is generated, or when a page is full. Anything less than -1000 means that % the page was filled, so we should add any footnotes only in this case. % \begin{macrocode} \ifnum\outputpenalty<-\@M\else% - \edef\tmp@{footnotetoks\botmark} - \expandafter\ifcsname footnotetoks\botmark\endcsname% - \global\setbox255=\vbox to \vsize{% - \unvbox255\vfill\outputfootnotes@endpage}% - \fi% - \fi% - \the\gentry@oldoutput + \if\gentryshouldoutput0% + \unvbox255\def\gentry@deferoutput{}% + \else% + \expandafter\ifcsname footnotetoks\botmark\endcsname% + \expandafter\if\expandafter\relax\expandafter% + \detokenize\expandafter{\csname footnotetoks\botmark\endcsname}\relax\else% + \global\setbox255=\vbox to \vsize{% + \unvbox255\vfill\outputfootnotes@endpage}% + \fi\fi% + \fi\fi% + \gentry@deferoutput% +% \the\gentry@oldoutput }\fi% % \end{macrocode} % +% % \subsection*{The Shuffling Algorithm} % % The basic shuffling algorithm is to first shuffle all entries, @@ -610,7 +654,7 @@ % fixed-index entries in order and swap them into their final place. % % The original version of this package had a bug relating to multiple -% fixed-index entries. +% fixed-index entries (now fixed). % In short, let $A$, $B$, and $C$ be indices; if $A<B$ and % (unshuffled) entry number $A$ was fixed at (shuffled) % location $B$, while (unshuffled) entry number $B$ was fixed at @@ -624,8 +668,8 @@ % \TeX\ has well beyond 255 token registers these days, so don't % bother to check that limit. % -% The LCG package provides a suitable pseudo-random number generator. -% What we want is a repeatable series of dispirate numbers, not an +% The |LCG| package provides a suitable pseudo-random number generator. +% What we want is a repeatable series of disparate numbers, not an % especially random one. % %\begin{enumerate} @@ -641,14 +685,18 @@ % \item If $R\ne i$ then swap macros \cs{gentrytoks}$R$ and \cs{gentrytoks}$i$ % \item If $R\ne i$ then swap macros \cs{paraIdx}$R$ and \cs{gentryidx}$i$ % \end{enumerate} +% \item If a jukebox index sort is requested, perform an optimisation +% pass (see below) % \item For $i=1:n$, output token reg $i$ %\end{enumerate} % % Define macros \cs{csname}~|paraIdx|$n$\cs{endcsname} containing the arabic -% original page number to be put out on the $n$th output ``page'' +% original gentry number to be put out on the $n$th output gentry. +% \begin{macro}{gentrycount} % \begin{macrocode} \xdef\gentrycount{\arabic{gentryctr}}% % \end{macrocode} +% \end{macro} % we can reuse |gentryctr|; we've finished this set of paras and % kept the total count. % \begin{macrocode} @@ -660,6 +708,7 @@ \whiledo{\not{\value{gentryctr}>\gentrycount}}{% \edef\gentryidxu{\arabic{gentryctr}}% \expandafter\xdef\csname paraIdx\gentryidxu\endcsname{\gentryidxu}% + \typeout{DEFINED paraIdx\gentryidxu}% \stepcounter{gentryctr}% }% % \end{macrocode} @@ -675,13 +724,13 @@ % 1, and this counter resets after each expansion of \cs{thegentries}). % \end{macro} % \begin{macrocode} - \if@gamebook@shuffle + \if@gamebook@shuffle% \setcounter{rand}{\gentrycount}% \addtocounter{rand}{-1}\edef% \stoppoint{\arabic{rand}}% % \end{macrocode} % First, shuffle everything that isn't fixed down. -% Don't renumber para~1 or \cs{gentrycount}; Knuth-shuffle the rest +% Don't renumber para~1 or \cs{gentrycount}; Fisher-Yates-shuffle the rest % NB: we stop at \cs{gentrycount}$-2$, because \cs{gentrycount}$-1$ would only shuffle % with itself. % \begin{macrocode} @@ -734,9 +783,77 @@ \expandafter\global\expandafter\let\csname fixedat\mydest\endcsname\@undefined% \addtocounter{gentryctr}{-1}% \fi% +% \end{macrocode} +% if we are doing a jukebox shuffle, remember which final entries are fixed, so +% they don't get moved. +% \begin{macrocode} + \ifcsname gamebook@jukebox\endcsname% + \expandafter\def\csname fixedto\mydest\endcsname{}% + \fi% \fi% \stepcounter{gentryctr}% - }}\fi%SHUFFLE END + }% +% \end{macrocode} +% The jukebox shuffle requires an extra pass. This must come after +% moving fixed entries into their final place, to allow us to compare +% the initial indicies. +% We make a reasonable effort:\begin{itemize} +% \item $t$ is the current index +% \item $u$ is the next index +% \item $r$ is a random index after $u$ (make 3 attempts to find a +% non-fixed $r$) +% \item if abs$(t-u)=1$ and $r$ is not fixed, then swap $u$ and $r$ +% \textbf{if} $u$ is not fixed; otherwise: +% \item if abs$(t-u)=1$ and $r$ is not fixed, then swap $t$ and $r$ +% \textbf{if} $t$ is not fixed; +% \item otherwise, give up. +% \end{itemize} +% \begin{macrocode} + \ifcsname gamebook@jukebox\endcsname% + \ifnum\gentrycount<6% + \gamebook@info{Jukebox pass skipped; too few entries}% + \else% + \setcounter{gentryctr}{2}% + \whiledo{\not{\value{gentryctr}=\stoppoint}}{% + \edef\gentryidxt{\arabic{gentryctr}}% + \edef\curpos{\csname paraIdx\gentryidxt\endcsname}% + \stepcounter{gentryctr}% + \edef\gentryidxu{\arabic{gentryctr}}% + \edef\nextpos{\csname paraIdx\gentryidxu\endcsname}% + \edef\pdiff{\the\numexpr\curpos+\nextpos}% + \ifnum\pdiff<0\edef\pdiff{\the\numexpr-\pdiff}\fi% + \edef\sdiff{\the\numexpr\curpos-\nextpos}% + \ifnum\sdiff<0\edef\sdiff{\the\numexpr-\sdiff}\fi% + \ifnum\pdiff<\sdiff\relax\def\thediff{\pdiff}\else\def\thediff{\sdiff}\fi% + \ifnum\thediff=1% + \gamebook@info{Jukebox: entries are too close: % + \gentryidxt,\gentryidxu\space (original \curpos,\nextpos)}% + \chgrand[first=\numexpr\gentryidxu+1]% + \rand% + \expandafter\ifcsname fixedto\arabic{rand}\endcsname\rand\fi% + \expandafter\ifcsname fixedto\arabic{rand}\endcsname\rand\fi% + \expandafter\ifcsname fixedto\arabic{rand}\endcsname% + \gamebook@info{Can't reshuffle: failed to find non-fixed index}% + \else% + \ifcsname fixedto\gentryidxu\endcsname% + \ifcsname fixedto\gentryidxt\endcsname% + \gamebook@info{Can't reshuffle: \curpos\space and % + \nextpos\space are both fixed.}% + \else% + \gamebook@info{Reshuffling \gentryidxt\space to \arabic{rand}}% + \macroswap{paraIdx\gentryidxt}{paraIdx\arabic{rand}}% + \fi% + \else% + \gamebook@info{Reshuffling \gentryidxu\space to% + \arabic{rand} (alt)}% + \macroswap{paraIdx\gentryidxu}{paraIdx\arabic{rand}}% + \fi\fi\fi% + }% + \fi\fi% + }\fi%SHUFFLE END +% \end{macrocode} +% Now we can output the |gentry| token registers to let \LaTeX\ do its thing: +% \begin{macrocode} \gamebook@info{Shuffled! Gentry order:}% \setcounter{gentryctr}{1}% \whiledo{\not{\value{gentryctr}>\gentrycount}}{% @@ -749,7 +866,7 @@ % We can reuse the same counter again to output % \begin{macrocode} \setcounter{gentryctr}{0}% - \gamebook@info{Outputting \gentrycount\ gamebook entries}% + \gamebook@info{Outputting \gentrycount\space gamebook entries}% \whiledo{\value{gentryctr}<\gentrycount}{% % \end{macrocode} % Use refstepcounter in the loop to allow \cs{label} to work as expected. @@ -772,6 +889,7 @@ }% % \end{macrocode} % Finally, we clear the registers and reset the counter in case we want to start again +% (NB: fixedto is scope to the current block only, so no need to clear that) % \begin{macrocode} \gamebook@info{All gamebook entries added to main vertical list}% \setcounter{gentryctr}{1}% @@ -786,7 +904,7 @@ \stepcounter{gentryctr}% }% \setcounter{gentryctr}{0}% - \eject + \eject% \endgroup% }% % \end{macrocode} @@ -820,6 +938,30 @@ }% % \end{macrocode} % +% \begin{macro}{gentryshouldoutput} +% \changes{v1.3}{2022/05/26}{Suppress short pages option} +% This macro \textbf{may} be called from the output routine, and can +% be used to suppress page breaks. It was added because it proves +% fairly easy to write custom divider routines that can produce blank +% pages. If it expands to the number 1, then the page will be output; +% if it expands to 0, it will not be. +% +% For example, the following will prevent pages that are less than 80\% full. +% +% \verb!\renewcommand{\gentryshouldoutput}{%! +% +% \verb! \ifdim\pagetotal>0.8\pagegoal\relax1\else 0\fi}! +% +% \textbf{Caution:} if this macro continually tests false, then +% material will eventually be discarded from the main vertical list to +% ensure that \TeX\ can complete the output of the document. If you +% redefine this macro, make sure to check out output carefully for +% missing text. +% \begin{macrocode} +\newcommand{\gentryshouldoutput}{1} +% \end{macrocode} +% \end{macro} +% % \DescribeMacro{\gentryfooter} % This takes no arguments and is simply expanded after the entry is typeset. The default adds some vertical % space and a simple separator. @@ -831,9 +973,7 @@ % \end{macrocode} % % \DescribeMacro{\ignorespacesandallpars@} -% This is a technique described on StackExchange: -% -% {\small https://tex.stackexchange.com/questions/179016/\\ignore-spaces-and-pars-after-an-environment\#179034} +% This is a technique described on StackExchange\footnote{\small https://tex.stackexchange.com/questions/179016/\\\mbox{~}\hspace{1in}ignore-spaces-and-pars-after-an-environment\#179034}. % % This is used to ensure that extra space at the start and end of the |gentry| % environment is ignored. @@ -916,14 +1056,14 @@ % % This macro takes one argument, being the maximum index of footnotes to output, inclusive. % Footnotes after this index will be excluded. If not provided, the -% value provided by the counter \cs{@mfpn}, which in the default +% value provided by the counter \cs{@mfpn}, which is the default % \LaTeX\ counter, will be used. % \begin{macrocode} \newcounter{fncounter}% \newcommand{\outputfootnotes}[1]{% \begingroup% \def\in@out{}% flag that footnotes are outputting; suppresses marks - \setcounter{fncounter}{1} + \setcounter{fncounter}{1}% % \end{macrocode} % Called in vertical mode, and don't want to throw a page break. % @@ -931,7 +1071,7 @@ % \begin{macrocode} \def\footnote@rule{% % \end{macrocode} -% The next line comess from the TUGboat suggestions, and protect against various user changes +% The next line comes from the TUGboat suggestions, and protect against various user changes % \begin{macrocode} \leftskip=0pt\rightskip=0pt\interlinepenalty=1000% \penalty-1000% @@ -971,9 +1111,7 @@ \edef\tmp@@{\csname footnotetoks\arabic{fncounter}\endcsname}% % \end{macrocode} % This \cs{detokenize} black magic tests if a token reg is actually -% empty: -% -% https://tex.stackexchange.com/questions/263733/\\whats-the-best-practice-way-to-test-whether-parameter-is-empty +% empty\footnote{\texttt{https://tex.stackexchange.com/questions/263733/\\\mbox{~}\hspace{1in}whats-the-best-practice-way-to-test-whether-parameter-is-empty}} % \begin{macrocode} \expandafter\if\expandafter\relax\expandafter% \detokenize\expandafter{\the\tmp@@}\relax\else% @@ -1042,7 +1180,7 @@ % \cs{botmark} has a value, then we can output all footnotes up to % that index. % \begin{macrocode} -\g@addto@macro{\gentry@footnotespergentry}{ +\g@addto@macro{\gentry@footnotespergentry}{% \newcommand{\outputfootnotes@endpage}{% \expandafter\if\expandafter\relax\expandafter% \detokenize\expandafter{\botmark}\relax\else% diff --git a/macros/latex/contrib/gamebooklib/gamebooklib.pdf b/macros/latex/contrib/gamebooklib/gamebooklib.pdf Binary files differindex 43657233fe..363ab33390 100644 --- a/macros/latex/contrib/gamebooklib/gamebooklib.pdf +++ b/macros/latex/contrib/gamebooklib/gamebooklib.pdf diff --git a/macros/latex/contrib/gamebooklib/gamebooklib_test.pdf b/macros/latex/contrib/gamebooklib/gamebooklib_test.pdf Binary files differindex ae56f8e953..fb06cdb58f 100644 --- a/macros/latex/contrib/gamebooklib/gamebooklib_test.pdf +++ b/macros/latex/contrib/gamebooklib/gamebooklib_test.pdf diff --git a/macros/latex/contrib/gamebooklib/gamebooklib_test.tex b/macros/latex/contrib/gamebooklib/gamebooklib_test.tex index 4d661dce63..3b5a116ba2 100644 --- a/macros/latex/contrib/gamebooklib/gamebooklib_test.tex +++ b/macros/latex/contrib/gamebooklib/gamebooklib_test.tex @@ -27,7 +27,7 @@ This document was used for testing during development of gamebooklib. Please see the \texttt{gamebooklib\_test.tex} file alongside the PDF to see how the package behaves. - +\tracingmacros=1 \begin{gentry}{codea} This is the first gentry! diff --git a/macros/latex/contrib/hvextern/Changes b/macros/latex/contrib/hvextern/Changes index b300396106..a3df3662af 100644 --- a/macros/latex/contrib/hvextern/Changes +++ b/macros/latex/contrib/hvextern/Changes @@ -1,5 +1,9 @@ hvextern.sty ---------------- +v 0.27 2022-05-27 - suppress overfull warnings for filename output + - fix for tcolorbox with minipage and breakable + - write filename in tt + - added verbose message for force=false v 0.26 2022-05-23 - set filenames for twocolumn mode outer/inner - added keywords outerFN and shiftFN v 0.25 2022-05-19 - added framesep keyword diff --git a/macros/latex/contrib/hvextern/README b/macros/latex/contrib/hvextern/README index 1c6052c9fd..adbd87ba71 100644 --- a/macros/latex/contrib/hvextern/README +++ b/macros/latex/contrib/hvextern/README @@ -1,6 +1,6 @@ -This package allows to write code in tex, latex, Perl, Lua, Python, -and MetaPost into external files, run that code and insert the -output as pdf/png image or text. Parts of the external code +This package allows to write code in TeX, LaTeX, ConTeXt, Java, Perl, +Lua, Python, Shell, and MetaPost into external files, run that code and +insert the output as pdf/png image or text. Parts of the external code can be listed in the document. The main LaTeX document must be run with the option --shell-escape diff --git a/macros/latex/contrib/hvextern/doc/hvextern.pdf b/macros/latex/contrib/hvextern/doc/hvextern.pdf Binary files differindex 473cd8972b..d70254252c 100644 --- a/macros/latex/contrib/hvextern/doc/hvextern.pdf +++ b/macros/latex/contrib/hvextern/doc/hvextern.pdf diff --git a/macros/latex/contrib/hvextern/doc/hvextern.tex b/macros/latex/contrib/hvextern/doc/hvextern.tex index d691aa3bed..ac218c986d 100644 --- a/macros/latex/contrib/hvextern/doc/hvextern.tex +++ b/macros/latex/contrib/hvextern/doc/hvextern.tex @@ -1,11 +1,14 @@ +%% $Id: hvextern.tex 559 2022-05-27 06:59:29Z herbert $ \RequirePackage{pdfmanagement-testphase} \DeclareDocumentMetadata{} -\documentclass[ngerman,a4paper]{article} +\documentclass[ngerman,a4paper,twoside]{article} \usepackage{babel} \usepackage{libertinus} \setmonofont[Scale=MatchLowercase,FakeStretch=0.88]{AnonymousPro} \usepackage{iftex} +\usepackage{fancyhdr} +\pagestyle{fancy} \usepackage{hvlogos} \usepackage{hvextern} \usepackage{hvindex} @@ -41,7 +44,8 @@ from within a main\newline \LaTeX\ document~--v. \hvexternFileversion} \begin{sloppypar} -This package allows to write external \MP, \TeX, \ConTeXt, \LaTeX, \LuaTeX, \LuaLaTeX, \XeTeX, \XeLaTeX, Lua, Perl, Java and/or Python +This package allows to write external \MP, \TeX, \ConTeXt, \LaTeX, \LuaTeX, \LuaLaTeX, \XeTeX, \XeLaTeX, +\Index{Lua}, \Index{Perl}, \Index{Java} and/or \Index{Python} source code, which will then be run via \texttt{shell escape} to create a PDF oder text output to include it into the main \LaTeX\ document. \end{sloppypar} @@ -56,7 +60,9 @@ source code ... \end{externalDocument} -\runExtCmd[<options>]{<command with arguments>}{<external filename without extension>} +\runExtCmd[<options>] + {<command with arguments>} + {<external filename without extension>} \end{verbatim} The main document \emph{must} be run with the \Loption{-shell-escape} option, otherwise it won't work, e.g.: @@ -85,10 +91,21 @@ with \texttt{lualatex} with the \texttt{-\kern1pt-shell-escape} option. \subsection{Without showing the code} This document was run with \LuaLaTeX. Suppose you want to insert the output of a document -which needs for several reasons a \pdfLaTeX\ run. Instead of created and running a document +which needs for several reasons a \XeLaTeX\ run. Instead of created and running a document outside of the main document and then to insert the output we can do this from within this \LuaLaTeX\ document itself. The external document is compiled -with \pdfLaTeX\ and the output is insert as pdf image. The \Index{source} code itself is not +with \XeLaTeX\ and the output is insert as pdf image: +\begin{externalDocument}[% + compiler=xelatex, inline, force, runs=2, grfOptions={height=8pt},% + crop, cropmargin=0, cleanup, docType=latex]{voss} +\documentclass{ctexart} +\pagestyle{empty} +\begin{document} +美好的一天. +\end{document} +\end{externalDocument} + +Another example which needs a \pdfLaTeX\ run. The \Index{source} code itself is also not shown by the environment \Lenv{externalDocument}. @@ -119,7 +136,7 @@ shown by the environment \Lenv{externalDocument}. \end{minipage} \begin{minipage}{.39\linewidth} \begin{externalDocument}[ - compiler=pdflatex,force=false,cleanup={log,aux},verbose]{voss} + compiler=pdflatex,force=false,cleanup={log,aux},showFilename=false]{voss} \documentclass{standalone} %StartVisiblePreamble \usepackage{fontenc} @@ -1583,10 +1600,11 @@ This is the start of the introduction. \begin{externalDocument}[ - grfOptions={width=0.3\linewidth}, - pages={1,2,3}, + grfOptions={width=0.31\linewidth}, + pages={1,2,3,4,5,6}, pagesep=2pt, frame, + framesep=0pt, verbose=false, compiler=context, showFilename, @@ -1616,16 +1634,20 @@ This is the start of the introduction. \chapter{Introduction} \input knuth \input knuth \chapter[rensselaer]{The Rensselaer family} +\input knuth \section{The first born} \input knuth \section{The early years} ... in those days Hasselt was ... +\input knuth \section{Living and workin in America} \input knuth \chapter[lansing]{The Lansing family} ... the Lansing family was also ... +\input knuth \chapter[cuyler]{The Cuyler family} ... much later Tydeman Cuyler ... +\input knuth \myhead[headlines]{And the end} foo \stoptext @@ -1665,6 +1687,7 @@ already exists, there is no need to create it with the next run again and again. \item[\Lkeyword{framesep}] Value for \Lcs{fbox} if keyword \texttt{frame} is used. \item[\Lkeyword{mpsep}] Distance between code and output (default 1\,em). \item[\Lkeyword{pagesep}] Distance between pages for multipage output (default 1\,em). +\item[\Lkeyword{verbose}] Print control messages into the terminal and logfile. \item[\Lkeyword{eps}] create an eps from the pdf (historical). \end{description} diff --git a/macros/latex/contrib/hvextern/latex/hvextern.sty b/macros/latex/contrib/hvextern/latex/hvextern.sty index 8b0fa75f1f..19f14ec67f 100644 --- a/macros/latex/contrib/hvextern/latex/hvextern.sty +++ b/macros/latex/contrib/hvextern/latex/hvextern.sty @@ -1,3 +1,4 @@ +%% $Id: hvextern.sty 559 2022-05-27 06:59:29Z herbert $ %% This is file `hvextern.sty', %% %% Copyright (C) 2016-22 @@ -11,8 +12,8 @@ %% and version 1.3c or later is part of all distributions of LaTeX %% version 2005/12/01 or later. -\def\hvexternFileversion{0.26} -\ProvidesFile{hvextern}[2022/05/23 v\hvexternFileversion: package for running external documents (HV)] +\def\hvexternFileversion{0.27} +\ProvidesFile{hvextern}[2022/05/27 v\hvexternFileversion: package for running external documents (HV)] \RequirePackage{shellesc,xkeyval,graphicx} \RequirePackage{fancyvrb,tikz,listings,ifplatform} @@ -30,6 +31,7 @@ \newcounter{hv@extern@runs} \newcounter{@@@runs} +\newcounter{hv@extern@pageCNT} \newsavebox\hv@extern@box \newlength\hv@LineWidth \newlength\hv@TextWidth @@ -42,7 +44,7 @@ \newlength\hv@extern@belowskip \newlength\hv@extern@shiftFN -\AtBeginDocument{\setlength\hv@LineWidth{\dimexpr\linewidth-2\fboxsep-2\fboxrule}} +\AtBeginDocument{\setlength\hv@LineWidth{\the\dimexpr\linewidth-2\fboxsep-2\fboxrule\relax}} \newlength\hv@extern@mpwidth \setlength\hv@extern@mpwidth{\z@} \define@key{hv}{progpath}{\def\hv@extern@progpath{#1}} @@ -57,7 +59,7 @@ \define@key{hv}{BObody}[black!8]{\def\hv@extern@BObody{#1}} \define@key{hv}{docType}[latex]{\def\hv@extern@docType{#1}} %\define@choicekey*+{hv}{docType}[\val\nr]{latex,mp,tex,py,pl,lua,java,context,sh}[latex]{% -% \hv@typeout{>>>> ´doc type \nr}% +% \hv@typeout{doc type \nr}% % \def\hv@extern@docType{\val}% %}{\PackageWarning{hvextern}{erroneous input (#1) for docType ignored. Using latex.}% % \def\hv@extern@docType{latex}% @@ -101,10 +103,11 @@ } \define@boolkey{hv}[hv@extern@]{eps}[true]{} \define@boolkey{hv}[hv@extern@]{verbose}[true]{} -\def\hv@typeout#1{\ifhv@extern@verbose\typeout{#1}\fi} +\def\hv@ex@typeout#1{\ifhv@extern@verbose\typeout{>>(hvextern) #1}\fi} + \define@choicekey*+{hv}{compiler}[\val\nr]{mpost,tex,latex,luatex,python3,perl,lua,java,% xetex,pdflatex,lualatex,xelatex,context,sh,texlua}[pdflatex]{% - \hv@typeout{>>>> Compiler type \nr}% + \hv@ex@typeout{Compiler type \nr}% \def\hv@extern@compiler{\val}% \edef\hv@extern@compilerNo{\nr}% }{\PackageWarning{hvextern}{erroneous input (#1) for compiler ignored. Using pdflatex.}% @@ -194,7 +197,7 @@ { \clist_map_inline:nn {#1}{% \ShellEscape{\hv@extern@progpath##1\space\hvExternDateiname} - %\hv@typeout{>>>> sequencerun: ##1} + %\hv@ex@typeout{sequencerun: ##1} } } \ExplSyntaxOff @@ -203,10 +206,10 @@ \tcbset{breakable,left=2pt,right=2pt,top=2pt,bottom=2pt,boxsep=0pt} \def\hv@marginpar#1{% - \leavevmode\checkoddpage - \sbox\hv@extern@box{\rotatebox[origin=C]{90}{\rule{2\hv@extern@shiftFN}{0pt}#1}}% + \sbox\hv@extern@box{\rotatebox[origin=C]{90}{\rule{2\hv@extern@shiftFN}{0pt}\texttt{#1}}}% \ht\hv@extern@box=\z@ \wd\hv@extern@box=\z@ \dp\hv@extern@box=\z@ \noindent + \checkoddpage \ifoddpage% we have a right page \if@twocolumn \ifhv@extern@outerFN @@ -239,23 +242,23 @@ } \newcommand\PreambleVerbatim[2][]{% - \hv@typeout{Running PreambleVerbatim with #1, #2}% + \hv@ex@typeout{Running PreambleVerbatim with #1, #2}% \ifhv@extern@tcbox \begin{tcolorbox}[before skip=0pt,after skip=0pt]\VerbatimInput[#1]{#2}\end{tcolorbox}% \else - \VerbatimInput[#1]{#2} + \VerbatimInput[#1]{#2}% \fi} \newcommand\BodyVerbatim[2][]{% - \hv@typeout{Running BodyVerbatim with #1, #2}% + \hv@ex@typeout{Running BodyVerbatim with #1, #2}% \ifhv@extern@tcbox \begin{tcolorbox}[before skip=0pt,after skip=0pt]\VerbatimInput[#1]{#2}\end{tcolorbox}% \else - \VerbatimInput[#1]{#2} + \VerbatimInput[#1]{#2}% \fi} \newcommand\PreambleListing[2][]{% - \hv@typeout{Running PreambleListing with #1, #2}% + \hv@ex@typeout{Running PreambleListing with #1, #2}% \ifhv@extern@tcbox \begin{tcolorbox}[before skip=0pt,after skip=0pt%,top=-2pt ]\expandafter\lstinputlisting\expandafter[#1,aboveskip=0pt,belowskip=0pt]{#2}\end{tcolorbox}% @@ -264,7 +267,7 @@ \fi} \newcommand\BodyListing[2][]{% - \hv@typeout{Running BodyListing with #1, #2}% + \hv@ex@typeout{Running BodyListing with #1, #2}% \ifhv@extern@tcbox \begin{tcolorbox}[before skip=0pt,after skip=0pt,%top=-2pt ]\expandafter\lstinputlisting\expandafter[#1,aboveskip=0pt,belowskip=0pt]{#2}\end{tcolorbox}% @@ -291,13 +294,13 @@ {\perCent StopVisiblePreamble} \def\hv@extern@runMP#1#2#3#4{% path compiler file extension - \hv@typeout{>>>> running #1#2 #3#4}% + \hv@ex@typeout{running #1#2 #3#4}% \ShellEscape{#1#2\space -tex=tex\space #3#4}% - \hv@typeout{>>>> running #1tex #3}% + \hv@ex@typeout{running #1tex #3}% \ShellEscape{#1tex\space "\string\input\space epsf\string\relax\string\nopagenumbers\string\epsfbox{#3.1}\string\bye"}% - \hv@typeout{>>>> running #1dvips #3}% + \hv@ex@typeout{running #1dvips #3}% \ShellEscape{#1dvips\space -j\space -E\space -o\space #3.eps\space epsf.dvi}% - \hv@typeout{>>>> running #1epstopdf #3}% + \hv@ex@typeout{running #1epstopdf #3}% \ShellEscape{#1epstopdf\space #3.eps}% } @@ -310,11 +313,11 @@ {\perCent StopVisiblePreamble} \def\hv@extern@runTEX#1#2#3#4{% path compiler file extension - \hv@typeout{>>>> running #1#2 #3#4}% + \hv@ex@typeout{running #1#2 #3#4}% \ShellEscape{#1tex\space #3#4}% - \hv@typeout{>>>> running #1dvips #3}% + \hv@ex@typeout{running #1dvips #3}% \ShellEscape{#1dvips\space #3.dvi}% - \hv@typeout{>>>> running ps2pdf #3.ps}% + \hv@ex@typeout{running ps2pdf #3.ps}% \ShellEscape{#1ps2pdf\space -dAutoRotatePages=/None\space -dALLOWPSTRANSPARENCY\space #3.ps}% } @@ -328,11 +331,11 @@ % only for the sequence latex->dvips->ps2pdf \def\hv@extern@runLATEX#1#2#3#4{% path-compiler-file-extension - \hv@typeout{>>>> running #1#2 #3#4}% + \hv@ex@typeout{running #1#2 #3#4}% \ShellEscape{#1#2\space #3#4}% - \hv@typeout{>>>> running #1dvips #3}% + \hv@ex@typeout{running #1dvips #3}% \ShellEscape{#1dvips\space #3.dvi}% - \hv@typeout{>>>> running ps2pdf #3.ps}% + \hv@ex@typeout{running ps2pdf #3.ps}% \ShellEscape{#1ps2pdf\space -dAutoRotatePages=/None\space -dALLOWPSTRANSPARENCY\space #3.ps}% } @@ -398,47 +401,47 @@ \xdef\hvExternDateiname{#2-\arabic{hv@example@counter}}% \gdef\hv@extern@savePara{#1}% \setkeys{hv}{#1}% \begin - \hv@typeout{External filename: \hvExternDateiname}% + \hv@ex@typeout{External filename: \hvExternDateiname}% \expandafter\IfFileExists\expandafter{\hv@extern@ExamplesDir\hvExternDateiname.pdf}{}{% \expandafter\IfFileExists\expandafter{\hv@extern@ExamplesDir\hvExternDateiname.png}{}{% \expandafter\IfFileExists\expandafter{\hv@extern@ExamplesDir\hvExternDateiname.txt}{}{% \global\hv@extern@forcetrue}}}% set force, if no pdf or png exists \begingroup - \hv@typeout{>>> writing file \hvExternDateiname\hv@extern@ext ...}% + \hv@ex@typeout{writing file \hvExternDateiname\hv@extern@ext ...}% \filecontents[force,noheader]{\hvExternDateiname\hv@extern@ext}% } {\endfilecontents% \end% - \hv@typeout{>>> ... done}% + \hv@ex@typeout{... done}% \global\stepcounter{hv@example@counter}% \endgroup \expandafter\hvExternSetKeys\expandafter{\hv@extern@savePara}% \xdef\hv@extern@fullPath{\hv@extern@ExamplesDir\hvExternDateiname}% \ifhv@extern@force \ifx\hv@extern@runsequence\@empty - \hv@typeout{>>>> force=true: running \hv@extern@progpath\hv@extern@compiler~ \hvExternDateiname\hv@extern@ext ...}% + \hv@ex@typeout{force=true: running \hv@extern@progpath\hv@extern@compiler~ \hvExternDateiname\hv@extern@ext ...}% \setcounter{@@@runs}{\value{hv@extern@runs}}% \loop\ifnum\the@@@runs > 0\relax \ifcase\hv@extern@compilerNo \relax % we have metapost - \hv@typeout{>>>> running mpost \hvExternDateiname\hv@extern@ext}% + \hv@ex@typeout{running mpost \hvExternDateiname\hv@extern@ext}% \hv@extern@runMP{\hv@extern@progpath}{\hv@extern@compiler}{\hvExternDateiname}{\hv@extern@ext}% \or %1 - \hv@typeout{>>>> running tex \hvExternDateiname\hv@extern@ext}% + \hv@ex@typeout{running tex \hvExternDateiname\hv@extern@ext}% \hv@extern@runTEX{\hv@extern@progpath}{\hv@extern@compiler}{\hvExternDateiname}{\hv@extern@ext}% \or %2 - \hv@typeout{>>>> running \hv@extern@compiler~ \hvExternDateiname\hv@extern@ext}% + \hv@ex@typeout{running \hv@extern@compiler~ \hvExternDateiname\hv@extern@ext}% \hv@extern@runLATEX{\hv@extern@progpath}{\hv@extern@compiler}{\hvExternDateiname}{\hv@extern@ext}% \else \ifhv@extern@redirect - \hv@typeout{>>>> running \hv@extern@compiler~ \hvExternDateiname\hv@extern@ext > \hvExternDateiname.txt}% + \hv@ex@typeout{running \hv@extern@compiler~ \hvExternDateiname\hv@extern@ext > \hvExternDateiname.txt}% \ShellEscape{\hv@extern@progpath\hv@extern@compiler\space\hvExternDateiname\hv@extern@ext\space > \hvExternDateiname.txt}% \else - \hv@typeout{>>>> running \hv@extern@compiler~ \hvExternDateiname\hv@extern@ext}% + \hv@ex@typeout{running \hv@extern@compiler~ \hvExternDateiname\hv@extern@ext}% \ShellEscape{\hv@extern@progpath\hv@extern@compiler\space\hvExternDateiname\hv@extern@ext}% \fi \fi \addtocounter{@@@runs}{-1}% \repeat - \hv@typeout{>>> ... done}% + \hv@ex@typeout{... done}% \ifhv@extern@biber \ShellEscape{biber \hvExternDateiname}% NAch biber nochmal latex \ShellEscape{\hv@extern@progpath\hv@extern@compiler\space\hvExternDateiname}% @@ -457,9 +460,11 @@ \ifhv@extern@eps \ShellEscape{pdftops -eps -f 1 -l 1 \hvExternDateiname.pdf}% \fi + \else + \hv@ex@typeout{force=false: NOT running \hvExternDateiname\hv@extern@ext ...}% \fi% end force \ifhv@extern@code%-------------- Code Part - \hv@typeout{>>>>Starting Code part with preamble ...}% + \hv@ex@typeout{Starting Code part with preamble ...}% \begingroup \if@twocolumn \ifhv@extern@outerFN @@ -472,6 +477,7 @@ \fi \ifdim\hv@extern@mpwidth>\z@ \noindent + \tcbset{breakable=false}% \minipage[\hv@extern@mpvalign]{\hv@extern@mpwidth} \vspace{0pt}% \else @@ -499,12 +505,12 @@ \fi \fi \ifhv@extern@showFilename - \hv@typeout{>>>> Set filename in the margin!}% + \hv@ex@typeout{Set filename in the margin!}% \noindent \hv@marginpar{\hvExternDateiname\hv@extern@ext}% \fi % - \hv@typeout{>>>>Starting Code body ...}%} + \hv@ex@typeout{Starting Code body ...}%} \ifhv@extern@usefancyvrb \@nameuse{\hv@extern@docType @initTextFancy}% \expandafter\fvset\expandafter{\hv@extern@lstOptions}% @@ -521,15 +527,15 @@ \fi % end code part \ifhv@extern@code\else % only if no code \ifhv@extern@showFilename - \hv@typeout{>>>> Set filename in the margin!}% + \hv@ex@typeout{Set filename in the margin!}% \noindent \hv@marginpar{\hvExternDateiname\hv@extern@ext}% \fi \fi \ifhv@extern@moveToExampleDir % \ShellEscape{mkdir\space\hv@extern@ExamplesDir/}% - \hv@typeout{>>>> Move file into example dir}% - \hv@typeout{>>>>>>\hvExternDateiname ----> \hv@extern@ExamplesDir}% + \hv@ex@typeout{Move file into example dir}% + \hv@ex@typeout{\hvExternDateiname ----> \hv@extern@ExamplesDir}% \ShellEscape{\hv@move \hvExternDateiname.*\space \hv@extern@ExamplesDir}% \fi \ifhv@extern@includegraphic @@ -537,10 +543,10 @@ \expandafter\includegraphics\expandafter[\hv@extern@grfOptions]{\hv@extern@fullPath}% \else \ifhv@extern@float - \hv@typeout{>>>> Floating environment}% + \hv@ex@typeout{Floating environment}% \expandafter\figure\expandafter[\hv@extern@floatsetting] \else - \hv@typeout{>>>> No floating environment}% + \hv@ex@typeout{No floating environment}% \vspace{\hv@extern@belowbodyskip} \ifdim\hv@extern@mpwidth>\z@ \hfill\minipage[\hv@extern@mpvalign]{\dimexpr\linewidth-\hv@extern@mpwidth-1em\relax}\vspace{0pt}% @@ -550,29 +556,28 @@ \fi \fi \hv@extern@align - \hv@typeout{>>>> Input image \hv@extern@fullPath}% + \hv@ex@typeout{Input image \hv@extern@fullPath}% \ifhv@extern@frame \begingroup \fboxsep=\hv@extern@framesep \expandafter\@for\expandafter\next\expandafter:\expandafter=\hv@extern@pages\do{% - \hv@typeout{>>>> Image loop: next=\next\space \hv@extern@fullPath}% \fbox{\expandafter\includegraphics\expandafter[\hv@extern@grfOptions,page=\next]% {\hv@extern@fullPath}}\hspace{\hv@extern@pagesep}}% \hspace*{-\hv@extern@pagesep}% \endgroup \else \expandafter\@for\expandafter\next\expandafter:\expandafter=\hv@extern@pages\do{% - \hv@typeout{>>>> Image loop: next=\next\space \hv@extern@fullPath}% \expandafter\includegraphics\expandafter[\hv@extern@grfOptions,page=\next]% {\hv@extern@fullPath}\hspace{\hv@extern@pagesep}}% \hspace*{-\hv@extern@pagesep}% \fi + \par% for \hv@extern@align \ifx\hv@extern@caption\@empty\else\caption{\hv@extern@caption}\fi \ifx\hv@extern@label\@empty\else\label{\hv@extern@label}\fi \ifhv@extern@float \endfigure \else - \ifdim\hv@extern@mpwidth>\z@ \endminipage\else\unskip\strut\par\endgroup\fi + \ifdim\hv@extern@mpwidth>\z@ \endminipage\else\endgroup\fi \fi \fi \else% no graphic, only text @@ -585,7 +590,7 @@ \fi \endminipage \else - \vspace{\hv@extern@belowbodyskip} + \vspace{\hv@extern@belowbodyskip}% \noindent \ifhv@extern@usefancyvrb \expandafter\VerbatimInput\expandafter[\hv@extern@textOptions]{\hv@extern@fullPath.txt}% @@ -603,7 +608,7 @@ \setkeys{hv}{code=false,includegraphic=false,#1}% \xdef\hvExternDateiname{#3-\arabic{hv@example@counter}}% \xdef\hv@extern@fullPath{\hv@extern@ExamplesDir\hvExternDateiname}% - \hv@typeout{>>>> running #2}% + \hv@ex@typeout{running #2}% \ifhv@extern@redirect \ShellEscape{\hv@extern@progpath#2 > \hv@extern@ExamplesDir\hvExternDateiname.txt}% \else @@ -616,14 +621,14 @@ \expandafter\includegraphics\expandafter[\hv@extern@grfOptions]{\hv@extern@ExamplesDir\hvExternDateiname}% \else \ifhv@extern@float - \hv@typeout{>>>> Floating environment}% + \hv@ex@typeout{Floating environment}% \expandafter\figure\expandafter[\hv@extern@floatsetting] \else - \hv@typeout{>>>> No floating environment}% + \hv@ex@typeout{No floating environment}% \par\noindent\begingroup \fi \hv@extern@align - \hv@typeout{>>>> Input image \hv@extern@ExamplesDir\hvExternDateiname}% + \hv@ex@typeout{Input image \hv@extern@ExamplesDir\hvExternDateiname}% \ifhv@extern@frame \begingroup \fboxsep=\hv@extern@framesep @@ -646,7 +651,7 @@ \fi \else% no graphic, only text \ifhv@extern@float - \hv@typeout{>>>> Floating environment}% + \hv@ex@typeout{Floating environment}% \expandafter\figure\expandafter[!htb] \ifhv@extern@usefancyvrb \expandafter\BodyVerbatim\expandafter[\hv@extern@lstOptions]{\hv@extern@fullPath.txt} diff --git a/macros/luatex/generic/lua-widow-control/README.md b/macros/luatex/generic/lua-widow-control/README.md index 025a8088a1..c1ce5258f0 100644 --- a/macros/luatex/generic/lua-widow-control/README.md +++ b/macros/luatex/generic/lua-widow-control/README.md @@ -9,7 +9,7 @@ lua-widow-control Lua-widow-control is a Plain TeX/LaTeX/ConTeXt/OpTeX package that removes widows and orphans without any user intervention. Using the power of LuaTeX, it does so _without_ stretching any glue or shortening any pages or columns. Instead, lua-widow-control automatically lengthens a paragraph on a page or column where a widow or orphan would otherwise occur. -Please see the [**package documentation**](https://github.com/gucci-on-fleek/lua-widow-control/releases/latest/download/lua-widow-control.pdf) for usage details or the [***TUGboat* article**](https://github.com/gucci-on-fleek/lua-widow-control/releases/latest/download/tb133chernoff-widows.pdf) for background information and discussion. +Please see the [**package manual**](https://github.com/gucci-on-fleek/lua-widow-control/releases/latest/download/lua-widow-control.pdf) for usage details or the [***TUGboat* article**](https://github.com/gucci-on-fleek/lua-widow-control/releases/latest/download/tb133chernoff-widows.pdf) for background information and discussion. Usage ----- @@ -48,4 +48,4 @@ Lua-widow-control is licensed under the [_Mozilla Public License_, version 2.0]( Please note that a compiled document is absolutely **not** considered to be an "Executable Form" as defined by the MPL. The use of lua-widow-control in a document does not place **any** obligations on the document's author or distributors. The MPL and CC-BY-SA licenses **only** apply to you if you distribute the lua-widow-control source code or documentation. --- -_v2.1.1 (2022-05-20)_ <!--%%version %%dashdate--> +_v2.1.2 (2022-05-26)_ <!--%%version %%dashdate--> diff --git a/macros/luatex/generic/lua-widow-control/lua-widow-control-2022-02-22.sty b/macros/luatex/generic/lua-widow-control/lua-widow-control-2022-02-22.sty index 16f01966ee..b7b2ab44c0 100644 --- a/macros/luatex/generic/lua-widow-control/lua-widow-control-2022-02-22.sty +++ b/macros/luatex/generic/lua-widow-control/lua-widow-control-2022-02-22.sty @@ -12,8 +12,8 @@ % report a real version number here for debugging. \PackageInfo{lua-widow-control}{% Real version: - 2022/05/20 %%dashdate - v2.1.1 %%version + 2022/05/26 %%slashdate + v2.1.2 %%version } \PackageWarning{lua-widow-control}{% diff --git a/macros/luatex/generic/lua-widow-control/lua-widow-control.lua b/macros/luatex/generic/lua-widow-control/lua-widow-control.lua index 2af5575799..4bd109e483 100644 --- a/macros/luatex/generic/lua-widow-control/lua-widow-control.lua +++ b/macros/luatex/generic/lua-widow-control/lua-widow-control.lua @@ -7,22 +7,24 @@ --- Tell the linter about node attributes --- @class node ---- @field prev node ---- @field next node +--- @field depth integer +--- @field height integer --- @field id integer ---- @field subtype integer +--- @field list node +--- @field next node --- @field penalty integer ---- @field height integer ---- @field depth integer +--- @field prev node +--- @field subtype integer --- Set some default variables +-- Initial setup lwc = lwc or {} lwc.name = "lua-widow-control" -lwc.nobreak_behaviour = "keep" -- Locals for `debug_print` -local write_nl = texio.write_nl +local debug_lib = debug local string_rep = string.rep +local write_nl = texio.write_nl + local write_log if status.luatex_engine == "luametatex" then write_log = "logfile" @@ -31,10 +33,11 @@ else end --- Prints debugging messages to the log, only if `debug` is set to `true`. +--- --- @param title string The "title" to use --- @param text string? The "content" to print --- @return nil -local function debug_print(title, text) +local function debug(title, text) if not lwc.debug then return end -- The number of spaces we need @@ -47,6 +50,7 @@ local function debug_print(title, text) end end + --[[ \lwc/ is intended to be format-agonistic. It only runs on Lua\TeX{}, but there are still some slight differences between formats. Here, we @@ -70,58 +74,73 @@ end --[[ Save some local copies of the node library to reduce table lookups. - This is probably a useless micro-optimization, but it can't hurt. + This is probably a useless micro-optimization, but it is done in all of the + ConTeXt and expl3 Lua code, so I should probably do it here too. ]] -- Node ID's +-- (We need to hardcode the subid's sadly) +local id_from_name = node.id local baselineskip_subid = 2 -local glue_id = node.id("glue") -local glyph_id = node.id("glyph") -local hlist_id = node.id("hlist") +local glue_id = id_from_name("glue") +local glyph_id = id_from_name("glyph") +local hlist_id = id_from_name("hlist") +local insert_id = id_from_name("insert") or id_from_name("ins") local line_subid = 1 local linebreakpenalty_subid = 1 -local par_id = node.id("par") or node.id("local_par") -local penalty_id = node.id("penalty") +local par_id = id_from_name("par") or id_from_name("local_par") +local penalty_id = id_from_name("penalty") -- Local versions of globals local abs = math.abs -local copy = node.copy_list or node.copylist +local copy = node.copy +local copy_list = node.copy_list or node.copylist local find_attribute = node.find_attribute or node.findattribute -local flush_list = node.flush_list or node.flushlist local free = node.free -local getattribute = node.get_attribute or node.getattribute +local free_list = node.flush_list or node.flushlist +local get_attribute = node.get_attribute or node.getattribute local insert_token = token.put_next or token.putnext local last = node.slide +local linebreak = tex.linebreak local new_node = node.new -local node_id = node.is_node or node.isnode local set_attribute = node.set_attribute or node.setattribute local string_char = string.char +local tex_box = tex.box +local tex_count = tex.count +local tex_dimen = tex.dimen +local tex_lists = tex.lists local traverse = node.traverse -local traverseid = node.traverse_id or node.traverseid +local traverse_id = node.traverse_id or node.traverseid local vpack = node.vpack -- Misc. Constants local iffalse = token.create("iffalse") local iftrue = token.create("iftrue") local INFINITY = 10000 +local INSERT_CLASS_MULTIPLE = 1000 * 1000 +local INSERT_FIRST_MULTIPLE = 1000 local min_col_width = tex.sp("250pt") -local SINGLE_LINE = 50 local PAGE_MULTIPLE = 100 +local SINGLE_LINE = 50 ---[[ - Package/module initialization +--[[ Package/module initialization. + + Here, we replace any format/engine-specific variables/functions with some + generic equivalents. This way, we can write the rest of the module without + worrying about any format/engine differences. ]] -local attribute, - contrib_head, +local contrib_head, emergencystretch, info, + insert_attribute, max_cost, pagenum, + paragraph_attribute, stretch_order, warning if lmtx then -- LMTX has removed underscores from most of the Lua parts - debug_print("LMTX") + debug("LMTX") contrib_head = "contributehead" stretch_order = "stretchorder" else @@ -130,23 +149,27 @@ else end if context then - debug_print("ConTeXt") + debug("ConTeXt") warning = logs.reporter(lwc.name, "warning") local _info = logs.reporter(lwc.name, "info") + --[[ We don't want the info messages on the terminal, but ConTeXt doesn't + provide any logfile-only reporters, so we need this hack. + ]] info = function (text) logs.pushtarget("logfile") _info(text) logs.poptarget() end - attribute = attributes.public(lwc.name) - pagenum = function() return tex.count["realpageno"] end + paragraph_attribute = attributes.public(lwc.name .. "_paragraph") + insert_attribute = attributes.public(lwc.name .. "_insert") + pagenum = function() return tex_count["realpageno"] end -- Dimen names emergencystretch = "lwc_emergency_stretch" max_cost = "lwc_max_cost" elseif plain or latex or optex then - pagenum = function() return tex.count[0] end + pagenum = function() return tex_count[0] end -- Dimen names if tex.isdimen("g__lwc_emergencystretch_dim") then @@ -158,11 +181,11 @@ elseif plain or latex or optex then end if plain or latex then - debug_print("Plain/LaTeX") + debug("Plain/LaTeX") luatexbase.provides_module { name = lwc.name, - date = "2022/05/14", --%%dashdate - version = "2.1.1", --%%version + date = "2022/05/26", --%%slashdate + version = "2.1.2", --%%version description = [[ This module provides a LuaTeX-based solution to prevent @@ -172,13 +195,15 @@ 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) + paragraph_attribute = luatexbase.new_attribute(lwc.name .. "_paragraph") + insert_attribute = luatexbase.new_attribute(lwc.name .. "_insert") elseif optex then - debug_print("OpTeX") + debug("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) + paragraph_attribute = alloc.new_attribute(lwc.name .. "_paragraph") + insert_attribute = alloc.new_attribute(lwc.name .. "_insert") end else -- This shouldn't ever happen error [[Unsupported format. @@ -186,7 +211,17 @@ else -- This shouldn't ever happen Please use LaTeX, Plain TeX, ConTeXt or OpTeX.]] end -local paragraphs = {} -- List to hold the alternate paragraph versions +--[[ Table to hold the alternate paragraph versions. + + This is global(ish) mutable state, which isn't ideal, but any other way of + passing this data around would be even worse. + ]] +local paragraphs = {} +local inserts = {} + +--[[ + Function definitions + ]] --- Gets the current paragraph and page locations --- @return string @@ -194,11 +229,9 @@ local function get_location() return "At " .. pagenum() .. "/" .. #paragraphs end ---[[ - Function definitions - ]] ---- Prints the initial glyphs and glue of an hlist +--- Prints the starting glyphs and glue of an `hlist` +--- --- @param head node --- @return nil local function get_chars(head) @@ -220,10 +253,12 @@ local function get_chars(head) end end - debug_print(get_location(), chars) + debug(get_location(), chars) end ---- The "cost function" to use. See the manual. + +--- The "cost function" to use. Users can redefine this if they wish. +--- --- @param demerits number The demerits of the broken paragraph --- @param lines number The number of lines in the broken paragraph --- @return number The cost of the broken paragraph @@ -231,6 +266,7 @@ function lwc.paragraph_cost(demerits, lines) return demerits / math.sqrt(lines) end + --- Checks if the ConTeXt "grid snapping" is active --- @return boolean local function grid_mode_enabled() @@ -238,7 +274,9 @@ local function grid_mode_enabled() return token.create("ifgridsnapping").mode == iftrue.mode end ---- Gets the next node of a type/subtype in a node list + +--- Gets the next node of a specified type/subtype in a node list +--- --- @param head node The head of the node list --- @param id number The node type --- @param args table? @@ -247,13 +285,17 @@ end --- @return node local function next_of_type(head, id, args) args = args or {} + if lmtx or not args.reverse then - for n, subtype in traverseid(id, head, args.reverse) do + for n, subtype in traverse_id(id, head, args.reverse) do if (subtype == args.subtype) or (args.subtype == nil) then return n end end - else -- Only LMTX has the built-in backwards traverser + else + --[[ Only LMTX has the built-in backwards traverser, so we need to do it + ourselves here. + ]] while head do if head.id == id and (head.subtype == args.subtype or args.subtype == nil) @@ -265,39 +307,24 @@ local function next_of_type(head, id, args) end end ---- Saves each paragraph, but lengthened by 1 line ---- ---- Called by the `pre_linebreak_filter` callback ---- ---- @param head node ---- @return node -function lwc.save_paragraphs(head) - if (head.id ~= par_id and context) or -- Ensure that we were actually given a par - status.output_active -- Don't run during the output routine - then - return head - end - -- Prevent the "underfull hbox" warnings when we store a potential paragraph - local renable_box_warnings - if (context or optex) or - #luatexbase.callback_descriptions("hpack_quality") == 0 - then -- See #18 and michal-h21/linebreaker#3 - renable_box_warnings = true - lwc.callbacks.disable_box_warnings.enable() - end - - -- We need to return the unmodified head at the end, so we make a copy here - local new_head = copy(head) +--- Breaks a paragraph one line longer than natural +--- +--- @param head node The unbroken paragraph +--- @return node long_node The broken paragraph +--- @return table long_info An info table about the broken paragraph +local function long_paragraph(head) + -- We can't modify the original paragraph + head = copy_list(head) -- Prevent ultra-short last lines (\TeX{}Book p. 104), except with narrow columns -- Equivalent to \\parfillskip=0pt plus 0.8\\hsize local parfillskip - if lmtx or last(new_head).id ~= glue_id then + if lmtx or last(head).id ~= glue_id then -- LMTX does not automatically add the \\parfillskip glue parfillskip = new_node("glue", "parfillskip") else - parfillskip = last(new_head) + parfillskip = last(head) end if tex.hsize > min_col_width then @@ -305,35 +332,78 @@ function lwc.save_paragraphs(head) parfillskip.stretch = 0.8 * tex.hsize -- Last line must be at least 20% long end - if lmtx or last(new_head).id ~= glue_id then - last(new_head).next = parfillskip + if lmtx or last(head).id ~= glue_id then + last(head).next = parfillskip end -- Break the paragraph 1 line longer than natural - local long_node, long_info = tex.linebreak(new_head, { + return linebreak(head, { looseness = 1, - emergencystretch = tex.getdimen(emergencystretch), + emergencystretch = tex_dimen[emergencystretch], }) +end - -- Break the natural paragraph so we know how long it was - nat_head = copy(head) +--- Breaks a paragraph at its natural length +--- +--- @param head node The unbroken paragraph +--- @return table natural_info An info table about the broken paragraph +local function natural_paragraph(head) + -- We can't modify the original paragraph + head = copy_list(head) + + --[[ Contrary to the documentation, LMTX does not automatically add + the \\parfillskip glue before `pre_linebreak_filter`, so we need + to add it here so that our \\prevgraf comparisons are correct. + ]] if lmtx then parfillskip = new_node("glue", "parfillskip") parfillskip[stretch_order] = 1 parfillskip.stretch = 1 -- 0pt plus 1fil - last(nat_head).next = parfillskip + last(head).next = parfillskip end - local natural_node, natural_info = tex.linebreak(nat_head) - flush_list(natural_node) + -- Break the paragraph naturally to get \\prevgraf + local natural_node, natural_info = linebreak(head) + free_list(natural_node) + + return natural_info +end + + +--- Saves each paragraph, but lengthened by 1 line +--- +--- Called by the `pre_linebreak_filter` callback +--- +--- @param head node +--- @return node +function lwc.save_paragraphs(head) + if (head.id ~= par_id and context) or -- Ensure that we were actually given a par + status.output_active or -- Don't run during the output routine + tex.nest.ptr > 1 -- Don't run inside boxes + then + return head + end + + -- Prevent the "underfull hbox" warnings when we store a potential paragraph + local renable_box_warnings + if (context or optex) or + #luatexbase.callback_descriptions("hpack_quality") == 0 + then -- See #18 and michal-h21/linebreaker#3 + renable_box_warnings = true + lwc.callbacks.disable_box_warnings.enable() + end + + long_node, long_info = long_paragraph(head) + + natural_info = natural_paragraph(head) if renable_box_warnings then lwc.callbacks.disable_box_warnings.disable() end if not grid_mode_enabled() then - -- Offset the accumulated \\prevdepth + -- Offset the \\prevdepth differences between natural and long local prevdepth = new_node("glue") prevdepth.width = natural_info.prevdepth - long_info.prevdepth last(long_node).next = prevdepth @@ -352,14 +422,14 @@ function lwc.save_paragraphs(head) -- Print some debugging information get_chars(head) - debug_print(get_location(), "nat lines " .. natural_info.prevgraf) - debug_print( + debug(get_location(), "nat lines " .. natural_info.prevgraf) + debug( get_location(), "nat cost " .. lwc.paragraph_cost(natural_info.demerits, natural_info.prevgraf) ) - debug_print(get_location(), "long lines " .. long_info.prevgraf) - debug_print( + debug(get_location(), "long lines " .. long_info.prevgraf) + debug( get_location(), "long cost " .. lwc.paragraph_cost(long_info.demerits, long_info.prevgraf) @@ -369,84 +439,117 @@ function lwc.save_paragraphs(head) return head end + --- Tags the beginning and the end of each paragraph as it is added to the page. --- --- We add an attribute to the first and last node of each paragraph. The ID is --- some arbitrary number for \lwc/, and the value corresponds to the ---- paragraphs index, which is negated for the end of the paragraph. Called by the ---- `post_linebreak_filter` callback. +--- paragraphs index, which is negated for the end of the paragraph. --- --- @param head node ---- @return node -function lwc.mark_paragraphs(head) +--- @return nil +local function mark_paragraphs(head) + -- Tag the paragraphs if not status.output_active then -- Don't run during the output routine -- Get the start and end of the paragraph local top_para = next_of_type(head, hlist_id, { subtype = line_subid }) local bottom_para = last(head) + while bottom_para.id == insert_id do + bottom_para = bottom_para.prev + end + if top_para ~= bottom_para then set_attribute( top_para, - attribute, + paragraph_attribute, #paragraphs + (PAGE_MULTIPLE * pagenum()) ) set_attribute( bottom_para, - attribute, + paragraph_attribute, -1 * (#paragraphs + (PAGE_MULTIPLE * pagenum())) ) else -- We need a special tag for a 1-line paragraph since the node can only - -- have one attribute value + -- have a single attribute value set_attribute( top_para, - attribute, + paragraph_attribute, #paragraphs + (PAGE_MULTIPLE * pagenum()) + SINGLE_LINE ) end end - - return head end ---- A "safe" version of the last/slide function. + +--- Tags the each line with the indices of any corresponding inserts. --- ---- Sometimes the node list can form a loop. Since there is no last element ---- of a looped linked-list, the `last()` function will never terminate. This ---- function provides a "safe" version of the `last()` function that will break ---- the loop at the end if the list is circular. Called by the `pre_output_filter` ---- callback. +--- We need to tag the first element of the hlist before the any insert nodes +--- since the insert nodes are removed before `pre_output_filter` gets called. --- ---- @param head node The start of a node list ---- @return node The last node in a list -local function safe_last(head) - local ids = {} - local prev - - while head.next do - local id = node_id(head) +--- @param head node +--- @return nil +local function mark_inserts(head) + local insert_indices = {} + for insert in traverse_id(insert_id, head) do + -- Save the found insert nodes for later + inserts[#inserts+1] = copy(insert) - if ids[id] then - warning [[Circular node list detected! -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)) + -- Tag the insert's content so that we can find it later + set_attribute(insert.list, insert_attribute, #inserts) - return prev + for n in traverse(insert.list.next) do + set_attribute(n, insert_attribute, -1 * #inserts) end - ids[id] = true - head.prev = prev - prev = head - head = head.next + --[[ Each hlist/line can have multiple inserts, but so we can't just tag + the hlist as we go. Instead, we need save up all of their indices, + then tag the hlist with the first and last indices. + ]] + insert_indices[#insert_indices+1] = #inserts + + if not insert.next or + insert.next.id ~= insert_id + then + local hlist_before = next_of_type(insert, hlist_id, { reverse = true} ) + + --[[ We tag the first element of the hlist/line with an integer + that holds the insert class and the first and last indices + of the inserts contained in the line. This won't work if + the line has multiple classes of inserts, but I don't think + that happens in real-world documents. + ]] + set_attribute( + hlist_before.list, + insert_attribute, + insert.subtype * INSERT_CLASS_MULTIPLE + + insert_indices[1] * INSERT_FIRST_MULTIPLE + + insert_indices[#insert_indices] + ) + + -- Clear the indices to prepare for the next line + insert_indices = {} + end end +end + + +--- Saves the inserts and tags a typeset paragraph. Called by the +--- `post_linebreak_filter` callback. +--- +--- @param head node +--- @return node +function lwc.mark_paragraphs(head) + mark_paragraphs(head) + mark_inserts(head) return head end + --- Checks to see if a penalty matches the widow/orphan/broken penalties +--- --- @param penalty number --- @return boolean function is_matching_penalty(penalty) @@ -455,15 +558,9 @@ function is_matching_penalty(penalty) local displaywidowpenalty = tex.displaywidowpenalty local brokenpenalty = tex.brokenpenalty - --[[ - We only need to process pages that have orphans or widows. If `paragraphs` - is empty, then there is nothing that we can do. - - The list of penalties is from: - https://tug.org/TUGboat/tb39-3/tb123mitt-widows-code.pdf#subsection.0.2.1 - ]] penalty = penalty - tex.interlinepenalty + -- https://tug.org/TUGboat/tb39-3/tb123mitt-widows-code.pdf#subsection.0.2.1 return penalty ~= 0 and penalty < INFINITY and ( penalty == widowpenalty or @@ -480,143 +577,233 @@ function is_matching_penalty(penalty) ) end ---- Remove the widows and orphans from the page, just after the output routine. ---- ---- This function holds the "meat" of the module. It is called just after the ---- end of the output routine, before the page is shipped out. If the output ---- penalty indicates that the page was broken at a widow or an orphan, we ---- 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. ---- ---- @param head node ---- @return node -function lwc.remove_widows(head) - local head_save = head -- Save the start of the `head` linked-list - debug_print("outputpenalty", tex.outputpenalty .. " " .. #paragraphs) +--- Reset any state saved between pages +--- +--- @return nil +local function reset_state() + paragraphs = {} - if not is_matching_penalty(tex.outputpenalty) or - #paragraphs == 0 - then - paragraphs = {} - return head_save + for _, insert in ipairs(inserts) do + free(insert) end - info("Widow/orphan/broken hyphen detected. Attempting to remove") + inserts = {} +end - local vsize = tex.dimen.vsize - local orig_height_diff = vpack(head).height - vsize - --[[ - Find the paragraph on the page with the least cost. - ]] - local paragraph_index = 1 - local best_cost = paragraphs[paragraph_index].cost +--- When we are unable to remove a widow/orphan, print a warning +--- +--- @return nil +local function remove_widows_fail() + warning("Widow/Orphan/broken hyphen NOT removed on page " .. pagenum()) + reset_state() +end + + +--- Finds the first and last paragraphs present on a page +--- +--- @param head node The node representing the start of the page +--- @return number first_index The index of the first paragraph on the page in +--- the `paragraphs` table +--- @return number last_index The index of the last paragraph on the page in the +--- `paragraphs` table +local function first_last_paragraphs(head) + local first_index, last_index - local last_paragraph - local head_last = last(head) -- Find the last paragraph on the page, starting at the end, heading in reverse - while head_last do - local value = getattribute(head_last, attribute) + local n = last(head) + while n do + local value = get_attribute(n, paragraph_attribute) if value then - last_paragraph = value % PAGE_MULTIPLE + last_index = value % PAGE_MULTIPLE break end - head_last = head_last.prev + n = n.prev end - local first_paragraph -- Find the first paragraph on the page, from the top - local first_attribute_val, first_attribute_head = find_attribute(head, attribute) - if first_attribute_val // 100 == pagenum() - 1 then - -- If the first complete paragraph on the page was initially broken on the - -- previous page, then we can't expand it here so we need to skip it. - first_paragraph = find_attribute( - first_attribute_head.next, - attribute + local first_val, first_head = find_attribute(head, paragraph_attribute) + if first_val // PAGE_MULTIPLE == pagenum() - 1 then + --[[ If the first complete paragraph on the page was initially broken on the + previous page, then we can't expand it here so we need to skip it. + ]] + first_index = find_attribute( + first_head.next, + paragraph_attribute ) % PAGE_MULTIPLE else - first_paragraph = first_attribute_val % PAGE_MULTIPLE + first_index = first_val % PAGE_MULTIPLE end + return first_index, last_index +end + + +--- Selects the "best" paragraph on the page to expand +--- +--- @param head node The node representing the start of the page +--- @return number? best_index The index of the paragraph to expand in the +--- `paragraphs` table +local function best_paragraph(head) + local first_paragraph_index, last_paragraph_index = first_last_paragraphs(head) + + -- Find the paragraph on the page with the least cost. + local best_index = 1 + local best_cost = paragraphs[best_index].cost + -- We find the current "best" replacement, then free the unused ones - for i, paragraph in pairs(paragraphs) do + for index, paragraph in pairs(paragraphs) do if paragraph.cost < best_cost and - i < last_paragraph and - i >= first_paragraph + index < last_paragraph_index and + index >= first_paragraph_index then - -- Clear the old best paragraph - flush_list(paragraphs[paragraph_index].node) - paragraphs[paragraph_index].node = nil + -- Free the old best paragraph + free_list(paragraphs[best_index].node) + paragraphs[best_index].node = nil -- Set the new best paragraph - paragraph_index, best_cost = i, paragraph.cost - elseif i > 1 then + best_index, best_cost = index, paragraph.cost + elseif index > 1 then -- Not sure why `i > 1` is required? - flush_list(paragraph.node) + free_list(paragraph.node) paragraph.node = nil end end - debug_print( + debug( "selected para", - pagenum() .. - "/" .. - paragraph_index .. - " (" .. - best_cost .. - ")" + pagenum() .. "/" .. best_index .. " (" .. best_cost .. ")" ) - if best_cost > tex.getcount(max_cost) or - paragraph_index == last_paragraph + if best_cost > tex_count[max_cost] or + best_index == last_paragraph_index then - -- If the best replacement is too bad, we can't do anything - warning("Widow/Orphan/broken hyphen NOT removed on page " .. pagenum()) - paragraphs = {} - return head_save + return nil + else + return best_index end +end - local target_node = paragraphs[paragraph_index].node +--- Gets any inserts present in the moved line +--- +--- @param last_line node The moved last line +--- @return table<node> inserts A list of the present inserts +local function get_inserts(last_line) + local selected_inserts = {} + + local n = last_line.list + while n do -- Iterate through the last line + local line_value + line_value, n = find_attribute(n, insert_attribute) + + if not n then + break + end + + --[[ With LuaMetaTeX, the subtype of `insert` nodes is always zero, + so we cannot detect their class therefore we can't fix any moved + footnotes. + ]] + if lmtx then + warning("!!!Incorrect footnotes on page " .. pagenum() .. "!!!") + return {} + end + + -- Demux the insert values + local class = line_value // INSERT_CLASS_MULTIPLE + local first_index = (line_value % INSERT_CLASS_MULTIPLE) // INSERT_FIRST_MULTIPLE + local last_index = line_value % INSERT_FIRST_MULTIPLE + + -- Get the output box containing the insert boxes + local insert_box = tex_box[class] + + local m = insert_box.list + while m do -- Iterate through the insert box + local box_value + box_value, m = find_attribute(m, insert_attribute) + + if not m then + break + end + + if abs(box_value) >= first_index and + abs(box_value) <= last_index + then + -- Remove the respective contents from the insert box + insert_box.list = node.remove(insert_box.list, m) + + if box_value > 0 then + selected_inserts[#selected_inserts + 1] = copy(inserts[box_value]) + end + end + + m = m.next + end + + if not insert_box.list then + tex_box[class] = nil + end + + n = n.next + end + + if #selected_inserts ~= 0 then + info("Moving footnotes on page " .. pagenum()) + end + + return selected_inserts +end + + +lwc.nobreak_behaviour = "keep" +--- Moves the last line of the page onto the following page. +--- +--- This is the most complicated function of the module since it needs to +--- look back to see if there is a heading preceding the last line, then it does +--- some low-level node shuffling. +--- +--- @param head node The node representing the start of the page +--- @return boolean success +local function move_last_line(head) -- Start of final paragraph - debug_print("remove_widows", "moving last line") + debug("remove_widows", "moving last line") -- Here we check to see if the widow/orphan was preceded by a large penalty - head = last(head_save).prev local big_penalty_found, last_line, hlist_head - while head do - if head.id == glue_id then + local n = last(head).prev + while n do + if n.id == glue_id then -- Ignore any glue nodes - elseif head.id == penalty_id and head.penalty >= INFINITY then + elseif n.id == penalty_id and n.penalty >= INFINITY then -- Infinite break penalty big_penalty_found = true - elseif big_penalty_found and head.id == hlist_id then + elseif big_penalty_found and n.id == hlist_id then -- Line before the penalty if lwc.nobreak_behaviour == "keep" then - hlist_head = head + hlist_head = n big_penalty_found = false elseif lwc.nobreak_behaviour == "split" then - head = last(head_save) + n = last(head) break elseif lwc.nobreak_behaviour == "warn" then - warning("Widow/Orphan/broken hyphen NOT removed on page " .. pagenum()) - paragraphs = {} - return head_save + debug("last line", "heading found") + return false end else -- Not found if hlist_head then - head = hlist_head + n = hlist_head else - head = last(head_save) + n = last(head) end break end - head = head.prev + n = n.prev end - local potential_penalty = head.prev.prev + local potential_penalty = n.prev.prev if potential_penalty and potential_penalty.id == penalty_id and @@ -626,43 +813,68 @@ function lwc.remove_widows(head) warning("Making a new widow/orphan/broken hyphen on page " .. pagenum()) end + last_line = copy_list(n) - last_line = copy(head) - last(last_line).next = copy(tex.lists[contrib_head]) + -- Reinsert any inserts originally present in this moved line + local selected_inserts = get_inserts(last_line) + for _, insert in ipairs(selected_inserts) do + last(last_line).next = insert + end + + -- Add back in the content from the next page + last(last_line).next = copy_list(tex_lists[contrib_head]) + + n.prev.prev.next = nil + + -- Set the content of the next page + last(last_line) + tex_lists[contrib_head] = last_line + + return true +end - head.prev.prev.next = nil - -- Move the last line to the next page - tex.lists[contrib_head] = last_line +--- Replace the chosen paragraph with its expanded version. +--- +--- This is the "core function" of the module since it is what ultimately causes +--- the expansion to occur. +--- +--- @param head node +--- @param paragraph_index number +local function replace_paragraph(head, paragraph_index) + local target_node = paragraphs[paragraph_index].node local free_next_nodes = false + local start_found = false + local end_found = false + -- Loop through all of the nodes on the page with the lwc attribute - head = head_save - while head do + local n = head + while n do local value - value, head = find_attribute(head, attribute) + value, n = find_attribute(n, paragraph_attribute) - if not head then + if not n then break end - debug_print("remove_widows", "found " .. value) + debug("remove_widows", "found " .. value) -- Insert the start of the replacement paragraph if value == paragraph_index + (PAGE_MULTIPLE * pagenum()) or value == paragraph_index + (PAGE_MULTIPLE * pagenum()) + SINGLE_LINE then - debug_print("remove_widows", "replacement start") - safe_last(target_node) -- Remove any loops + debug("remove_widows", "replacement start") + start_found = true -- Fix the `\\baselineskip` glue between paragraphs height_difference = ( - next_of_type(head, hlist_id, { subtype = line_subid }).height - + next_of_type(n, hlist_id, { subtype = line_subid }).height - next_of_type(target_node, hlist_id, { subtype = line_subid }).height ) local prev_bls = next_of_type( - head, + n, glue_id, { subtype = baselineskip_subid, reverse = true } ) @@ -671,7 +883,7 @@ function lwc.remove_widows(head) prev_bls.width = prev_bls.width + height_difference end - head.prev.next = target_node + n.prev.next = target_node free_next_nodes = true end @@ -679,45 +891,101 @@ function lwc.remove_widows(head) if value == -1 * (paragraph_index + (PAGE_MULTIPLE * pagenum())) or value == paragraph_index + (PAGE_MULTIPLE * pagenum()) + SINGLE_LINE then - debug_print("remove_widows", "replacement end") - local target_node_last = safe_last(target_node) + debug("remove_widows", "replacement end") + end_found = true + + local target_node_last = last(target_node) if grid_mode_enabled() then -- Account for the difference in depth local after_glue = new_node("glue") - after_glue.width = head.depth - target_node_last.depth + after_glue.width = n.depth - target_node_last.depth target_node_last.next = after_glue - after_glue.next = head.next + after_glue.next = n.next else - target_node_last.next = head.next + target_node_last.next = n.next end break end if free_next_nodes then - head = free(head) + n = free(n) else - head = head.next + n = n.next end end - local new_height_diff = vpack(head_save).height - vsize - -- We need the original height discrepancy in case there are \\vfill's - local net_height_diff = orig_height_diff - new_height_diff + if not (start_found and end_found) then + warning("Paragraph NOT expanded on page " .. pagenum()) + end +end + + +--- Remove the widows and orphans from the page, just after the output routine. +--- +--- This is called just after the end of the output routine, before the page is +--- shipped out. If the output penalty indicates that the page was broken at a +--- widow or an orphan, we 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. +--- +--- @param head node +--- @return node +function lwc.remove_widows(head) + debug("outputpenalty", tex.outputpenalty .. " " .. #paragraphs) + + -- See if there is a widow/orphan for us to remove + if not is_matching_penalty(tex.outputpenalty) then + reset_state() + return head + end + + info("Widow/orphan/broken hyphen detected. Attempting to remove") + + -- Nothing that we can do if there aren't any paragraphs available to expand + if #paragraphs == 0 then + remove_widows_fail() + return head + end + + -- Check the original height of \\box255 + local vsize = tex_dimen.vsize + local orig_height_diff = vpack(head).height - vsize + + -- Find the paragraph to expand + local paragraph_index = best_paragraph(head) + + if not paragraph_index then + remove_widows_fail() + return head + end + + -- Move the last line of the page to the next page + if not move_last_line(head) then + remove_widows_fail() + return head + end + + -- Replace the chosen paragraph with its expanded version + replace_paragraph(head, paragraph_index) --[[ The final \\box255 needs to be exactly \\vsize tall to avoid over/underfull box warnings, so we correct any discrepancies here. ]] + local new_height_diff = vpack(head).height - vsize + -- We need the original height discrepancy in case there are \\vfill's + local net_height_diff = orig_height_diff - new_height_diff + if abs(net_height_diff) > 0 and -- A difference larger than 0.25\\baselineskip is probably not from \lwc/ abs(net_height_diff) < tex.skip.baselineskip.width / 4 then local bottom_glue = new_node("glue") bottom_glue.width = net_height_diff - last(head_save).next = bottom_glue + last(head).next = bottom_glue end info( @@ -727,12 +995,14 @@ function lwc.remove_widows(head) .. pagenum() ) - paragraphs = {} -- Clear paragraphs array at the end of the page + reset_state() - return head_save + return head end + --- Create a table of functions to enable or disable a given callback +--- --- @param t table Parameters of the callback to create --- callback: string = The \LuaTeX{} callback name --- func: function = The function to call @@ -756,8 +1026,9 @@ local function register_callback(t) } elseif context and not t.lowlevel then return { - -- Register the callback when the table is created, - -- but activate it when `enable()` is called. + --[[ Register the callback when the table is created, + but activate it when `enable()` is called. + ]] enable = nodes.tasks.appendaction(t.category, t.position, "lwc." .. t.name) or function() nodes.tasks.enableaction(t.category, "lwc." .. t.name) @@ -767,13 +1038,12 @@ local function register_callback(t) end, } elseif context and t.lowlevel then - --[[ - Some of the callbacks in \ConTeXt{} have no associated "actions". Unlike - with \LuaTeX{}base, \ConTeXt{} leaves some \LuaTeX{} callbacks unregistered - and unfrozen. Because of this, we need to register some callbacks at the - engine level. This is fragile though, because a future \ConTeXt{} update - may decide to register one of these functions, in which case - \lwc/ will crash with a cryptic error message. + --[[ Some of the callbacks in \ConTeXt{} have no associated "actions". Unlike + with \LuaTeX{}base, \ConTeXt{} leaves some \LuaTeX{} callbacks unregistered + and unfrozen. Because of this, we need to register some callbacks at the + engine level. This is fragile though, because a future \ConTeXt{} update + may decide to register one of these functions, in which case + \lwc/ will crash with a cryptic error message. ]] return { enable = function() callback.register(t.callback, t.func) end, @@ -791,6 +1061,7 @@ local function register_callback(t) end end + -- Add all of the callbacks lwc.callbacks = { disable_box_warnings = register_callback({ @@ -822,51 +1093,53 @@ lwc.callbacks = { } -local enabled = false ---- Enable the paragraph callbacks +local lwc_enabled = false +--- Enables the paragraph callbacks function lwc.enable_callbacks() - debug_print("callbacks", "enabling") - if not enabled then + debug("callbacks", "enabling") + if not lwc_enabled then lwc.callbacks.save_paragraphs.enable() lwc.callbacks.mark_paragraphs.enable() - enabled = true + lwc_enabled = true else info("Already enabled") end end ---- Disable the paragraph callbacks + +--- Disables the paragraph callbacks function lwc.disable_callbacks() - debug_print("callbacks", "disabling") - if enabled then + debug("callbacks", "disabling") + if lwc_enabled then lwc.callbacks.save_paragraphs.disable() lwc.callbacks.mark_paragraphs.disable() - --[[ - We do \emph{not} disable `remove_widows` callback, since we still want - to expand any of the previously-saved paragraphs if we hit an orphan - or a widow. + --[[ We do \emph{not} disable `remove_widows` callback, since we still want + to expand any of the previously-saved paragraphs if we hit an orphan + or a widow. ]] - enabled = false + lwc_enabled = false else info("Already disabled") end end function lwc.if_lwc_enabled() - debug_print("iflwc") - if enabled then + debug("iflwc") + if lwc_enabled then insert_token(iftrue) else insert_token(iffalse) end end + --- Mangles a macro name so that it's suitable for a specific format +--- --- @param name string The plain name --- @param args table<string> The TeX types of the function arguments ---- @return string The mangled name +--- @return string name The mangled name local function mangle_name(name, args) if plain then return "lwc@" .. name:gsub("_", "@") @@ -879,7 +1152,9 @@ local function mangle_name(name, args) end end + --- Creates a TeX command that evaluates a Lua function +--- --- @param name string The name of the csname to define --- @param func function --- @param args table<string> The TeX types of the function arguments @@ -921,6 +1196,7 @@ local function register_tex_cmd(name, func, args) end end + register_tex_cmd("if_enabled", lwc.if_lwc_enabled, {}) register_tex_cmd("enable", lwc.enable_callbacks, {}) register_tex_cmd("disable", lwc.disable_callbacks, {}) @@ -954,13 +1230,15 @@ register_tex_cmd( --- 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. +--- +--- @return nil local function silence_luatexbase() - local nups = debug.getinfo(luatexbase.add_to_callback).nups + local nups = debug_lib.getinfo(luatexbase.add_to_callback).nups for i = 1, nups do - local name, func = debug.getupvalue(luatexbase.add_to_callback, i) + local name, func = debug_lib.getupvalue(luatexbase.add_to_callback, i) if name == "luatexbase_log" then - debug.setupvalue( + debug_lib.setupvalue( luatexbase.add_to_callback, i, function(text) @@ -976,11 +1254,18 @@ local function silence_luatexbase() end end --- Activate \lwc/ -if plain or latex then + +--[[ Call `silence_luatexbase` in Plain and LaTeX, unless the undocmented global + `LWC_NO_DEBUG` is set. We provide this opt-out in case something goes awry + with the `debug` library calls. + ]] +if (plain or latex) and + not LWC_NO_DEBUG --- @diagnostic disable-line +then silence_luatexbase() end +-- Activate \lwc/ lwc.callbacks.remove_widows.enable() return lwc diff --git a/macros/luatex/generic/lua-widow-control/lua-widow-control.opm b/macros/luatex/generic/lua-widow-control/lua-widow-control.opm index a9edffc77c..f1261e6c24 100644 --- a/macros/luatex/generic/lua-widow-control/lua-widow-control.opm +++ b/macros/luatex/generic/lua-widow-control/lua-widow-control.opm @@ -3,7 +3,7 @@ % SPDX-License-Identifier: MPL-2.0+ % SPDX-FileCopyrightText: 2022 Max Chernoff -\_codedecl\lwcenable{lua-widow-control <v2.1.1>} %%version +\_codedecl\lwcenable{lua-widow-control <v2.1.2>} %%version \_namespace{lwc} \_clubpenalty=1 diff --git a/macros/luatex/generic/lua-widow-control/lua-widow-control.pdf b/macros/luatex/generic/lua-widow-control/lua-widow-control.pdf Binary files differindex cb803ce68f..f2d8337ee8 100644 --- a/macros/luatex/generic/lua-widow-control/lua-widow-control.pdf +++ b/macros/luatex/generic/lua-widow-control/lua-widow-control.pdf diff --git a/macros/luatex/generic/lua-widow-control/lua-widow-control.sty b/macros/luatex/generic/lua-widow-control/lua-widow-control.sty index 0e3462d22b..89f417a457 100644 --- a/macros/luatex/generic/lua-widow-control/lua-widow-control.sty +++ b/macros/luatex/generic/lua-widow-control/lua-widow-control.sty @@ -13,7 +13,7 @@ \DeclareRelease{}{0000-00-00}{lua-widow-control-2022-02-22.sty} \DeclareRelease{v1.1.6}{2022-02-22}{lua-widow-control-2022-02-22.sty} -\DeclareCurrentRelease{v2.1.1}{2022-05-20} %%version %%dashdate +\DeclareCurrentRelease{v2.1.2}{2022-05-26} %%version %%dashdate % If this version of LaTeX doesn't support command hooks, then we load % the last v1.1.X version of the package. @@ -23,8 +23,8 @@ \ProvidesExplPackage {lua-widow-control} - {2022/05/14} %%dashdate - {v2.1.1} %%version + {2022/05/26} %%slashdate + {v2.1.2} %%version {Use Lua to remove widows and orphans} % Unconditional Package Loads diff --git a/macros/luatex/generic/lua-widow-control/lua-widow-control.tex b/macros/luatex/generic/lua-widow-control/lua-widow-control.tex index 5c2c93662f..b8ae064c04 100644 --- a/macros/luatex/generic/lua-widow-control/lua-widow-control.tex +++ b/macros/luatex/generic/lua-widow-control/lua-widow-control.tex @@ -3,7 +3,7 @@ % SPDX-License-Identifier: MPL-2.0+ % SPDX-FileCopyrightText: 2022 Max Chernoff -\wlog{lua-widow-control v2.1.1} %%version +\wlog{lua-widow-control v2.1.2} %%version \ifx\directlua\undefined \errmessage{% diff --git a/macros/luatex/generic/lua-widow-control/lwc-sample.tex b/macros/luatex/generic/lua-widow-control/lwc-manual-samples.tex index 6951f7a5bb..6951f7a5bb 100644 --- a/macros/luatex/generic/lua-widow-control/lwc-sample.tex +++ b/macros/luatex/generic/lua-widow-control/lwc-manual-samples.tex diff --git a/macros/luatex/generic/lua-widow-control/lwc-manual.mkxl b/macros/luatex/generic/lua-widow-control/lwc-manual.mkxl index 7623b51d1c..e4f49861b9 100644 --- a/macros/luatex/generic/lua-widow-control/lwc-manual.mkxl +++ b/macros/luatex/generic/lua-widow-control/lwc-manual.mkxl @@ -93,7 +93,7 @@ fonts.handlers.otf.addfeature { % Acronym styling \definecharacterkerning[acronymkerning][factor=0.05] -\definealternativestyle[acronymstyle][{\feature[+][allsmall]\switchtobodyfont[1.1em]\setcharacterkerning[acronymkerning]}][] +\definealternativestyle[acronymstyle][{\feature[+][allsmall]\switchtobodyfont[1.2em]\setcharacterkerning[acronymkerning]}][] \definehighlight[acronym][style=acronymstyle] \startsetups[commandtable] diff --git a/macros/luatex/generic/lua-widow-control/lwc-manual.tex b/macros/luatex/generic/lua-widow-control/lwc-manual.tex index 7d68de6201..d2eb12c82e 100644 --- a/macros/luatex/generic/lua-widow-control/lwc-manual.tex +++ b/macros/luatex/generic/lua-widow-control/lwc-manual.tex @@ -37,12 +37,12 @@ \definetype[inlineTEX][option=tex, escape={$,$}, lines=hyphenated] \definetype[inlineLUA][option=lua, escape={$,$}, lines=hyphenated] -\input lwc-sample +\input lwc-manual-samples \startdocument[ title=lua-widow-control, author=Max Chernoff, - version=2.1.1, %%version + version=2.1.2, %%version github=https://github.com/gucci-on-fleek/lua-widow-control, ctan=https://www.ctan.org/pkg/lua-widow-control, ] @@ -621,7 +621,7 @@ same internal mechanisms, \lwc/ removes \waos/ between columns just as it does with \waos/ between pages. \Lwc/ is known to work with the \LaTeX{} class option \type{twocolumn} -and the two-column output routine from Chapter~23 of \cite[texbook]. +and the two-column output routine from Chapter~23 of \titlecite[texbook]. \subsection{Performance} @@ -648,11 +648,19 @@ commands. Specifically, if you give a line a penalty that matches either exactly as it would a \woo/. So while these commands won't break \lwc/, they are likely to lead to some unexpected behaviour. +\subsection{Grids} + +\Lwc/ is fully compatible with the grid snapping features of \ConTeXt{} \mkiv/ and \mkxl/. + +\subsection{Footnotes} + +If there are footnotes (or any other type of inline \tex{insert}) present in the moved line, \lwc/ will move both the \q{footnote mark} and the \q{footnote text} such that both are on the same page. However, this may lead to an odd blank space at the bottom of the page since \lwc/ needs to move both the line and its footnotes. Footnotes cause the same page-breaking issues in unmodified Plain~\TeX{} and \LaTeX{}, so this is mostly unavoidable. + \section{Stability} The documented interfaces of \lwc/ can be considered stable: I'm not planning on removing or modifying any existing options or commands in any way that would break documents. -However, \lwc/'s page breaking \emph{is} subject to change. I will attempt to keep page breaks the same wherever reasonable; however, I will rarely make modifications to the algorithm when I can improve the output quality. +However, \lwc/'s page breaking \emph{is} subject to change. I will attempt to keep page breaks the same whenever reasonable; however, I will \emph{rarely} make modifications to the algorithm when I can improve the output quality. Any such changes will be clearly noted in the release notes. \section{Short last lines} @@ -681,6 +689,8 @@ ultra-short~lines. \item \Lwc/ only attempts to expand paragraphs; it never attempts to shrink them. See the \emph{TUGboat}~article\cite[article] §15.3 for further discussion. \githubissue{33} + \item \Lwc/ can only expand paragraphs that fit completely on a page. This is unavoidable due to the one-page-at-a-time model: you can't modify the bottom half of a paragraph since its top half has already shipped out, and you can't expand the top half of a paragraph since that can't remove orphans. This only causes issues if your document has paragraphs so long that a page only has two half-paragraphs and zero whole paragraphs. + \item Sometimes a \woo/ cannot be eliminated because no paragraph has enough stretch. Sometimes this can be remediated by increasing \lwc/'s \estretch/; however, some pages just don't have @@ -701,7 +711,7 @@ ultra-short~lines. impossible to solve in a single pass. Very rarely would such a system remove widows or orphans that \lwc/ cannot. - \item If there is a footnote on the last line of the page with a \woo/, \lwc/ will sometimes move the \q{footnote mark} but not the \q{footnote text}, thus breaking up a footnote. \githubissue{26} + \item \Lwc/ does not move footnotes in \mkxl/ due to limitations with the \LuaMetaTeX{} engine. \stopitemize \section{Contributions} @@ -726,6 +736,12 @@ Please note that a compiled document is \bold{not} considered to be an \q{Execut ] \section[sec:implementation]{Implementation} +From here and until the end of this manual is the raw source code of \lwc/. This is primarily of interest to developers; most users need not read further. + +This code vaguely resembles the typical \LaTeX{} literate programming style, although I use extensive inline comments instead of arcane \sans{docstrip} macros. Hopefully this is useful as a reference for advanced \lwc/ users as well as anyone doing extensive node manipulation in \LuaTeX{}. + +If want to offer any improvements to the code below, please open an issue or a \acronym{PR} on \goto{GitHub}[url(projecturl)]. + \usemodule[scite] \setupbodyfont[10pt] \setuphead[subsection][ diff --git a/macros/luatex/generic/lua-widow-control/t-lua-widow-control.mkxl b/macros/luatex/generic/lua-widow-control/t-lua-widow-control.mkxl index 13637756ae..682e8d23c5 100644 --- a/macros/luatex/generic/lua-widow-control/t-lua-widow-control.mkxl +++ b/macros/luatex/generic/lua-widow-control/t-lua-widow-control.mkxl @@ -1,10 +1,10 @@ %D \module %D [ file=t-lua-widow-control, -%D version=2.1.1, %%version +%D version=2.1.2, %%version %D title=lua-widow-control, %D subtitle=\ConTeXt module for lua-widow-control, %D author=Max Chernoff, -%D date=2022-05-20, %%dashdate +%D date=2022-05-26, %%dashdate %D copyright=Max Chernoff, %D license=MPL-2.0+, %D url=https://github.com/gucci-on-fleek/lua-widow-control] diff --git a/macros/luatex/generic/lua-widow-control/tb133chernoff-widows.pdf b/macros/luatex/generic/lua-widow-control/tb133chernoff-widows.pdf Binary files differindex da1fbfa7e9..9c9de8bd02 100644 --- a/macros/luatex/generic/lua-widow-control/tb133chernoff-widows.pdf +++ b/macros/luatex/generic/lua-widow-control/tb133chernoff-widows.pdf diff --git a/macros/xetex/latex/bidi/README b/macros/xetex/latex/bidi/README index f54d2a2278..955449d560 100644 --- a/macros/xetex/latex/bidi/README +++ b/macros/xetex/latex/bidi/README @@ -1,6 +1,6 @@ ___________________ The bidi package -v37.0 +v37.1 This package provides a convenient interface for typesetting bidirectional texts in Plain TeX and LaTeX, using XeTeX engine. @@ -25,7 +25,7 @@ The announcements for the new releases of the package will also appear in the Discussions space under the Announcements category. -Current version release date: 2022/05/24 +Current version release date: 2022/05/27 ___________________________________________ Vafa Khalighi diff --git a/macros/xetex/latex/bidi/bidi-doc-intro.ltx b/macros/xetex/latex/bidi/bidi-doc-intro.ltx index 086bc4557a..fb1f37974a 100644 --- a/macros/xetex/latex/bidi/bidi-doc-intro.ltx +++ b/macros/xetex/latex/bidi/bidi-doc-intro.ltx @@ -20,7 +20,7 @@ If you use \textsf{bidi} package to write any input \TeX\ document, and then run * Copyright (c) 2007--2022 Vafa Khalighi * Copyright (c) 2018--2020 bidi-tex GitHub Organization * -* v37.0, 2022/05/24 +* v37.1, 2022/05/27 * * License: LaTeX Project Public License, version * 1.3c or higher (your choice) diff --git a/macros/xetex/latex/bidi/bidi-doc-title.ltx b/macros/xetex/latex/bidi/bidi-doc-title.ltx index e1b7a505fd..254c51d806 100644 --- a/macros/xetex/latex/bidi/bidi-doc-title.ltx +++ b/macros/xetex/latex/bidi/bidi-doc-title.ltx @@ -3,7 +3,7 @@ \begin{document} \title{\textcolor{blue!65!black}{\Huge The \textsf{bidi} \textit{Package}}\\ \LARGE Bidirectional typesetting in plain \TeX{} and \LaTeX, using \XeTeX{} engine\\[5pt] -\large User documentation for \textcolor{blue!65!black}{version 37.0}} +\large User documentation for \textcolor{blue!65!black}{version 37.1}} \author{Vafa Khalighi\thanks{Thanks to the bidi-tex GitHub organization consisted of David Carlisle and other members of the {\LaTeX} team to maintain the package in the period 2018--2020.}} \setcounter{page}{0} \thispagestyle{empty} @@ -13,7 +13,7 @@ \textcolor{blue!65!black}{\Huge The \textsf{bidi} \textit{Package}} \Large -User documentation for \textcolor{blue!65!black}{version 37.0}} +User documentation for \textcolor{blue!65!black}{version 37.1}} \vskip 0pt plus 3fill \textit{\large to my Master, \textbf{Ferdowsi The Great}} diff --git a/macros/xetex/latex/bidi/bidi-doc.ltx b/macros/xetex/latex/bidi/bidi-doc.ltx index 2e468609a0..2fa0627548 100644 --- a/macros/xetex/latex/bidi/bidi-doc.ltx +++ b/macros/xetex/latex/bidi/bidi-doc.ltx @@ -1,7 +1,7 @@ \documentclass{ltxdoc} \input{bidi-doc-preamble.ltx} \hypersetup{% - pdftitle={User documentation for version 37.0 of the bidi + pdftitle={User documentation for version 37.1 of the bidi package}% } \begin{document} diff --git a/macros/xetex/latex/bidi/bidi-doc.pdf b/macros/xetex/latex/bidi/bidi-doc.pdf Binary files differindex 0e2223f9a5..178336960c 100644 --- a/macros/xetex/latex/bidi/bidi-doc.pdf +++ b/macros/xetex/latex/bidi/bidi-doc.pdf diff --git a/macros/xetex/latex/bidi/bidi.dtx b/macros/xetex/latex/bidi/bidi.dtx index 793628e4fa..7cb0111153 100644 --- a/macros/xetex/latex/bidi/bidi.dtx +++ b/macros/xetex/latex/bidi/bidi.dtx @@ -5,7 +5,7 @@ %<*readme> ___________________ The bidi package -v37.0 +v37.1 This package provides a convenient interface for typesetting bidirectional texts in Plain TeX and LaTeX, using XeTeX engine. @@ -30,7 +30,7 @@ The announcements for the new releases of the package will also appear in the Discussions space under the Announcements category. -Current version release date: 2022/05/24 +Current version release date: 2022/05/27 ___________________________________________ Vafa Khalighi @@ -239,7 +239,7 @@ by Vafa Khalighi. \documentclass{ltxdoc} \input{bidi-doc-preamble.ltx} \hypersetup{% - pdftitle={Source documentation for version 37.0 of the bidi + pdftitle={Source documentation for version 37.1 of the bidi package}% } \EnableCrossrefs @@ -258,7 +258,7 @@ by Vafa Khalighi. % \GetFileInfo{\jobname.dtx} % \title{\textcolor{blue!65!black}{\Huge The \textsf{bidi} \textit{Package}}\\ %\LARGE Bidirectional typesetting in plain \TeX{} and \LaTeX, using \XeTeX{} engine\\[5pt] -%\large Source documentation for \textcolor{blue!65!black}{version 37.0}} +%\large Source documentation for \textcolor{blue!65!black}{version 37.1}} % \author{Vafa Khalighi\thanks{Thanks to the bidi-tex GitHub organization consisted of David Carlisle and other members of the {\LaTeX} team to maintain the package in the period 2018--2020.}} %\setcounter{page}{0} %\thispagestyle{empty} @@ -268,7 +268,7 @@ by Vafa Khalighi. %\textcolor{blue!65!black}{\Huge The \textsf{bidi} \textit{Package}} % %\Large -%Source documentation for \textcolor{blue!65!black}{version 37.0}} +%Source documentation for \textcolor{blue!65!black}{version 37.1}} % %\vskip 0pt plus 3fill %\textit{\large to my Master, \textbf{Ferdowsi The Great}} @@ -317,7 +317,7 @@ by Vafa Khalighi. %\iffalse %<*table> %\fi -%% \CheckSum{51690} +%% \CheckSum{52030} %% \CharacterTable %% {Upper-case \A\B\C\D\E\F\G\H\I\J\K\L\M\N\O\P\Q\R\S\T\U\V\W\X\Y\Z %% Lower-case \a\b\c\d\e\f\g\h\i\j\k\l\m\n\o\p\q\r\s\t\u\v\w\x\y\z @@ -2388,9 +2388,9 @@ by Vafa Khalighi. \fi } -\expandafter\let\csname beamer@@tmpop@subsection in toc@default\endcsname\relax +\bidi@csundef{beamer@@tmpop@subsection in toc@default} -\expandafter\let\csname beamer@@tmpop@subsubsection in toc@default\endcsname\relax +\bidi@csundef{beamer@@tmpop@subsubsection in toc@default} \defbeamertemplate*{subsection in toc}{default} {\leavevmode\bidi@beamer@leftskip=1.5em\inserttocsubsection\par} @@ -2407,11 +2407,11 @@ by Vafa Khalighi. \fi } -\expandafter\let\csname beamer@@tmpop@itemize item@default\endcsname\relax +\bidi@csundef{beamer@@tmpop@itemize item@default} -\expandafter\let\csname beamer@@tmpop@itemize subitem@default\endcsname\relax +\bidi@csundef{beamer@@tmpop@itemize subitem@default} -\expandafter\let\csname beamer@@tmpop@itemize subsubitem@default\endcsname\relax +\bidi@csundef{beamer@@tmpop@itemize subsubitem@default} \defbeamertemplate*{itemize item}{default}{\usebeamerfont*{itemize item}\raise1.25pt\hbox{\donotcoloroutermaths\bidi@beamer@blacktriangle}} \defbeamertemplate*{itemize subitem}{default}{\usebeamerfont*{itemize subitem}\raise1.5pt\hbox{\donotcoloroutermaths\bidi@beamer@blacktriangle}} @@ -2464,8 +2464,8 @@ by Vafa Khalighi. % \section{File \texttt{bidi.sty}} % \begin{macrocode} \NeedsTeXFormat{LaTeX2e} -\def\bididate{2022/05/24} -\def\bidi@version{37.0} +\def\bididate{2022/05/27} +\def\bidi@version{37.1} \ProvidesPackage{bidi}[\bididate\space v\bidi@version\space Bidirectional typesetting in plain TeX and LaTeX] \edef\@bidi@info{****************************************************^^J% @@ -2553,7 +2553,7 @@ Bidirectional typesetting in plain TeX and LaTeX] } \newcommand*{\@bidi@pdfcustomproperties}{% This is the bidi package, Version \bidi@version, - Released May 24, 2022. + Released May 27, 2022. } \newcommand*{\bidi@beamer@ragged}{} \@RTLmainfalse @@ -2797,9 +2797,9 @@ Bidirectional typesetting in plain TeX and LaTeX] %\fi % \section{File \texttt{bidi.tex}} % \begin{macrocode} -\def\bididate{2022/05/24} +\def\bididate{2022/05/27} \catcode`@=11 -\def\bidi@version{37.0} +\def\bidi@version{37.1} \let \protect \relax \newlinechar`\^^J \edef\@bidi@info{****************************************************^^J% @@ -2901,7 +2901,7 @@ Bidirectional typesetting in plain TeX and LaTeX] } \def\@bidi@pdfcustomproperties{% This is the bidi package, Version \bidi@version, - Released May 24, 2022. + Released May 27, 2022. } \def\bidi@pdfm@mark{% \special{pdf:% @@ -4438,6 +4438,9 @@ ill-advised in the old versions of bidi package.% \bidi@newrobustcmd{\bidi@undef}[1]{\let#1\bidi@undefined} +\bidi@newrobustcmd{\bidi@csundef}[1]{% + \expandafter\let\csname #1\endcsname\bidi@undefined +} \let\bidi@dbg@trce\@gobble \let\bidi@dbg@init\@gobble @@ -18818,7 +18821,7 @@ to\rtside{\box\bslice\hfil}} %\fi % \section{File \texttt{scrartcl-xetex-bidi.def}} % \begin{macrocode} -\ProvidesFile{scrartcl-xetex-bidi.def}[2022/05/22 v1.2 bidi adaptations for scrartcl class for XeTeX engine] +\ProvidesFile{scrartcl-xetex-bidi.def}[2022/05/27 v1.3 bidi adaptations for scrartcl class for XeTeX engine] \newcommand{\scr@saved@LTRfootnotetext}{} \newcommand{\scr@saved@RTLfootnotetext}{} \let\scr@saved@footnotetext\@footnotetext @@ -19327,6 +19330,89 @@ to\rtside{\box\bslice\hfil}} \fi } +\renewcommand{\scr@@makesinglelinecaption}[3]{% + {\usekomafont{caption}{\strut\ignorespaces + #1{{\usekomafont{captionlabel}{#2\captionformat}}}% + \ignorespaces #3\unskip}}% +} + +\renewcommand{\scr@makemultilinecaption}[3]{% + {% + \usefontofkomafont{caption}{% + \hb@xt@\hsize{% + \cap@left + \vbox{\hsize\cap@width\linewidth\hsize + \scr@ifundefinedorrelax{@captype}{% + \ClassWarning{\KOMAClassName}{% + `\string\caption' outside float.\MessageBreak + Seems you are using `\string\caption' outside a + float.\MessageBreak + Maybe you are using a package that uses + `\string\@makecaption'\MessageBreak + without setting `\string\@captype' before.\MessageBreak + Because I cannot detect the caption type, I'm using\MessageBreak + the empty one.% + }% + \let\@captype\@empty + }{}% + \scr@ifundefinedorrelax{ragged\@captype caption}{\raggedcaption}{% + \@nameuse{ragged\@captype caption}% + }% + \ifdofullc@p + \@hangfrom{% + \strut + \scr@smashdp{% + #1{% + \usekomafont{caption}{% + \usekomafont{captionlabel}{#2\captionformat}% + }% + }% + }% + }% + {{\usekomafont{caption}{#3\scr@endstrut}}\par}% + \else + \ifdim\cap@indent<\z@ + \strut + #1{% + {\usekomafont{caption}{% + \usekomafont{captionlabel}{#2\captionformat}% + }% + }\par}% + \noindent{{\usekomafont{caption}{% + \hspace*{-\cap@indent}#3\scr@endstrut + }}\par}% + \else + \if@capbreak + \strut + #1{% + {\usekomafont{caption}{% + \usekomafont{captionlabel}{#2\captionformat}% + }% + }\par}% + \noindent\@hangfrom{\hspace*{\cap@indent}}% + {{\usekomafont{caption}{#3\scr@endstrut}}\par}% + \else + \@hangfrom{\scr@smashdp\strut\hspace*{\cap@indent}}% + {% + \hspace{-\cap@indent}% + \scr@smashdp{% + #1{\usekomafont{caption}{% + \usekomafont{captionlabel}{#2\captionformat}% + }% + }% + }% + {{\usekomafont{caption}{#3\scr@endstrut}}\par}% + }% + \fi + \fi + \fi + }% + \cap@right + }\par\kern\dp\strutbox + }% + }\kern-\dp\strutbox +} + \bidi@AfterFile{longtable-xetex-bidi.def}{% \if@komalongtable \renewcommand{\LT@makecaption}[3]{% @@ -19381,7 +19467,7 @@ to\rtside{\box\bslice\hfil}} \csname scr@tso@#1@entryformat\endcsname[1]{\usekomafont{partentry}{##1}}% \expandafter\renewcommand% \csname scr@tso@#1@pagenumberformat\endcsname[1]{% - \usekomafont{partentry}{\usekomafont{partentrypagenumber}{##1}}}% + {\usekomafont{partentry}{\usekomafont{partentrypagenumber}{##1}}}}% \expandafter\renewcommand\expandafter*% \csname scr@tso@#1@beforeskip\endcsname{2.25em \@plus\p@}% } @@ -19399,7 +19485,7 @@ to\rtside{\box\bslice\hfil}} \expandafter\renewcommand% \csname scr@tso@#1@entryformat\endcsname{\usekomafont{sectionentry}}% \expandafter\renewcommand% - \csname scr@tso@#1@pagenumberformat\endcsname[1]{\usekomafont{sectionentry}{\usekomafont{sectionentrypagenumber}{##1}}}% + \csname scr@tso@#1@pagenumberformat\endcsname[1]{{\usekomafont{sectionentry}{\usekomafont{sectionentrypagenumber}{##1}}}}% \expandafter\renewcommand\expandafter*% \csname scr@tso@#1@beforeskip\endcsname{1.0em \@plus\p@}% } @@ -19408,6 +19494,9 @@ to\rtside{\box\bslice\hfil}} \TOCEntryStyleStartInitCode{subsection}{% \expandafter\renewcommand% \csname scr@tso@#1@linefill\endcsname{\TOCLineLeaderFill\relax}% + \expandafter\renewcommand% + \csname scr@tso@#1@pagenumberformat\endcsname[1]{% + {\normalfont\normalcolor ##1}}% } \CloneTOCEntryStyle{subsection}{default} @@ -19478,7 +19567,7 @@ to\rtside{\box\bslice\hfil}} %\fi % \section{File \texttt{scrbook-xetex-bidi.def}} % \begin{macrocode} -\ProvidesFile{scrbook-xetex-bidi.def}[2022/05/22 v1.2 bidi adaptations for scrbook class for XeTeX engine] +\ProvidesFile{scrbook-xetex-bidi.def}[2022/05/27 v1.3 bidi adaptations for scrbook class for XeTeX engine] \newcommand{\scr@saved@LTRfootnotetext}{} \newcommand{\scr@saved@RTLfootnotetext}{} \let\scr@saved@footnotetext\@footnotetext @@ -20008,6 +20097,91 @@ to\rtside{\box\bslice\hfil}} \vskip\belowcaptionskip \fi } + +\renewcommand{\scr@@makesinglelinecaption}[3]{% + {\usekomafont{caption}{\strut\ignorespaces + #1{{\usekomafont{captionlabel}{#2\captionformat}}}% + \ignorespaces #3\unskip}}% +} + +\renewcommand{\scr@makemultilinecaption}[3]{% + {% + \usefontofkomafont{caption}{% + \hb@xt@\hsize{% + \cap@left + \vbox{\hsize\cap@width\linewidth\hsize + \scr@ifundefinedorrelax{@captype}{% + \ClassWarning{\KOMAClassName}{% + `\string\caption' outside float.\MessageBreak + Seems you are using `\string\caption' outside a + float.\MessageBreak + Maybe you are using a package that uses + `\string\@makecaption'\MessageBreak + without setting `\string\@captype' before.\MessageBreak + Because I cannot detect the caption type, I'm using\MessageBreak + the empty one.% + }% + \let\@captype\@empty + }{}% + \scr@ifundefinedorrelax{ragged\@captype caption}{\raggedcaption}{% + \@nameuse{ragged\@captype caption}% + }% + \ifdofullc@p + \@hangfrom{% + \strut + \scr@smashdp{% + #1{% + \usekomafont{caption}{% + \usekomafont{captionlabel}{#2\captionformat}% + }% + }% + }% + }% + {{\usekomafont{caption}{#3\scr@endstrut}}\par}% + \else + \ifdim\cap@indent<\z@ + \strut + #1{% + {\usekomafont{caption}{% + \usekomafont{captionlabel}{#2\captionformat}% + }% + }\par}% + \noindent{{\usekomafont{caption}{% + \hspace*{-\cap@indent}#3\scr@endstrut + }}\par}% + \else + \if@capbreak + \strut + #1{% + {\usekomafont{caption}{% + \usekomafont{captionlabel}{#2\captionformat}% + } + }\par}% + \noindent\@hangfrom{\hspace*{\cap@indent}}% + {{\usekomafont{caption}{#3\scr@endstrut}}\par}% + \else + \@hangfrom{\scr@smashdp\strut\hspace*{\cap@indent}}% + {% + \hspace{-\cap@indent}% + \scr@smashdp{% + #1{\usekomafont{caption}{% + \usekomafont{captionlabel}{#2\captionformat}% + }% + }% + }% + {{\usekomafont{caption}{#3\scr@endstrut}}\par}% + }% + \fi + \fi + \fi + }% + \cap@right + }\par\kern\dp\strutbox + }% + }\kern-\dp\strutbox +} + + \bidi@AfterFile{longtable-xetex-bidi.def}{% \if@komalongtable \renewcommand{\LT@makecaption}[3]{% @@ -20062,7 +20236,7 @@ to\rtside{\box\bslice\hfil}} \csname scr@tso@#1@entryformat\endcsname[1]{\usekomafont{partentry}{##1}}% \expandafter\renewcommand% \csname scr@tso@#1@pagenumberformat\endcsname[1]{% - \usekomafont{partentry}{\usekomafont{partentrypagenumber}{##1}}}% + {\usekomafont{partentry}{\usekomafont{partentrypagenumber}{##1}}}}% \expandafter\renewcommand\expandafter*% \csname scr@tso@#1@beforeskip\endcsname{2.25em \@plus\p@}% } @@ -20081,7 +20255,7 @@ to\rtside{\box\bslice\hfil}} \csname scr@tso@#1@entryformat\endcsname{\usekomafont{chapterentry}}% \expandafter\renewcommand% \csname scr@tso@#1@pagenumberformat\endcsname[1]{% - \usekomafont{chapterentry}{\usekomafont{chapterentrypagenumber}{##1}}}% + {\usekomafont{chapterentry}{\usekomafont{chapterentrypagenumber}{##1}}}}% \expandafter\renewcommand\expandafter*% \csname scr@tso@#1@beforeskip\endcsname{1.0em \@plus\p@}% } @@ -20090,6 +20264,9 @@ to\rtside{\box\bslice\hfil}} \TOCEntryStyleStartInitCode{section}{% \expandafter\renewcommand% \csname scr@tso@#1@linefill\endcsname{\TOCLineLeaderFill\relax}% + \expandafter\renewcommand% + \csname scr@tso@#1@pagenumberformat\endcsname[1]{% + {\normalfont\normalcolor ##1}}% } \CloneTOCEntryStyle{section}{default} @@ -20170,7 +20347,7 @@ to\rtside{\box\bslice\hfil}} %\fi % \section{File \texttt{scrreprt-xetex-bidi.def}} % \begin{macrocode} -\ProvidesFile{scrreprt-xetex-bidi.def}[2022/05/22 v1.2 bidi adaptations for scrreprt class for XeTeX engine] +\ProvidesFile{scrreprt-xetex-bidi.def}[2022/05/27 v1.3 bidi adaptations for scrreprt class for XeTeX engine] \newcommand{\scr@saved@LTRfootnotetext}{} \newcommand{\scr@saved@RTLfootnotetext}{} \let\scr@saved@footnotetext\@footnotetext @@ -20692,6 +20869,91 @@ to\rtside{\box\bslice\hfil}} \vskip\belowcaptionskip \fi } + +\renewcommand{\scr@@makesinglelinecaption}[3]{% + {\usekomafont{caption}{\strut\ignorespaces + #1{{\usekomafont{captionlabel}{#2\captionformat}}}% + \ignorespaces #3\unskip}}% +} + +\renewcommand{\scr@makemultilinecaption}[3]{% + {% + \usefontofkomafont{caption}{% + \hb@xt@\hsize{% + \cap@left + \vbox{\hsize\cap@width\linewidth\hsize + \scr@ifundefinedorrelax{@captype}{% + \ClassWarning{\KOMAClassName}{% + `\string\caption' outside float.\MessageBreak + Seems you are using `\string\caption' outside a + float.\MessageBreak + Maybe you are using a package that uses + `\string\@makecaption'\MessageBreak + without setting `\string\@captype' before.\MessageBreak + Because I cannot detect the caption type, I'm using\MessageBreak + the empty one.% + }% + \let\@captype\@empty + }{}% + \scr@ifundefinedorrelax{ragged\@captype caption}{\raggedcaption}{% + \@nameuse{ragged\@captype caption}% + }% + \ifdofullc@p + \@hangfrom{% + \strut + \scr@smashdp{% + #1{% + \usekomafont{caption}{% + \usekomafont{captionlabel}{#2\captionformat}% + }% + }% + }% + }% + {{\usekomafont{caption}{#3\scr@endstrut}}\par}% + \else + \ifdim\cap@indent<\z@ + \strut + #1{% + {\usekomafont{caption}{% + \usekomafont{captionlabel}{#2\captionformat}% + }% + }\par}% + \noindent{{\usekomafont{caption}{% + \hspace*{-\cap@indent}#3\scr@endstrut + }}\par}% + \else + \if@capbreak + \strut + #1{% + {\usekomafont{caption}{% + \usekomafont{captionlabel}{#2\captionformat}% + }% + }\par}% + \noindent\@hangfrom{\hspace*{\cap@indent}}% + {{\usekomafont{caption}{#3\scr@endstrut}}\par}% + \else + \@hangfrom{\scr@smashdp\strut\hspace*{\cap@indent}}% + {% + \hspace{-\cap@indent}% + \scr@smashdp{% + #1{\usekomafont{caption}{% + \usekomafont{captionlabel}{#2\captionformat}% + }% + }% + }% + {{\usekomafont{caption}{#3\scr@endstrut}}\par}% + }% + \fi + \fi + \fi + }% + \cap@right + }\par\kern\dp\strutbox + }% + }\kern-\dp\strutbox +} + + \bidi@AfterFile{longtable-xetex-bidi.def}{% \if@komalongtable \renewcommand{\LT@makecaption}[3]{% @@ -20746,7 +21008,7 @@ to\rtside{\box\bslice\hfil}} \csname scr@tso@#1@entryformat\endcsname[1]{\usekomafont{partentry}{##1}}% \expandafter\renewcommand% \csname scr@tso@#1@pagenumberformat\endcsname[1]{% - \usekomafont{partentry}{\usekomafont{partentrypagenumber}{##1}}}% + {\usekomafont{partentry}{\usekomafont{partentrypagenumber}{##1}}}}% \expandafter\renewcommand\expandafter*% \csname scr@tso@#1@beforeskip\endcsname{2.25em \@plus\p@}% } @@ -20765,7 +21027,7 @@ to\rtside{\box\bslice\hfil}} \csname scr@tso@#1@entryformat\endcsname{\usekomafont{chapterentry}}% \expandafter\renewcommand% \csname scr@tso@#1@pagenumberformat\endcsname[1]{% - \usekomafont{chapterentry}{\usekomafont{chapterentrypagenumber}{##1}}}% + {\usekomafont{chapterentry}{\usekomafont{chapterentrypagenumber}{##1}}}}% \expandafter\renewcommand\expandafter*% \csname scr@tso@#1@beforeskip\endcsname{1.0em \@plus\p@}% } @@ -20774,6 +21036,9 @@ to\rtside{\box\bslice\hfil}} \TOCEntryStyleStartInitCode{section}{% \expandafter\renewcommand% \csname scr@tso@#1@linefill\endcsname{\TOCLineLeaderFill\relax}% + \expandafter\renewcommand% + \csname scr@tso@#1@pagenumberformat\endcsname[1]{% + {\normalfont\normalcolor ##1}}% } \CloneTOCEntryStyle{section}{default} @@ -21688,7 +21953,7 @@ to\rtside{\box\bslice\hfil}} %\fi % \section{File \texttt{tocbasic-xetex-bidi.def}} % \begin{macrocode} -\ProvidesFile{tocbasic-xetex-bidi.def}[2022/05/22 v0.3 bidi adaptations for tocbasic package for XeTeX engine] +\ProvidesFile{tocbasic-xetex-bidi.def}[2022/05/27 v0.4 bidi adaptations for tocbasic package for XeTeX engine] \bidi@undef\scr@dte@def@l@undottedtocline \bidi@undef\scr@dte@undottedtocline@init \DeclareTOCEntryStyle{undottedtocline}[{% @@ -22080,7 +22345,7 @@ to\rtside{\box\bslice\hfil}} {\parfillskip\@flushglue\par}\nobreak\hskip-\bidi@leftskip }{}% }% - \@nameuse{scr@tso@#1@entryformat}{% + {\@nameuse{scr@tso@#1@entryformat}{% #2% \nobreak {% @@ -22092,8 +22357,7 @@ to\rtside{\box\bslice\hfil}} \@nameuse{scr@tso@#1@pagenumberformat}{#3}% }% }% - \@par - }% + }}\@par }% }% \tocbasic@SetPenaltyByTOCLevel{#1}% @@ -22377,20 +22641,20 @@ to\rtside{\box\bslice\hfil}} \expandafter\renewcommand% \csname scr@tso@#1@numberformat\endcsname[1]{##1}% \Ifstr{#1}{part}{% - \expandafter\renewcommand% + \expandafter\providecommand% \csname scr@tso@#1@entryformat\endcsname[1]{\large\bfseries ##1}% }{% \Ifstr{#1}{chapter}{% - \expandafter\renewcommand% + \expandafter\providecommand% \csname scr@tso@#1@entryformat\endcsname[1]{\bfseries ##1}% }{% \scr@ifundefinedorrelax{l@chapter}{% \Ifstr{#1}{section}{% - \expandafter\renewcommand% + \expandafter\providecommand% \csname scr@tso@#1@entryformat\endcsname[1]{\bfseries ##1}% }{}% }{}% - \expandafter\renewcommand% + \expandafter\providecommand% \csname scr@tso@#1@entryformat\endcsname[1]{##1}% }% }% @@ -22415,20 +22679,20 @@ to\rtside{\box\bslice\hfil}} }{}% \expandafter\renewcommand\csname Ifscr@tso@#1@raggedpage\endcsname[2]{##2}% \Ifstr{#1}{part}{% - \expandafter\renewcommand% + \expandafter\providecommand% \csname scr@tso@#1@pagenumberformat\endcsname[1]{\large\bfseries ##1}% }{% \Ifstr{#1}{chapter}{% - \expandafter\renewcommand% + \expandafter\providecommand% \csname scr@tso@#1@pagenumberformat\endcsname[1]{\bfseries ##1}% }{% \scr@ifundefinedorrelax{l@chapter}{% \Ifstr{#1}{section}{% - \expandafter\renewcommand% + \expandafter\providecommand% \csname scr@tso@#1@pagenumberformat\endcsname[1]{\bfseries ##1}% }{}% }{}% - \expandafter\renewcommand% + \expandafter\providecommand% \csname scr@tso@#1@pagenumberformat\endcsname[1]{% {\normalfont\normalcolor ##1}}% }% @@ -22450,6 +22714,15 @@ to\rtside{\box\bslice\hfil}} }]{% \scr@dte@tocline{#1}{##1}{##2}% } + +\renewcommand*{\scr@tso@figure@pagenumberformat}[1]{% + {\normalfont\normalcolor #1}% + } + +\renewcommand*{\scr@tso@table@pagenumberformat}[1]{% + {\normalfont\normalcolor #1}% + } + % \end{macrocode} % \iffalse %</tocbasic-xetex-bidi.def> @@ -23040,7 +23313,7 @@ to\rtside{\box\bslice\hfil}} %\fi % \section{File \texttt{latex-xetex-bidi.def}} % \begin{macrocode} -\ProvidesFile{latex-xetex-bidi.def}[2022/02/18 v1.4.0 bidi adaptations for `latex.ltx' for XeTeX engine] +\ProvidesFile{latex-xetex-bidi.def}[2022/05/27 v1.4.1 bidi adaptations for `latex.ltx' for XeTeX engine] \newcommand{\TeXXeTOn}{\TeXXeTstate\@ne} \newcommand{\TeXXeTOff}{\TeXXeTstate\z@} \TeXXeTOn @@ -23848,6 +24121,7 @@ $\@@underline{\hbox{#1}}\m@th$\relax\fi\fi} \if@bidi@csundef{@idxitem}{}{% \renewcommand\@idxitem{\par\hangindent \if@RTL-\fi40\p@}% } +\@ifpackageloaded{tocbasic}{\input{tocbasic-xetex-bidi.def}}{} \@ifclassloaded{article}{\@ifclassloaded{svmult}{}{\input{article-xetex-bidi.def}}}{} \@ifclassloaded{extarticle}{\input{extarticle-xetex-bidi.def}}{} \@ifclassloaded{artikel1}{\input{artikel1-xetex-bidi.def}}{} @@ -23986,7 +24260,6 @@ $\@@underline{\hbox{#1}}\m@th$\relax\fi\fi} \@ifpackageloaded{tabls}{\input{tabls-xetex-bidi.def}}{} \@ifpackageloaded{tabulary}{\input{tabulary-xetex-bidi.def}}{} \@ifpackageloaded{tcolorbox}{\input{tcolorbox-xetex-bidi.def}}{} -\@ifpackageloaded{tocbasic}{\input{tocbasic-xetex-bidi.def}}{} \@ifpackageloaded{tocstyle}{\input{tocstyle-xetex-bidi.def}}{} \@ifpackageloaded{tocbibind}{\@ifclassloaded{memoir}{}{\input{tocbibind-xetex-bidi.def}}}{} \@ifpackageloaded{tocloft}{\@ifclassloaded{memoir}{}{\input{tocloft-xetex-bidi.def}}}{} diff --git a/macros/xetex/latex/bidi/bidi.pdf b/macros/xetex/latex/bidi/bidi.pdf Binary files differindex f84f7aa0e8..51cf1a35c7 100644 --- a/macros/xetex/latex/bidi/bidi.pdf +++ b/macros/xetex/latex/bidi/bidi.pdf |