4

In a macro, I need to know whether I'm at the top of a page or not (excluding the top float). So I use the length of \pagetotal to find out whether I am or not. If \pagetotal is zero, then I'm at the top of the page. However, I've noticed that \pagetotal isn't always "up to date" and distorts the use of my macro.

How can I force \pagetotal to be updated? Or have I misunderstood the meaning of \pagetotal?

You'll find an example below where \pagetotal ~ 552 pt while the macro is at the top of the page.

enter image description here

\documentclass{article}
\usepackage{lipsum}

\newcommand{\test}{%
\par
\ifdim\pagetotal=0pt
    TRUE%
\else
    FALSE : \textbackslash pagetotal = \the\pagetotal
\fi}

\begin{document}
\lipsum[1]

\lipsum[1]

\lipsum[1]

\lipsum[3]

\test

\the\pagetotal\lipsum[1]

\the\pagetotal
\end{document}

Context

This question is linked to two of my previous questions: How to determine from the document body whether or not a page has a top float? and How can we ensure that the spacing before or after a \vbox is correct when using \addvspace?.

I'm trying to create a macro which adds a different vertical space depending on the context.
Normally, I'd like to insert a space of value \myLength at the beginning of my macro and at the end.
If the macro falls at the top of the page, after a page break (where \pagetotal=0pt, if I've understood correctly), \vspace{\myLength} is ignored and no vertical space is added, which is what I want.
BUT the problem arises when, as in the previous case, the macro falls at the top of the page, but is preceded by one or more top floats (it's no longer really the "top of the page" stricto sensu, but if I've understood correctly, we still have \pagetotal=0pt). In this case, I'd like the macro to insert a vertical \vspace*{\dimexpr\myLength-\textfloatsep} to maintain consistency.

Here's an example to illustrate:

Here, the macro behaves correctly: when it falls at the top of the page, no space is inserted at the start of the macro; when it is not at the top of the page, \vspace{\myLength} is correctly inserted.

enter image description here

However, when the macro falls at the top of the page and is preceded by a top float, the spaces are totally unbalanced.

enter image description here

Here is a non-working minimal example :

\documentclass{article}
\usepackage{lipsum, mwe, refcount}

\makeatletter
%From : https://tex.stackexchange.com/questions/712713/how-to-determine-from-the-document-body-whether-or-not-a-page-has-a-top-float
\def \@combinefloats {%
    \ifx \@toplist\@empty%
    \else%
        \@cflt%
        \immediate\write\@auxout{\string\global\string\@namedef{pageWithTopFloat-\thepage}{1}}%
    \fi%
    \ifx \@botlist\@empty \else \@cflb \fi%
}

\newcounter{ifTopFloatCnt}

\def\iftopfloat#1#2{%
    \global\advance\c@ifTopFloatCnt\@ne%
    \label{\the\c@ifTopFloatCnt @ifTopFloat}%    
    \ifcsname pageWithTopFloat-\getpagerefnumber{\the\c@ifTopFloatCnt @ifTopFloat}\endcsname%
        #1%
    \else%
        #2%
    \fi%
}
\makeatother

\newlength{\myLength}
\setlength{\myLength}{30pt}% exaggerated value to illustrate

\newcommand{\mycommand}{\par%
    \iftopfloat{%
        \ifdim\pagetotal=0pt% If there's a top float AND you're right after it
            \vspace*{\dimexpr\myLength-\textfloatsep}                         
        \else% If there is a top float BUT you are not right after it
            \vspace{\myLength}
        \fi
    }{% If there is no top float
        \vspace{\myLength}
    }
    {\centering\LARGE * * *\par}
    \vspace{\myLength}
}

\begin{document}
\lipsum[1]

\lipsum[1]

\lipsum[1]

\lipsum[3]

\begin{figure}[t]
\centering
\includegraphics{example-image}
\end{figure}

\mycommand

\lipsum[1]

\lipsum[2]

\mycommand

\lipsum[1]

\mycommand

\lipsum[1]
\end{document}

Test with \pdfsavepos

As suggested in the comments, I've tried using the \pdfsavepos and \pdflastypos primitives. This seems to work better than using \pagetotal, but I still encountered a few "bugs" with the use of my command.

