“过满的水平盒子”实际上并不是过满的水平盒子---如何处理位移的方程式数字

“过满的水平盒子”实际上并不是过满的水平盒子---如何处理位移的方程式数字

我经常会遇到方程式过长,以至于方程式编号会显得很尴尬,但又不会长到产生“水平盒子过满”的警告。例如,参见下图。

有问题的文档长达数百页,检查整个文档来查找此类问题非常不方便。理想情况下,像这样的东西会发出警告,这样我就可以快速跳转到有问题的方程式并修复它们。

在这种情况下有没有办法生成警告?

与此相关的是,看起来方程式相对于整个文本主体居中*,这导致左侧出现未使用的空间,这有助于防止方程式编号移位。在下面的例子中,方程式仍然太大,但在某些情况下,相对于左边距和方程式编号(而不是左右边距)居中可以防止这种情况发生。

有没有办法让方程的对齐将方程编号的宽度考虑进去?

我相信,如果 TeX 表现得好像右边距由方程式数字的左端定义(加上少量“填充”),那么这两个问题都可以解决。有办法实现这一点吗?

(* ---- 我实际上正在使用该fleqn选项,因此它并不是完全居中。)

“水平盒子过满”

\documentclass{article}

\usepackage{mathtools}

\usepackage[paperwidth=5in,left=.9375in,right=.9375in]{geometry}

\begin{document}

    \begin{equation}
        \begin{split}
            z & =a+b+c+d+e+f+g+h+i+j+k \\
            & +l+m+n+o+p+q+r+s+t+u+v % Does not throw a warning
        \end{split}
    \end{equation}

    \begin{equation}
        \begin{split}
            z & =a+b+c+d+e+f+g+h+i+j+k+l \\
            & +m+n+o+p+q+r+s+t+u+v % Should throw a warning
        \end{split}
    \end{equation}

    \begin{equation}
        \begin{split}
            z & =a+b+c+d+e+f+g+h+i+j+k+l+m\\&+n+o+p+q+r+s+t+u+v+w % Overfull hbox
        \end{split}
    \end{equation}

\end{document}

答案1

grmmbl 没有提供测试文件..

\documentclass{article}

\usepackage{amsmath}

\makeatletter
%  \begin{macro}{\place@tag}
%    \cs{place@tag} takes care of the placment of tags in the
%    \env{align} environments.
%    \begin{macrocode}
\def\place@tag{%
    \iftagsleft@
        \kern-\tagshift@
        \if1\shift@tag\row@\relax
            \rlap{\vbox{%
                \normalbaselines
                \boxz@
                \vbox to\lineht@{}%
                \raise@tag
            }}%
        \else
            \rlap{\boxz@}%
        \fi
        \kern\displaywidth@
    \else
        \kern-\tagshift@
        \if1\shift@tag\row@\relax
%    \end{macrocode}
%    Added depth to correct vertical spacing of shifted
%    equation tags.---dmj, 1994/12/29
%    \begin{macrocode}
\typeout{===========================}%
\typeout{BEWARE: EQUATION TAG MOVING}%
\typeout{BEWARE: \theequation}%
\typeout{===========================}%
            \llap{\vtop{%
                \raise@tag
                \normalbaselines
                \setbox\@ne\null
                \dp\@ne\lineht@
                \box\@ne
                \boxz@
            }}%
        \else
            \llap{\boxz@}%
        \fi
    \fi
}
%    \end{macrocode}
%  \end{macro}
%

\begin{document}

\setcounter{equation}{123456789}
aaa
\begin{flalign}
a+b+c+d&=111+222+333+444+555\\
aaaaaaa+bbbbbbb+cccccc+dddd+eeee&=b
\end{flalign}

bbb
\begin{flalign}
a+b+c+d&=111+222+333+444+5555\\
aaaaaaa+bbbbbbb+cccccc&=b
\end{flalign}
\end{document}

生产

在此处输入图片描述

其中(仅)方程编号 123456790 被移动。

终端和日志有微妙的警告:

===========================
BEWARE: EQUATION TAG MOVING
BEWARE: 123456790
===========================

答案2

解决方案

对齐

首先,我最终针对align环境采用的解决方案(主要归功于 David Carlisle)如下。

\apptocmd{\place@tag}{%
    \if1\shift@tag\row@%
    \PackageWarning{custom}{Equation number (\theequation ) has been displaced}%
    \fi 
}{}{}

