3

I'm unsure how to present the question, so I'll just go with something and edit if it is deemed necessary.

The PDF extensions currently extend the basic TeX macros with at least the following three macros

  1. \pdfsavepos,
  2. \pdflastxpos and
  3. \pdflastypos,

which allow the user to extract the current page positioning at shipout. This is discussed in some detail in the TUG article here: https://www.tug.org/TUGboat/tb30-1/tb94zyka.pdf

I'm trying to sort a counter, based on \pdfsavepos. The idea is that I have a few macros, say, \csname macro-1-savedposition\endcsname all the way up to, say, \csname macro-4-savedposition\endcsname (in the case of four instances of the macro), and the expanded values hold \the\pdflastypos. I then wish to sort these macros so that another macro, say \csname macro-3\endcsname, expands correctly based on the value of the savedposition instance.

As a concrete example, say I have four macros (the value after the arrow is the (once) expanded definition of the macro):

  1. \csname macro-1-savedposition\endcsname -> 12345
  2. \csname macro-2-savedposition\endcsname -> 1234
  3. \csname macro-3-savedposition\endcsname -> 123456
  4. \csname macro-4-savedposition\endcsname -> 1234567

Since page positioning happens from the bottom to the top, I wish to sort them with order "largest first", so in this case the order should be 4-3-1-2. The corresponding macros should then have the expanded values of \csname macro-4\endcsname=1, \csname macro-3\endcsname=2, \csname macro-1\endcsname=3 and \csname macro-2\endcsname=4.

I've been pondering how to get this done for a while now and I feel like I'm stuck. In the simplest case I could simply extract the absolute page positioning at macro callout rather than at page shipout, but clearly the mechanism isn't coded this way and I'm unsure whether such functionality is even supported.

Digging deeper, this means that identifiers corresponding to the saved positions have to be added in retro-actively, since (to my knowledge) counters are generally not incremented at page shipout. This asynchronous handling of the positioning calculations and the counter values makes life much harder. One option would be to produce chains of these positioning commands, a method I also used in my other question: Reflowing marginpars typeset using pdfsavepos, but I have not found anything to loop over these chains in this website or on foreign websites concerning the TeX language.

Moving forward to the sorting part, a preliminary implementation of (alpha)numeric sorting that I found useful but could not adapt to this use case was presented here: How to sort an alphanumeric list (I may have messed up expansion control in this respect.)

The below example shows the issue quite nicely:

\documentclass{article}
\usepackage{lipsum}
\makeatletter
\def\@oddfoot{\hskip\textwidth\smash{\raise\footskip\vbox to .9\textheight{\hsize\marginparwidth%
  \hrule\@height\z@%
  \box123
  \vskip\z@\@plus\textheight}}}
