2

I tried making a figure environment that is not a float.

Problem

When two figures are near enough that the second one gets pushed to the next page by the TeX page-breaking mechanism, the first of two gets ignored.

List of Figures

Here we see the output of my homemade list of figures, appropriately named, \mylistoffigures: enter image description here

Missing Image

Here we see the missing figure-not listed above: enter image description here

Code

My code is written such that no image.jpg is necessary. I adjusted the label-reference system to use my running counter. This is because I needed a unique number abstraction from the printed counter, because I reset the printed \thefigure at every language version, simulated by \setcurrentlanguage.

\documentclass{article}
\usepackage{fontspec}% xelatex
\usepackage{xparse}
\usepackage{environ}
\usepackage{lipsum}
\usepackage{tikz}
\usepackage{hyperref}
\makeatletter

% Define counter
\newcounter{runningfigurecounter}% latex counter, equiv of \global\newcount\runningfigurecounter\runningfigurecounter=0

% Redefine hyperref unique label
\renewcommand*{\theHfigure}{runningfigurecounter.\the\value{runningfigurecounter}} % Sets fifth field in second component of \newlabel to provide unique labels independent of what gets printed i.e. the counter \c@figure etc.

% https://tex.stackexchange.com/questions/273098/mimicking-latexs-table-of-contents-functionality?noredirect=1&lq=1
\long\def\myfigure@addtocontents#1#2{%
  \protected@write\@auxout%
    {\let\label\@gobble \let\index\@gobble \let\glossary\@gobble}%
    {\string\myfigure@writefile{#1}{#2}}}%use our own version of \@writefile

\long\def\myfigure@writefile#1#2{% Redefine our own file handler. This gets called by aux file macros.
  \@ifundefined{#1}\relax%
    {\@temptokena{#2}%
     \immediate\write\csname #1\endcsname{\the\@temptokena}% open file handler
    }%
}

\NewDocumentEnvironment{myfigure}{ O{} m O{build:fig:\therunningfigurecounter} }{% \therunningfigurecounter instead \the\runningfigurecounter because it was defined using latex and not tex
% 1: optional graphicx arg
% 2: file
% 3: label
    \myfigure@getBODY%
  }%
  {%
    \endmyfigure@getBODY%
    \refstepcounter{runningfigurecounter}% global backend figure counter
    \@temptokena{\protect\contentsline{figure}{\protect\numberline{\thefigure}\protect{\ignorespaces\myfigurecaption\relax\protect}}{\thepage}{figure.runningfigurecounter.\therunningfigurecounter}}% Contents Line (hyperref format)
    \myfigure@addtocontents{lof_\mylanguage}{\the\@temptokena}% Adds \@writefile{lof_en-US}{expanded contents of \@temptokena} to aux
    \def\@captype{figure}% see source2e for using caption outside of float
    \medbreak% adds \medskip but only if preceding space is less than what \medbreak would insert
    \centering% ensure figure and caption is centered within environment
    \vbox{% keep stuff on the same page
    \IfFileExists{#2}%
      {% ensure graphic exists in filesystem
        \caption{\myfigurecaption}% handles printed counter logic
        \label{#3}% requires redef of \label \ref \autoref \nameref to include build\mylanguage suffix
        \includegraphics[width=.8\linewidth,#1]{#2}%
      }% True
      {%
        \caption{\myfigurecaption}% handles printed counter logic
        \label{#3}% \requires redef of \label \ref \autoref \nameref to include build:\mylanguage suffix
        \tikz \draw [fill=red!15] (0,0) rectangle node [align=center] {Missing Image\\#2} (.8\linewidth, 50mm);
        \typeout{Error: Missing figure file #2.}%
      }% False
    }% end vbox
    \medbreak% adds \medskip but only if preceding space is less than what \medbreak would insert
  }%


\NewEnviron{myfigure@getBODY}% The goal of collecting \BODY of myfigure is to separate translated text from LaTeX code.
  {\global\let\myfigurecaption\BODY}%

  \NewDocumentCommand{\mylistoffigures}{}{% Provides list of figures local, works with \@mystarttoc
% to a language
  \newpage\section{\listfigurename
    \@mkboth{%
      \MakeUppercase\listfigurename}{\MakeUppercase\listfigurename}}%
  \@mystarttoc{lof_\mylanguage}\newpage% Language-specific lof file is only made if \mylistoffigures called. %\vfill% \vspace*{glue} fill vertical space below line  in which is appears %\vfill ends graf immediately ad add vertical space
}

\def\@mystarttoc#1{% imitates latex \@starttoc, except removes tf@ prefix to file handle
\begingroup
 \makeatletter
 \@input{\jobname.#1}%
 \if@filesw% Controls whether writing is enabled. Controls whether writing is enabled. Conditional returns false if    \nofiles is issued and no writing to aux has been performed.
   \expandafter\newwrite\csname #1\endcsname% make file handle
   \immediate\openout \csname #1\endcsname \jobname.#1\relax% open file handle
 \fi
 \@nobreakfalse
\endgroup}

\makeatother

\NewDocumentCommand\setcurrentlanguage{ m }{\edef\mylanguage{#1}}

\begin{document}
\setcurrentlanguage{en-US}
\mylistoffigures
\newpage
\begin{myfigure}{image.jpg}[fig:label]
Apples.
\end{myfigure}
\lipsum[1-3]

filler

filler

\begin{myfigure}{image.jpg}[fig:label]
Bananas.
\end{myfigure}
\lipsum[1-3]
\newpage
\begin{myfigure}{image.jpg}[fig:label]
Pears.
\end{myfigure}
\end{document}
2

You get

\contentsline {figure}{\numberline {2}{\ignorespaces Bananas.\relax }}{2}{figure.runningfigurecounter.2}
\contentsline {figure}{\numberline {2}{\ignorespaces Bananas.\relax }}{2}{figure.runningfigurecounter.2}
\contentsline {figure}{\numberline {3}{\ignorespaces Pears.\relax }}{4}{figure.runningfigurecounter.3}

in your .lof file as you are just writing the value of the token register when the page is shipped out, so if you have more than one figure on a page you get the last one repeated as often as there are figures.

You can use

\myfigure@addtocontents{lof_\mylanguage}{\protect\contentsline{figure}{\protect\numberline{\thefigure}\protect{\ignorespaces\myfigurecaption\relax\protect}}{\thepage}{figure.runningfigurecounter.\therunningfigurecounter}}% Adds \@writefile{lof_en-US}{expanded contents of \@temptokena} to aux

so just moving your \@temptokena settings to the addtocontents then you get

\contentsline {figure}{\numberline {0}{\ignorespaces Apples.\relax }}{2}{figure.runningfigurecounter.1}
\contentsline {figure}{\numberline {1}{\ignorespaces Bananas.\relax }}{2}{figure.runningfigurecounter.2}
\contentsline {figure}{\numberline {2}{\ignorespaces Pears.\relax }}{4}{figure.runningfigurecounter.3}

which gets three different entries but numbered 0,1,2 not 1,2,3 as you do the write before incrementing the counter.

So moving this to after the \caption gets:

enter image description here

\documentclass{article}
\usepackage{fontspec}% xelatex
\usepackage{xparse}
\usepackage{environ}
\usepackage{lipsum}
\usepackage{tikz}
\usepackage{hyperref}
\makeatletter

% Define counter
\newcounter{runningfigurecounter}% latex counter, equiv of \global\newcount\runningfigurecounter\runningfigurecounter=0

% Redefine hyperref unique label
\renewcommand*{\theHfigure}{runningfigurecounter.\the\value{runningfigurecounter}} % Sets fifth field in second component of \newlabel to provide unique labels independent of what gets printed i.e. the counter \c@figure etc.

% https://tex.stackexchange.com/questions/273098/mimicking-latexs-table-of-contents-functionality?noredirect=1&lq=1
\long\def\myfigure@addtocontents#1#2{%
  \protected@write\@auxout%
    {\let\label\@gobble \let\index\@gobble \let\glossary\@gobble}%
    {\string\myfigure@writefile{#1}{#2}}}%use our own version of \@writefile

\long\def\myfigure@writefile#1#2{% Redefine our own file handler. This gets called by aux file macros.
  \@ifundefined{#1}\relax%
    {\@temptokena{#2}%
     \immediate\write\csname #1\endcsname{\the\@temptokena}% open file handler
    }%
}

\NewDocumentEnvironment{myfigure}{ O{} m O{build:fig:\therunningfigurecounter} }{% \therunningfigurecounter instead \the\runningfigurecounter because it was defined using latex and not tex
% 1: optional graphicx arg
% 2: file
% 3: label
    \myfigure@getBODY%
  }%
  {%
    \endmyfigure@getBODY%
    \refstepcounter{runningfigurecounter}% global backend figure counter
    \def\@captype{figure}% see source2e for using caption outside of float
    \medbreak% adds \medskip but only if preceding space is less than what \medbreak would insert
    \centering% ensure figure and caption is centered within environment
    \vbox{% keep stuff on the same page
    \IfFileExists{#2}%
      {% ensure graphic exists in filesystem
        \caption{\myfigurecaption}% handles printed counter logic
        \label{#3}% requires redef of \label \ref \autoref \nameref to include build\mylanguage suffix
        \includegraphics[width=.8\linewidth,#1]{#2}%
      }% True
      {%
        \caption{\myfigurecaption}% handles printed counter logic
        \label{#3}% \requires redef of \label \ref \autoref \nameref to include build:\mylanguage suffix
        \tikz \draw [fill=red!15] (0,0) rectangle node [align=center] {Missing Image\\#2} (.8\linewidth, 50mm);
        \typeout{Error: Missing figure file #2.}%
      }% False
    \myfigure@addtocontents{lof_\mylanguage}{\protect\contentsline{figure}{\protect\numberline{\thefigure}\protect{\ignorespaces\myfigurecaption\relax\protect}}{\thepage}{figure.runningfigurecounter.\therunningfigurecounter}}% Adds \@writefile{lof_en-US}{expanded contents of \@temptokena} to aux
    }% end vbox
    \medbreak% adds \medskip but only if preceding space is less than what \medbreak would insert
  }%


\NewEnviron{myfigure@getBODY}% The goal of collecting \BODY of myfigure is to separate translated text from LaTeX code.
  {\global\let\myfigurecaption\BODY}%

  \NewDocumentCommand{\mylistoffigures}{}{% Provides list of figures local, works with \@mystarttoc
% to a language
  \newpage\section{\listfigurename
    \@mkboth{%
      \MakeUppercase\listfigurename}{\MakeUppercase\listfigurename}}%
  \@mystarttoc{lof_\mylanguage}\newpage% Language-specific lof file is only made if \mylistoffigures called. %\vfill% \vspace*{glue} fill vertical space below line  in which is appears %\vfill ends graf immediately ad add vertical space
}

\def\@mystarttoc#1{% imitates latex \@starttoc, except removes tf@ prefix to file handle
\begingroup
 \makeatletter
 \@input{\jobname.#1}%
 \if@filesw% Controls whether writing is enabled. Controls whether writing is enabled. Conditional returns false if    \nofiles is issued and no writing to aux has been performed.
   \expandafter\newwrite\csname #1\endcsname% make file handle
   \immediate\openout \csname #1\endcsname \jobname.#1\relax% open file handle
 \fi
 \@nobreakfalse
\endgroup}

\makeatother

\NewDocumentCommand\setcurrentlanguage{ m }{\edef\mylanguage{#1}}

\begin{document}
\setcurrentlanguage{en-US}
\mylistoffigures
\newpage
\begin{myfigure}{image.jpg}[fig:label]
Apples.
\end{myfigure}
\lipsum[1-3]

filler

filler

\begin{myfigure}{image.jpg}[fig:label]
Bananas.
\end{myfigure}
\lipsum[1-3]
\newpage
\begin{myfigure}{image.jpg}[fig:label]
Pears.
\end{myfigure}
\end{document}
  • 1
    @JonathanKomar it immediately writes from the aux file to the toc but it writes the wrong thing as the aux data was written with \protected@write which is non-immediate (look at the \myfigure@writefile lines in the aux file in this version and the original) you can't do an immediate write to the aux file as you will get the wrong page number in general. – David Carlisle Oct 31 '17 at 7:45
  • Aha, so \protected@write\@auxout is like a data holding tank, which in my version is holding \@temptokena, waiting to be expanded and written by a \shipout call. In your version, it is holding a serious of tokens that have been semi-expanded (except protected ones and \thepage, which get expanded by \shipout). Is that the right intepretation? – Jonathan Komar Oct 31 '17 at 9:21
  • more or less yes, protected write pre-expands everything (modulo latex \protect) but also prevents expansion of \thepage and then puts it all in a non immediate write to be expanded again in shipout, this time with \thepage expanding normally. @JonathanKomar – David Carlisle Oct 31 '17 at 9:24

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.