我想测试一下最后一段是否有超出范围的行。
段落结束后再查看 的简单方法\badness
是行不通的,因为它只会给出最后一行的 badness。例如,以下代码显示 之后的 badness 为 1000000,Hello, world!
但 之后的 badness 为 0 Hello, world, X!
,这仅仅是因为最后一行 ( X!
) 的宽度小于 15pt。
\hsize=15pt
Hello, world!\par \immediate\write16{BADNESS=\the\badness} % 1000000
Hello, world X!\par \immediate\write16{BADNESS=\the\badness} % 0
\bye
我使用它来尝试排版,使用不同的行长,直到找到一个足够大的行长以避免行过满。之后,我重新调整结果以使其适合页面。这对于文本来说当然是一个糟糕的想法,但我排版的材料是数学,我无法控制,有时可能无法很好地分成短行。最好得到一些难以阅读的东西(小),而不是让它超出页面。由于这个应用程序,我也对一种了解段落中最长行的方法感兴趣。
答案1
由于\badness
每次换行都会被覆盖,因此跟踪行错误的唯一方法是格式化TeX
一个段落,然后模仿它并逐行手动重新格式化,以跟踪我们感兴趣的内容。
这种分解-重组并不是特别难做,唯一烦人的部分是恢复精确的垂直间距(事实上我还没有完全做到)。一些问题已经展示了这种技术,请参阅在段落的每一行文字周围放置一个方框以及链接的。
但我不会用另一个答案中的一些简单的代码清理来烦你。在我看来,通过反复试验来自动化你正在执行的过程而不是仅仅促进它,这将是最有趣的(=对我来说很有趣)。
我编写了一个宏,它接受一个段落和一个行宽范围。使用给定的步骤进行探索,它会搜索最小化段落中存在的最大不良程度的那个。然后它将段落设置为最佳宽度,并重新缩放以适应原始宽度。
结果如下:
左边是原始线宽与最优线宽的比值,也就是比例因子。右边是每条线的劣化程度,最大值为粗体。
这比你要求的要多一点,但为了做到这一点,我必须找出每行的坏处(并跟踪最大值)。这正是你要做的,以找出一个段落是否包含过满的行(通过检查最大值,或者如果发现一个,也许更优雅地中断递归)。基本技术就在那里。
不过,我认为用某种工具来搜索最佳宽度并输出它比你手动操作更方便一些。
另外,你可能还对另一个答案感兴趣这里。
以下是代码:
% Unessential but I don't want to
% reimplement \resizebox from scratch.
\input miniltx
\input graphics.sty
% internal variables
\newbox\linebox
\newif\ifmeasuring
\newif\ifverbose
% line width variables:
\newdimen\lwMIN % range minimum
\newdimen\lwMAX % range maximum
\newdimen\lwINC % increase step
\newdimen\lwCUR % currently evaluated
\newdimen\lwOPT % optimum
% line badness variables:
\newcount\lbCUR % current line badness
\newcount\lbMAX % max of the paragraph
\newcount\lbOPT % optimum
% \fit formats the following paragraph
% finding the line width in the given
% range by minimizing the maximum line
% badness and rescaling in to fit.
\def\fit#1#2#3#4\par{
% read parameters
\lwMIN=#1
\lwMAX=#2
\lwINC=#3
% find optimum
\measuringtrue
\lwCUR=\lwMIN
\lbOPT=1000000
{\find#4\par}
% format optimum
\verbosetrue
\measuringfalse
\lwCUR=\hsize
{\hsize=\lwOPT\global\setbox0=\vbox{#4\par\eat}}
% place optimum
\noindent\resizebox\hsize!%
{\ifverbose\llap{\bf$\the\hsize\over\the\lwOPT$~}\fi\box0}\par
}
% \find searches line widths in the
% given range to find the optimum to
% format the passed paragraph
\def\find#1\par{
% prepare measure at current line width
\hsize=\lwCUR
\lbMAX=0
% measure paragraph's maximum badness
\setbox0=\vbox{#1\par\eat}
% if in valid range
\ifnum\lwCUR<\lwMAX
% update optima
\ifnum\lbMAX<\lbOPT
\global\lbOPT=\lbMAX
\global\lwOPT=\lwCUR\fi
% evaluate next line width
\advance\lwCUR\lwINC
\find#1\par\fi
}
% \eat eats lines of the last paragraph
% either measuring its maximum badness
% or re-setting it with annotations.
\def\eat{{
% eat line and measure its badness
\setbox\linebox=\lastbox
\setbox0=\hbox to \hsize{\unhcopy\linebox}
\lbCUR=\badness
% if measuring update the maximum badness of the paragraph
\ifmeasuring\ifnum\lbCUR>\lbMAX\global\lbMAX=\lbCUR\fi\fi
% unless paragraph is over
\ifvoid\linebox\else
% nest call and process preceeding line
\unskip\unpenalty\eat
% unless measuring
\ifmeasuring\else
% set current line
\ifhmode\break\fi\noindent\box\linebox
% if verbose annotate badnesses
\ifverbose\rlap{~$\ifnum\lbCUR=\lbOPT\bf\fi\the\lbCUR$}\fi\fi\fi
}}
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\input zapf\par\fit{200pt}{600pt}{.1pt}\input zapf\par
\input zapf\par\fit{250pt}{350pt}{10pt}\input zapf\par
\input zapf\par\fit{600pt}{800pt}{50pt}\input zapf\par
\input zapf\par
\bye