我正在玩一个非常有趣的游戏zref
,但由于我的扩展能力不足,我一度陷入困境,也许有人会发现这个问题更容易解决。
zref
\protected@write
使用(不是)将标签写入辅助文件,\immediate
以便标签按照这些标签引用的对象被发送出去的顺序写入那里(据我所知,如果这个假设是错误的,请告诉我,并忽略以下内容)。而且我想我可以使用标签的“发送顺序”来推断(至少是近似的)页面上不同标签的相对位置(哪个在前,哪个在后)。所以我试图按照它们在辅助文件中出现的顺序检索标签序列。
zref
辅助文件中存储的信息位于名为的宏下\zref@newlabel
,其定义如下:
\def\ZREF@RefPrefix{Z@R}
\ZREF@Robust\edef\zref@newlabel{%
\noexpand\@newl@bel{\ZREF@RefPrefix}%
}
(据我所知,对于当前的 TeXLive,也就是我这里所读的,\ZREF@Robust
将是\protected
)。
我的想法是,我可以使用(修补/挂钩/重新定义)这个宏来完成它要做的事情,同时将clist
我想要的信息提供给(L3)。
但是,是文档\zref@newlabel
中提到的那些“向前看的命令”之一。事实上,接收 3 个参数,并且只传递第一个参数,后面两个参数将被向前扫描。据我所知,这意味着使用对于这种情况是不可行的。(编辑:正如我后来发现的,不可行的另一个原因是,当读取 .aux 文件时,钩子实际上不可用,它们仅在之后出现)。ltcmdhooks
\@newl@bel
\zref@newlabel
ltcmdhooks
ltcmdhooks
\begin{document}
我试图重新定义它,但不幸的是,我无法适当地控制扩展,所以这里的问题是我如何正确地进行这样的重新定义。
这是一个说明情况的 MWE,其中包括我的失败尝试:
\documentclass{book}
\usepackage[user]{zref}
\usepackage{hyperref}% just for \phantomsection
\usepackage{econlipsum}
\ExplSyntaxOn
\makeatletter
\clist_new:N \g__auxfile_labelseq_clist
% That's more or less what I'd like to do, but... I can't make it work.
% \cs_gset_protected:Npn \zref@newlabel #1#2 {
% \clist_gput_right:Nn \g__zrefcheck_auxfile_labelseq_clist {#1}
% \exp_not:N \@newl@bel {\ZREF@RefPrefix} {#1} {#2}
% }
\makeatother
\ExplSyntaxOff
\begin{document}
\chapter{Chapter 1}
\zlabel{cha:chapter-1}
\econ[1]
\begin{figure}[b]
\centering
\caption{Figure 1}
\zlabel{fig:figure-1}
\end{figure}
A reference to the figure ``above''~\zref{fig:figure-1}%
\phantomsection\zlabel{fig:figure-1:reference}.
\end{document}
答案1
Ulrike 在评论中的建议(如何在辅助文件中检索 zref 标签的顺序) 很好,确实有效,但它涉及\ZREF@RefPrefix
重新定义的硬编码。虽然在这种情况下这应该是无害的,但我还是对此事进行了进一步的思考,最终得出以下结论:
\seq_new:N \g__zrefcheck_auxfile_labelseq_seq
\ZREF@Robust\edef\zref@newlabel#1{
\noexpand\seq_gput_right:Nn \noexpand\g__zrefcheck_auxfile_labelseq_seq {#1}
\noexpand\@newl@bel{\ZREF@RefPrefix}{#1}
}
据我所知,它的行为方式与原始版本(当然,加上添加的代码)完全相同。(看来我在这个过程中学到了一些东西 ;-)
然而,与此同时,我发现我肯定不是唯一一个对现有标签列表感兴趣的人。CTAN 上至少有两个包也检索此信息:crossreftools
和listlbls
。第一个为此目的构建了一个特殊的 toc(带有相应的文件),而后者直接读取 .aux 文件。
listlbls
是基于 David Carlisle 的回答(所有带超链接的标签列表) 在 TeX.SX 上。我认为这是一个很好的方法,因为它不需要重新定义任何内部或额外的基础设施。可惜的是,这个包是为处理常规标签而设计的,而不是 的标签zref
,而且并没有真正以可检索的方式公开信息(据我所知)。所以我做了一个改编版本(一个穷人版):
\seq_new:N \g__zrefcheck_auxfile_labelseq_seq
\tl_set:Nn \g_tmpa_tl { \c_sys_jobname_str .aux }
\file_if_exist:nT { \g_tmpa_tl }
{
\ior_open:Nn \g_tmpa_ior { \g_tmpa_tl }
\group_begin:
\ior_map_inline:Nn \g_tmpa_ior
{
\tl_set:Nn \l_tmpa_tl { \zref@newlabel }
\tl_set:Nx \l_tmpb_tl { \tl_item:nn {#1} { 1 } }
\tl_if_eq:NNT \l_tmpa_tl \l_tmpb_tl
{
\tl_set:Nx \l_tmpa_tl { \tl_item:nn {#1} { 2 } }
\seq_gput_right:NV \g__zrefcheck_auxfile_labelseq_seq \l_tmpa_tl
}
}
\group_end:
\ior_close:N \g_tmpa_ior
}
答案2
您可以修补\zref@newlabel
以计数标签,将标签的编号和名称添加到 expl3 序列,将标签的编号作为属性的值添加labelnumber
到标签本身,以及使用\@newl@bel
不同的前缀将 zref 标签的编号映射到其名称:
\errorcontextlines=10000
\documentclass{article}
\usepackage[user]{zref}
\usepackage{atveryend}
\makeatletter
\@ifdefinable\UD@stopromannumeral{\chardef\UD@stopromannumeral=`\^^00}%
\zref@newprop{labelnumber}{0}%
\newcommand*\labelcount{0}%
\ZREF@Robust\edef\zref@newlabel{%
\xdef\noexpand\labelcount{\noexpand\number\numexpr\noexpand\labelcount+1\relax}%
\noexpand\expandafter\noexpand\AddElementAndCallNewlabel\noexpand\expandafter{\noexpand\labelcount}{\ZREF@RefPrefix}%
}
\makeatother
\ExplSyntaxOn
\makeatletter
\NewDocumentCommand\AddElementAndCallNewlabel {mmmm} {
% #1 = number of label
% #2 \@newl@bel-prefix
% #3 name of label
% #4 property-list of label
\__MyStuff_AddElement:nn{#1}{#3}
\@newl@bel{MYmapping}{#1}{#3}
\@newl@bel{#2}{#3}{#4\labelnumber{#1}}
}
\newcommand\GetLabelNameFromLabelNumber[2]{
% #1 = number of label
% #2 = tokens in case no label name is mapped to number of label
\romannumeral
\cs_if_exist:cTF{MYmapping@#1}
{ \exp_args:Nc \use_ii_i:nn
{MYmapping@#1}
{\exp_args:No \use_ii_i:nn}{\UD@stopromannumeral} }
{\UD@stopromannumeral#2}
}
% Error-message in case no label name is mapped to number of label;
% like zref@refused, but for prefix MYmapping instead of \ZREF@RefPrefix=Z@R:
\NewDocumentCommand\LabelNumberNotMapped{m}{
% #1 = term denoting number of label
\protect\G@refundefinedtrue\@latex@warning {No~zref-label~is~mapped~to~number~`#1'~on~page~\thepage}
}
\makeatother
\seq_new:N \g__zrefcheck_auxfile_labelseq_seq
\cs_new:Nn \__MyStuff_AddElement:nn {
\seq_gput_right:Nn \g__zrefcheck_auxfile_labelseq_seq {{#1}{#2}}
}
\NewDocumentCommand \ListOfZrefLabels {} {%
\seq_map_function:NN \g__zrefcheck_auxfile_labelseq_seq \__MyStuff_PrintElement:n
}
\cs_new:Nn \__MyStuff_PrintElement:n {
\__MyStuff_PrintElement:nn #1
}
\cs_new:Nn \__MyStuff_PrintElement:nn{
#1.\nobreakspace#2,~
}
\AfterLastShipout{\xdef\labelcount{0}\cs_set:Nn \__MyStuff_AddElement:nn {}}%
\ExplSyntaxOff
\makeatletter
% For the sake of having fun define a loop which prints the names of all labels defined
% before and after the label specified as argument:
\@ifdefinable\gobbletorelax{\long\def\gobbletorelax#1\relax{}}%
\@ifdefinable\delivertorelax{\long\def\delivertorelax#1\relax{#1}}%
\newcommand\UD@CheckWhetherRelax[1]{%
\ifcat$\detokenize\expandafter{\gobbletorelax#1\relax}$%
\expandafter\@secondoftwo\else\expandafter\@firstofone\fi
{%
\ifcat$\detokenize\expandafter{\romannumeral\delivertorelax\UD@stopromannumeral#1}$%
\expandafter\@firstoftwo\else\expandafter\@secondoftwo\fi
{\@firstoftwo}%
}%
{\@secondoftwo}%
}%
\newcommand\AllPreceedingAndFollowingLabels[2]{%
\romannumeral
\zref@ifrefundefined{#1}{\UD@stopromannumeral#2}{%
\AllSubSequentLabelsLoop{#1}{%
\AllSubSequentLabelsLoop{#1}%
{\expandafter\UD@stopromannumeral\@firstofone}%
{+}%
}{-}{\textbf{#1}}%
}%
}%
\newcommand\jointhem[4]{\AllSubSequentLabelsLoop{#1}{#2}{#3}{#4, #1}}%
\newcommand\jointhemexchanged[4]{\AllSubSequentLabelsLoop{#1}{#2}{#3}{#1, #4}}%
\newcommand\AllSubSequentLabelsLoop[4]{%
% #1 = name of label.
% #2 = tokens to aplly to the list of label-names gathered so far when the loop is done.
% #3 = +/- ; marker for obtaining name of following/previous label.
% #4 = list of label-names gathered so far.
\expandafter\expandafter\expandafter\UD@CheckWhetherRelax
\expandafter\expandafter\expandafter{%
\GetLabelNameFromLabelNumber{%
\number\numexpr\zref@extractdefault{#1}{labelnumber}{-2}#31\relax
}{\relax}%
}%
{#2{#4}}%
{%
\ifx#3-%
\expandafter\@firstoftwo\else\expandafter\@secondoftwo\fi
{\expandafter\expandafter\expandafter\jointhemexchanged}%
{\expandafter\expandafter\expandafter\jointhem}%
\expandafter\expandafter\expandafter{%
\GetLabelNameFromLabelNumber{%
\number\numexpr\zref@extractdefault{#1}{labelnumber}{-2}#31\relax
}{\relax}%
}{#2}{#3}{#4}%
}%
}%
\makeatother
\begin{document}
\hrulefill
Some text.
\hrulefill
\ListOfZrefLabels
\hrulefill
\makeatletter
LabelUndef has number \zref@extractdefault{LabelUndef}{labelnumber}{\zref@refused{LabelUndef}\textsf{-not available-}}%
LabelA has number \zref@extractdefault{LabelA}{labelnumber}{\zref@refused{LabelA}\textsf{-not available-}}%
LabelB has number \zref@extractdefault{LabelB}{labelnumber}{\zref@refused{LabelB}\textsf{-not available-}}%
LabelC has number \zref@extractdefault{LabelC}{labelnumber}{\zref@refused{LabelC}\textsf{-not available-}}%
LabelD has number \zref@extractdefault{LabelD}{labelnumber}{\zref@refused{LabelD}\textsf{-not available-}}%
LabelE has number \zref@extractdefault{LabelE}{labelnumber}{\zref@refused{LabelE}\textsf{-not available-}}%
\hrulefill
The name of label number 0 is
\GetLabelNameFromLabelNumber{0}{\LabelNumberNotMapped{0}\textsf{-not available-}}
The name of label number 1 is
\GetLabelNameFromLabelNumber{1}{\LabelNumberNotMapped{1}\textsf{-not available-}}
The name of label number 2 is
\GetLabelNameFromLabelNumber{2}{\LabelNumberNotMapped{2}\textsf{-not available-}}
The name of label number 3 is
\GetLabelNameFromLabelNumber{3}{\LabelNumberNotMapped{3}\textsf{-not available-}}
The name of label number 4 is
\GetLabelNameFromLabelNumber{4}{\LabelNumberNotMapped{4}\textsf{-not available-}}
The name of label number 5 is
\GetLabelNameFromLabelNumber{5}{\LabelNumberNotMapped{5}\textsf{-not available-}}
The name of label number 6 is
\GetLabelNameFromLabelNumber{6}{\LabelNumberNotMapped{6}\textsf{-not available-}}
The name of label number W is
\GetLabelNameFromLabelNumber{W}{\LabelNumberNotMapped{W}\textsf{-not available-}}
The name of the label which was placed after LabelC is
\GetLabelNameFromLabelNumber{%
\number\numexpr\zref@extractdefault{LabelC}{labelnumber}{-2}+1\relax
}{\LabelNumberNotMapped{%
<Number of LabelC>+1
}%
\textsf{-not available-}%
}
The name of the label which was placed after LabelE is
\GetLabelNameFromLabelNumber{%
\number\numexpr\zref@extractdefault{LabelE}{labelnumber}{-2}+1\relax
}{\LabelNumberNotMapped{%
<Number of LabelE>+1
}%
\textsf{-not available-}%
}
\hrulefill
\AllPreceedingAndFollowingLabels{LabelUndef}{\zref@refused{LabelUndef}\textsf{-not available-}}%
\AllPreceedingAndFollowingLabels{LabelA}{\zref@refused{LabelA}\textsf{-not available-}}%
\AllPreceedingAndFollowingLabels{LabelB}{\zref@refused{LabelB}\textsf{-not available-}}%
\AllPreceedingAndFollowingLabels{LabelC}{\zref@refused{LabelC}\textsf{-not available-}}%
\AllPreceedingAndFollowingLabels{LabelD}{\zref@refused{LabelD}\textsf{-not available-}}%
\AllPreceedingAndFollowingLabels{LabelE}{\zref@refused{LabelE}\textsf{-not available-}}%
\hrulefill
\zref@labelbylist{LabelA}{main}%
\zref@labelbylist{LabelB}{main}%
\zref@labelbylist{LabelC}{main}%
\zref@labelbylist{LabelD}{main}%
\zref@labelbylist{LabelE}{main}%
\end{document}