我想确保引用符合浮点数出现的顺序。也就是说,我不想改变浮点数的顺序,但我想避免这种情况:
... 如图 2 所示。...如图 1 所示。
如果第一次引用的图表或表格的编号高于以下编号,我该如何发出警告?当然需要否决此案
...如图 1 所示。 ...如图 2 所示。 ...如图 1 所示。
答案1
我建议在每次引用时检查所引用图形的编号,看看它是否是最高的,如果不是,检查它是否之前被引用过。否则:抛出错误。
请考虑以下文档:
\documentclass{scrartcl}
\usepackage{hyperref}
\usepackage[demo]{graphicx}
\begin{document}
\section{Figures}\label{sec}
\begin{figure}
\includegraphics[width=4cm,height=2cm]{}
\caption{first figure}
\label{first}
\end{figure}
\autoref{first}
\autoref{second}\autoref{sec}\autoref{first}
\begin{figure}
\includegraphics[width=2cm,height=4cm]{}
\caption{second figure}
\label{second}
\end{figure}
\end{document}
这导致
如果我没看错的话,这应该没问题,但是删除第一的 \autoref{first}
会导致错误。
为了实现这一点,请注意hyperref
\r@<labelname>
处理标签:对于每个标签,定义一个由 5 位信息组成的命令,在本例中(对于标签first
):
> \r@first=macro:
->{1}{1}{first figure}{figure.1}{}.
对我们来说重要的是:第四组由 组成<counter name>.<counter value>
。这可以通过定义来提取,例如,
\def\parse#1#2#3#4#5{\@parse#4\@nil}
\def\@parse#1.#2\@nil{\def\current@type{#1}\def\current@number{#2}}
现在呼叫
\expandafter\parse\r@first
figure
中\current@type
和1
中保存\current@number
。但请注意,\r@<label>
值不是在第一次运行 LaTeX 时定义,因为它的定义\newlabel
存在于aux
文件中。因此,任何使用它都应该包含在测试中,以查看命令是否已定义!
接下来,我们引入一个计数器来跟踪已被引用的数字:
\newcounter{highestyet}
\setcounter{highestyet}{0}
每次调用\autoref
都应将当前值与该值进行对比。让我们收集我们想要\autoref
在某个命令中执行的所有操作,并使用一个参数作为我们引用的标签名称:
首先,我们应该检查是否\r@#1
已定义。如果是,我们使用\parse
宏来提取计数器名称和值,否则我们将它们设置为0
以避免错误:
\ifcsname r@#1\endcsname%
\expandafter\expandafter\expandafter\parse%
\csname r@#1\endcsname%
\else\def\current@type{0}\def\current@number{0}\fi%
这etoolbox
包提供了测试\ifdefstring
,我们可以使用它来查看计数器名称是否为figure
。在这种情况下,我们可以使用\ifnumgreater
当前值与迄今为止看到的最高值进行比较。如果\current@number
大于,highestyet
我们将替换它。如果它较小,我们检查这个图是否已经被引用。这个想法是为每个图号设置一个布尔值,一旦它被引用,就设置为true
。为此,我们可以使用\providebool
命令(再次来自etoolbox
) 使我们在检查布尔值之前不必担心布尔值是否 (未) 定义。如果布尔值为 false,我们会发出警告。否则,什么也不会发生:
\ifdefstring{\current@type}{figure}{%
\ifnumgreater{\current@number}{\value{highestyet}}
{\setcounter{highestyet}{\current@number}}
{\ifnumgreater{\value{highestyet}}{\current@number}
{\expandafter\providebool{figure@\current@number @isrefd}%
\expandafter\ifbool{figure@\current@number @isrefd}
{}{Warning!}}{}}%
\expandafter\providebool{figure@\current@number @isrefd}%
\expandafter\booltrue{figure@\current@number @isrefd}%
}{}%
好的,就是这样!如果我们调用这个宏\stuff
,我们需要做的就是附加\stuff
到(或您选择的参考命令...但请注意,命令的\autoref
结构在解析宏中明确使用,如果您使用任何其他标签包,您可能需要检查它们没有重新定义它!)。hyperref
\newlabel
然而,直接修补是困难的\autoref
。仔细观察(\show
从\xshowcmd
xpatch
是你的朋友在这里...),\autoref
实际上调用\HyRef@autoref
,这是实际与我们感兴趣的参数(标签名称)一起工作的命令(请注意,标签存储在#2
!):
> \autoref =macro:
->\leavevmode \@ifstar {\HyRef@autoref \@gobbletwo }{\HyRef@autoref \hyper@@link }.
> \HyRef@autoref=macro:
#1#2->\begingroup \Hy@safe@activestrue \expandafter \HyRef@autosetref \csname r@#2\endcsname {#2}{#1}\endgroup .
我们可以简单地修补它,\xapptocmd
使用xpatch
包裹:
\xapptocmd{\HyRef@autoref}{\stuff{#2}}{}{}
事实上,在这种情况下,\apptocmd
来自etoolbox
如果你正在使用一个没有l3
(因此没有) 的旧系统,也可以解决这个问题xpatch
) 支持。
对于您希望遵守此行为的任何其他命令也是如此:例如,当使用cleveref
包(应该加载后 hyperref
!),可以检查所有变体\cref
(例如\Cref
)最终都会调用\@cref
,它将调用的命令和标签名称作为参数,并将后者再次存储在中#2
。因此,如上所述,应该添加
\xapptocmd{\@cref}{\stuff{#2}}{}{}
到文件(与上述相同的注释适用于x
)。由于它们使用相同的计数器,因此命令也将协作,即先前的引用使用\autocite
也将由后续的检测到\cref
,等等。
现在,完整文档
\documentclass{scrartcl}
\usepackage[demo]{graphicx}
\usepackage{xpatch}
\usepackage{hyperref}
\makeatletter
\def\parse#1#2#3#4#5{\@parse#4\@nil}
\def\@parse#1.#2\@nil{\def\current@type{#1}\def\current@number{#2}}
\newcounter{highestyet}
\setcounter{highestyet}{0}
\def\stuff#1{%
\ifcsname r@#1\endcsname%
\expandafter\expandafter\expandafter\parse%
\csname r@#1\endcsname%
\else\def\current@type{0}\def\current@number{0}\fi%
\ifdefstring{\current@type}{figure}{%
\ifnumgreater{\current@number}{\value{highestyet}}
{\setcounter{highestyet}{\current@number}}
{\ifnumgreater{\value{highestyet}}{\current@number}
{\expandafter\providebool{figure@\current@number @isrefd}%
\expandafter\ifbool{figure@\current@number @isrefd}
{}{Warning!}}{}}%
\expandafter\providebool{figure@\current@number @isrefd}%
\expandafter\booltrue{figure@\current@number @isrefd}%
}{}%
}
\xapptocmd{\HyRef@autoref}{\stuff{#2}}{}{}
\makeatother
\begin{document}
\section{Figures}\label{sec}
\begin{figure}
\includegraphics[width=4cm,height=2cm]{}
\caption{first figure}
\label{first}
\end{figure}
%\autoref{first}
\autoref{second}\autoref{sec}\autoref{first}
\begin{figure}
\includegraphics[width=2cm,height=4cm]{}
\caption{second figure}
\label{second}
\end{figure}
\end{document}
编译为
如果删除行中的注释%\autoref{first}
,结果与上面相同(即警告消失)。
在实践中,你可能希望用类似以下内容替换“警告!”
\GenericWarning{}{Warning! Figure \current@number\space referenced after
figure \thehighestyet\space on page \thepage\space without being
referenced before}%
生产
Warning! Figure 1 referenced after figure 2 on page 1 without being referenced
before on input line 45.
(也可以看看errmessage 中的命令扩展)
最后要说明的是:这种方法当然适用于更多的计数器figure
。例如,要包含表格,需要区分计数器highestyet
,即将它们命名为highestyet@figure
和,highestyet@table
并添加
\ifdefstring{\current@type}{table}
检查宏中false
的部分。这应该包括与上面相同的代码,并在适当的位置设置适当的计数器。\ifdefstring{\current@type}{figure}
\stuff
答案2
这只是 Jonathans 代码的更新版本,它包含针对软件包用户的小错误修复caption
,并且针对表格进行了扩展。
这也可作为如何在fancyref
包中使用此功能的示例。
\documentclass{scrartcl}
\usepackage[demo]{graphicx}
\usepackage{xpatch}
\usepackage{fancyref}
\usepackage{hyperref}
\usepackage{caption}
\makeatletter
\def\parse#1#2#3#4#5{\@parse#4\@nil}
%%Begin fix for caption package: %%
\def\@parse#1.#2\@nil{
\def\current@type{#1}
\ifdefstring{\current@type}{figure}{\@@parse#2\@nil}
{\ifdefstring{\current@type}{table}{\@@parse#2\@nil}}
{}
}
\def\@@parse#1.#2\@nil{\def\current@number{#2}}
%%End fix for caption package: %%
%If you ar not using \usepackage{caption} then simply use this line instead:
%\def\@parse#1.#2\@nil{\def\current@type{#1}\def\current@number{#2}}
\newcounter{highestfigureyet}
\setcounter{highestfigureyet}{0}
\newcounter{highesttableyet}
\setcounter{highesttableyet}{0}
\def\checkfloatreforder#1{%
\ifcsname r@#1\endcsname%
\expandafter\expandafter\expandafter\parse%
\csname r@#1\endcsname%
\else\def\current@type{0}\def\current@number{0}\fi%
\ifdefstring{\current@type}{figure}{%
\ifnumgreater{\current@number}{\value{highestfigureyet}}
{\setcounter{highestfigureyet}{\current@number}}
{\ifnumgreater{\value{highestfigureyet}}{\current@number}
{\expandafter\providebool{figure@\current@number @isrefd}%
\expandafter\ifbool{figure@\current@number @isrefd}
{}{\GenericWarning{}{Warning! Figure \current@number\space referenced after
figure \thehighestfigureyet\space on page \thepage\space without being referenced before}}}{}}%
\expandafter\providebool{figure@\current@number @isrefd}%
\expandafter\booltrue{figure@\current@number @isrefd}%
}{%
\ifdefstring{\current@type}{table}{%
\ifnumgreater{\current@number}{\value{highesttableyet}}
{\setcounter{highesttableyet}{\current@number}}
{\ifnumgreater{\value{highesttableyet}}{\current@number}
{\expandafter\providebool{table@\current@number @isrefd}%
\expandafter\ifbool{table@\current@number @isrefd}
{}{\GenericWarning{}{Warning! Table \current@number\space referenced after
table \thehighesttableyet\space on page \thepage\space without being referenced before}}}{}}%
\expandafter\providebool{table@\current@number @isrefd}%
\expandafter\booltrue{table@\current@number @isrefd}%
}{}
}%
}
\let\old@fref\fref
\renewcommand*{\fref}[1]{\old@fref{#1}
\checkfloatreforder{#1}}
\makeatother
\begin{document}
\section{Figures}\label{sec:figures}
\begin{figure}
\includegraphics[width=4cm,height=2cm]{}
\caption{first figure}
\label{fig:first}
\end{figure}
\fref{fig:second} in \fref{sec:figures} and \fref{fig:first}
\begin{figure}
\includegraphics[width=2cm,height=4cm]{}
\caption{second figure}
\label{fig:second}
\end{figure}
\section{Tables}\label{sec:tables}
\begin{table}
\includegraphics[width=4cm,height=2cm]{}
\caption{first table}
\label{tab:first}
\end{table}
\fref{tab:second} in \fref{sec:tables} and \fref{tab:first}
\begin{table}
\includegraphics[width=2cm,height=4cm]{}
\caption{second table}
\label{tab:second}
\end{table}
\end{document}
造成这一切的原因是,caption
包改变了您必须解析的字符串:
{1}{1}{first figure\relax }{figure.1}{}
更改为{1}{1}{first figure\relax}{figure.caption.1}{}
这也适用于表格
各部分的列表项标签保持不变:
{1}{1}{Figures}{section.1}{}
遗迹{1}{1}{Figures}{section.1}{}