方程

对于方程环境,我最终选择了以下解决方案(解释如下)。

\tikzcdset{ampersand replacement=\&} % This makes it so that "\&", not "&", is treated as the column separator in tikzcd environments.  (We do this so that it these environments can be passed as arguments (via "\BODY") to our redefinition of the equation environment.)  (See https://tex.stackexchange.com/questions/15093/.)
\let\@ldtikzcd\tikzcd % So that we don't have to manually write "\&", we update the "tikzcd" environment so that it automatically replaces the "&"s with "\&"s.
\let\@ldtikzcd\tikzcd
\let\@ldendtikzcd\endtikzcd
\RenewEnviron{tikzcd}[1][]{%
    \xpatchcmd*{\BODY}{&}{\&}{}{}% For compatibility with tikz.  (Also see \tikzcdset{ampersand replacement=\&} above.)
    \@ldtikzcd[#1]%
    \BODY%
    \@ldendtikzcd%
}

\newbox\tempb@x
\newdimen\stest
\newdimen\smin
\newdimen\smax
\def\find#1#2#3{% Used for \getstretch and \getshrink below.  (From https://tex.stackexchange.com/questions/191140/measure-total-glue-in-a-box)
    \let\@ldhbadness\hbadness% Need to temporarily redefine \hbadness and \hfuzz so don't get a ridiculously number of warnings as we test for stretchability/shrinkability.
    \hbadness=1000000%
    \let\@ldhfuzz\hfuzz%
    \hfuzz=\maxdimen%
    \smin = -\maxdimen
    \smax = \maxdimen
    \loop
    \stest = \smin
    \advance \stest by \smax
    \divide \stest by 2 % now \stest=(\smin+\smax)/2 truncated to 0
    \ifdim \stest = \smax
    \advance \stest by -1sp % ensure that \smin<=\stest<\smax.
    \fi
    \setbox 0 = \hbox spread #2 1sp {%
        \unhcopy\tempb@x
        \hskip 0pt #3-\stest
    }%
    \ifnum \badness > 100 % (shrink/stretch in \tempb@x) <= \stest
    \smax = \stest
    \else
    \smin = \stest
    \advance \smin by 1sp
    % since ">\stest" implies ">=\stest+1sp"
    \fi
    % In both cases, the interval [\smin,\smax] becomes smaller.
    \ifdim\smin<\smax
    \repeat
    \ifdim\smin>\smax\BOOM\fi% cannot happen, I think
    \hfuzz=\@ldhfuzz%
    \hbadness=\@ldhbadness%
}
\def\getstretch#1{% Returns the amount of stretchable glue in the \smin register.
    \setbox\tempb@x\hbox{#1}%
    \find{stretch}{}{plus}%
}
\def\getshrink#1{% Returns the amount of shrinkable glue in the \smin register.
    \setbox\tempb@x\hbox{#1}%
    \find{shrink}{-}{minus}%    
}

% Modifies "equation" environment to test if equation number has been displaced.
\ifdef{\mathindent}{}{\newlength{\mathindent}}% In case \mathindent hasn't been defined, define it. (Will be used in redefinition of equation environment.)
% Modifies "equation" environment to test if equation number has been displaced.
\newlength{\widthofequ@tion}
\newlength{\widthofnumber}
\newlength{\tempshrink@bility}
\newlength{\halfqu@d}
\def\initializelengthsdispl@ced{%
    \setlength{\halfqu@d}{\widthof{$\displaystyle \hspace{.5em}$}}%
    \getshrink{\ensuremath{\BODY}}% Stores shrink in \smin.
    \setlength{\tempshrink@bility}{\smin}%
    \setlength{\widthofequ@tion}{\widthof{\ensuremath{\displaystyle\BODY}}}%
    \setlength{\widthofnumber}{\widthof{\ensuremath{(\displaystyle\theequation )}}}%
}