\def\mymacroX{\setbox123\hbox{Here is my macro X \mymacro}}
\def\saveposition#1{%
\pdfsavepos\write\@auxout{%
  \noexpand\savedpos%
    {#1}{\the\c@page}{\the\pdflastxpos}{\the\pdflastypos}}}
\def\savedpos#1#2#3#4{%
\begingroup
  \let\@positions\relax
  \expandafter\xdef\csname sp@#1-#2\endcsname{%
    \expandafter\ifx\csname sp@#1-#2\endcsname\relax
    \else
      \csname sp@#1-#2\endcsname
    \fi
  \@positions{#3}{#4}}%
  \endgroup}%
\newcount\mymacrocounter
\newcount\mymacrohelper
\def\mymacro{%
\saveposition{mymacro}
}
\makeatother
\begin{document}
\lipsum[1]
\mymacro
\mymacroX
\lipsum[1]
\mymacro
\lipsum[1]
\mymacro
\lipsum[1]
\mymacro
\end{document}

If we look at the aux file, we see that the fourth instance, created by \mymacroX, has a higher value of \pdflastypos than the previous instance, even though it was called second and typeset last.

I'll add in more examples when I finally manage to compile one.

EDIT The most concrete example I can think of is the actual use case. The code can be found at gist.github.com/TobiasBouma/de4fa02c4e7fa12141ca and gist.github.com/TobiasBouma/798444f698dd637cafa2, which shows why I'm asking for a position-based solution spefically. (Just for completeness, I'm not recommending the usage of that code as an MWE.)

2

3 Answers 3

1
+100

Here I do a bubble sort (with diagnostics that can be commented). Then I employ the indirect addressing requested by the OP.

EDITED to automate necessary initializations.

\documentclass{article}
\usepackage{ifthen}

    \expandafter\def\csname macro-1-savedposition\endcsname{12345}
    \expandafter\def\csname macro-2-savedposition\endcsname{1234}
    \expandafter\def\csname macro-3-savedposition\endcsname{123456}
    \expandafter\def\csname macro-4-savedposition\endcsname{1234567}
%    \expandafter\def\csname macro-5-savedposition\endcsname{12}

\def\npos{4}
%\def\npos{5}

\newcounter{outer}
\newcounter{inner}
\newcounter{cntA}
\newcounter{cntB}
\newcommand\sortpos[1]{%
% FIRST DO A BUBBLE SORT
  \setcounter{outer}{0}%
  \whiledo{\value{outer}<#1}{%
    \stepcounter{outer}%
    \expandafter\edef\csname macro--\theouter\endcsname{\theouter}%
    \bubblesort{\theouter}%
    \listorder{#1}\par% CAN COMMENT THIS WHEN SATISFIED WITH OUTPUT
  }%
% THEN ACHIEVE THE INDIRECT ADDRESSING
  \setcounter{outer}{0}%
  \whiledo{\value{outer}<#1}{%
    \stepcounter{outer}%
    \expandafter\edef%
      \csname macro-\csname macro--\theouter\endcsname\endcsname{\theouter}%
  }%
}
\newcommand\bubblesort[1]{%
  \setcounter{inner}{#1}%
  \whiledo{\value{inner} > 1}{%
    \setcounter{cntA}{\theinner}%
    \edef\entryA{%
      \csname macro-\csname macro--\thecntA\endcsname-savedposition\endcsname}%
    \setcounter{cntB}{\numexpr\theinner-1}%
    \edef\entryB{%
      \csname macro-\csname macro--\thecntB\endcsname-savedposition\endcsname}%
    \ifthenelse{\entryB<\entryA}{%
      \edef\tmp{\csname macro--\thecntB\endcsname}%
      \expandafter\edef\csname macro--\thecntB\endcsname{%
        \csname macro--\thecntA\endcsname}%
      \expandafter\edef\csname macro--\thecntA\endcsname{\tmp}%
    }{}%
    \addtocounter{inner}{-1}%
  }%
}
% CAN REMOVE THE FOLLOWING DIAGNOSTIC MACROS AND INITIALIZATIONS 
% WHEN HAPPY WITH ALGORITHM
\newcounter{diag}
\newcommand\init[1]{%
  \setcounter{diag}{0}%
  \whiledo{\value{diag}<#1}{%
    \stepcounter{diag}%
    \expandafter\def\csname macro--\thediag\endcsname{0}%
  }%
}
\init{\npos}
\newcommand\listorder[1]{(%
  \setcounter{diag}{0}%
  \whiledo{\value{diag}<#1}{%
    \stepcounter{diag}%
    \csname macro--\thediag\endcsname%
    \ifthenelse{\value{diag}=#1}{}{-}%
  }%
  )
}
%%%%% END DIAGNOSTICS
\begin{document}

\sortpos{\npos}

\noindent\hrulefill

\verb|\csname macro-1\endcsname|: \csname macro-1\endcsname

\verb|\csname macro-2\endcsname|: \csname macro-2\endcsname

\verb|\csname macro-3\endcsname|: \csname macro-3\endcsname

\verb|\csname macro-4\endcsname|: \csname macro-4\endcsname

%\verb|\csname macro-5\endcsname|: \csname macro-5\endcsname

\end{document}

enter image description here


Just to demonstrate for a larger case, where

\expandafter\def\csname macro-1-savedposition\endcsname{12345}
\expandafter\def\csname macro-2-savedposition\endcsname{1234}
\expandafter\def\csname macro-3-savedposition\endcsname{123456}
\expandafter\def\csname macro-4-savedposition\endcsname{1234567}
\expandafter\def\csname macro-5-savedposition\endcsname{12}
\expandafter\def\csname macro-6-savedposition\endcsname{123}

and

\def\npos{6}

the results are

enter image description here

The (4,3,1,2,6,5) should be interpreted as the largest number in savedposition 4, the 2nd largest number in savedposition 3, etc. and the smallest number in savedposition 5.

The indirect addressing is such that

\csname macros-1\endcsname: 3
\csname macros-2\endcsname: 4
\csname macros-3\endcsname: 2
\csname macros-4\endcsname: 1
\csname macros-5\endcsname: 6
\csname macros-6\endcsname: 5

implies that savedposition 1 is the third largest, savedposition 2 is the fourth largest, savedposition 3 is the second largest, etc.

5
  • 1
    This looks promising!! I think \npos can be automated as well, but I won't bother you with that malarkey. I'll keep you posted on how this fares in the document. :-)
    – 1010011010
    Aug 11, 2015 at 15:19
  • @1010011010 As you may see from the updated code, I have three commented lines, which when uncommented, test the algorithm with 5 rather than 4 data entries. So far, everything is behaving the way I expect. Aug 11, 2015 at 15:21
  • Is it possible to extend the algorithm to take macros like this one: \def\macro@savedposition#1#2{% \expandafter\def\csname macro-#1-savedposition\endcsname{#2} }? I need to expand the value of #1 in some cases, but i can't "hop over" the alphabetic characters with \expandafter.
    – 1010011010
    Aug 14, 2015 at 7:53
  • @1010011010 On the surface, I don't understand the difficulty here, which probably means I don't appreciate the problem. I can introduce the above definition that you indicate, and then define the data as \macro@savedposition{1}{12345} \macro@savedposition{2}{1234} \macro@savedposition{3}{123456} \macro@savedposition{4}{1234567}. Everything works just fine that way. So what am I missing? Aug 17, 2015 at 2:43
  • I figured out a workaround. It should be fine now. (I forgot to delete the comment.)
    – 1010011010
    Aug 17, 2015 at 15:29
1

I don't think you want \pdfsavepos at all. You are not really interested in the coordinates of the figures when printed, you just want to use the numbering based on typeset order, so it is that order that you should record, not the position.

Note figure numbering only gets out of order if you use non floating floats as the float mechanism goes to some trouble to preserve ordering.

However here is a document with dubious caption order:

enter image description here

Produced by

\documentclass{article}


\usepackage{capt-of}
\raggedright
\setlength\textheight{.5\textheight}

\begin{document}

\begin{minipage}{\textwidth}\centering
\fbox{HHHHH}
\captionof{figure}{zzzzz}
\end{minipage}

text text text

\begin{figure}[t]\centering
\fbox{TTTT}
\caption{ttttttttt}
\end{figure}

\begin{minipage}{\textwidth}\centering
\fbox{HHHHH}
\captionof{figure}{zzzzz}
\end{minipage}

\begin{figure}[b]\centering
\fbox{BBBB}
\caption{bbbb}
\end{figure}

\begin{minipage}{\textwidth}\centering
\fbox{HHHHH}
\captionof{figure}{z2z2z2z2z2z}
\end{minipage}

text tetx text 
\end{document}

If you record the order of the caption commands you can define \thefigure to use a modified numbering scheme:

enter image description here

\documentclass{article}

\let\oldcaption\caption
\makeatletter
\newcount\realfig
\def\caption{%
\protected@write\@auxout{}{\string\real@fig{\the\value{figure}}}%
\expandafter\csname real@@fig@\the\value{figure}\endcsname
\oldcaption}
\def\real@fig#1{%
\global\advance\realfig\@ne
\expandafter\xdef\csname real@@fig@#1\endcsname{%
\def\noexpand\thefigure{\the\realfig}%
}}
\makeatletter

\usepackage{capt-of}
\raggedright
\setlength\textheight{.5\textheight}

\begin{document}

\begin{minipage}{\textwidth}\centering
\fbox{HHHHH}
\captionof{figure}{zzzzz}
\end{minipage}

text text text

\begin{figure}[t]\centering
\fbox{TTTT}
\caption{ttttttttt}
\end{figure}

\begin{minipage}{\textwidth}\centering
\fbox{HHHHH}
\captionof{figure}{zzzzz}
\end{minipage}

\begin{figure}[b]\centering
\fbox{BBBB}
\caption{bbbb}
\end{figure}

\begin{minipage}{\textwidth}\centering
\fbox{HHHHH}
\captionof{figure}{z2z2z2z2z2z}
\end{minipage}

text tetx text 
\end{document}
3
  • The issue is the following: I could have a LaTeX float at the bottom of the page, while I could also have a non-LaTeX float at the top of the margin area. The typesetting order is then text block --> floats --> footer (as far as I know), in which case the caption numbering is still messed up. In the case the float would be at the top, I would want to have the float to have a lower counter value than the marginfigure...
    – 1010011010
    Jul 9, 2015 at 21:00
  • @1010011010 I doubt it. Make an example:-) Jul 9, 2015 at 21:01
