引用 Verbatim 环境中第三行的标签跳转到错误的位置

引用 Verbatim 环境中第三行的标签跳转到错误的位置

我想要

  1. 参考软件包Verbatim提供的环境中的行fancyvrb,并
  2. 使引用成为超链接,跳转到定义相应标签的正确位置,借助hyperref

如果简单地使用fancyvrbhyperref这些引用都会跳转到hyperref的命名目标Doc-Start(使用以下 MWE)。因此我修改\FV@refstepcounter为直接使用\steprefcounter

此修改对 env 的第一行和第二行的标签有效Verbatim。但是,从第三行开始,跳转到(使用以下 MWE)\ref{vrb:n}的位置。\label{vrb:n-1}

\documentclass{article}
\usepackage{fancyvrb}
\usepackage{hyperref}

\makeatletter
\def\FV@refstepcounter#1{%
%  \PackageError{FV}{pre step: \the\c@FancyVerbLine, \@currentHref}{}%
  \refstepcounter{#1}%
%  \PackageError{FV}{post step: \the\c@FancyVerbLine, \@currentHref}{}%
}

%% original def
%\def\FV@refstepcounter#1{%  
%  \stepcounter{#1}
%  \protected@edef\@currentlabel{\csname p@#1\endcsname\arabic{FancyVerbLine}}%
%}
\makeatother

\begin{document}
\begin{verbatim}
1. For n = 1 or 2, \ref{vrb:n} jumps to destination \label{n}.
2. For n >= 3, \ref{vrb:n} jumps to destination \label{n - 1}.
\end{verbatim}

\begin{Verbatim}[commandchars=\\\{\},numbers=left]
  first   \label{vrb:1}
  second  \label{vrb:2}
  third   \label{vrb:3}
  forth   \label{vrb:4}
  fifth   \label{vrb:5}
  sixth   \label{vrb:6}
\end{Verbatim}

\ref{vrb:1}, \ref{vrb:2}, \ref{vrb:3}, \ref{vrb:4}, \ref{vrb:5}, and \ref{vrb:6}
\newpage\null
\end{document}

mwe 的输出

使用 texlive 2019 中包含的 pdflatex、lualatex 或 xelatex 均可重现此问题。我已安装最新fancyvrb版本hyperref

我所调查的

  1. .aux表明每个标签都会创建不同的超级目标,例如\label{vrb:3}创建\newlabel{vrb:3}{...}{1}{}{FancyVerbLine.3}{}
  2. 输出PDF内容显示和的命名目标坐标FancyVerbLine.2相同FancyVerbLine.3,导致跳转到错误位置的问题。
  3. 取消注释 行将\PackageError显示仅在 内部进行反向步进\FV@refstepcounter
  4. 此外,listings运行hyperref正常。
\documentclass{article}
\usepackage{listings}
\usepackage{hyperref}

% use example in `texdoc listings`, sec. 8
\lstset{escapeinside={(*@}{@*)}}
\begin{document}
\begin{lstlisting}[numbers=left]
for i:=maxint to 0 do(*@\label{lst:1}@*)
begin                (*@\label{lst:2}@*)
{ comment }          (*@\label{lst:3}@*)
end;
\end{lstlisting}
\ref{lst:1}, \ref{lst:2} and \ref{lst:3}
\end{document}

答案1

这个问题仅与以下相关fancyvrbhyperref使“内部信息”显而易见,但信息没有改变。所讨论的 mwe 提供的关键信息(我仍然对此感到困惑)是,

  • “FancyVerbLine.2” 的 ypos 始终比“FancyVerbLine.1”低 10pt(无论在Verbatimenv 之外设置了多少字体大小),并且
  • “FancyVerbLine.3” 的 ypos 始终与“FancyVerbLine.2”相同

通过\FV@StepLineNo从早期命令移至\FV@@PreProcessLine非常晚期命令\FV@ListProcessLine(逐行执行实际输出),这个问题似乎已解决。我不确定此补丁是否会导致任何新问题。

\documentclass{article}
\usepackage{etoolbox}
\usepackage{fancyvrb}
\usepackage{hyperref}

\makeatletter
\let\FV@refstepcounter\refstepcounter

\patchcmd\FV@@PreProcessLine
  {\FV@StepLineNo}
  {}
  {}{\fail}

\patchcmd\FV@ListProcessLine
  {\kern\leftmargin}
  {\FV@StepLineNo\kern\leftmargin}
  {}{\fail}
\makeatother

\begin{document}
\begin{Verbatim}[numbers=left]
first
second
third
forth
fifth
sixth
\end{Verbatim}

\def\xlink#1{\hyperlink{FancyVerbLine.#1}{#1}}
\xlink{1}, \xlink{2}, \xlink{3}, \xlink{4}, \xlink{5}, \xlink{6}
\newpage\null
\end{document}

一些解释,或者个人的理解:

  1. hyperref重新定义\refstepcounter

    1. 更新\@currentHref(存储FancyVerbLine.<n>在 mwe 中)和
    2. 将命名的目的地写入 PDF(更一般地说,驱动程序)。
  2. 写入命名目标时,将使用间接坐标。对于 xetex + (x)dvipdfmx,将使用@xpos@ypos(而不是直接坐标数字),它们是从继承而来的dvipdfm
  3. 因此,问题在于,\refstepcounter{FancyVerbLine}在错误的位置调用。可以使用包的 savepos 模块检查这一点zref,而无需加载hyperref。(参见以下示例)
  4. fancyvrb用作\FV@StepLineNo的包装器\refstepcounter,以支持以下选项numberblanklines
% !TeX program = pdflatex
\documentclass{article}
\usepackage{fancyvrb}
\usepackage[savepos]{zref}

\makeatletter
\let\FV@refstepcounter\refstepcounter

\newcounter{zRef}
\newcommand\showYPos[1][]{%
  \stepcounter{zRef}%
  \zsavepos{curr:\arabic{zRef}}%
  \def\x{\the\expandafter\numexpr\zposy{curr:\arabic{zRef}}/65536\relax}
  \PackageError{FV}{#1, line: \the\c@FancyVerbLine, ref \the\c@zRef, ypos \x}{}%
}

\def\FV@@PreProcessLine{%
  \showYPos[before step]%  ypos is wrong steped
  \FV@StepLineNo
  \FV@Gobble
  \expandafter\FV@ProcessLine\expandafter{\FV@Line}}

\def\FV@ListProcessLine#1{%
  \hbox to \hsize{%  inside this hbox, ypos is ok
    \FV@StepLineNo
%    \showYPos[inside hbox]%  ypos is ok
    \kern\leftmargin
    \hbox to \linewidth{%
      \FV@LeftListNumber
      \FV@LeftListFrame
      \FancyVerbFormatLine{#1}\hss
      \FV@RightListFrame
      \FV@RightListNumber}%
    \hss}%
%  \showYPos[outside hbox]%  ypos is wrong steped
}
\makeatother

\begin{document}
\fontsize{20}{50}\selectfont
\begin{Verbatim}
a
b
c
d
e
f
\end{Verbatim}
\end{document}

相关内容