11

When I mix \topinserts and \midinserts in Plain TeX, the floats are out of order:

\def\bsptext{%
Das ist ein Test. Das ist ein guter Test. Das ist ein langer Test. Mal sehen, ob alles funktioniert. Das ist ein Test. Das ist ein guter Test. Das ist ein langer Test. Mal sehen, ob alles funktioniert. Das ist ein Test. Das ist ein guter Test. Das ist ein langer Test. Mal sehen, ob alles funktioniert. Das ist ein Test. Das ist ein guter Test. Das ist ein langer Test. Mal sehen, ob alles funktioniert. Das ist ein Test. Das ist ein guter Test. Das ist ein langer Test. Mal sehen, ob alles funktioniert. Das ist ein Test. Das ist ein guter Test. Das ist ein langer Test. Mal sehen, ob alles funktioniert. Das ist ein Test. Das ist ein guter Test. Das ist ein langer Test. Mal sehen, ob alles funktioniert. Das ist ein Test. Das ist ein guter Test. Das ist ein langer Test. Mal sehen, ob alles funktioniert. Das ist ein Test. Das ist ein guter Test. Das ist ein langer Test. Mal sehen, ob alles funktioniert. Das ist ein Test. Das ist ein guter Test. Das ist ein langer Test. Mal sehen, ob alles funktioniert. Das ist ein Test. Das ist ein guter Test. Das ist ein langer Test. Mal sehen, ob alles funktioniert. Das ist ein Test. Das ist ein guter Test. Das ist ein langer Test. Mal sehen, ob alles funktioniert. Das ist ein Test. Das ist ein guter Test. Das ist ein langer Test. Mal sehen, ob alles funktioniert. Das ist ein Test. Das ist ein guter Test. Das ist ein langer Test. Mal sehen, ob alles funktioniert. Das ist ein Test. Das ist ein guter Test. Das ist ein langer Test. Mal sehen, ob alles funktioniert.}

\newcount\figno
\def\makefigno{\global\advance\figno1\relax Fig. \number\figno}
\def\mypicture{\vrule height 88pt depth 20pt width300pt}

\bsptext
\midinsert
\mypicture \makefigno
\endinsert

\topinsert
\mypicture \makefigno
\endinsert

\topinsert
\mypicture \makefigno
\endinsert

\midinsert
\mypicture \makefigno
\endinsert

\topinsert
\mypicture \makefigno
\endinsert

\midinsert
\mypicture \makefigno
\endinsert

\midinsert
\mypicture \makefigno
\endinsert

\bsptext

\bye

Is there a way to get the right order?

Wrong float order

  • In keeping with the traditions of TeX, number the figures by hand. BTW, a related question in LaTeX: https://tex.stackexchange.com/questions/425892/place-figure-at-bottom-of-this-page-or-the-top-of-the-next-page – John Kormylo Nov 09 '18 at 15:24
  • It is better to add a comment to an answer if you need more explanations than to put a request for more information in a bounty offer. In the first case the author of the answer is informed about your request. Thus if you want more information from wipet, I suggest to create a comment to his answer. – Udo Wermuth Aug 14 '19 at 12:24
  • Thanks for your comment, but the answers here are not what I am looking for. I like Plain TeX because of its simplicity and I sucseeded in writing many macros to get some functions as in LaTeX but more efficient (many packages add so many lines of code). But I do not know enough about output routines. I need floats in the right order as in LaTeX and wide floats in twocolum output (as \figure*). I have solved all the other problems I had (table of contents, cross referencing, hyperlinks, section headings, grid typesetting), but here my knowledge ends... – Weißer Kater Aug 14 '19 at 16:19
  • I think I understand your question now much better and I have reworked my answer. May I give an advice for future questions: Add more text not only code in a question. All answers and the comments went in a direction that has nothing to do with your intentions; more information in the question would have avoid that. – Udo Wermuth Aug 15 '19 at 20:28

2 Answers2

7

You can use \write primitive and read the right order of figures from auxiliary file in second run of TeX:

\newcount\figno
\newwrite\fileout
\newread\filein

\def\pgset#1{\advance\figno by1
   \expandafter\edef\csname pg:#1\endcsname{\the\figno}}

\openin\filein=\jobname.pg
\ifeof\filein \else \closein\filein \input\jobname.pg \fi
\figno=0

