4

I want to

  1. make reference to lines in Verbatim environment provided by fancyvrb pacakge, and
  2. make the reference a hyperlink which jumps to the right position where corresponding label is defined, with the help of hyperref

If simply use fancyvrb with hyperref, these references all jump to hyperref's named destination Doc-Start (use the following MWE). Thus I modified \FV@refstepcounter to directly use \steprefcounter.

This modification works on the label of first and second line of Verbatim env. But, from the third line, \ref{vrb:n} jumps to the position of \label{vrb:n-1} (use the following MWE).

\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}

output of mwe

This problem is re-producible with either pdflatex, lualatex or xelatex contained in texlive 2019. And I have latest fancyvrb and hyperref installed.

What I have investigated

  1. .aux shows that every label creates distinct hyper target, e.g., \label{vrb:3} creates \newlabel{vrb:3}{...}{1}{}{FancyVerbLine.3}{}.
  2. Contents of output PDF shows that the coordinates of named destination of FancyVerbLine.2 and FancyVerbLine.3 are the same, which causes the jump-to-wrong-position problem.
  3. Un-comment the lines of \PackageError will show that the counter stepping is only taken inside \FV@refstepcounter.
  4. Further more, listings and hyperref works ok.
\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

This question is related about fancyvrb only. hyperref makes the "internal info" obvious, but the info is unchanged. The key info that the mwe in question provides (and I am still confusing about) is,

  • the ypos of "FancyVerbLine.2" is always 10pt lower than that of "FancyVerbLine.1" (no matter how much the font size is set outside Verbatim env), and
  • ypos of "FancyVerbLine.3" is always the same as "FancyVerbLine.2"

By moving \FV@StepLineNo from the early \FV@@PreProcessLine to the very late command \FV@ListProcessLine (which does the actual output line-by-line), this problems seems solved. I am not sure if this patch would cause any new problems.

\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}

Some explanations, or personal understanding:

  1. hyperref redefined \refstepcounter to

    1. update \@currentHref (which stores FancyVerbLine.<n> in the mwe) and
    2. write named destination to PDF (more general speaking, the driver).
  2. When named destinations are written, indirect coordinates are used. For xetex + (x)dvipdfmx, @xpos and @ypos are used (instead of direct coordinate numbers), which are inherited from dvipdfm.
  3. Hence, the problem is, \refstepcounter{FancyVerbLine} is called at a wrong position. One can check this with the savepos module of zref package, and without loading of hyperref. (see the following example)
  4. fancyvrb uses \FV@StepLineNo as a wrapper of \refstepcounter, in order to support options like 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}

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.