%% %% This is file `pbalance.sty', %% generated with the docstrip utility. %% %% The original source files were: %% %% pbalance.dtx (with options: `package') %% Copyright 2020-2022 Nelson Lago %% %% This work may be distributed and/or modified under the conditions of the %% LaTeX Project Public License, either version 1.3c of this license or (at %% your option) any later version. The latest version of this license can be %% found at http://www.latex-project.org/lppl.txt and version 1.3 or later %% is part of all distributions of LaTeX version 2005/12/01 or later. %% %% This work has the LPPL maintenance status `maintained'. %% %% The Current Maintainer of this work is Nelson Lago . %% \NeedsTeXFormat{LaTeX2e}[2015/01/01] \ProvidesPackage{pbalance}[2022/07/28 v1.4.0 Poor man's balance] \RequirePackage{etoolbox} \RequirePackage{expl3} \RequirePackage{atbegshi} % Manipulate the page output routine \RequirePackage{atveryend} % Write to the aux file after processing ends \RequirePackage{zref-abspage} % Figure out the current page \RequirePackage{filehook} % Patch other packages that modify the output routine \newtoggle{@PBpatchFailed} \providetoggle{@PBstabilized} \providetoggle{@PBimpossible} \newtoggle{@PBnoBalancePackage} \DeclareOption{safe}{\toggletrue{@PBnoBalancePackage}} \newtoggle{@PBdraft} \DeclareOption{draft}{\toggletrue{@PBdraft}} \ProcessOptions\relax \AtEndPreamble{\@ifpackageloaded{lineno}{\toggletrue{@PBnoBalancePackage}}{}} \@ifpackageloaded{lineno}{\toggletrue{@PBnoBalancePackage}}{} \AtEndOfPackage{ \ifboolexpr{togl {@PBdraft} or togl {@PBnoBalancePackage}} {} {\RequirePackage{balance}} } \gdef \@reinserts{% \ifvbox\@kludgeins\insert\@kludgeins {\unvbox\@kludgeins}\fi \ifvoid\footins\else\insert\footins{\unvbox\footins}\fi } \AtBeginDocument{ \@PBifShouldBalanceNascentPage {\@PBStartBalancing} {} } \AtBeginShipout{ \@PBifShouldBalanceNascentPage {\@PBStartBalancing} {} } \newcommand\@PBifShouldBalanceNascentPage[2]{ \ifboolexpr { togl {@PBdraft} or not togl {@PBstabilized} or test {\ifdefvoid{\@PBprevLastPage}} } {#2} { % abspage refers to the finished page, not the nascent page \ifnumcomp{\@PBprevLastPage - 1}{=}{\value{abspage}} {#1} {#2} } } \newcommand\shrinkLastPage[1]{\dimgdef\@PBslack{#1}} \newcommand\balancePageNum[1]{\gdef\@PBthepage{#1}} \newcommand\nopbalance{\toggletrue{@PBdraft}} \newcommand\@PBStartBalancing{ \ifdefvoid{\@PBslack} {\@PBautomaticBalance} { \PackageInfo{pbalance}{Automatic mode disabled, doing manual adjustment} \@PBshrinkPage } } \newcommand\@PBautomaticBalance{ \@PBifBalancePossible { \@PBifBalancePkgPossible {\balance} % easy peasy, use the balance package { \@PBifPoorBalancePossible { % use the poor man's balance algorithm \@PBcalculateShrinkage \@PBshrinkPage } {\PackageInfo{pbalance}{Page "naturally" balanced, doing nothing}} } } {\PackageInfo{pbalance}{Float-only column at last page, doing nothing}} } \newcommand\@PBcalculateShrinkage{ % Let's use some shorter names, please \dimdef\@PBtmpH{\csuse{@PBunbalpg\@Roman{\@PBprevLastPage}Height}} \dimdef\@PBtmpUL{\csuse{@PBunbalpg\@Roman{\@PBprevLastPage}UsedLeft}} \dimdef\@PBtmpUR{\csuse{@PBunbalpg\@Roman{\@PBprevLastPage}UsedRight}} \dimdef\@PBtmpLH{\csuse{@PBunbalpg\@Roman{\@PBprevLastPage}LeftHeight}} \dimdef\@PBtmpLFH{\csuse{@PBunbalpg\@Roman{\@PBprevLastPage}LeftFloatsHeight}} \dimdef\@PBtmpRH{\csuse{@PBunbalpg\@Roman{\@PBprevLastPage}RightHeight}} \dimdef\@PBtmpRFH{\csuse{@PBunbalpg\@Roman{\@PBprevLastPage}RightFloatsHeight}} % Figure out the amount of unused space on the last page (both % cols); we will reduce the first column height by half that amount \dimgdef\@PBslack{\@PBtmpH *2 - \@PBtmpUL - \@PBtmpUR} \dimgdef\@PBslack{\@PBslack /2} % Actually, I lied. While half, as said above, sometimes yields % perfectly balanced columns, that is often not the case (the page % may have an odd number of lines, or textheight may not be a % multiple of baselineskip, or there may be glues etc.). Sometimes % the difference is very small, less than a line, which does not % look good at all, and sometimes the right column is slightly % higher than the left, which is also not ideal. Here, we force % the left column to always be somewhat taller than the left; the % result is often ``better''. \dimgdef\@PBslack{\@PBslack - 1.5\baselineskip} % If necessary, reduce @PBslack to prevent LaTeX from adding a page \@PBsafetyCheck } \def\@PBshrinkPage{ % Zero obviously means ``do nothing''. We could % ``enlarge'' by 0, but IDK, maybe that would % trigger another LaTeX pass or something. \ifdimcomp{\@PBslack}{=}{0pt} {} { % Shrink the first column \enlargethispage{-\@PBslack} % Modify the behavior of \@outputdblcol % to raise footnotes in the second column. \global\let\@PBbalanceSecondColumn\@PBrealBalanceSecondColumn } } \pretocmd{\@outputdblcol}{\@PBbalanceSecondColumn} {} { \toggletrue{@PBpatchFailed} \PackageWarningNoLine{pbalance}{Patch failed (@outputdblcol)} } \let\@PBbalanceSecondColumn\relax \def\@PBrealBalanceSecondColumn{ \if@firstcolumn % Affects the next column \global\let\@PBorigtextbottom\@textbottom \gdef\@textbottom{\vskip \@PBslack plus .0001fil minus 10000fill\relax \@PBorigtextbottom} \else % Back to normal before starting the next page \global\let\@textbottom\@PBorigtextbottom \global\let\@PBbalanceSecondColumn\relax \fi } \newcommand\@PBifmidfloat[2]{\ifx\@midlist\empty #2\else #1\fi} \newcommand\@PBiftopfloat[2]{\ifx\@toplist\empty #2\else #1\fi} \newcommand\@PBifbotfloat[2]{\ifx\@botlist\empty #2\else #1\fi} \newcommand\@PBiffootnote[2]{\ifvoid\footins #2\else #1\fi} \newcommand\@PBiffloatcol[2]{\if@fcolmade #1\else #2\fi} \newcommand\@PBifdbltopfloat[2]{\ifx\@dbltoplist\empty #2\else #1\fi} \newcommand\@PBifdblbotfloat[2]{% \ifdefvoid{\@dblbotlist} {#2} {\ifx\@dblbotlist\empty #2\else #1\fi}% } \newtoggle{@PBtmpHasFootnotes} \newtoggle{@PBtmpHasFloats} \newtoggle{@PBtmpHasMarginpars} \def\@PBcollectPageInfo{ \ifboolexpr { test {\@PBifmidfloat} or test {\@PBiftopfloat} or test {\@PBifbotfloat} or test {\@PBifdblbotfloat} or test {\@PBifdbltopfloat} } {\global\toggletrue{@PBtmpHasFloats}} {} \@PBiffootnote {\global\toggletrue{@PBtmpHasFootnotes}} {} } \pretocmd{\@makecol}{\@PBcollectPageInfo} {} { \toggletrue{@PBpatchFailed} \PackageWarningNoLine{pbalance}{Patch failed (@makecol)} } \pretocmd{\@addmarginpar}{\global\toggletrue{@PBtmpHasMarginpars}} {} { \toggletrue{@PBpatchFailed} \PackageWarningNoLine{pbalance}{Patch failed (@addmarginpar)} } \ExplSyntaxOn \seq_gclear_new:N \@PBtmpLeftFloatHeights \seq_gclear_new:N \@PBtmpLeftFloatSpacesBelow \def\@PBcollectFloatHeights{ \if@firstcolumn \dimdef\@PBfloatHeight{\@textfloatsheight - \@PBtmpHeightBefore} \ifdimcomp{\@PBfloatHeight}{>}{0pt} { \global\seq_put_right:NV \@PBtmpLeftFloatHeights \@PBfloatHeight \dimdef\@PBspaceBelow{\@colroom - \@pageht - \@PBfloatHeight} % within \@addtocurcol, \@pageht corresponds to all % currently used space in the column, including footnotes % (check \@specialoutput in the LaTeX kernel). Let's % exclude the footnotes, we only want the space below % the float. \ifvoid\footins\else \dimdef\@PBspaceBelow{\@PBspaceBelow + \ht\footins + \skip\footins + \dp\footins} \fi % No idea why, but this can happen; maybe because of the page depth? \ifdimcomp{\@PBspaceBelow}{<}{0pt} {\dimdef\@PBspaceBelow{0pt}} {} \global\seq_put_right:NV \@PBtmpLeftFloatSpacesBelow \@PBspaceBelow } {} \fi } \ExplSyntaxOff \pretocmd{\@addtocurcol}{\dimgdef{\@PBtmpHeightBefore}{\@textfloatsheight}} {} { \toggletrue{@PBpatchFailed} \PackageWarningNoLine{pbalance}{Patch failed (@addtocurcol)} } \apptocmd{\@addtocurcol}{\@PBcollectFloatHeights} {} { \toggletrue{@PBpatchFailed} \PackageWarningNoLine{pbalance}{Patch failed (@addtocurcol)} } \newtoggle{@PBtmpHasFloatcol} \newtoggle{@PBtmpFloatcolBoth} \newcommand\@PBcollectColumnUsedHeight{ \setbox\@tempboxa=\vbox{\unvcopy\@outputbox\unskip\unskip} \dimgdef\@PBtmpColumnUsedHeight{\ht\@tempboxa\relax} } \apptocmd{\@makecol}{\@PBcollectColumnUsedHeight} {} { \toggletrue{@PBpatchFailed} \PackageWarningNoLine{pbalance}{Patch failed (@makecol)} } % Let's make sure our patched code actually runs. We only switch this % to true after we verify that the patches in @makecol and @outputdblcol % are actually executed when the page is built (therefore, after the % preamble has ended). \newtoggle{@PBpatchStillActive} \newcommand\@PBcollectColumnInfo{ \iftoggle{@PBpatchFailed} {} { \ifdefvoid{\@PBtmpColumnUsedHeight} {} % the patch to @makecol is not active, so we do nothing here { % the patch to @makecol is active and so is ours, we're good to go! \global\toggletrue{@PBpatchStillActive} \@PBreallyCollectColumnInfo } } } \newcommand\@PBreallyCollectColumnInfo{ \if@firstcolumn \@PBiffloatcol {\global\toggletrue{@PBtmpHasFloatcol}} {} % Available vertical space excluding % dblfloats; the same for both columns \dimgdef\@PBtmpHeight{\@colht} % Available vertical space excluding top/bottom floats \dimgdef\@PBtmpLeftHeight{\@colroom} % Space used by \texttt{here} floats \dimgdef\@PBtmpLeftFloatsHeight{\@textfloatsheight} \dimgdef\@PBtmpUsedLeft{\@PBtmpColumnUsedHeight} \else \@PBiffloatcol { \iftoggle{@PBtmpHasFloatcol} {\global\toggletrue{@PBtmpFloatcolBoth}} {\global\toggletrue{@PBtmpHasFloatcol}} } {} \dimgdef\@PBtmpRightHeight{\@colroom} \dimgdef\@PBtmpRightFloatsHeight{\@textfloatsheight} \dimgdef\@PBtmpUsedRight{\@PBtmpColumnUsedHeight} \fi } \pretocmd{\@outputdblcol}{\@PBcollectColumnInfo} {} { \toggletrue{@PBpatchFailed} \PackageWarningNoLine{pbalance}{Patch failed (@outputdblcol)} } \ifboolexpr{togl {@PBdraft} or togl {@PBnoBalancePackage}} {} { \AtEndOfPackage{ \pretocmd{\@BAdblcol}{\@PBcollectColumnInfo} {} { \toggletrue{@PBpatchFailed} \PackageWarningNoLine{pbalance}{Patch failed (@BAdblcol)} } % Revert \balance before next page if it exists (it might be a float page) \patchcmd{\@BAdblcol}{\endgroup}{\endgroup\nobalance} {} { \toggletrue{@PBpatchFailed} \PackageWarningNoLine{pbalance}{Patch failed (@BAdblcol)} } } } \AtEndOfPackageFile{stfloats} { \pretocmd{\@addtocurcol}{\dimgdef{\@PBtmpHeightBefore}{\@textfloatsheight}} {} { \toggletrue{@PBpatchFailed} \PackageWarningNoLine{pbalance}{Patch failed (stfloats/@addtocurcol)} } \apptocmd{\@addtocurcol}{\@PBcollectFloatHeights} {} { \toggletrue{@PBpatchFailed} \PackageWarningNoLine{pbalance}{Patch failed (stfloats/@addtocurcol)} } } \AtEndOfPackageFile*{stfloats} { \pretocmd{\fn@makecol}{\@PBcollectPageInfo} {} { \toggletrue{@PBpatchFailed} \PackageWarningNoLine{pbalance}{Patch failed (stfloats/fn@makecol)} } \pretocmd{\org@makecol}{\@PBcollectPageInfo} {} { \toggletrue{@PBpatchFailed} \PackageWarningNoLine{pbalance}{Patch failed (stfloats/org@makecol)} } \apptocmd{\fn@makecol}{\@PBcollectColumnUsedHeight} {} { \toggletrue{@PBpatchFailed} \PackageWarningNoLine{pbalance}{Patch failed (stfloats/fn@makecol)} } \apptocmd{\org@makecol}{\@PBcollectColumnUsedHeight} {} { \toggletrue{@PBpatchFailed} \PackageWarningNoLine{pbalance}{Patch failed (stfloats/org@makecol)} } } \ifdefvoid{\mem@makecol} {} { \pretocmd{\mem@makecol}{\@PBcollectPageInfo} {} { \toggletrue{@PBpatchFailed} \PackageWarningNoLine{pbalance}{Patch failed (memoir/mem@makecol)} } \apptocmd{\mem@makecol}{\@PBcollectColumnUsedHeight} {} { \toggletrue{@PBpatchFailed} \PackageWarningNoLine{pbalance}{Patch failed (memoir/mem@makecol)} } \pretocmd{\mem@makecolbf}{\@PBcollectPageInfo} {} { \toggletrue{@PBpatchFailed} \PackageWarningNoLine{pbalance}{Patch failed (memoir/mem@makecolbf)} } \apptocmd{\mem@makecolbf}{\@PBcollectColumnUsedHeight} {} { \toggletrue{@PBpatchFailed} \PackageWarningNoLine{pbalance}{Patch failed (memoir/mem@makecolbf)} } } \AtEndOfPackageFile{footmisc} { \pretocmd{\@makecol}{\@PBcollectPageInfo} {} { \toggletrue{@PBpatchFailed} \PackageWarningNoLine{pbalance}{Patch failed (footmisc/@makecol)} } \apptocmd{\@makecol}{\@PBcollectColumnUsedHeight} {} { \toggletrue{@PBpatchFailed} \PackageWarningNoLine{pbalance}{Patch failed (footmisc/@makecol)} } } \AtEndOfPackageFile{ftnright} { \pretocmd{\@makecol}{\@PBcollectPageInfo} {} { \toggletrue{@PBpatchFailed} \PackageWarningNoLine{pbalance}{Patch failed (ftnright/@makecol)} } \apptocmd{\@makecol}{\@PBcollectColumnUsedHeight} {} { \toggletrue{@PBpatchFailed} \PackageWarningNoLine{pbalance}{Patch failed (ftnright/@makecol)} } \pretocmd{\@outputdblcol}{\@PBbalanceSecondColumn} {} { \toggletrue{@PBpatchFailed} \PackageWarningNoLine{pbalance}{Patch failed (ftnright/@outputdblcol)} } \pretocmd{\@outputdblcol}{\@PBcollectColumnInfo} {} { \toggletrue{@PBpatchFailed} \PackageWarningNoLine{pbalance}{Patch failed (ftnright/@outputdblcol)} } } \AtEndOfPackageFile{flafter} { \pretocmd{\@addtocurcol}{\dimgdef{\@PBtmpHeightBefore}{\@textfloatsheight}} {} { \toggletrue{@PBpatchFailed} \PackageWarningNoLine{pbalance}{Patch failed (flafter/@addtocurcol)} } \apptocmd{\@addtocurcol}{\@PBcollectFloatHeights} {} { \toggletrue{@PBpatchFailed} \PackageWarningNoLine{pbalance}{Patch failed (flafter/@addtocurcol)} } } \AtEndOfPackageFile{fnpos} { \pretocmd{\@makecol}{\@PBcollectPageInfo} {} { \toggletrue{@PBpatchFailed} \PackageWarningNoLine{pbalance}{Patch failed (fnpos/@makecol)} } \apptocmd{\@makecol}{\@PBcollectColumnUsedHeight} {} { \toggletrue{@PBpatchFailed} \PackageWarningNoLine{pbalance}{Patch failed (fnpos/@makecol)} } } \AtEndOfPackageFile{cuted} { \pretocmd{\@outputdblcol}{\@PBbalanceSecondColumn} {} { \toggletrue{@PBpatchFailed} \PackageWarningNoLine{pbalance}{Patch failed (cuted/@outputdblcol)} } \pretocmd{\@outputdblcol}{\@PBcollectColumnInfo} {} { \toggletrue{@PBpatchFailed} \PackageWarningNoLine{pbalance}{Patch failed (cuted/@outputdblcol)} } } \AtEndOfPackageFile{midfloat} { \pretocmd{\@outputdblcol}{\@PBbalanceSecondColumn} {} { \toggletrue{@PBpatchFailed} \PackageWarningNoLine{pbalance}{Patch failed (midfloat/@outputdblcol)} } \pretocmd{\@outputdblcol}{\@PBcollectColumnInfo} {} { \toggletrue{@PBpatchFailed} \PackageWarningNoLine{pbalance}{Patch failed (midfloat/@outputdblcol)} } } \ExplSyntaxOn \AtBeginShipout{ \iftoggle{@PBpatchFailed} {} { % It only makes sense to consider pages that actually have two columns. \if@twocolumn \iftoggle{@PBpatchStillActive} {\@PBfinalizePageData} { \global\toggletrue{@PBpatchFailed} \PackageWarningNoLine{pbalance}{Patch~disabled~by~another~package} } \fi % Reset before processing next page \global\togglefalse{@PBtmpHasFloats} \global\togglefalse{@PBtmpHasFloatcol} \global\togglefalse{@PBtmpHasFootnotes} \global\togglefalse{@PBtmpHasMarginpars} \global\togglefalse{@PBtmpFloatcolBoth} \seq_gclear_new:N \@PBtmpLeftFloatHeights \seq_gclear_new:N \@PBtmpLeftFloatSpacesBelow \gundef{\@PBtmpHeight} \gundef{\@PBtmpLeftHeight} \gundef{\@PBtmpLeftFloatsHeight} \gundef{\@PBtmpUsedLeft} \gundef{\@PBtmpRightHeight} \gundef{\@PBtmpRightFloatsHeight} \gundef{\@PBtmpUsedRight} } } \ExplSyntaxOff \newcommand\@PBfinalizePageData{ % LaTeX3 does not do short-circuit evaluation, % so we cannot use a single ifboolexpr here \ifdefvoid{\@PBtmpUsedRight} % This is probably a float page or the user disabled % the package somehow and recompiled. Either way, % this is not the last text page, ignore {} { \ifboolexpr { % If both cols are float cols, this is not the last text page, ignore togl {@PBtmpFloatcolBoth} or % A page with a float column on the left and an empty % column on the right is not the last text page, ignore ( togl {@PBtmpHasFloatcol} and test {\ifdimcomp{\@PBtmpUsedRight}{=}{\topskip}} ) } {} { \ifdefvoid{\@PBthepage} { % The user did not choose a page, so this page is % a candidate to be the last two-column text page \xdef\@PBlastPage{\the\value{abspage}} \@PBcopyPageData{tmp}{candidate} } { \ifnumcomp{\@PBthepage}{=}{\the\value{abspage}} { % This is the page the user indicated % as the last two-column text page \xdef\@PBlastPage{\the\value{abspage}} \@PBcopyPageData{tmp}{candidate} } {} } } } } \AfterLastShipout{ % If there are no two-column pages in the % document, there is no candidate page. \ifcsdef{@PBlastPage} {\@PBcopyPageData{candidate}{pg\@Roman{\@PBlastPage}}} {\PackageInfo{pbalance}{No two-column pages, doing nothing}} } \AfterLastShipout{ % Reset \@PBimpossible if we are in draft mode, % so the user can start over if they want to. \iftoggle{@PBdraft}{\togglefalse{@PBimpossible}}{} \ifboolexpr{togl {@PBdraft} or togl {@PBimpossible} or togl {@PBpatchFailed}} { \iftoggle{@PBimpossible} {\PackageWarningNoLine{pbalance}{Could not balance, doing nothing}} { \iftoggle{@PBpatchFailed} { \PackageWarningNoLine{pbalance}{Patching failed, cannot balance; try \MessageBreak loading pbalance earlier or later} } {\PackageInfo{pbalance}{Draft mode, doing nothing}} } } { \ifdefvoid{\@PBprevLastPage} {\@PBsaveUnbalancedInfo\@PBnotifyRerun} % First pass { \iftoggle{@PBstabilized} {\@PBmanageBalancingPasses} { \@PBifSomethingChanged[unbal] % Document still changing; discard data from previous pass {\@PBsaveUnbalancedInfo[update]\@PBnotifyRerun} % No changes, so let's balance from now on (once @PBstabilized % becomes true, it never reverts back to false, unless the aux % file is deleted). {\toggletrue{@PBstabilized}\@PBsaveUnbalancedInfo\@PBnotifyRerun} } } \@PBsaveToggle{@PBstabilized} } \@PBsaveToggle{@PBimpossible} \immediate\write\@mainaux{\gdef\string\@PBprevLastPhysPage{\the\value{abspage}}} } \newcommand\@PBmanageBalancingPasses{ % This is the second pass or later after we started balancing \providetoggle{@PBpg\@Roman{\@PBlastPage}alreadyBalanced} \iftoggle{@PBpg\@Roman{\@PBlastPage}alreadyBalanced} { % Third pass or later; document was already % balanced, so there should be no more changes \@PBifSomethingChanged % The document has changed, so we need to measure % stuff again; clear aux file and start over. {\@PBnotifyRerun} { % Nothing changed, just write the same info down \@PBsaveUnbalancedInfo \@PBsaveBalancedInfo \PackageInfo{pbalance}{Done balancing page \@PBlastPage} } } % Second pass, which means something did change: we have just % balanced the columns for the first time. { \xdef\@tempa{\the\value{abspage}} \ifboolexpr { test {\ifdefstrequal{\@PBprevLastPage}{\@PBlastPage}} and test {\ifdefstrequal{\@PBprevLastPhysPage}{\@tempa}} } % All is well; save the new info {\@PBsaveUnbalancedInfo\@PBsaveBalancedInfo} % Oh, no! As we attempted to balance, we actually changed % the number of pages (maybe we added a float page, maybe % we turned the last float page into a text page etc.). If % we continue, the document might never converge (endless % ``please rerun LaTeX'' messages). Let's give up balancing. {\toggletrue{@PBimpossible}} } } \newcommand\@PBifBalancePossible[2]{ \iftoggle{@PBunbalpg\@Roman{\@PBprevLastPage}HasFloatcol} {#2} {#1} } \newcommand\@PBifBalancePkgPossible[2]{ \ifboolexpr { togl {@PBunbalpg\@Roman{\@PBprevLastPage}HasFloats} or togl {@PBunbalpg\@Roman{\@PBprevLastPage}HasFootnotes} or togl {@PBunbalpg\@Roman{\@PBprevLastPage}HasMarginpars} or togl {@PBnoBalancePackage} } {#2} {#1} } \newcommand\@PBifPoorBalancePossible[2]{ % Let's use some shorter names, please \dimdef\@PBtmpH{\csuse{@PBunbalpg\@Roman{\@PBprevLastPage}Height}} \dimdef\@PBtmpUL{\csuse{@PBunbalpg\@Roman{\@PBprevLastPage}UsedLeft}} \dimdef\@PBtmpUR{\csuse{@PBunbalpg\@Roman{\@PBprevLastPage}UsedRight}} \dimdef\@PBtmpLH{\csuse{@PBunbalpg\@Roman{\@PBprevLastPage}LeftHeight}} \dimdef\@PBtmpLFH{\csuse{@PBunbalpg\@Roman{\@PBprevLastPage}LeftFloatsHeight}} \dimdef\@PBtmpRH{\csuse{@PBunbalpg\@Roman{\@PBprevLastPage}RightHeight}} \dimdef\@PBtmpRFH{\csuse{@PBunbalpg\@Roman{\@PBprevLastPage}RightFloatsHeight}} \ifboolexpr{ % Plenty of space on right column, difference is worth it test {\ifdimcomp{\@PBtmpUL}{>}{\@PBtmpUR + 6\baselineskip}} or ( test {\ifdimcomp{\@PBtmpUL}{>}{4\baselineskip}} and test {\ifdimcomp{\@PBtmpUR}{=}{\topskip}} % column is empty ) }{#1}{#2} } \ExplSyntaxOn \def\@PBsafetyCheck{ % Let's use some shorter names, please \dimdef\@PBtmpH{\csuse{@PBunbalpg\@Roman{\@PBprevLastPage}Height}} \dimdef\@PBtmpUL{\csuse{@PBunbalpg\@Roman{\@PBprevLastPage}UsedLeft}} \dimdef\@PBtmpUR{\csuse{@PBunbalpg\@Roman{\@PBprevLastPage}UsedRight}} \dimdef\@PBtmpLH{\csuse{@PBunbalpg\@Roman{\@PBprevLastPage}LeftHeight}} \dimdef\@PBtmpLFH{\csuse{@PBunbalpg\@Roman{\@PBprevLastPage}LeftFloatsHeight}} \dimdef\@PBtmpRH{\csuse{@PBunbalpg\@Roman{\@PBprevLastPage}RightHeight}} \dimdef\@PBtmpRFH{\csuse{@PBunbalpg\@Roman{\@PBprevLastPage}RightFloatsHeight}} % Make sure we do not push top/bottom floats out of the left col \dimdef\@PBmaxSlack{\@PBtmpLH} \dimdef\@PBtmpdim{0pt} % Start from the bottom \numdef{\@PBtmpcnt}{\seq_count:c {@PBunbalpg\@Roman{\@PBprevLastPage}LeftFloatHeights}} \int_while_do:nNnn {\@PBtmpcnt} > {0} { \dimdef\@PBcurrentFloatHeight {\seq_item:cn {@PBunbalpg\@Roman{\@PBprevLastPage}LeftFloatHeights} {\@PBtmpcnt}} \dimdef\@PBcurrentFloatSpaceBelow {\seq_item:cn {@PBunbalpg\@Roman{\@PBprevLastPage}LeftFloatSpacesBelow} {\@PBtmpcnt}} \dimdef{\@PBtmpdim}{\@PBtmpdim + \@PBcurrentFloatHeight} \ifboolexpr { % Does the float fit in the second column? test {\ifdimcomp{\@PBtmpdim}{<}{\@PBtmpRH - \@PBtmpRFH}} and % Is there something above this float, i.e., if we move % this float to the next column, will anything remain? test { \ifdimcomp {\@PBcurrentFloatSpaceBelow + \@PBcurrentFloatHeight} {<} {\@PBtmpLH - 6\baselineskip} } } {\numdef{\@PBtmpcnt}{\@PBtmpcnt - 1}} % this one is ok; check next { % this one should not be moved, we found the limit \ifdimcomp{\@PBmaxSlack}{>}{\@PBcurrentFloatSpaceBelow} {\dimdef{\@PBmaxSlack}{\@PBcurrentFloatSpaceBelow}} {} \numdef{\@PBtmpcnt}{0} % exit the loop } } \ifdimcomp{\@PBmaxSlack}{<}{\@PBslack} {\dimgdef{\@PBslack}{\@PBmaxSlack}} {} } \ExplSyntaxOff \newcommand\@PBifSomethingChanged[3][bal]{ \ifdefstrequal{\@PBprevLastPage}{\@PBlastPage} { \@PBifPageDataMatches {pg\@Roman{\@PBlastPage}} {#1pg\@Roman{\@PBlastPage}} {#3} {#2} } {#2} } \newcommand\@PBifPageDataMatches[4]{ \ifboolexpr{ test {\@PBifTogglesMatch{@PB#1HasFloats}{@PB#2HasFloats}} and test {\@PBifTogglesMatch{@PB#1HasFloatcol}{@PB#2HasFloatcol}} and test {\@PBifTogglesMatch{@PB#1HasFootnotes}{@PB#2HasFootnotes}} and test {\@PBifTogglesMatch{@PB#1HasMarginpars}{@PB#2HasMarginpars}} and test {\@PBifDimensionsMatch{@PB#1Height}{@PB#2Height}} and test {\@PBifDimensionsMatch{@PB#1LeftHeight}{@PB#2LeftHeight}} and test {\@PBifDimensionsMatch{@PB#1RightHeight}{@PB#2RightHeight}} and test {\@PBifDimensionsMatch{@PB#1UsedLeft}{@PB#2UsedLeft}} and test {\@PBifDimensionsMatch{@PB#1UsedRight}{@PB#2UsedRight}} } {#3} {#4} } \newcommand\@PBifTogglesMatch[4]{ \ifboolexpr{(togl{#1} or not togl{#2}) and (togl{#2} or not togl{#1})} {#3} {#4} } \newcommand\@PBifDimensionsMatch[4]{ \ifdimcomp{\csuse{#1}}{=}{\csuse{#2}} {#3} {#4} } \newcommand\@PBsaveUnbalancedInfo[1][]{ % If there are no two-column pages in the % document, there is nothing to save. \ifcsdef{@PBlastPage} { \immediate\write\@mainaux{\gdef\string\@PBprevLastPage{\@PBlastPage}} \ifboolexpr{test {\ifdefvoid{\@PBprevLastPage}} or test {\ifstrequal{#1}{update}}} % Columns are currently unbalanced, either because this is % the first pass (we do not have any previous information) % or because the document has not stabilized yet, so any % previous information is unreliable. Save the information % we just collected as ``unbalanced''. {\@PBsavePageDataAs{pg\@Roman{\@PBlastPage}}{unbalpg\@Roman{\@PBlastPage}}} % Not the first pass, so columns are possibly balanced; % to save the unbalanced information, repeat what was % gathered during the first pass. {\@PBsavePageData{unbalpg\@Roman{\@PBprevLastPage}}} } {} } \def\@PBsaveBalancedInfo{ \global\toggletrue{@PBpg\@Roman{\@PBlastPage}alreadyBalanced} \@PBsaveToggle{@PBpg\@Roman{\@PBlastPage}alreadyBalanced} \@PBsavePageDataAs{pg\@Roman{\@PBlastPage}}{balpg\@Roman{\@PBlastPage}} } \def\@PBnotifyRerun{ \AtVeryEndDocument{ % If there are no two-column pages in the document, % it makes no sense to emit this warning. \ifcsdef{@PBlastPage} { \PackageWarningNoLine{pbalance} {Last two-column page cols not balanced. Rerun LaTeX} } { \PackageWarningNoLine{pbalance} {No two-column pages, doing nothing} } } } \ExplSyntaxOn \newcommand\@PBcopyPageData[2]{ \global\providetoggle{@PB#2HasFloats} \global\providetoggle{@PB#2HasFloatcol} \global\providetoggle{@PB#2HasFootnotes} \global\providetoggle{@PB#2HasMarginpars} \iftoggle{@PB#1HasFloats} {\global\toggletrue{@PB#2HasFloats}} {\global\togglefalse{@PB#2HasFloats}} \iftoggle{@PB#1HasFloatcol} {\global\toggletrue{@PB#2HasFloatcol}} {\global\togglefalse{@PB#2HasFloatcol}} \iftoggle{@PB#1HasFootnotes} {\global\toggletrue{@PB#2HasFootnotes}} {\global\togglefalse{@PB#2HasFootnotes}} \iftoggle{@PB#1HasMarginpars} {\global\toggletrue{@PB#2HasMarginpars}} {\global\togglefalse{@PB#2HasMarginpars}} \csdimgdef{@PB#2Height}{\csuse{@PB#1Height}} \csdimgdef{@PB#2LeftHeight}{\csuse{@PB#1LeftHeight}} \csdimgdef{@PB#2LeftFloatsHeight}{\csuse{@PB#1LeftFloatsHeight}} \csdimgdef{@PB#2RightHeight}{\csuse{@PB#1RightHeight}} \csdimgdef{@PB#2RightFloatsHeight}{\csuse{@PB#1RightFloatsHeight}} \csdimgdef{@PB#2UsedLeft}{\csuse{@PB#1UsedLeft}} \csdimgdef{@PB#2UsedRight}{\csuse{@PB#1UsedRight}} \seq_gclear_new:c {@PB#2LeftFloatHeights} \seq_gclear_new:c {@PB#2LeftFloatSpacesBelow} \seq_gset_eq:cc {@PB#2LeftFloatHeights} {@PB#1LeftFloatHeights} \seq_gset_eq:cc {@PB#2LeftFloatSpacesBelow} {@PB#1LeftFloatSpacesBelow} } \ExplSyntaxOff \newcommand\@PBsavePageDataAs[2]{ \@PBsaveDimAs{@PB#1Height}{@PB#2Height} \@PBsaveDimAs{@PB#1LeftHeight}{@PB#2LeftHeight} \@PBsaveDimAs{@PB#1LeftFloatsHeight}{@PB#2LeftFloatsHeight} \@PBsaveDimAs{@PB#1RightHeight}{@PB#2RightHeight} \@PBsaveDimAs{@PB#1RightFloatsHeight}{@PB#2RightFloatsHeight} \@PBsaveDimAs{@PB#1UsedLeft}{@PB#2UsedLeft} \@PBsaveDimAs{@PB#1UsedRight}{@PB#2UsedRight} \@PBsaveToggleAs{@PB#1HasFloats}{@PB#2HasFloats} \@PBsaveToggleAs{@PB#1HasFloatcol}{@PB#2HasFloatcol} \@PBsaveToggleAs{@PB#1HasFootnotes}{@PB#2HasFootnotes} \@PBsaveToggleAs{@PB#1HasMarginpars}{@PB#2HasMarginpars} \@PBsaveSeqAs{@PB#1LeftFloatHeights}{@PB#2LeftFloatHeights} \@PBsaveSeqAs{@PB#1LeftFloatSpacesBelow}{@PB#2LeftFloatSpacesBelow} } \newcommand\@PBsavePageData[1]{ \@PBsavePageDataAs{#1}{#1} } \newcommand\@PBsaveToggleAs[2]{ \immediate\write\@mainaux{\providetoggle{#2}} \iftoggle{#1} {\immediate\write\@mainaux{\global\toggletrue{#2}}} {\immediate\write\@mainaux{\global\togglefalse{#2}}} } \newcommand\@PBsaveToggle[1]{ \@PBsaveToggleAs{#1}{#1} } \newcommand\@PBsaveDimAs[2]{ \immediate\write\@mainaux{\csdimgdef{#2}{\csuse{#1}}} } \newcommand\@PBsaveDim[1]{ \@PBsaveDimAs{#1}{#1} } \ExplSyntaxOn \newcommand\@PBsaveSeqAs[2]{ \immediate\write\@mainaux{\string\ExplSyntaxOn} \immediate\write\@mainaux{\string\seq_gclear_new:c {#2}} \seq_map_inline:cn {#1} {\immediate\write\@mainaux{\string\global\string\seq_put_right:cn {#2}{##1}}} \immediate\write\@mainaux{\string\ExplSyntaxOff} } \ExplSyntaxOff \newcommand\@PBsaveSeq[1]{ \@PBsaveSeqAs{#1}{#1} } \endinput %% %% End of file `pbalance.sty'.