\def\istagdispl@ced{% The Lua code expands to 1 if the equation number will be moved, and 0 otherwise.
    \directlua{% See pg. 188 of the TeXBook for the notation and algorithm.
        w0 = \strip@pt\widthofequ@tion;%
        e = \strip@pt\widthofnumber;%
        halfquad = \strip@pt\halfqu@d;%
        q = e+halfquad;% Knuth says that this should be a quad, not a half quad, but that doesn't seem to work (half a quad seems to work perfectly though).
        z = \strip@pt\displaywidth;%
        shrinkability = \strip@pt\tempshrink@bility;%.
        %
        if w0-shrinkability+q > z then
        tex.print("1");
        else
        tex.print("0");
        end
    }%
}
\def\settagdispl@ced{%
    \initializelengthsdispl@ced%
    \edef\tagdispl@ced{\istagdispl@ced}%
}
\let\@ldequation\equation
\let\@ldendequation\endequation
\RenewEnviron{equation}{%
    \@ldequation% Start equation environment.
    %
    \BODY% Display actual equation.
    % Don't want to "label" twice, so we disable labeling here (will be "label"ed below when defining \@tempequationlength, and need the label to appear there because we want it to be measured when it comes to the legnth).
    \let\@ldlabel\label%
    \renewcommand{\label}[1]{}%
    \let\@ldfootnote\footnote% Similarly for footnotes.
    \renewcommand{\footnote}[1]{}%
    % Actually test if the equation number has been displaced, and if so, give a warning.
    \settagdispl@ced%
    \if1\tagdispl@ced%
    \PackageWarning{custom}{Equation number (\theequation ) has been displaced}%
    \getshrink{\hspace{\mathindent}}%
    \hspace{\smin}% Sometimes there are cases where the equation number should be displaced, but instead the equation has been shifted into the left margin.  If this should be the case, we add the extra space to actually displace the equation number in order to avoid confusion.
    \fi%
    % Restore \footnote and \label commands.
    \let\footnote\@ldfootnote%
    \let\label\@ldlabel%
    %
    \@ldendequation% Close equation environment.
}

equation这实际上是我为获得与 David 发现的环境行为类似的环境行为问题而提出的第二个解决方案align

无论是第一次还是第二次尝试,我的基本思路都是测量方程的宽度和方程的编号,然后将这些宽度的总和与 进行比较\displaywidth。第一次尝试时,我天真地只考虑了这些,而且我通过实验发现,与 进行比较.95572663\displaywidth会产生更好的结果。这种方法还行,但如果字体或边距发生变化,或者只是某些方程发生变化,就会产生错误的结果(这里的有效系数只有在使用我最初测试的设置时才能完美地工作)。这有两个原因。(顺便说一句,我通过查看 TeX 显示方程的算法(如 TeXbook 第 188 页所述)找到了答案。)

我忘记考虑的一个显而易见的事情是方程和方程编号之间的空格——Knuth 说这是一个四边形,但只有当我使用半个四边形而不是一个四边形时,它才能完美地工作。我忘记考虑的不那么明显的是方程的可收缩性。TeX 首先尝试以方程的自然宽度显示方程,如果这样会取代方程编号,它会尝试将方程缩小到足以容纳方程编号。只有当收缩能力不够时,TeX 才会取代方程编号。因此,我真正想要比较的是

\widthof{EQUATION}+\widthof{NUMBER}+.5em-\shrinkability{EQUATION}

宽度\displaywidth

为了计算收缩率,我使用了以下解决方案这个问题。最终,我们利用宏\getshrink(上面定义),它接受一个参数,将其放入\hbox,并将其可收缩性存储\hbox在维度寄存器中\smin

然而不幸的是,事情仍然不像计算上面的长度、将其与进行比较\displaywidth、然后在必要时发出警告那么简单。

首先,使用\BODY来实际显示方程式并\BODY测量方程式的大小会导致 TeX 抱怨重复标记,因此我不得不手动禁用\label进行测量的那个。对于可能出现在方程式中的脚注也是如此。

第三个需要的“技巧”是考虑包含交换图的方程。显然,tikz将 的 catcode 更改&为活动字符,这在传递给其他宏时效果不佳。所以我不得不告诉tikzcd不要这样做(而是使用 来\&实现此目的);然后我“修补”了tikzcd环境以自动将每个 替换&\&

评论

如上所述,这需要 LuaLaTeX。我相信您只需使用 pdfLaTeX 即可完成相同的操作,但我发现为算法的主要部分编写 Lua 代码更容易。

我非常怀疑这样的方法是否能最终解决问题。仅在我的单个文档中,我就遇到了两个需要解决的障碍,因此我可以想象,这个当前解决方案会在我没有亲自测试的其他场景中失效。

