在第三个“危险弯道”第178页TeXbook有以下描述\vphantom
:
比 更有用
\phantom
,\vphantom
它创建一个隐形的盒子,其高度和深度与相应的 \phantom 相同,但宽度为零。
在附录 B(第 360 页)中,可以找到和的定义\vphantom
:\hphantom
\phantom
\newif\ifv@ \newif\ifh@
\def\vphantom{\v@true\h@false\ph@nt}
\def\hphantom{\v@false\h@true\ph@nt}
\def\phantom{\v@true\h@true\ph@nt}
\def\ph@nt{\ifmmode\def\next{\mathpalette\mathph@nt}%
\else\let\next=\makeph@nt\fi \next}
\def\makeph@nt#1{\setbox0=\hbox{#1}\finph@nt}
\def\mathph@nt#1#2{\setbox0=\hbox{$\m@th#1{#2}$}\finph@nt}
\def\finph@nt{\setbox2=\null \ifv@ \ht2=\ht0 \dp2=\dp0 \fi
\ifh@ \wd2=\wd0 \fi \box2 }
ltmath.dtx
以下是 LaTeX 内核 ( ,第 256 页)中的定义source2e.pdf
:
\newif\ifv@
\newif\ifh@
\def\vphantom{\v@true\h@false\ph@nt}
\def\hphantom{\v@false\h@true\ph@nt}
\def\phantom{\v@true\h@true\ph@nt}
\def\ph@nt{%
\ifmmode
\expandafter\mathpalette\expandafter\mathph@nt
\else
\expandafter\makeph@nt
\fi}
\def\makeph@nt#1{%
\setbox\z@\hbox{\color@begingroup#1\color@endgroup}\finph@nt}
\def\mathph@nt#1#2{%
\setbox\z@\hbox{$\m@th#1{#2}$}\finph@nt}
\def\finph@nt{%
\setbox\tw@\null
\ifv@ \ht\tw@\ht\z@ \dp\tw@\dp\z@\fi
\ifh@ \wd\tw@\wd\z@\fi \box\tw@}
由于\vphantom
保留了总高度(=高度+深度),我想知道为什么没有仅深度和仅高度的幻像?更准确地说,上述定义如何扩展以定义\dphantom
(仅用于深度)和\htphantom
(仅用于高度)?前者会产生一个不可见的盒子,其深度与对应的相同\phantom
,但其宽度和高度为零,后者将产生一个不可见的盒子,其高度与相应的相同,\phantom
但其宽度和深度为零。
答案1
例如,\ifv@
可以将开关拆分为高度和深度两个开关:\ifv@ht@
和\ifv@dp@
。
宏以这种方式改变(未改变的宏被注释掉):
纯 TeX
% \newif\ifh@
\newif\ifv@ht@ \newif\ifv@dp@
\def\vphantom {\v@ht@true \v@dp@true \h@false\ph@nt}
\def\hphantom {\v@ht@false\v@dp@false\h@true \ph@nt}
\def\htphantom{\v@ht@true \v@dp@false\h@false\ph@nt}
\def\dpphantom{\v@ht@false\v@dp@true \h@false\ph@nt}
\def\phantom {\v@ht@true \v@dp@true \h@true \ph@nt}
%\def\ph@nt{\ifmmode\def\next{\mathpalette\mathph@nt}%
% \else\let\next=\makeph@nt\fi \next}
%\def\makeph@nt#1{\setbox0=\hbox{#1}\finph@nt}
%\def\mathph@nt#1#2{\setbox0=\hbox{$\m@th#1{#2}$}\finph@nt}
\def\finph@nt{%
\setbox2=\null
\ifv@ht@ \ht2=\ht0 \fi
\ifv@dp@ \dp2=\dp0 \fi
\ifh@ \wd2=\wd0 \fi
\box2 %
}
% Macro \test, which visualizes the bounding box
\def\test#1#2{%
\leavevmode
\hbox{%
\vrule
\vbox{%
\hrule
\vtop{%
\hbox{#1{#2}}%
\hrule
}%
}%
\vrule
}%
}
g \test\phantom{g} \test\hphantom{g} \test\vphantom{g}
\test\htphantom{g} \test\dpphantom{g}
\bye
乳胶
LaTeX 从纯 TeX 继承了这些宏。它只做了一些改变\makeph@nt
以添加颜色支持。但此命令无需更改。以下示例为 LaTeX 重新定义了这些宏:
\documentclass{article}
\makeatletter
\newif\ifv@ht@ \newif\ifv@dp@
\renewcommand*{\vphantom} {\v@ht@true \v@dp@true \h@false\ph@nt}
\renewcommand*{\hphantom} {\v@ht@false\v@dp@false\h@true \ph@nt}
\newcommand* {\htphantom}{\v@ht@true \v@dp@false\h@false\ph@nt}
\newcommand* {\dpphantom}{\v@ht@false\v@dp@true \h@false\ph@nt}
\renewcommand*{\phantom} {\v@ht@true \v@dp@true \h@true \ph@nt}
\renewcommand*{\finph@nt}{%
\setbox2=\null
\ifv@ht@ \ht2=\ht0 \fi
\ifv@dp@ \dp2=\dp0 \fi
\ifh@ \wd2=\wd0 \fi
\box2 %
}
\makeatother
\begin{document}
% Macro \test visualizes the bounding box
\newcommand*{\test}[2]{%
\begingroup
\setlength{\fboxsep}{0pt}%
\fbox{#1{#2}}%
\endgroup
}
g \test\phantom{g} \test\hphantom{g} \test\vphantom{g}
\test\htphantom{g} \test\dpphantom{g}
\end{document}
答案2
你amsmath
可以定义
\newcommand{\heightphantom}[1]{\vphantom{\smash[b]{#1}}
\newcommand{\depthphantom}[1]{\vphantom{\smash[t]{#1}}
不如 Heiko 的那么高效,但定义起来更简单。
expl3
具有键值接口的版本。如果*
确实需要,命令的 -version 不会强制水平模式。
键指定要保留哪些尺寸;不指定任何尺寸意味着保留所有三个尺寸(高度、深度和宽度)。因此,\xphantom{g}
主要相当于\phantom{g}
(除了自动的\leavevmode
),而\xphantom[width]{g}
和分别\xphantom[totalheight]{g}
相当于\hphantom
和。为这些提供了\vphantom
简写\xhphantom
和。\xvphantom
\documentclass{article}
\usepackage{xparse}
\ExplSyntaxOn
\NewDocumentCommand{\xphantom}{som}
{% #2 is a list of key-value options, #3 is the text
\IfBooleanF{#1}{\leavevmode}
\IfNoValueTF{#2}
{
\manual_phantom:nn { totalheight , width } { #3 }
}
{
\manual_phantom:nn { #2 } { #3 }
}
}
\NewDocumentCommand{\xhphantom}{sm}
{
\IfBooleanTF{#1}
{
\xphantom*[width]{#2}
}
{
\xphantom[width]{#2}
}
}
\NewDocumentCommand{\xvphantom}{sm}
{
\IfBooleanTF{#1}
{
\xphantom*[totalheight]{#2}
}
{
\xphantom[totalheight]{#2}
}
}
\keys_define:nn { manual/xphantom }
{
height .bool_set:N = \l__manual_phantom_keepht_bool,
depth .bool_set:N = \l__manual_phantom_keepdp_bool,
width .bool_set:N = \l__manual_phantom_keepwd_bool,
totalheight .code:n =
\bool_set_true:N \l__manual_phantom_keepht_bool
\bool_set_true:N \l__manual_phantom_keepdp_bool,
height .default:n = true,
depth .default:n = true,
width .default:n = true,
totalheight .default:n = ,
}
\box_new:N \l__manual_phantom_input_box
\box_new:N \l__manual_phantom_box
\cs_new_protected:Npn \manual_phantom:nn #1 #2
{
\group_begin:
\keys_set:nn { manual/xphantom } { #1 }
\hbox_set:Nn \l__manual_phantom_input_box { #2 }
\hbox_set:Nn \l__manual_phantom_box {}
\bool_if:NT \l__manual_phantom_keepht_bool
{
\box_set_ht:Nn \l__manual_phantom_box { \box_ht:N \l__manual_phantom_input_box }
}
\bool_if:NT \l__manual_phantom_keepdp_bool
{
\box_set_dp:Nn \l__manual_phantom_box { \box_dp:N \l__manual_phantom_input_box }
}
\bool_if:NT \l__manual_phantom_keepwd_bool
{
\box_set_wd:Nn \l__manual_phantom_box { \box_wd:N \l__manual_phantom_input_box }
}
\box_use:N \l__manual_phantom_box
\group_end:
}
\ExplSyntaxOff
\begin{document}
\newcommand*{\test}[1]{%
\begingroup
\setlength{\fboxsep}{0pt}%
\fbox{#1}%
\endgroup
}
g
\test{\xphantom{g}}
\test{\xphantom[width]{g}}
\test{\xphantom[totalheight]{g}}
\test{\xphantom[height]{g}}
\test{\xphantom[depth]{g}}
g
\test{\xphantom[height,width]{g}}
\test{\xphantom[depth,width]{g}}
g
\test{\xhphantom{g}}
\test{\xvphantom{g}}
\end{document}
还实现数学模式的版本:
\documentclass{article}
\usepackage{amsmath,xparse}
\ExplSyntaxOn
\NewDocumentCommand{\xphantom}{som}
{% #2 is a list of key-value options, #3 is the text
\IfBooleanF{#1}{ \mode_if_math:F { \leavevmode } }
\IfNoValueTF{#2}
{
\manual_phantom:nn { totalheight , width } { #3 }
}
{
\manual_phantom:nn { #2 } { #3 }
}
}
\NewDocumentCommand{\xhphantom}{sm}
{
\IfBooleanTF{#1}
{
\xphantom*[width]{#2}
}
{
\xphantom[width]{#2}
}
}
\NewDocumentCommand{\xvphantom}{sm}
{
\IfBooleanTF{#1}
{
\xphantom*[totalheight]{#2}
}
{
\xphantom[totalheight]{#2}
}
}
\keys_define:nn { manual/xphantom }
{
height .bool_set:N = \l__manual_phantom_keepht_bool,
depth .bool_set:N = \l__manual_phantom_keepdp_bool,
width .bool_set:N = \l__manual_phantom_keepwd_bool,
totalheight .code:n =
\bool_set_true:N \l__manual_phantom_keepht_bool
\bool_set_true:N \l__manual_phantom_keepdp_bool,
height .default:n = true,
depth .default:n = true,
width .default:n = true,
totalheight .default:n = ,
}
\box_new:N \l__manual_phantom_input_box
\box_new:N \l__manual_phantom_box
\cs_new_protected:Npn \manual_phantom:nn #1 #2
{
\group_begin:
\keys_set:nn { manual/xphantom } { #1 }
\hbox_set:Nn \l__manual_phantom_box {}
\mode_if_math:TF
{
\__manual_phantom_math:n { #2 }
}
{
\hbox_set:Nn \l__manual_phantom_input_box { #2 }
\__manual_phantom_set:
}
\group_end:
}
\cs_new_protected:Npn \__manual_phantom_set:
{
\bool_if:NT \l__manual_phantom_keepht_bool
{
\box_set_ht:Nn \l__manual_phantom_box { \box_ht:N \l__manual_phantom_input_box }
}
\bool_if:NT \l__manual_phantom_keepdp_bool
{
\box_set_dp:Nn \l__manual_phantom_box { \box_dp:N \l__manual_phantom_input_box }
}
\bool_if:NT \l__manual_phantom_keepwd_bool
{
\box_set_wd:Nn \l__manual_phantom_box { \box_wd:N \l__manual_phantom_input_box }
}
\box_use:N \l__manual_phantom_box
}
% Now math mode; there's currently no syntactic sugar for \mathpalette
\cs_new_protected:Npn \__manual_phantom_math:n #1
{
\mathpalette \__manual_make_phantom_math:nn { #1 }
}
\cs_new_protected:Npn \__manual_make_phantom_math:nn #1 #2
{
\hbox_set:Nn \l__manual_phantom_input_box { $ #1 {#2} \mathsurround=0pt $ }
\__manual_phantom_set:
}
\ExplSyntaxOff
\begin{document}
\newcommand*{\test}[1]{%
\begingroup
\setlength{\fboxsep}{0pt}%
\fbox{#1}%
\endgroup
}
g
\test{\xphantom{g}}
\test{\xphantom[width]{g}}
\test{\xphantom[totalheight]{g}}
\test{\xphantom[height]{g}}
\test{\xphantom[depth]{g}}
g
\test{\xphantom[height,width]{g}}
\test{\xphantom[depth,width]{g}}
g
\test{\xhphantom{g}}
\test{\xvphantom{g}}
$\displaystyle\sum$
\test{$\displaystyle\xphantom{\sum}$}
\test{$\displaystyle\xvphantom{\sum}$}
\test{$\displaystyle\xhphantom{\sum}$}
\test{$\displaystyle\xphantom[height]{\sum}$}
\test{$\displaystyle\xphantom[depth]{\sum}$}
\test{$\displaystyle\xphantom[height,width]{\sum}$}
\test{$\displaystyle\xphantom[depth,width]{\sum}$}
$\textstyle\sum$
\test{$\textstyle\xphantom{\sum}$}
\test{$\textstyle\xvphantom{\sum}$}
\test{$\textstyle\xhphantom{\sum}$}
$\scriptstyle\sum$
\test{$\scriptstyle\xphantom{\sum}$}
\test{$\scriptstyle\xvphantom{\sum}$}
\test{$\scriptstyle\xhphantom{\sum}$}
$\scriptscriptstyle\sum$
\test{$\scriptscriptstyle\xphantom{\sum}$}
\test{$\scriptscriptstyle\xvphantom{\sum}$}
\test{$\scriptscriptstyle\xhphantom{\sum}$}
\end{document}
答案3
这不会扩展现有\vphantom
定义,但提供了替代方案。它适用于文本模式以及所有数学模式。已编辑以删除对 的包依赖scalerel
,但保留的\thisstyle
和\savedstyle
定义在很大程度上模仿了包(\ThisStyle
和\SavedStyle
)中的定义。已编辑以添加\wdphantom
、\htwdphantom
和\dpwdphantom
。
\documentclass{article}
\makeatletter
\def\@mstyleD{\displaystyle}
\def\@mstyleT{\textstyle}
\def\@mstyleS{\scriptstyle}
\def\@mstyles{\scriptscriptstyle}
\let\@mstyleX\relax
\def\savedstyle{\csname @mstyle\m@switch\endcsname}
%
\def\thisstyle#1{%
\ifmmode\mathchoice%
{\edef\m@switch{D}#1}{\edef\m@switch{T}#1}{\edef\m@switch{S}#1}{\edef\m@switch{s}#1}%
\else\edef\m@switch{X}#1\fi%
}
\makeatother
\def\preservebox#1{\thisstyle{\setbox0=\hbox{\savedstyle#1}}}
\def\htphantom#1{\preservebox{#1}\rule{0pt}{\ht0}}
\def\dpphantom#1{\preservebox{#1}\rule[-\dp0]{0pt}{\dp0}}
\def\wdphantom#1{\preservebox{#1}\rule{\wd0}{0pt}}
\def\htwdphantom#1{\preservebox{#1}\rule{0pt}{\ht0}\rule{\wd0}{0pt}}
\def\dpwdphantom#1{\preservebox{#1}\rule[-\dp0]{0pt}{\dp0}\rule{\wd0}{0pt}}
\def\test#1{#1 \fbox{\phantom{#1}} \fbox{\vphantom{#1}}
\fbox{\wdphantom{#1}} \fbox{\htphantom{#1}} \fbox{\dpphantom{#1}}
\fbox{\htwdphantom{#1}} \fbox{\dpwdphantom{#1}} \par}
\fboxsep=0pt\relax\fboxrule=.2pt
\begin{document}
\test{g}\test{$g$}\test{$\scriptscriptstyle g$}\test{\Large g}
\test{$\sum_{i=1}^N$}
\test{$\displaystyle\sum_{i=1}^N$}
\end{document}