\immediate\openout\fileout=\jobname.pg
\def\printfigno{\global\advance\figno by1
   \expandafter\ifx\csname pg:\the\figno\endcsname\relax
   ??\the\figno \else \csname pg:\the\figno\endcsname \fi
   \edef\tmp{\write\fileout{\string\pgset{\the\figno}}}\tmp
}
\def\bsptext{%
Das ist ein Test. Das ist ein guter Test. Das ist ein langer Test. Mal sehen, ob alles funktioniert. Das ist ein Test. Das ist ein guter Test. Das ist ein langer Test. Mal sehen, ob alles funktioniert. Das ist ein Test. Das ist ein guter Test. Das ist ein langer Test. Mal sehen, ob alles funktioniert. Das ist ein Test. Das ist ein guter Test. Das ist ein langer Test. Mal sehen, ob alles funktioniert. Das ist ein Test. Das ist ein guter Test. Das ist ein langer Test. Mal sehen, ob alles funktioniert. Das ist ein Test. Das ist ein guter Test. Das ist ein langer Test. Mal sehen, ob alles funktioniert. Das ist ein Test. Das ist ein guter Test. Das ist ein langer Test. Mal sehen, ob alles funktioniert. Das ist ein Test. Das ist ein guter Test. Das ist ein langer Test. Mal sehen, ob alles funktioniert. Das ist ein Test. Das ist ein guter Test. Das ist ein langer Test. Mal sehen, ob alles funktioniert. Das ist ein Test. Das ist ein guter Test. Das ist ein langer Test. Mal sehen, ob alles funktioniert. Das ist ein Test. Das ist ein guter Test. Das ist ein langer Test. Mal sehen, ob alles funktioniert. Das ist ein Test. Das ist ein guter Test. Das ist ein langer Test. Mal sehen, ob alles funktioniert. Das ist ein Test. Das ist ein guter Test. Das ist ein langer Test. Mal sehen, ob alles funktioniert. Das ist ein Test. Das ist ein guter Test. Das ist ein langer Test. Mal sehen, ob alles funktioniert. Das ist ein Test. Das ist ein guter Test. Das ist ein langer Test. Mal sehen, ob alles funktioniert.}

\def\makefigno{Fig. \printfigno}
\def\mypicture{\vrule height 88pt depth 20pt width300pt}

\bsptext
\midinsert
\mypicture \makefigno
\endinsert

\topinsert
\mypicture \makefigno
\endinsert

\topinsert
\mypicture \makefigno
\endinsert

\midinsert
\mypicture \makefigno
\endinsert

\topinsert
\mypicture \makefigno
\endinsert

\midinsert
\mypicture \makefigno
\endinsert

\midinsert
\mypicture \makefigno
\endinsert

\bsptext

\bye
wipet
  • 74,238
3

Update: A major rework after understanding the question better (see comments above).

Plain TeX keeps the right sequence for each insertion class as stated on page 122 of the TeXbook:

Each class of insertions is independent, but \TeX preserves the order of insertions within a class.

There is only one insertion class for floats: \topinsert. All pageinserts are topinserts and midinserts are implemented using either topinsert or they use direct output. Thus topinsert and pageinserts are always in the right order but midinserts might get out of sequence.

For example, in your example Figure 1 (midinsert) is inserted after the first bsptext. Then you request a topinsert, i.e., a figure that has to be placed at the top of the current page or if that is not possible at the top of the next page. Here it is possible so it starts the current page. The next topinsert is placed after the first because it fits too. This mix gives inserts out of order.

The TeXbook describes the general procedure in chapter 15 on pages 115--116 (dangerous bend paragraphs); look at the example above exercise 15.5 and at this exercise for an example with topinserts and a single midinsert. The general procedure how inserts are added to pages is given on page 123 (double dangerous bend paragraphs).

On page 363 of the TeXbook you find the code. Look at the definition of \endinsert to see the conversion of midinserts: The first \if@mid checks if the insert fits on the page; if not @midfalse is set and the second \if@mid outputs a topinsert.

Conclusions:

(1) Midinserts and topinserts cannot be used in parallel without the risk of an out-of-order sequence;

(2) using only topinserts and pageinserts guarantees to keep the order.

(3) A new insertion class could be defined that places the floats not on the top of pages if (2) is not liked. In this class the order is kept. But this means also a change to the output routine as it must handle the insertions.

(4) A new command similar to midinserts can be defined that outputs floats either directly or converts them to topinserts and forces after the first conversion for the rest of the page additional midinserts to topinserts and topinserts to midinserts. This does not require a change to the output routine.

