调试 - 数学模式下的间距

调试 - 数学模式下的间距

我试图在数学模式下进行一些与间距相关的调试。更具体地说,我想创建一些垂直标尺来检查数学模式下水平对齐的差异。通过这个问题,我想为调试提供一般帮助。我为此苦苦挣扎,也在这里找到了好的资源和相关问题,所以我想解决找不到任何关于一般的 在 Latex 数学模式中调试。mhelvens 的问题给我留下了深刻的印象(当大量使用 \phantom、\mathrlap 等时,如何修复数学间距?),并希望使用线条/标尺创建类似的输出,如他提供的最后一张图片所示:

目标

在获取这些尺子的过程中,我遇到了一些问题。

资源

我所研究的内容可能对其他人也有帮助:

相关问题汇总

我只是列出这些作为有用的补充。它们有些很难找到,或者没有标记为调试相关,所以它们也可能是很好的资源。

数学环境的垂直扩展

出现的一个问题是数学环境总是根据其内容(垂直)扩展。

编辑:使用\smash可以解决第一个问题。感谢 Circumscribe 的指出!更多信息请参阅 Alexander R. Perlis 的资源。

水平间距因使用\mathrlap

\mathrlap,相当于\rlap数学模式中的 ,不应占用任何水平空间。但它确实会改变间距。请考虑以下示例:

\documentclass[varwidth=3cm,margin=2mm]{standalone}

\usepackage{amsmath}
\usepackage{mathtools}
\usepackage{xcolor}

\AtBeginDocument{%
    \abovedisplayskip=0pt%12pt plus 3pt minus 9pt
    \abovedisplayshortskip=0pt%0pt plus 3pt
    \belowdisplayskip=0pt%12pt plus 3pt minus 9pt
    \belowdisplayshortskip=0pt%7pt plus 3pt minus 4pt
}


\begin{document}
\noindent%
\begin{align} % two plain \mathrlaps (red)
    \Phi = \mathrlap{\smash{\color{red}\vrule depth 5pt height 10pt width .2pt}}\sum
    \mathrlap{\smash{\color{red}\vrule depth 20pt height 10pt width .2pt}}
\end{align}

\begin{align} % one plain \mathrlap (orange)
    \Phi = \sum
    \mathrlap{\smash{\color{orange}\vrule depth 20pt height 10pt width .2pt}}
\end{align}

\begin{align} % target spacing (untouched)
    \Phi = \sum
\end{align}

\begin{align} % one \mathrlap in the beginning: no issues (green)
    \mathrlap{\smash{\color{green}\vrule depth 30pt height 80pt width .2pt}}
    \Phi = \sum
\end{align}

\begin{align} % corrected \mathrlaps (blue)
    \Phi = \mathopen{\mathrlap{\smash{\color{blue}\vrule depth 5pt height 95pt width .2pt}}}
    \sum
    \mathclose{\mathrlap{\smash{\color{blue}\vrule depth 5pt height 60pt width .2pt}}}
\end{align}
\end{document}

mathrlap改变水平间距

(1)有两个简单的 rlap:求和之前和之后(红色)。
(2)有一个简单的 rlap:求和之后(橙色)。
(3)根本没有 rlap(目标布局)。
(4)有一个简单的 rlap:等式之前(绿色)。
(5)有两个更正的 rlap:求和之前和之后(蓝色)。

那么如何在数学模式下实现 rlaps 周围的适当间距?这与数学原子有关,请参阅 egreg 对此问题的精彩回答: 右对齐环境的正确对齐的正确空间是多少?

在我上面的示例代码中,你可以看到开头的 rlaps 不会影响定位。我手动强制其他 rlaps 被视为合适的原子类型。我怎样才能实现这一自动化?也许这个问题可以帮助改进包\mathrlap中的宏mathtools


在 Circumscribe 的回答中,可以使用以下方法解决重复打印问题