此外,我对这类事情没有特别的经验,所以我的解决方案可能还有改进的空间。如果您发现任何改进方法,我将非常乐意听听。

完整的最小工作示例

最终的 MWE 如下所示(连同 .log 文件的输出和相关行)。

日志

Package custom Warning: Equation number (2) has been displaced on input line 151.
Package custom Warning: Equation number (3) has been displaced on input line 157.
Overfull \hbox (5.49112pt too wide) in paragraph at lines 157--157
Package custom Warning: Equation number (123456790) has been displaced on input line 164.
Package custom Warning: Equation number (123456791) has been displaced on input line 164.
Overfull \hbox (92.71948pt too wide) in paragraph at lines 164--164
Package custom Warning: Equation number (123456792) has been displaced on input line 170.
Overfull \hbox (33.8307pt too wide) in paragraph at lines 170--170

输出输出

代码

\makeatletter

\documentclass{article}

\usepackage{etoolbox}
\usepackage{regexpatch}
\usepackage{xparse}
\usepackage{calc}
\usepackage{environ}
\usepackage[paperwidth=5in,left=.9375in,right=.9375in]{geometry}
\usepackage{mathtools}
\usepackage{tikz}
\usetikzlibrary{cd}
\tikzcdset{ampersand replacement=\&} % This makes it so that "\&", not "&", is treated as the column separator in tikzcd environments.  (We do this so that it these environments can be passed as arguments (via "\BODY") to our redefinition of the equation environment.)  (See https://tex.stackexchange.com/questions/15093/.)
\let\@ldtikzcd\tikzcd % So that we don't have to manually write "\&", we update the "tikzcd" environment so that it automatically replaces the "&"s with "\&"s.
\let\@ldtikzcd\tikzcd
\let\@ldendtikzcd\endtikzcd
\RenewEnviron{tikzcd}[1][]{%
    \xpatchcmd*{\BODY}{&}{\&}{}{}% For compatibility with tikz.  (Also see \tikzcdset{ampersand replacement=\&} above.)
    \@ldtikzcd[#1]%
    \BODY%
    \@ldendtikzcd%
}

\newbox\tempb@x
\newdimen\stest
\newdimen\smin
\newdimen\smax
\def\find#1#2#3{% Used for \getstretch and \getshrink below.  (From https://tex.stackexchange.com/questions/191140/measure-total-glue-in-a-box)
    \let\@ldhbadness\hbadness% Need to temporarily redefine \hbadness and \hfuzz so don't get a ridiculously number of warnings as we test for stretchability/shrinkability.
    \hbadness=1000000%
    \let\@ldhfuzz\hfuzz%
    \hfuzz=\maxdimen%
    \smin = -\maxdimen
    \smax = \maxdimen
    \loop
    \stest = \smin
    \advance \stest by \smax
    \divide \stest by 2 % now \stest=(\smin+\smax)/2 truncated to 0
    \ifdim \stest = \smax
    \advance \stest by -1sp % ensure that \smin<=\stest<\smax.
    \fi
    \setbox 0 = \hbox spread #2 1sp {%
        \unhcopy\tempb@x
        \hskip 0pt #3-\stest
    }%
    \ifnum \badness > 100 % (shrink/stretch in \tempb@x) <= \stest
    \smax = \stest
    \else
    \smin = \stest
    \advance \smin by 1sp
    % since ">\stest" implies ">=\stest+1sp"
    \fi
    % In both cases, the interval [\smin,\smax] becomes smaller.
    \ifdim\smin<\smax
    \repeat
    \ifdim\smin>\smax\BOOM\fi% cannot happen, I think
    \hfuzz=\@ldhfuzz%
    \hbadness=\@ldhbadness%
}
\def\getstretch#1{% Returns the amount of stretchable glue in the \smin register.
    \setbox\tempb@x\hbox{#1}%
    \find{stretch}{}{plus}%
}
\def\getshrink#1{% Returns the amount of shrinkable glue in the \smin register.
    \setbox\tempb@x\hbox{#1}%
    \find{shrink}{-}{minus}%    
}

