我粗略搜索后没有找到关于此内容的参考资料,因此我想我会发布一个问题;我知道这可能在 Tex 书或类似书籍中进行了详细讨论,但不幸的是我现在没有时间深入研究这些细节。
基本上,我试图理解答案中的代码在段落的每一行文字周围放置一个方框,特别是这个宏:
\def\hmmx{%
\@tempcnta\z@
\loop
\advance\@tempcnta\@ne
\setbox\z@\lastbox
\global\dimen@i\ht\z@
\skip@\lastskip\unskip
\count@\lastpenalty\unpenalty
\ifdim 5sp=\wd\z@
\else
\global\setbox\@ne\vbox{%
\penalty\count@
\vskip\skip@
\ifodd\@tempcnta
\hbox{\reflectbox{\box\z@}}%
\else
\box\z@
\fi
\unvbox\@ne
}%
\repeat
}%
我可以看到它循环,然后它基本上为一行设置初始框,这是有道理的 - 但我不明白,之后从源文件读取的字符在哪里?例如,如果有类似的东西:
\ifodd\@tempcnta
\hbox{\reflectbox{\box\z@}}\PROCEEDwithRESTofLINE%
\else
\box\z@\PROCEEDwithRESTofLINE
\fi
... 对我来说这更有意义 - 但显然那里没有\PROCEEDwithRESTofLINE
。我本来会假设像 a 这样的函数\PROCEEDwithRESTofLINE
会从输入(源文件)中读取下一个字符,并决定它是否属于该行:如果属于,则将字形写入 PDF 输出;如果不属于,则将输出换行符(除非 PDF 文字中没有“换行符”,如果我没记错的话 - 只是将文本字形定位在 X/Y 处);这将是“返回”函数的信号\hmmx
,以便\repeat
可以再次执行。但同样 - 这只是我的猜测;我不知道这实际上是如何工作的。
那么,有人可以详细分析一下 (La)Tex 中行处理的情况吗:考虑到逐个字符(或逐个标记)输入 tex,引擎何时将字形输出到 PDF;引擎如何知道何时换行;以及如何将(换行或新行)信号发送给上面的\loop
/ \repeat
- 以便代码只能在“打开”框时进行干预?我们不妨讨论最简单的例子,例如:
\documentclass{article}
\begin{document}
Just a long, or somewhat long line here; long enough to cause a line break within a paragraph in Latex ...
\end{document}
我知道这可能不是最简单的答案 - 因此,只要提供一些可以解释这个问题的文档的直接链接,我将非常感激...
答案1
描述 TeX 将段落分成行的算法需要一章书的篇幅。您可以在 TeXbook 或 TeX by Topic 中找到详细信息。
让我简单描述一下正常情况,不提悬挂缩进或\parshape
。首先,将整个段落读入内存。任何“水平命令”都会导致段落开始;通常\parindent
会插入一个宽度为前水平命令的操作,除非水平命令是\noindent
。\par
显式命令或由空行生成的命令启动将段落分成行的过程。
第一个操作是丢弃段落末尾可能出现的粘连,并添加\penalty10000
粘连\parfillskip
符,\penalty-'10000000000
从而强制进行最后的断句。
接下来,TeX 会检查段落,尝试不使用连字符来换行。尝试每个可行的换行点,并且只考虑不会导致行超出当前行的换行点\pretolerance
。如果发现一组换行符,TeX 会选择产生最少缺点的序列。这些缺点是通过查看相邻的行来评估的,高缺点对应于连续的视觉上不兼容的行:例如,一条松散的行旁边是一条紧密的行;松散或紧密是指单词间距的拉伸或收缩量。
如果不能\pretolerance
满足要求,TeX 会尝试使用连字符,这次它只会选择可行的断点,使行不超过当前的\tolerance
。在此阶段,还会对连续的连字符行或倒数第二行中的连字符进行扣分。
如果无法满足容差要求,TeX 会尝试另一个类似的过程,将当前的量添加到\emergencystretch
段落中的粘连可拉伸性中,以降低不良程度。如果连这一过程都失败了,TeX 会用过满或过少的行来排版段落。
无论如何,已经选定了断点序列。TeX 现在将材料传递到下一个阶段,通过拉伸或收缩可用的胶水,将每行打包成\hbox
宽度为 的行。\hsize
这些框是堆叠的,在它们之间可以插入惩罚项(例如,在第一行\clubpenalty
插入之后,最后一行\widowpenalty
使用之前);这些惩罚将影响分页。在这个惩罚之后(可以省略),计算并插入行间粘连。
最后,TeX 将框、惩罚和粘合的序列添加到当前垂直列表中,并练习页面构建器(基本上,它会尝试查看页面是否已完成)。
如果所有这些都发生在内部垂直模式中(基本上在 内\vbox
),则此列表仍然可用,直到贡献新的框为止。因此,如果确实如此
\setbox0=\lastbox
段落的最后一行将从垂直列表中删除,并存储在框寄存器 0 中。然后可以执行
\skip0=\lastskip\unskip
这会将最后一行上方的行间粘连存储在跳过寄存器 0 中并将其移除。类似地,可以将可能的惩罚存储在计数寄存器中,\lastpenalty
并使用 将其移除\unpenalty
。这个过程可以持续到所有贡献的框、粘连和惩罚都被移除。这些项目可以以各种方式使用;在审查的情况下,这些项目用于重建同一段,但通过交替水平反射框。请注意,该过程从最后一行向第一行倒退。
完成一段后,行数可在内部寄存器 中获得\prevgraf
。这可用于确保反映偶数行,方法是设置\@tempcnta=\prevgraf
和 而不是\advance\@tempcnta by -1
。\advance\@tempcnta by 1
这也可以避免使用具有“奇怪”厚度的特殊框来结束递归的技巧。
答案2
此代码有效向后 向上段落。首先将整个段落分成几行并放入一个框中(该框假定包含一行文本的水平框交替项和基线跳过粘贴的粘贴项)。
然后从列表末尾移除框(使用\lastbox
)并将其放入正在构建的新框中或先翻转,具体取决于奇偶校验,任何惩罚和跳过都会被类似地移除并复制,使用\unpenalty
和\unskip
,然后一旦在框 1 中构建了整个新框,原始答案中的下一段代码就会将其取消装箱到主垂直列表中,以便它参与分页。
答案3
请注意:我能找到的最好的探索方法是安装 Latex3,然后使用绎在OP中的文件上进行打包,如下所示:
\documentclass{article}
\tracingonline=1
\tracingoutput=1
\tracingpages=1
\tracingparagraphs=1
\usepackage{unravel}
\begin{document}
\showoutput
\unravel{
Just a long, or somewhat long line here; long enough to cause a line break within a paragraph in Latex ...
\end{document}
}\relax
...然后运行pdflatex test.tex
,并在终端中输入 Enter。
我最终会尝试写出一个更好的答案;但基本上,首先进行字符扫描;只有在添加期间\end{document}
,\par
并且只有在此后,显然才先计算断线:
||
|> Just a long, or somewhat long line here; long enough to cause a line
|> break within a paragraph in Latex ... \end {document}
[===== Step 1 =====] = blank space
||
|> Just a long, or somewhat long line here; long enough to cause a line
|> break within a paragraph in Latex ... \end {document}
[===== Step 2 =====] J= the letter J : \everypar={}
||
|> Just a long, or somewhat long line here; long enough to cause a line
|> break within a paragraph in Latex ... \end {document}
...
[===== Step 109 =====] = blank space
<| Just a long, or somewhat long line here; long enough to cause a line
<| break within a paragraph in Latex ...
||
|> \end {document}
[===== Step 110 =====] \end = macro:#1->\csname end#1\endcsname \@chec...
<| Just a long, or somewhat long line here; long enough to cause a line
<| break within a paragraph in Latex ...
||
|> \csname enddocument\endcsname \@checkend {document}\expandafter
|> \endgroup \if@endpe \@doendpe \fi \if@ignore \@ignorefalse \ignorespaces
|> \fi
...
[===== Step 133 =====] \if@nobreak =false
<| Just a long, or somewhat long line here; long enough to cause a line
<| break within a paragraph in Latex ...
||
|> \par \vfil \penalty -\@M \write \m@ne {}\vbox {}\penalty -\@Mi
|> \begingroup \if@filesw \immediate \closeout \@mainaux \let \@setckpt
|> \@gobbletwo \let \@newl@bel \@testdef \@tempswafalse \makeatletter \@@input
|> \jobname .aux \fi \@dofilelist \ifdim \font@submax >\fontsubfuzz \relax
|> \@font... (465 chars)
@firstpass
[]\OT1/cmr/m/n/10 Just a long, or somewhat long line here; long enough to cause
a line break
@ via @@0 b=1 p=0 d=121
@@1: line 1.2 t=121 -> @@0
within a paragraph in Latex ...
@\par via @@1 b=0 p=-10000 d=100
@@2: line 2.2- t=221 -> @@1
%% goal height=550.0, max depth=5.0
% t=0.0 g=550.0 b=10000 p=0 c=100000#
% t=10.0 g=550.0 b=10000 p=300 c=100000#
[===== Step 134 =====] Paragraph end.
<| Just a long, or somewhat long line here; long enough to cause a line
<| break within a paragraph in Latex ... \par
||
|> \vfil \penalty -\@M \write \m@ne {}\vbox {}\penalty -\@Mi \begingroup
|> \if@filesw \immediate \closeout \@mainaux \let \@setckpt \@gobbletwo \let
|> \@newl@bel \@testdef \@tempswafalse \makeatletter \@@input \jobname .aux
|> \fi \@dofilelist \ifdim \font@submax >\fontsubfuzz \relax \@font@warn...
|> (460 chars)
...
[===== Step 137 =====] -
<| Just a long, or somewhat long line here; long enough to cause a line
<| break within a paragraph in Latex ... \par
|| \penalty -
|> \@M \write \m@ne {}\vbox {}\penalty -\@Mi \begingroup \if@filesw
|> \immediate \closeout \@mainaux \let \@setckpt \@gobbletwo \let \@newl@bel
|> \@testdef \@tempswafalse \makeatletter \@@input \jobname .aux \fi
|> \@dofilelist \ifdim \font@submax >\fontsubfuzz \relax \@font@warning {Size
|> substi... (444 chars)
[===== Step 138 =====] \@M = \mathchar"2710= 10000
<| Just a long, or somewhat long line here; long enough to cause a line
<| break within a paragraph in Latex ... \par
|| \penalty -10000
|> \write \m@ne {}\vbox {}\penalty -\@Mi \begingroup \if@filesw \immediate
|> \closeout \@mainaux \let \@setckpt \@gobbletwo \let \@newl@bel \@testdef
|> \@tempswafalse \makeatletter \@@input \jobname .aux \fi \@dofilelist \ifdim
|> \font@submax >\fontsubfuzz \relax \@font@warning {Size substituti... (440
|> chars)
% t=22.0 g=550.0 b=10000 p=0 c=100000#
% t=23.94444 plus 1.0fil g=550.0 b=0 p=-10000 c=-10000#
Completed box being shipped out [1]
\vbox(633.0+0.0)x407.0
.\glue 16.0
.\vbox(617.0+0.0)x345.0, shifted 62.0
..\vbox(12.0+0.0)x345.0, glue set 12.0fil
...\glue 0.0 plus 1.0fil
[...]
...\hbox(6.94444+1.94444)x345.0, glue set 0.19885
....\hbox(0.0+0.0)x15.0
....\OT1/cmr/m/n/10 J
....\OT1/cmr/m/n/10 u
....\OT1/cmr/m/n/10 s
....\OT1/cmr/m/n/10 t
....\glue 3.33333 plus 1.66666 minus 1.11111
....\OT1/cmr/m/n/10 a
[...]
[===== Step 144 =====] \vbox
<| Just a long, or somewhat long line here; long enough to cause a line
<| break within a paragraph in Latex ... \par
|| \vbox
|> {}\penalty -\@Mi \begingroup \if@filesw \immediate \closeout \@mainaux
|> \let \@setckpt \@gobbletwo \let \@newl@bel \@testdef \@tempswafalse
|> \makeatletter \@@input \jobname .aux \fi \@dofilelist \ifdim \font@submax
|> >\fontsubfuzz \relax \@font@warning {Size substitutions with
|> differences\... (419 chars)
[===== Step 145 =====] \vbox {
<| Just a long, or somewhat long line here; long enough to cause a line
<| break within a paragraph in Latex ... \par \vbox {
||
|> }\penalty -\@Mi \begingroup \if@filesw \immediate \closeout \@mainaux
|> \let \@setckpt \@gobbletwo \let \@newl@bel \@testdef \@tempswafalse
|> \makeatletter \@@input \jobname .aux \fi \@dofilelist \ifdim \font@submax
|> >\fontsubfuzz \relax \@font@warning {Size substitutions with
|> differences\M... (418 chars)
%% goal height=550.0, max depth=5.0
% t=0.0 g=550.0 b=10000 p=0 c=100000#
...
[===== Step 203 =====] Set \deadcycles(\deadcycles)=0
<| Just a long, or somewhat long line here; long enough to cause a line
<| break within a paragraph in Latex ... \par \vbox {}\begingroup \endgroup
||
|> \@@end \@checkend {document}\expandafter \endgroup \if@endpe \@doendpe
|> \fi \if@ignore \@ignorefalse \ignorespaces \fi
[===== Step 204 =====] End everything!
<| Just a long, or somewhat long line here; long enough to cause a line
<| break within a paragraph in Latex ... \par \vbox {}\begingroup \endgroup
||
|> \tex_hbox:D to\tex_hsize:D {}\tex_vfill:D \tex_penalty:D -'10000000000
|> \@@end \@checkend {document}\expandafter \endgroup \if@endpe \@doendpe \fi
|> \if@ignore \@ignorefalse \ignorespaces \fi
[===== Step 205 =====] \tex_hbox:D
<| Just a long, or somewhat long line here; long enough to cause a line
<| break within a paragraph in Latex ... \par \vbox {}\begingroup \endgroup
|| \tex_hbox:D
|> to\tex_hsize:D {}\tex_vfill:D \tex_penalty:D -'10000000000 \@@end
|> \@checkend {document}\expandafter \endgroup \if@endpe \@doendpe \fi
|> \if@ignore \@ignorefalse \ignorespaces \fi
...
[===== Step 208 =====] \tex_hbox:D to345.0pt{
<| Just a long, or somewhat long line here; long enough to cause a line
<| break within a paragraph in Latex ... \par \vbox {}\begingroup \endgroup
<| \tex_hbox:D to345.0pt{
||
|> }\tex_vfill:D \tex_penalty:D -'10000000000 \@@end \@checkend
|> {document}\expandafter \endgroup \if@endpe \@doendpe \fi \if@ignore
|> \@ignorefalse \ignorespaces \fi
%% goal height=550.0, max depth=5.0
% t=0.0 g=550.0 b=10000 p=0 c=100000#
...
[===== Step 224 =====] 0
<| Just a long, or somewhat long line here; long enough to cause a line
<| break within a paragraph in Latex ... \par \vbox {}\begingroup \endgroup
<| \tex_hbox:D to345.0pt{}
|| \tex_penalty:D -'10000000000
|> \@@end \@checkend {document}\expandafter \endgroup \if@endpe \@doendpe
|> \fi \if@ignore \@ignorefalse \ignorespaces \fi
% t=10.0 g=550.0 b=10000 p=0 c=100000#
% t=10.0 plus 1.0fill g=550.0 b=0 p=-1073741824 c=-1073741824#
...
[===== Step 246 =====] 0
<| Just a long, or somewhat long line here; long enough to cause a line
<| break within a paragraph in Latex ... \par \vbox {}\begingroup \endgroup
<| \tex_hbox:D to345.0pt{}\tex_hbox:D to345.0pt{}
|| \tex_penalty:D -'10000000000
|> \@@end \@checkend {document}\expandafter \endgroup \if@endpe \@doendpe
|> \fi \if@ignore \@ignorefalse \ignorespaces \fi
% t=10.0 g=16383.99998 b=10000 p=0 c=100000#
% t=10.0 plus 1.0fill g=16383.99998 b=0 p=-1073741824 c=-1073741824#
[===== Step 247 =====] \tex_penalty:D -'10000000000
<| Just a long, or somewhat long line here; long enough to cause a line
<| break within a paragraph in Latex ... \par \vbox {}\begingroup \endgroup
<| \tex_hbox:D to345.0pt{}\tex_hbox:D to345.0pt{}
||
|> \@@end \@checkend {document}\expandafter \endgroup \if@endpe \@doendpe
|> \fi \if@ignore \@ignorefalse \ignorespaces \fi
[===== The end! =====]
)</path/to/texlive/2011/texmf-dist/fonts/type1/public/amsfonts/cm/cmr
10.pfb>
Output written on teest.pdf (1 page, 15383 bytes).