\newcommand*\printlater@printnow[2]{%
  \@ifundefined{printlater@anchorx@#1}{}{%
    \AtBeginShipoutNext{%
      \AtBeginShipoutUpperLeft{%
        \message{*** PRINTING NO #1 ***
        }%
        \@ifundefined{printlater@printed@#1}{\message{***UNDEF***}%
          \setlength{\unitlength}{1sp}%
          \put(\@nameuse{printlater@anchorx@#1},\the\numexpr\@nameuse{printlater@anchory@#1}-\paperheight){#2}%
          \expandafter\gdef\csname printlater@printed@#1\endcsname{\relax}
        }{\message{***DEF***}}%
      }%
    }%
  }%
}

您可以安全地删除\messages,它们只是为了调试。

答案1

这是一个相当复杂的解决方案,它根本不会影响任何间距(水平或垂直)。\vrule您可以存储想要打印的坐标,然后在发货前(即实际创建页面时)在此位置打印,而不是立即打印。

以下代码需要运行两次才能运行,我已经使用 LaTeX、pdfLaTeX、LuaLaTeX 和 XeLaTeX 对其进行了测试。\begin{document}和之间的部分\end{document}与您的代码相同,只是我将其替换\mathrlap{\smash{...}}\printlater{...}

(此代码似乎与standalone类不兼容,可能是因为\paperheight不是实际的纸张高度。)

%\documentclass[varwidth=3cm,margin=2mm]{standalone} %% <- does not work, not sure why
\documentclass[a4paper]{article} %% <- paper size option is important!

\usepackage{amsmath} 
%% ^ remove \ifmeasuring@\else and \fi below if you don't use amsmath
% \usepackage{mathtools} %% <- not needed
\usepackage{xcolor}

\AtBeginDocument{%
    \abovedisplayskip=0pt%12pt plus 3pt minus 9pt
    \abovedisplayshortskip=0pt%0pt plus 3pt
    \belowdisplayskip=0pt%12pt plus 3pt minus 9pt
    \belowdisplayshortskip=0pt%7pt plus 3pt minus 4pt
}

\usepackage{atbegshi} %% <- for \AtBeginShipout and \AtBeginShipoutUpperLeft

\makeatletter %% <- change @ so that it can be used in command names

  \@ifundefined{savepos}
  {  %% <- For pdflatex and xelatex
    \let\savepos\pdfsavepos
    \let\lastxpos\pdflastxpos
    \let\lastypos\pdflastypos
  }{} %% <- For lualatex
  \newcounter{printlatercounter} %% <- unique ids for things we want to print later
  \newcommand*\printlater[1]{%
    \ifmeasuring@\else %% <- Do nothing if in amsmath's "measuring" mode
      \stepcounter{printlatercounter}%
      \expandafter\printlater@writepos\expandafter{\number\c@printlatercounter}%
      \expandafter\printlater@printnow\expandafter{\number\c@printlatercounter}{#1}%
    \fi
  }
  \newcommand*\printlater@writepos[1]{% %% <- Writes the current coordinates to the aux file
    \savepos %% <- get current position on page
    \write\@auxout{\global\string\@namedef{printlater@anchorx@#1}{\the\lastxpos}}% %% <- write x coord.
    \write\@auxout{\global\string\@namedef{printlater@anchory@#1}{\the\lastypos}}% %% <- write y coord.
  }
  \newcommand*\printlater@printnow[2]{% %% <- Prints #2 at the #1-th set of coordinates
    \@ifundefined{printlater@anchorx@#1}{}{% %% <- test if the coordinates are set
      \AtBeginShipoutNext{% %% <- execute just before shipout
        \AtBeginShipoutUpperLeft{% %% <- insert things at the top left corner of the page
          \setlength{\unitlength}{1sp}% %% <- coordinates are in sp (65536sp = 1pt)
          \put(\@nameuse{printlater@anchorx@#1},\the\numexpr\@nameuse{printlater@anchory@#1}-\paperheight){#2}%
        }%
      }%
    }%
  }

\makeatother %% <- return @ to normal

\begin{document}

\begin{align*} % two plain \printlaters (red)
    \Phi = \printlater{\color{red}\vrule depth 5pt height 10pt width .2pt}\sum
    \printlater{\color{red}\vrule depth 20pt height 10pt width .2pt}
\end{align*}

\begin{align*} % one plain \printlater (orange)
    \Phi = \sum
    \printlater{\color{orange}\vrule depth 20pt height 10pt width .2pt}
\end{align*}

\begin{align*} % target spacing (untouched)
    \Phi = \sum
\end{align*}

\begin{align*} % one \printlater in the beginning (green)
    \printlater{\color{green}\vrule depth 30pt height 80pt width .2pt}
    \Phi = \sum
\end{align*}

\begin{align*} % corrected \printlaters (blue)
    \Phi = \mathopen{\printlater{\color{blue}\vrule depth 5pt height 95pt width .2pt}}
    \sum
    \mathclose{\printlater{\color{blue}\vrule depth 5pt height 60pt width .2pt}}
\end{align*}

\end{document}

笔记:[a4paper]提供的选项很\documentclass重要,因为它可以确保\paperheight(我们需要的)值是正确的。默认情况下,\paperheight即使您正在制作 A4 文档,其值也为 279.4 毫米(信纸大小),这会导致所有规则偏移 297 毫米 - 279.4 毫米 = 17.6 毫米。(只有当xcolor包也被删除时才会发生这种情况,因为由于某种原因,加载此包也会改变的值\paperheight。)

在此处输入图片描述

我上面定义的宏\printlater接受一个参数,并在 shipout 之前将其打印在当前位置。它做了四件事

  • 它首先使用 来检查我们是否处于 的amsmath“测量模式” \ifmeasuring@,如果是,则不执行任何操作。如果没有此检查,则\printlater每当在环境等内部使用 的参数时,都会打印两次align。(参见这个问题
  • 它会增加printlatercounter,为我们想要打印的内容生成唯一的 ID。
  • \(pdf)savepos它使用和检索当前 x/y 坐标,\(pdf)last<x/y>pos并将它们作为写入辅助文件\printlater@anchor<x/y>@<id>。这些坐标以 sp(65536sp = 1pt)为单位,相对于页面的左下角。
  • 如果printlater@x@<id>已定义(第二次运行时也是如此),它会指示 TeX 在传送页面之前打印参数。宏\AtBeginShipoutNext\AtBeginShipoutUpperLeft来自atbegshi包裹处理此事。

之所以需要两次运行,是因为直到页面交付时才知道内容的确切坐标,而那时再打印其他内容就太晚了。但是,我们仍然可以将这些坐标写入辅助文件,然后在第二次运行时使用它们。

如果您想要手动移动其中一个\vrules,比如说.5pt向左移动,您可以在它前面加上一个\hskip-.5pt(在 内\printlater)。

在 shipout 上绘制的内容目前在后台,在后面文本(您不会注意到,因为它没有触及任何文本)。这可能是可取的,但如果您希望它位于前台,您需要使用,\AtBeginShipoutUpperLeftForeground而不是\AtBeginShipoutUpperLeft


您也可以使用 TikZ 来\put打印内容。您需要加载 TikZ 并使用:

\newcommand*\printlater@printnow[2]{%
  \@ifundefined{printlater@anchorx@#1}{}{%
    \AtBeginShipoutNext{%
      \AtBeginShipoutUpperLeftForeground{%
        \tikz[overlay,remember picture]
          \node[anchor=base]
            at ([xshift=\@nameuse{printlater@anchorx@#1}sp,
                 yshift=\@nameuse{printlater@anchory@#1}sp]current page.south west)
                   {\rlap{#2}};%
      }%
    }%
  }%
}

而不是先前的定义。我看不出你现在为什么要这么做,但如果你在某个时候决定要绘制比水平/垂直线更复杂的东西,那么知道这一点很有用。


编辑:这是另一个测试,它比较了原始的\colon、、和周围的间距。彩色线的位置是自动化的,灰线各自分开(从红线开始)。amsmath\colon:\mathpunct:\mathord:1pt

\documentclass[a4paper]{article} %% <- paper size option is important!

\let\oldcolon\colon
\usepackage{amsmath}
%% ^ remove \ifmeasuring@\else and \fi below if you don't use amsmath
\let\amsmathcolon\colon

\usepackage{xcolor}

\usepackage{atbegshi} %% <- for \AtBeginShipout and \AtBeginShipoutUpperLeft

\makeatletter %% <- change @ so that it can be used in command names
  \@ifundefined{savepos}
  {  %% <- For pdflatex and xelatex
    \let\savepos\pdfsavepos
    \let\lastxpos\pdflastxpos
    \let\lastypos\pdflastypos
  }{} %% <- For lualatex
  \newcounter{printlatercounter} %% <- unique ids for things we want to print later
  \newcommand*\printlater[1]{%
    \ifmeasuring@\else %% <- Do nothing if in amsmath's "measuring" mode
      \stepcounter{printlatercounter}%
      \expandafter\printlater@writepos\expandafter{\number\c@printlatercounter}%
      \expandafter\printlater@printnow\expandafter{\number\c@printlatercounter}{#1}%
    \fi
  }
  \newcommand*\printlater@writepos[1]{% %% <- Writes the current coordinates to the aux file
    \savepos %% <- get current position on page
    \write\@auxout{\global\string\@namedef{printlater@anchorx@#1}{\the\lastxpos}}% %% <- write x coord.
    \write\@auxout{\global\string\@namedef{printlater@anchory@#1}{\the\lastypos}}% %% <- write y coord.
  }
  \newcommand*\printlater@printnow[2]{% %% <- Prints #2 at the #1-th set of coordinates
    \@ifundefined{printlater@anchorx@#1}{}{% %% <- test if the coordinates are set
      \AtBeginShipoutNext{% %% <- execute just before shipout
        \AtBeginShipoutUpperLeft{% %% <- insert things at the top left corner of the page
          \setlength{\unitlength}{1sp}% %% <- coordinates are in sp (65536sp = 1pt)
          \put(\@nameuse{printlater@anchorx@#1},\the\numexpr\@nameuse{printlater@anchory@#1}-\paperheight){#2}%
        }%
      }%
    }%
  }
\makeatother %% <- return @ to normal

\begin{document}

\newcommand*\myrule[2][0pt]{\printlater{\hskip#1\color{#2}\vrule depth 5pt height 10pt width .2pt}}
\newcommand*\grayrules{\myrule[1pt]{gray}\myrule[2pt]{gray!50!white}\myrule[3pt]{gray!25!white}\myrule[4pt]{gray!12.5!white}}

\begin{align*}
    f\myrule{red}\grayrules\oldcolon \myrule{green}{\myrule{blue}X} &\to Y
\\
    f\myrule{red}\grayrules\amsmathcolon \myrule{green} {\myrule{blue}X} &\to Y
\\
    f\myrule{red}\grayrules: \myrule{green} {\myrule{blue}X} &\to Y
\\
    f\myrule{red}\grayrules\mathpunct: \myrule{green} {\myrule{blue}X} &\to Y
\\
    f\myrule{red}\grayrules\mathord: \myrule{green} {\myrule{blue}X} &\to Y
\end{align*}

\end{document}

在此处输入图片描述


编辑:添加了关于该选项必要性的评论a4paper并修复了重复打印问题。感谢@nox 指出这些问题。

相关内容