我试图在数学模式下进行一些与间距相关的调试。更具体地说,我想创建一些垂直标尺来检查数学模式下水平对齐的差异。通过这个问题,我想为调试提供一般帮助。我为此苦苦挣扎,也在这里找到了好的资源和相关问题,所以我想解决找不到任何关于一般的 在 Latex 数学模式中调试。mhelvens 的问题给我留下了深刻的印象(当大量使用 \phantom、\mathrlap 等时,如何修复数学间距?),并希望使用线条/标尺创建类似的输出,如他提供的最后一张图片所示:
在获取这些尺子的过程中,我遇到了一些问题。
资源
我所研究的内容可能对其他人也有帮助:
- 摘自TUGboat,第22卷(2001年),第4期:
\smash
宏:对、\llap
和 的补充\rlap
作者:Alexander R. Perlis - 的文件 数学模式 作者:Herbert Voß(标记为过时)
相关问题汇总
我只是列出这些作为有用的补充。它们有些很难找到,或者没有标记为调试相关,所以它们也可能是很好的资源。
数学环境的垂直扩展
出现的一个问题是数学环境总是根据其内容(垂直)扩展。
编辑:使用\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}
(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***}}%
}%
}%
}%
}
您可以安全地删除\message
s,它们只是为了调试。
答案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
包裹处理此事。
之所以需要两次运行,是因为直到页面交付时才知道内容的确切坐标,而那时再打印其他内容就太晚了。但是,我们仍然可以将这些坐标写入辅助文件,然后在第二次运行时使用它们。
如果您想要手动移动其中一个\vrule
s,比如说.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 指出这些问题。