% Modifies "equation" environment to test if equation number has been displaced.
\ifdef{\mathindent}{}{\newlength{\mathindent}}% In case \mathindent hasn't been defined, define it. (Will be used in redefinition of equation environment.)
% Modifies "equation" environment to test if equation number has been displaced.
\newlength{\widthofequ@tion}
\newlength{\widthofnumber}
\newlength{\tempshrink@bility}
\newlength{\halfqu@d}
\def\initializelengthsdispl@ced{%
    \setlength{\halfqu@d}{\widthof{$\displaystyle \hspace{.5em}$}}%
    \getshrink{\ensuremath{\BODY}}% Stores shrink in \smin.
    \setlength{\tempshrink@bility}{\smin}%
    \setlength{\widthofequ@tion}{\widthof{\ensuremath{\displaystyle\BODY}}}%
    \setlength{\widthofnumber}{\widthof{\ensuremath{(\displaystyle\theequation )}}}%
}

\def\istagdispl@ced{% The Lua code expands to 1 if the equation number will be moved, and 0 otherwise.
    \directlua{% See pg. 188 of the TeXBook for the notation and algorithm.
        w0 = \strip@pt\widthofequ@tion;%
        e = \strip@pt\widthofnumber;%
        halfquad = \strip@pt\halfqu@d;%
        q = e+halfquad;% Knuth says that this should be a quad, not a half quad, but that doesn't seem to work (half a quad seems to work perfectly though).
        z = \strip@pt\displaywidth;%
        shrinkability = \strip@pt\tempshrink@bility;%.
        %
        if w0-shrinkability+q > z then
        tex.print("1");
        else
        tex.print("0");
        end
    }%
}
\def\settagdispl@ced{%
    \initializelengthsdispl@ced%
    \edef\tagdispl@ced{\istagdispl@ced}%
}
\let\@ldequation\equation
\let\@ldendequation\endequation
\RenewEnviron{equation}{%
    \@ldequation% Start equation environment.
    %
    \BODY% Display actual equation
    % Don't want to "label" twice, so we disable labeling here (will be "label"ed below when defining \@tempequationlength, and need the label to appear there because we want it to be measured when it comes to the legnth).
    \let\@ldlabel\label%
    \renewcommand{\label}[1]{}%
    \let\@ldfootnote\footnote% Similarly for footnotes.
    \renewcommand{\footnote}[1]{}%
    % Actually test if the equation number has been displaced, and if so, give a warning.
    \settagdispl@ced%
    \if1\tagdispl@ced%
    \PackageWarning{custom}{Equation number (\theequation ) has been displaced}%
    \getshrink{\hspace{\mathindent}}%
    \hspace{\smin}% Sometimes there are cases where the equation number should be displaced, but instead the equation has been shifted into the left margin.  If this should be the case, we add the extra space to actually displace the equation number in order to avoid confusion.
    \fi%
    % Restore \footnote and \label commands.
    \let\footnote\@ldfootnote%
    \let\label\@ldlabel%
    %
    \@ldendequation% Close equation environment.
}

% Something simliar as above but for "align" environments and related.  (See https://tex.stackexchange.com/questions/384222/.)
\apptocmd{\place@tag}{%
    \if1\shift@tag\row@%
    \PackageWarning{custom}{Equation number (\theequation ) has been displaced}%
    \fi 
}{}{}

\begin{document}

    \begin{equation}
    \begin{split}
    z & =a+b+c+d+e+f+g+h+i+j+k \\
    & +l+m+n+o+p+q+r+s+t+u+v % Does not throw a warning
    \end{split}
    \end{equation}

    \begin{equation}
    \begin{split}
    z & =a+b+c+d+e+f+g+h+i+j+k+l \\
    & +m+n+o+p+q+r+s+t+u+v % Should throw a warning
    \end{split}
    \end{equation}

    \begin{equation}
    \begin{split}
    z & =a+b+c+d+e+f+g+h+i+j+k+l+m\\&+n+o+p+q+r+s+t+u+v+w % Overfull hbox
    \end{split}
    \end{equation}

    \setcounter{equation}{123456789}
    aaa
    \begin{flalign}
    a+b+c+d&=111+222+333+444+555\\
    aaaaaaa+bbbbbbb+cccccc+dddd+eeee&=b
    \end{flalign}

    bbb
    \begin{flalign}
    a+b+c+d&=111+222+333+444+5555\\
    aaaaaaa+bbbbbbb+cccccc&=b
    \end{flalign}

\end{document}

\makeatother

相关内容