按照 \ref 的思路重新定义 \ref*,允许在章节中使用“本地标签”

按照 \ref 的思路重新定义 \ref*,允许在章节中使用“本地标签”

我使用来自这个答案自动为标签添加前缀,以便能够在同一文档的不同位置重复使用相同的标签。


背景:为什么?

有两个文档(doc1.tex& doc2.tex),其内容超出了我的控制范围。我需要创建第三个文档(main.tex),其中包含另外两个文档的内容。doc1.tex和中的相同标签doc2.tex会导致 中的重复标签main.tex

完整(非工作)示例(我正在加载,cleveref因为我在实际代码中使用它 - 不确定它是否与此相关):

% file main.tex:
\documentclass{scrartcl}
\usepackage{hyperref}
\usepackage{cleveref}
% "doc1":
\begin{filecontents}{doc1.tex}
\documentclass{scrartcl}
\begin{document}
\input{doc1_body}
\end{document}
\end{filecontents}
\begin{filecontents}{doc1_body.tex}
\section{About Foo} \label{sec:foo}
\ref{sec:foo}
\end{filecontents}

% "doc2" (structurally identical to "doc1"):
\begin{filecontents}{doc2.tex}
\documentclass{scrartcl}
\begin{document}
\input{doc2_body}
\end{document}
\end{filecontents}
\begin{filecontents}{doc2_body.tex}
\section{About Foo} \label{sec:foo} % same label as in doc2_body.tex
\ref{sec:foo}
\end{filecontents}

\begin{document}
% cannot modify doc1_body or doc1_body!
\input{doc1_body}
\input{doc2_body} % Label `sec:foo' multiply defined.
\end{document}

“本地标签”黑客攻击自动为标签添加前缀,以便能够在同一文档的不同位置重复使用相同的标签。

filecontentsMWE,使用第一个示例中的环境生成的文件:

\documentclass{scrartcl}
\usepackage{hyperref}
\usepackage{cleveref}

%%% Automatically prefix labels %%%
% https://tex.stackexchange.com/a/82115/37118
\makeatletter
\AtBeginDocument{%
\let\origref\ref
\let\origlabel\label
\newcommand\locallabels[1]{%
  \renewcommand\label[1]{\origlabel{#1##1}}%
  \renewcommand\ref[1]{\origref{#1##1}}%
}}
\makeatother

\begin{document}
\locallabels{part1:}
\input{doc1_body} % contains \ref{sec:foo}

\locallabels{part2:}
\input{doc2_body}  % contains \ref{sec:foo}
\end{document}

这非常适合\ref\ref{sec:foo}在 中doc1_body.tex打印“1”,而在 中打印“2” doc2_body.tex

问题:如何对带星号的版本实现相同的功能\ref*,以便\ref*可以在部分命令中使用,例如:\section{More on \ref*{sec:foo}}

修补后的版本\ref*在使用时应该可以按预期工作hyperref:打印相同的输出,\ref但没有超链接。


我尝试过的:

  • 最初,我天真地尝试添加\renewcommand\ref*[1]{\origref*{#1##1}},但当然修补带有星号版本的命令并不是那么简单……
  • 然后,我尝试遵循这个答案并根据我的问题进行调整。(我知道有使用星号变体重新定义命令的各种方法,但链接的答案似乎与我的用例非常接近)。

在此基础上,我想出了以下解决方案:

\makeatletter
\AtBeginDocument{%
\let\origref\ref
\let\origlabel\label
\newcommand*{\newrefstar}[1]{}
\newcommand*{\newref}[1]{}
\newcommand\locallabels[1]{%
  \renewcommand\label[1]{\origlabel{#1##1}}%
  \renewcommand*{\ref}{\@ifstar\newrefstar\newref}%
  \renewcommand*{\newrefstar}[1]{\origref*{#1##1}}%
  \renewcommand*{\newref}[1]{\hyperref[#1##1]{\newrefstar{##1}}}%
}}
\makeatother

用此代码替换我最初的 MWE 序言中的代码(几乎)有效:我可以\ref*{sec:foo}像 一样使用\ref{sec:foo}。提供了完整的 MWE这里(为了让这个问题简短些)。但是,当我使用\ref*within时,文档就崩溃了\section


问题:我怎样才能修复上述代码以使其\section{\ref*{sec:foo}}正常工作?完全不工作的示例。

在评论中,Ulrike Fischer 指出,这可能会导致目录和书签出现问题。这是不可避免的,还是可以\ref*用其预期的输出来替代它被写入TOC文件了吗?

请注意,我无法更改初始示例中的doc1.texdoc2.tex(或其内容)。因此,仅仅避免重复标签或更改这些文档中的reflabel命令是不可行的。这就是我使用“本地标签”技巧并尝试对其进行调整的原因。

答案1

我会重新定义相关命令。请注意,您还需要以类似的方式重新定义cleveref所需的特定命令。

% "doc1":
\begin{filecontents}{doc1_body.tex}
\section{About Foo} \label{sec:foo}
\ref{sec:foo} \ref*{sec:foo}
\end{filecontents}

% "doc2" (structurally identical to "doc1"):
\begin{filecontents}{doc2_body.tex}
\section{About Foo} \label{sec:foo} % same label as in doc2_body.tex
\ref{sec:foo} \ref*{sec:foo}
\section{About \ref{sec:foo}}
\end{filecontents}

% file main.tex:
\documentclass{scrartcl}
\usepackage{hyperref}
\usepackage{cleveref}

\makeatletter
\newcommand{\locallabels}[1]{\gdef\local@labels{#1}}
\locallabels{}% initialize
\AtBeginDocument{%
  \NewCommandCopy\KEPT@ref\ref
  \NewCommandCopy\KEPT@label\label
  \RenewExpandableDocumentCommand{\ref}{sm}{%
    \IfBooleanTF{#1}{\KEPT@ref*{\local@labels #2}}{\KEPT@ref{\local@labels #2}}%
  }%
  \RenewDocumentCommand{\label}{m}{\KEPT@label{\local@labels #1}}
}

\begin{document}
% cannot modify doc1_body or doc1_body!
\tableofcontents
\locallabels{part1:}
\input{doc1_body}
\locallabels{part2:}
\input{doc2_body} % Label `sec:foo' multiply defined.
\end{document}

在此处输入图片描述

请注意,目录没有问题,因为文件.toc将具有

\contentsline {section}{\numberline {1}About Foo}{1}{section.1}%
\contentsline {section}{\numberline {2}About Foo}{1}{section.2}%
\contentsline {section}{\numberline {3}About \KEPT@ref {part2:sec:foo}}{1}{section.3}%
\providecommand \tocbasic@end@toc@file {}\tocbasic@end@toc@file

相关内容