修复 BioMed Central 对 \end 的重新定义以实现 breqn 兼容性

修复 BioMed Central 对 \end 的重新定义以实现 breqn 兼容性

BioMed Central LaTeX 课程,,以某种方式bmcart.cls重新定义\end,当与 结合使用时会导致下游错误breqn.sty

违规内容bmcart.cls如下:

\def\end#1{%
  \@ifundefined{pseudo@#1}%
    {\org@end{#1}}{\csname pseudo@#1\endcsname[1]\relax}%
}

该类别使用修改后的定义\end来允许新语法元素的功能,\newpseudoenvironment该语法元素似乎被发布者用来以更方便的方式定义全局定义。

很遗憾,breqn\end以不兼容的方式重新定义了。在类breqn之后加载时bmcart,会出现以下错误:

Runaway argument?
\@ifundefined {pseudo@##1}{\org@end {##1}}{\csname pseudo@##1\endcsname \ETC.
/usr/share/texlive/texmf-dist/tex/latex/breqn/breqn.sty:848: Paragraph ended be
fore \@tempa was complete.
<to be read again> 
                   \par 
l.848   \par

bmcart.cls如果注释掉上述几行,则不会出现该错误。

在 LianTze Lim 和 Graham Douglas 的帮助下,我现在存储了 LaTeX 默认定义,\end然后在加载后恢复它bmcart.cls,同时存储它的定义。此当前补丁是作为此问题的答案提供的。

虽然此解决方案允许我的项目进行编译,但它并不能完全解决根本问题,因为它不允许\newpseudoenvironment按照预期运行(请参阅 的第 302-320 行以了解其定义及其相关的和bmcart.cls的重新定义)(发布者可能需要)。更一般地说,找到一个允许使用 的定义的解决方案会很好,同时允许所有其他包使用适用的默认或特定于类的定义。\start\endbreqn\end

下面我们提供了各种命令的定义\end,由 Graham Douglas 友情编辑:

\latexend:
\csname end#1\endcsname \@checkend {#1}\expandafter \endgroup
\if@endpe \@doendpe \fi \if@ignore \@ignorefalse \ignorespaces \fi

\brqend:
\csname end#1\endcsname \latex@end {#1}

\latex@end:
\@checkend {#1}\expandafter \endgroup \if@endpe \@doendpe
\fi \if@ignore \@ignorefalse \ignorespaces \fi

\org@end:
\csname end#1\endcsname \@checkend {#1}\expandafter \endgroup
\if@endpe \@doendpe \fi \if@ignore \@ignorefalse \ignorespaces \fi

\bmcend:
\@ifundefined {pseudo@#1}{\org@end {#1}}{\csname pseudo@#1\endcsname
[1]\relax }

有人能提出一个改进的解决方案,实现以下其中一件事(或提出一些更好的替代方案)吗?

  1. 封装这样breqn的定义\end,使得它不会引起兼容性问题,但仍可用于所有breqn环境。
  2. 封装bmcart.cls的定义\end,使得它不会引起兼容性问题,但允许pseudo@#1自动定义其相关情况并仅由 使用\newpseudoenvironment
  3. 加载后恢复bmcart.cls的定义,但仍允许环境(如)正常运行。\endbreqn.stybreqndmath

答案1

诀窍在于,两个包都可以正常工作,如果breqn \end在里面使用bmcart \end,则breqn必须先加载。这会很奇怪,并可能导致其他错误,因此我们交换了定义:

\documentclass{bmcart}

\makeatletter
\let\bmcend\end % save \bmcend
\let\end\org@end % breqn has to see the original \end
\usepackage{breqn}
\let\org@end\end % let bmcart believe the breqn \end is the original
\let\end\bmcend % and give control back to bmcart
\makeatother

% Some demonstration
\newpseudoenvironment{pseudo}{}{}
\newenvironment{real}{}{}
\begin{document}
\begin{dmath}
abc=def=ghi=jkl
\end{dmath}

\begin{real}
  \the\currentgrouplevel
\end{real}

\begin{pseudo}
  \the\currentgrouplevel
\end{pseudo}
\end{document}

我添加了一些环境来显示一切正常。特别是没有由伪环境创建的组: 在此处输入图片描述

您无需在加载包之前和之后使用命令拆分此修复,而是可以使用包scrlfile将命令插入到正确的位置:

\documentclass{bmcart}
\usepackage{scrlfile}
\makeatletter
\BeforePackage{breqn}{
  \let\bmcend\end % save \bmcend
  \let\end\org@end % breqn has to see the original \end
}
\AfterPackage{breqn}{
  \let\org@end\end % let bmcart believe the breqn \end is the original
  \let\end\bmcend % and give control back to bmcart
}
\makeatletter
\usepackage{breqn}

如果它由另一个包加载,这尤其有用breqn,即使根本没有加载它也能工作。

或者,bmcart伪环境可以集成到系统中breqn:伪环境只需要定义一个\end...包含两个参数的命令:

\documentclass{bmcart}
\makeatletter
\let\end\org@end
\def\newpseudoenvironment#1#2#3{%
  \global\@namedef{end#1}##1##2{#3}%
  \global\@namedef{pseudo@#1}[##1]{%
    \relax#2%
  }%
}
\makeatletter
\usepackage{breqn}

我认为第二种方法看起来更简洁,但在某些特殊情况下可能会失效:

伪环境与正常环境有两个根本区别:

  1. 它不引入组。这意味着伪环境内的定义可能会泄漏到环境中。例如,假设pseudoreal是上面代码片段中的伪环境,那么

    \begin{real}
      \scshape This is in small caps.
    \end{real}
    Here we have a normal font again.
    \begin{pseudo}
      \scshape This is in small caps again
    \end{pseudo}
    Oops, still small caps.
    

    例如,这对于类似于 LaTeXlrbox环境的环境很有用。它将其内容保存在作为参数传递的保存框中。为了使保存框的分配在环境之外可见,必须避免分组。这可以通过伪环境来实现。lrbox本身必须使用一些技巧。

    这可能是伪环境的预期用例。

  2. 还有另一个区别。如果你曾经尝试创建一个名为 eg 的环境def(例如用于定义),你就会知道不可能存在与 LaTeX 宏同名的 LaTeX 环境,因为名为 eg 的环境foo是由宏\foo和定义的\endfoo。这种命名冲突已在伪环境中得到解决,因此

    \newpseudoenvironment{def}{Definition: }{. End of Definition}
    % or even
    \newcommand\something{I'm a macro.}
    \newpseudoenvironment{something}{I'm an environment.}{The end.}
    

    是有效的。如果必须将相同的功能作为命令和环境提供,这将很有用。使用一些技巧与正常环境产生类似效果的一个例子是beamers\frame{frame}

  3. 这也适用于\end...。因此你可以定义

    \newcommand\endsomething{...}
    \newpseudoenvironment{something}{...}{...}
    

    这种情况下,一个独立的\end...宏可以与bmcart第一个修复一起使用,但不能与第二个修复一起使用。但我不知道为什么有人会这样做,所以这通常不应该是个问题。另外,LaTeX 不允许定义任何以\end...via开头的命令\newcommand,所以这需要使用\def原语。

对于第一种方法来说这应该不是问题。

答案2

注意:这个答案不能从总体上令人满意地解决上述问题。如果能提供更多想法,我将不胜感激。


上面提到的当前补丁如下:

% before loading bmcart.cls
\RequirePackage{letltxmacro}

\GlobalLetLtxMacro{\LaTeXend}{\end}

% ... 

% after loading the class

\LetLtxMacro{\bmcend}{\end}
\LetLtxMacro{\end}{\LaTeXend}

% ...

\usepackage{breqn}

谢谢林连子格雷厄姆·道格拉斯寻找类似的解决方案,本文就是从该解决方案改编而来的。

相关内容