An approach for (3) can be found in this TUGboat article by David Salomon: http://www.tug.org/TUGboat/tb11-4/tb30salomon.pdf; see page 593ff.

How to implement (4)? A couple of \ifs are set if a new midinsert-like command is processed, let's call it seqinsert. If it is placed in the text \seqinsertplacedtrue is executed which will convert topinserts for the page. If a seqinsert is converted to a topinsert \blockseqinserts is set and all following seqinserts on the page are converted to topinserts too. Both flags are reset when the page number is increased; this happens in the macro \advancepageno.

\newif\ifitisaseqinsert  % true: a seqinsert is processed (used in \endinsert)
\newif\ifseqinsertplaced % true: a seqinsert was output as midinsert
\newif\ifblockseqinserts % true: a seqinsert was moved to the next page as topinsert

\def\seqinsert{\itisaseqinserttrue\midinsert}% extended \midinsert (not a \newinsert !)
\catcode`@=11
%               harmless changes to \endinsert; no problem with existing plain
\def\endinsert{\egroup % finish the \vbox
  \ifseqinsertplaced \if@mid\else \@midtrue\fi\fi                    % new (A)
  \if@mid
    \ifitisaseqinsert \ifblockseqinserts \@midfalse\p@gefalse\fi\fi  % new (B)
    \dimen@\ht\z@ \advance\dimen@\dp\z@ \advance\dimen@12\p@
    \advance\dimen@\pagetotal \advance\dimen@-\pageshrink
    \ifdim\dimen@>\pagegoal\@midfalse\p@gefalse
       \ifitisaseqinsert \global\blockseqinsertstrue                 % new (C)
          \global\seqinsertplacedfalse\fi                            % new
    \fi\fi
  \if@mid \bigskip\box\z@\bigbreak
     \ifitisaseqinsert \global\seqinsertplacedtrue\fi                % new (D)
  \else\insert\topins{\penalty100 % floating insertion
    \splittopskip\z@skip
    \splitmaxdepth\maxdimen \floatingpenalty\z@
    \ifp@ge \dimen@\dp\z@
    \vbox to\vsize{\unvbox\z@\kern-\dimen@}% depth is zero
    \else \box\z@\nobreak\bigskip\fi}\fi\endgroup
  \itisaseqinsertfalse}                                              % new (E)
\catcode`@=12
% minor addition for the output routine
\let\orgadvancepageno=\advancepageno
\def\advancepageno{\orgadvancepageno
   \global\blockseqinsertsfalse\global\seqinsertplacedfalse}% reset flags for every page

Here are the explanations of the changes to \endinsert:

(A) converts topinserts to midinserts if one seqinsert was not converted to a topinsert

(B) a seqinsert was converted to a topinsert so the next is converted too

(C) here the flag for the conversion seqinsert to topinsert is set (and the flag for (D) is reset to avoid overhead by (A))

(D) here the flag for a non-converted seqinsert is set

(E) reset the flag that shows that seqinsert was called

If this code is added to the example topinserts, pageinserts, and seqinserts can be used without getting out of sequence; the command \midinsert might destroy the sequence and should not be used.

For example, add the above code and change your output to:

\bsptext
\topinsert \mypicture \makefigno \endinsert
\seqinsert \mypicture \makefigno \endinsert
\topinsert \mypicture \makefigno \endinsert
\seqinsert \mypicture \makefigno \endinsert
\topinsert \mypicture \makefigno \endinsert
\seqinsert \mypicture \makefigno \endinsert
\seqinsert \mypicture \makefigno \endinsert
\bsptext

Note, I never used this code; I created it to answer the question.

Udo Wermuth
  • 2,612
  • @JohnKormylo The way how numbers are assigned has nothing to do with the placement of topinserts and midinserts. If the first midinsert is the third figure then its code shall be moved in the source. No one numbers the inserts 3,1,2. – Udo Wermuth Nov 09 '18 at 11:04
  • They do if they want to mix mid inserts and top inserts. Anyway, I meant to place the comment on the question. Sorry about that. – John Kormylo Nov 09 '18 at 15:25
  • @UdoWermuth Thank you so much. This is what I need. :) I am going to make some experiments, maybe I am now able to define something similar to \figure* from LaTeX in twocolumn output. May I ask you another question if I get into trouble? :) – Weißer Kater Aug 15 '19 at 20:54