从维度中剥离 pt

从维度中剥离 pt

LaTeX 在ltfssbas.dtx宏中定义一个叫做\strip@pt(在第 [246] 行) 的宏,它pt从 返回的维度中剥离部分\the\dimension。该定义如下所示,是一个最小工作示例:

\documentclass[11pt]{article} 
\begin{document}
\parindent=0pt
\makeatletter

\begingroup
  \catcode `P=12  % digits and punct. catcode
  \catcode `T=12  % digits and punct. catcode
  \lowercase{%
  \def\x{\def\rem@pt##1.##2PT{##1\ifnum##2>\z@.##2\fi}}}
     \expandafter\endgroup\x%
\def\strip@pt{\expandafter\rem@pt\the}

\newdimen\normallineskiplimit \normallineskiplimit=0.001pt

\texttt{Original dimen \the\normallineskiplimit}\\
\texttt{Stripped dimen \strip@pt\normallineskiplimit}\\

\makeatother
\end{document}

在宏中,catcode字母 'PT' 的 被改为类别 12,这是数字和标点符号的类别代码。然后它将其更改为lowercase以匹配pt返回的\the\dimension。我可以理解其意图是用PT返回的来分隔参数\the\dimension,但为什么小写字母不能单独工作?还有第二个问题(请注意\relax一个问题的限制)。就在 之前,它\def\strip@pt 写道\expandafter\endgroup\x。这也可以写成\x\endgroup并保存\expandafter。在我提供的最小值中,两种方式都可以。我遗漏了什么吗?

答案1

关于“PT”问题,需要的是小写标记pt类别代码为 12。正如您所说,\the<dimen>返回后跟的值pt,但这些是“其他”标记,而不是“字母”。所以类似

\def\rem@pt#1.#2pt{...

不起作用,即使使用\lowercase(改变字符代码但不改变类别代码):TeX 将继续寻找类别代码为“other”的字符“pt”,可能找不到它们并会引发错误。

关于第二个问题,\x\endgroup只会\rem@pt在您关闭的组内定义。您可以使用\gdef来解决这个问题,但该\expandafter路线也有效,因为 的内容\x在组外展开。您的示例仍可编译,因为内核已经全局定义了\rem@pt。尝试在您的示例中为其指定一个不同的名称,并观察在关闭组之前展开时发生的“未定义的控制序列错误” \x


另一种创建相同宏但不使用的方法\lowercase

\edef\rem@pt{%
  \def\noexpand\rem@pt##1.##2\string p\string t{%
    ##1\noexpand\ifnum##2>\noexpand\z@.##2\noexpand\fi
  }
}
\rem@pt

pt这是通过使用原语创建“字符串”来实现的\string(使用 e-TeX,您也可以使用\detokenize)。 的第一个定义\rem@pt设置了第二个定义,最终与 LaTeX 内核方法相同。


评论中要求使用 LaTeX3 实现。目前,我们有一个内部函数\__dim_strip_pt:n可以完成这项工作:这与 LaTeX2e 代码非常相似,但具有维度表达式评估。设置当前显示为

\cs_new:Npn \__dim_strip_pt:n #1
  {
    \exp_after:wN
      \__dim_strip_pt:w \dim_use:N \__dim_eval:w #1 \__dim_eval_end: \q_stop
  }
\use:x
  {
    \cs_new:Npn \exp_not:N \__dim_strip_pt:w
      ##1 . ##2 \tl_to_str:n { pt } ##3 \exp_not:N \q_stop
      {
        ##1
        \exp_not:N \int_compare:nNnT {##2} > \c_zero
          { . ##2 }
      }
  }

\dim_use:N \__dim_eval:w #1 \__dim_eval_end:相同,\dim_eval:n但避免了强制扩展两次,可以使用

\cs_new:Npn \__dim_strip_pt:n #1
   {
     \exp_after:wN \exp_after:wN \exp_after:wN
       \__dim_strip_pt:w \dim_eval:n {#1} \q_stop
   }

如果对此有更广泛的需求,‘剥离该pt功能,然后它可以从内部移到一般用途。’(它旨在支持由团队编写的驱动程序级代码,因此是内部的。)

答案2

ConTeXt 中使用了另一种定义,它不使用这个\lowercase技巧。以下代码取自syst-aux.mkiv

%D \macros
%D   {withoutpt,PtToCm,
%D    numberofpoints,dimensiontocount}
%D
%D We can convert point into centimeters with:
%D
%D \starttyping
%D \PtToCm{dimension}
%D \stoptyping

{\catcode`\.=\othercatcode
 \catcode`\p=\othercatcode
 \catcode`\t=\othercatcode
 \gdef\WITHOUTPT#1pt{#1}}                                                                                                                                                                                      

\def\withoutpt#1%
  {\expandafter\WITHOUTPT#1}

%D The capitals are needed because \type{p} and \type{t} have
%D \CATCODE~12, while macronames only permit tokens with the
%D \CATCODE~11. As a result we cannot use the \type{.group}
%D primitives. Those who want to know more about this kind of
%D manipulations, we advice to study the \TEX book in detail.
%D Because this macro does not do any assignment, we can use it
%D in the following way too.

\def\PtToCm#1%
   {\withoutpt\the\dimexpr0.0351459804\dimexpr#1\relax\relax cm}

与 LaTeX 宏不同,此宏不会尝试截断小数点后的数字,而是让用户\the在需要时添加。

相关内容