In some cases, \pdfsavepos gives the information that the command is at the top of the page (\pdflastypos = 0) when it isn't (see MWE below). On the other hand, sometimes when the command is at the top of the page, just after a top-of-page float, \pdflastypos is not zero, suggesting that \pdflastypos actually refers to the bottom of the previous page.

Example: Here I get \pdflastypos ≠ 0 even though the command is at the top of the page.

enter image description here

\documentclass{article}
\usepackage{lipsum, mwe, refcount, iftex}

\ifluatex% For compatibility with LuaTeX, which I generally use.
    \let\pdfsavepos\savepos
    \let\pdflastypos\lastypos
\fi

\makeatletter
%From : https://tex.stackexchange.com/questions/712713/how-to-determine-from-the-document-body-whether-or-not-a-page-has-a-top-float
\def \@combinefloats {%
    \ifx \@toplist\@empty%
    \else%
        \@cflt%
        \immediate\write\@auxout{\string\global\string\@namedef{pageWithTopFloat-\thepage}{1}}%
    \fi%
    \ifx \@botlist\@empty \else \@cflb \fi%
}

\newcounter{ifTopFloatCnt}

\long\def\ifTopFloat#1#2{%
    \global\advance\c@ifTopFloatCnt\@ne%
    \label{\the\c@ifTopFloatCnt @ifTopFloat}\nopagebreak%
    \ifcsname pageWithTopFloat-\getpagerefnumber{\the\c@ifTopFloatCnt @ifTopFloat}\endcsname%
        #1%
    \else%
        #2%
    \fi%
}

\newlength{\myLength}
\setlength{\myLength}{30pt}% exaggerated value to illustrate

\newcounter{myCommandCnt}
\newcounter{myCommandCntAux}

\newcommand{\myCommand}{\par%
    \stepcounter{myCommandCnt}%
    \pdfsavepos%
    \write\@auxout{%
        \string\global\string\stepcounter{myCommandCntAux}%
        \string\global\string\@namedef{myCommand-\string\themyCommandCntAux}{\the\pdflastypos}%
    }%
    \ifTopFloat{%
        \ifnum\@nameuse{myCommand-\themyCommandCnt}=0% If there's a top float AND you're right after it
            \vspace*{\dimexpr\myLength-\textfloatsep}
            {\centering\LARGE * * * (a, \textbackslash lastypos: \@nameuse{myCommand-\themyCommandCnt} )\par}
        \else% If there is a top float BUT you are not right after it
            \vspace{\myLength}
            {\centering\LARGE * * * (b, \textbackslash lastypos: \@nameuse{myCommand-\themyCommandCnt} )\par}
        \fi
    }{% If there is no top float
        \vspace{\myLength}
        {\centering\LARGE * * * (c, \textbackslash lastypos: \@nameuse{myCommand-\themyCommandCnt} )\par}
    }    
    \vspace{\myLength}
}

\makeatother

\begin{document}

\lipsum[1]

\lipsum[1]

\lipsum[1]

\lipsum[2]

\begin{figure}[t]
\centering
\includegraphics{example-image}
\end{figure}

\myCommand

\lipsum[1]

\lipsum[2]

\begin{figure}[t]
\centering
\includegraphics{example-image-9x16}
\end{figure}

\myCommand

\lipsum[2]

\myCommand

\lipsum[1]

\myCommand

\lipsum[1]
\end{document}
20
  • TeX is essentially never at the top of a page.
    – egreg
    Commented Mar 25 at 18:33
  • 3
    Possibly an XY problem .... Can you explain the purpose of your macro? Then somebody might be able to suggest an alternative approach.
    – cfr
    Commented Mar 25 at 18:42
  • 2
    you should use pdflastpos as I say and look on the next run. pagetotal is completely unusable here for example it is never updated during a paragraph so if you have a long paragraph text breaking over a page, if you test the value anywhere in the paragraph it will refer to the value at the start which may not even be on the same page as the point you test. if you test in vertical mode it's a bit more reliable, but not much Commented Mar 27 at 0:36
  • 1
    you can't use \immediate\write that defeats the point of the \write which is to write it once it is known, as the page is shipped out Commented Apr 8 at 17:00
  • 1
    I once had a similar question and David Carlisle provided a similar answer: tex.stackexchange.com/a/336910/107497
    – Teepeemm
    Commented Apr 8 at 20:53

0

You must log in to answer this question.

Browse other questions tagged .