l3 更新错误,来自今天早些时候?

l3 更新错误,来自今天早些时候?

编辑:已发现问题,MWE 在这里:

% !TeX program = LuaLaTeX
% !TeX encoding = UTF-8
\documentclass{article}
\usepackage{fontspec}
\setmainfont{Linux Libertine O}
\ExplSyntaxOn
\let\myaddfontfeatures\addfontfeatures
\let\addfontfeatures\relax
%% This works, with l3 2017/02/10 and fontspec-luatex 2017/02/12,
%% and prior versions:
\DeclareDocumentCommand \AddFontFeatures { m } {%%%%%%
  \myaddfontfeatures{#1}%
}
\let\addfontfeatures\AddFontFeatures
%% This fails, with l3 2017/02/10 and fontspec 2017/02/12:
%% However, it works with l3 2017/02/07 and fontspec-luatex 2017/01/24.
% \DeclareDocumentCommand \addfontfeatures { m } {%%%%%%
%   \myaddfontfeatures{#1}%
% }
%%
\ExplSyntaxOff
\begin{document}
Whatever.\par
{\addfontfeatures{Letters=SmallCaps} Whatever.}\par
Whatever.\par
\end{document}

在我最初的问题中,我想知道 2017/02/10 更新 l3 中是否存在错误。第二个猜测是 fontspec,2017/02/12。我有在这些更新之前运行良好的代码,但在编译时挂起失败(没有错误消息)。

从下面早期的评论中可以看出,我被引导去寻找可能的扩展问题。但事实证明,扩展与此无关。问题出在上面的代码中。

最初(上面注释掉的)我重新定义了一个文档命令,没有经过中间步骤。在更新之前,该代码运行良好。如果这是我的错误代码,它仍然运行正常。但更新导致它挂起,可能是由于自引用问题。

固定代码采用中间定义,没有问题。

快速浏览 xparser 和 fontspec-luatex 没有发现为什么会发生改变,但我不是这方面的专家。

答案1

这基本上与导致创建该letltxmacro计划的情况相同。

\let\myaddfontfeatures\addfontfeatures

您定义的\myaddfontfeatures宏与“用户级”宏相同\addfontfeatures。直到上次更新之前xparse,仅接受强制参数的宏使用了简化的定义,因此如果您这样做

\show\addfontfeatures

你可能会收到如下信息

\addfontfeatures=\protected macro:
#1->\fontspec_if_fontspec_font:TF [...]

从上次更新开始,精简功能不再使用,您将获得

> \addfontfeatures=\protected macro:
->\__xparse_start:nNNnnn {m}\addfontfeatures  \addfontfeatures code {\__xparse_grab_m_1:w }{}{}.

它告诉我们“真实代码”被保留了下来\addfontfeatures code(是的,宏名中有一个空格)。

现在\myaddfontfeatures被设置为这个意思。如果你这样做

\DeclareDocumentCommand\addfontfeatures{m}{...}

一旦使用 ,就会创建一个无限循环\addfontfeatures。实际上,这将执行\addfontfeatures code包含对 的调用\myaddfontfeatures,而这又将调用\addfontfeatures code

无限循环。

的当前定义\addfontfeatures可以在 中找到fontspec-luatex.sty(或者-xetex,它们是相同的):

\DeclareDocumentCommand \addfontfeatures {m}
 {
  \fontspec_if_fontspec_font:TF
   {
    \group_begin:
      \keys_set_known:nnN {fontspec-addfeatures} {#1} \l__fontspec_tmp_tl
      \prop_get:cnN {g__fontspec_ \f@family _prop} {options} \l__fontspec_options_tl
      \prop_get:cnN {g__fontspec_ \f@family _prop} {fontname} \l__fontspec_fontname_tl
      \bool_set_true:N \l__fontspec_disable_defaults_bool
      \use:x
       {
        \__fontspec_select_font_family:nn
          { \l__fontspec_options_tl , #1 } {\l__fontspec_fontname_tl}
       }
    \group_end:
    \fontfamily\l_fontspec_family_tl\selectfont
   }
   {
    \__fontspec_warning:nx {addfontfeatures-ignored} {#1}
   }
  \ignorespaces
 }
\cs_set_eq:NN \addfontfeature \addfontfeatures

如果遵循以下指导原则会更好:

\DeclareDocumentCommand \addfontfeatures {m}
 {
  \fontspec_addfontfeatures:n { #1 }
 }
\cs_set_eq:NN \addfontfeature \addfontfeatures
\cs_new_protected:Nn \fontspec_addfontfeatures:n
 {
  \fontspec_if_fontspec_font:TF
   {
    \group_begin:
      \keys_set_known:nnN {fontspec-addfeatures} {#1} \l__fontspec_tmp_tl
      \prop_get:cnN {g__fontspec_ \f@family _prop} {options} \l__fontspec_options_tl
      \prop_get:cnN {g__fontspec_ \f@family _prop} {fontname} \l__fontspec_fontname_tl
      \bool_set_true:N \l__fontspec_disable_defaults_bool
      \use:x
       {
        \__fontspec_select_font_family:nn
          { \l__fontspec_options_tl , #1 } {\l__fontspec_fontname_tl}
       }
    \group_end:
    \fontfamily\l_fontspec_family_tl\selectfont
   }
   {
    \__fontspec_warning:nx {addfontfeatures-ignored} {#1}
   }
  \ignorespaces
 }

所以你可以

\ExplSyntaxOn
\RenewDocumentCommand{\addfontfeatures}{m}
 {
  <code before>
  \fontspec_addfontfeatures:n { #1 }
  <code after>
 }
\cs_set_eq:NN \addfontfeature \addfontfeatures
\ExplSyntaxOff

无需多个\let步骤。

唉,它不符合指南。;-(


如果您想要禁用\addfontfeatures{Color=<color>},最好的策略是禁用该Color键:

\documentclass{article}
\usepackage{fontspec}
\setmainfont{Latin Modern Roman}

\ExplSyntaxOn
\keys_define:nn { fontspec }
 {
  Color .code:n = ,
  Colour .code:n = ,
}
\ExplSyntaxOff

\begin{document}

Abc {\addfontfeatures{Color=red} Abc}

\end{document}

您可以添加警告:

\documentclass{article}
\usepackage{fontspec}
\setmainfont{Latin Modern Roman}

\ExplSyntaxOn
\msg_new:nnn { RobtA } { invalid-key }
 {
  Specifying~'Color=#1'~is~disabled
 }
\keys_define:nn { fontspec }
 {
  Color .code:n = \msg_warning:nnn { RobtA } { invalid-key } { #1 },
  Colour .code:n = \msg_warning:nnn { RobtA } { invalid-key } { #1 },
}
\ExplSyntaxOff

\begin{document}

Abc {\addfontfeatures{Color=red} Abc}

\end{document}

答案2

既然 egreg 询问了,我就来解释一下我为什么使用这段代码。下面是另一个 MWE,它准确地说明了我的策略:

% !TeX program = LuaLaTeX
% !TeX encoding = UTF-8
\documentclass{article}
\usepackage{fontspec}
\usepackage[gray]{xcolor}
\usepackage{xstring}
\setmainfont{Linux Libertine O}
\def\mycheckfeature#1{}
%% Uncomment to see the difference:
% \def\mycheckfeature#1{%
%   \IfSubStr{#1}{Color}{%
%     \ClassError{mine}{You cannot add Color as font feature}%
%     {Remove the Color font feature from your text.}%
%   }{}%
%   \IfSubStr{#1}{Colour}{%
%     \ClassError{mine}{You cannot add Color as font feature}%
%     {Remove the Color font feature from your text.}%
%   }{}%
% }
%%
\ExplSyntaxOn
\let\myaddfontfeatures\addfontfeatures
\let\addfontfeatures\relax
\let\addfontfeature\relax
\DeclareDocumentCommand \AddFontFeatures { m } {%
  \mycheckfeature{#1}%
  \myaddfontfeatures{#1}%
}
\let\addfontfeatures\AddFontFeatures
\let\addfontfeature\AddFontFeatures
\ExplSyntaxOff
\begin{document}
It was a dark and story night. Lord Withens mounted his \textcolor{brown}{brown horse} and rode through the gloom to the {\addfontfeatures{Color=009900}Green Castle}.\par
\end{document}

必需[gray]{xcolor}可防止棕色马出现在棕色文本中;它以灰度显示。但它不会阻止绿色城堡显示为绿色,因为 fontspec 使用不受 xcolor 控制的不同策略。

取消注释代码,看看绿色城堡中如何出现错误消息。

我也将它与 Opacity 一起使用。而且,我还拦截了\setmainfont类似的命令。它有效。还有一些地方,我会拦截禁止的用户设置,并提供我自己的消息和说明。

相关内容