0

Here's my preliminary solution, which incorporates everything. It doesn't fully work yet, but I'll update it when it does.

\documentclass{classnofonts}

\usepackage[demo]{graphicx}
\usepackage{letltxmacro}
\usepackage{atbegshi}
\usepackage{ifthen}
\usepackage{everypage}

\makeatletter
\newcount\caption@counter@i \caption@counter@i\z@
\newcount\caption@counter@ii \caption@counter@ii\z@
\newcount\caption@counter@iii \caption@counter@iii\z@

\def\savecaptionposition{%
\pdfsavepos
\global\advance\caption@counter@i\@ne
\divide\caption@counter@i\tw@
\expandafter\write
\expandafter\@auxout
\expandafter{%
\expandafter\string
\expandafter\expandafter
\expandafter\string
\expandafter\gdef
\expandafter\noexpand
\expandafter\csname
\expandafter m%
\expandafter a%
\expandafter c%
\expandafter r%
\expandafter o%
\expandafter-\the\caption@counter@i-savedposition-page-\thepage
\string\endcsname
{\the\pdflastypos}}}

\LetLtxMacro{\oldcaption}{\caption}

\def\caption[#1]#2{%
\global\advance\caption@counter@ii\@ne
\expandafter\ifx\csname macro-\the\caption@counter@ii\endcsname\relax
  \def\thefigure{\textbf{??}}%
\else
  \advance\caption@counter@iii\csname macro-\the\caption@counter@ii\endcsname
  \def\thefigure{\the\caption@counter@iii}%
\fi
\oldcaption[#1]{#2\protect\savecaptionposition}}

\newcounter{outer}
\newcounter{inner}
\newcounter{cntA}
\newcounter{cntB}
\newcommand\sortpos[1]{%
  \setcounter{outer}{0}%
  \whiledo{\value{outer}<#1}{%
    \stepcounter{outer}%
    \expandafter\edef\csname macro--\theouter\endcsname{\theouter}%
    \bubblesort{\theouter}%
  }%
  \setcounter{outer}{0}%
  \whiledo{\value{outer}<#1}{%
    \stepcounter{outer}%
    \expandafter\xdef
      \csname macro-\csname macro--\theouter\endcsname\endcsname{\theouter}%
  }%
}
\newcommand\bubblesort[1]{%
  \setcounter{inner}{#1}%
  \whiledo{\value{inner} > 1}{%
    \setcounter{cntA}{\theinner}%
    \edef\entryA{%
      \csname macro-\csname macro--\thecntA\endcsname-savedposition-page-\thepage\endcsname}%
    \setcounter{cntB}{\numexpr\theinner-1}%
    \edef\entryB{%
      \csname macro-\csname macro--\thecntB\endcsname-savedposition-page-\thepage\endcsname}%
    \ifthenelse{\entryB<\entryA}{%
      \edef\tmp{\csname macro--\thecntB\endcsname}%
      \expandafter\edef\csname macro--\thecntB\endcsname{%
        \csname macro--\thecntA\endcsname}%
      \expandafter\edef\csname macro--\thecntA\endcsname{\tmp}%
    }{}%
    \addtocounter{inner}{-1}}}


\AtBeginDocument{%
\ifdefined\runNo
  \ifnum\the\runNo>\tw@
    \ifnum\csname npos-page-\thepage\endcsname>\z@
      \sortpos{\csname npos-page-\thepage\endcsname}%
\fi\fi\fi}

\AtBeginDocument{%
  \ifx\runNo\@undefined
    \newcount\runNo \runNo\@ne
  \else
    \ifnum\the\runNo<\thr@@
      \advance\runNo\@ne
  \fi\fi
  \write\@auxout{%
    \string\newcount\string\runNo ^^J
    \string\global\runNo=\the\runNo}}

\AtBeginShipout{%
\immediate\write\@auxout{\string\expandafter\string\gdef\noexpand\csname npos-page-\thepage\string\endcsname{\the\caption@counter@ii}}%
\ifdefined\runNo
  \ifnum\the\runNo>\tw@
    \ifnum\csname npos-page-\thepage\endcsname>\z@
      \global\advance\caption@counter@iii\csname npos-page-\thepage\endcsname
      \sortpos{\csname npos-page-\thepage\endcsname}%
\fi\fi\fi
\global\caption@counter@i\z@
\global\caption@counter@ii\z@}
\makeatother

\begin{document}

stuff

% doesnt work if this is commented ?!?!?!
\begin{figure*}[b]
\includegraphics[width=.99\linewidth]{example-image-a}%
\caption{fafqfaf}%
\end{figure*}

\begin{marginfigure}%
\includegraphics[width=\linewidth,height=2\linewidth]{example-image-a}%
\caption{a4a4a4a4a4}%
\end{marginfigure}

stuff

\newpage

\begin{figure*}[b]
\includegraphics[width=.99\linewidth]{example-image-a}%
\caption{A really long caption. A really long caption. A really long caption. A really long caption. A really long caption. A really long caption. A really long caption. A really long caption.}%
\end{figure*}

\begin{marginfigure}%
\includegraphics[width=\linewidth]{example-image-a}%
\caption{A really long caption}%
\end{marginfigure}

some more stuff

\newpage

\begin{figure*}[b]
\includegraphics[width=.99\linewidth]{example-image-a}%
\caption{A really long caption. A really long caption. A really long caption. A really long caption. A really long caption. A really long caption. A really long caption. A really long caption.}%
\end{figure*}

\begin{marginfigure}%
\includegraphics[width=\linewidth]{example-image-a}%
\caption{A really long caption}%
\end{marginfigure}

some more stuff

\end{document}

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .