当我在 Plain TeX 中混合\topinsert
s 和\midinsert
s 时,浮点数会乱序:
\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
有没有办法得到正确的顺序?
答案1
更新:在更好地理解问题之后进行了重大的返工(参见上面的评论)。
Plain TeX 为每个插入类保留正确的顺序,如 TeXbook 第 122 页所述:
每类插入都是独立的,但
\TeX
保留了类内插入的顺序。
浮点数只有一个插入类:\topinsert
。所有页面插入都是 topinsert,midinsert 要么使用 topinsert 实现,要么使用直接输出。因此,topinsert 和 pageinsert 始终按正确顺序排列,但 midinsert 可能会乱序。
例如,在您的示例中,图 1(midinsert)插入在第一个 bsptext 之后。然后您请求一个 topinsert,即必须放置在当前页面顶部的图,或者如果无法放置在下一页顶部,则放置在下一页顶部。这里可以这样做,因此它从当前页面开始。下一个 topinsert 放置在第一个之后,因为它也合适。这种混合会导致插入顺序混乱。
TeXbook 在第 15 章第 115-116 页(危险弯曲段落)中描述了一般程序;请查看练习 15.5 上方的示例以及本练习中的示例,其中有顶部插入和单个中间插入。第 123 页(双重危险弯曲段落)给出了将插入内容添加到页面的一般程序。
在 TeXbook 的第 363 页上,您可以找到代码。查看 \endinsert 的定义以了解 midinserts 的转换:第一个 \if@mid 检查插入内容是否适合页面;如果不适合,则设置 @midfalse,第二个 \if@mid 输出 topinsert。
结论:
(1) Midinsert 和 topinsert 不能并行使用,否则有乱序的风险;
(2)仅使用 topinserts 和 pageinserts 保证保持顺序。
(3)如果不喜欢(2),可以定义一个新的插入类,将浮动元素放置在页面顶部以外的位置。在这个类中,顺序保持不变。但这也意味着输出例程的改变,因为它必须处理插入。
(4)可以定义一个类似于 midinserts 的新命令,该命令直接输出浮点数或将其转换为 topinserts,并在对页面的其余部分进行第一次转换后强制将其他 midinserts 转换为 topinserts,将 topinserts 转换为 midinserts。这不需要更改输出例程。
对于(3)的方法,可以在 David Salomon 撰写的 TUGboat 文章中找到: http://www.tug.org/TUGboat/tb11-4/tb30salomon.pdf;参见第 593 页。
如何实现(4)?如果处理了新的 midinsert 类命令,则会设置几个\if
s,我们称之为序插入。如果将其放置在文本中,\seqinsertplacedtrue
则执行将为页面转换 topinserts 的命令。如果将 seqinsert 转换为 topinsert,则
\blockseqinserts
设置该命令,并且页面上所有后续 seqinserts 也将转换为 topinserts。当页码增加时,两个标志都会重置;这发生在宏中\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
以下是对 的更改的解释\endinsert
:
(A)如果一个 seqinsert 未转换为 topinsert,则将 topinsert 转换为 midinsert
(B)seqinsert 被转换为 topinsert,因此下一个也被转换
(C)这里设置了将 seqinsert 转换为 topinsert 的标志(并且重置了(D)的标志以避免(A)的开销)
(D)此处设置了未转换序列插入的标志
(E)重置表明 seqinsert 被调用的标志
如果将此代码添加到示例 topinserts、pageinserts 和 seqinserts 中,则可以使用而不会超出序列;该命令
\midinsert
可能会破坏序列,因此不应使用。
例如,添加上述代码并将输出更改为:
\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
请注意,我从未使用过此代码;我创建它是为了回答这个问题。
答案2
您可以使用\write
原始数据并在 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