我使用来自这个答案自动为标签添加前缀,以便能够在同一文档的不同位置重复使用相同的标签。
背景:为什么?
有两个文档(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}
这“本地标签”黑客攻击自动为标签添加前缀,以便能够在同一文档的不同位置重复使用相同的标签。
filecontents
MWE,使用第一个示例中的环境生成的文件:
\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.tex
或doc2.tex
(或其内容)。因此,仅仅避免重复标签或更改这些文档中的ref
和label
命令是不可行的。这就是我使用“本地标签”技巧并尝试对其进行调整的原因。
答案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