Я вижу следующие сообщения об ошибках от pdflatex в действительно гигантском проекте:
(see the transcript file for additional information)pdfTeX warning (dest): name
{lstnumber.-14.11} has been referenced but does not exist, replaced by a fixed
one
pdfTeX warning (dest): name{lstnumber.-18.22} has been referenced but does not
exist, replaced by a fixed one
pdfTeX warning (dest): name{lstnumber.-9.40} has been referenced but does not e
xist, replaced by a fixed one
pdfTeX warning (dest): name{lstnumber.-4.3} has been referenced but does not ex
ist, replaced by a fixed one
Верхняя часть моего основного файла выглядит так:
\documentclass[ebook,10pt,oneside,final]{memoir}
\usepackage{microtype}
% support for code listings
\usepackage[final]{listings}
\include{autodedent}
\lstset{
basicstyle=\ttfamily\footnotesize,
numberstyle=\footnotesize,
breaklines=true,
numbers=left,
firstnumber=1,
rangeprefix=//,
includerangemarker=false
}
% support for indexing
\usepackage{makeidx}
\makeindex
% make _ a non-special character
\usepackage{underscore}
% support for cross-references
\usepackage{hyperref}
% \newcommand{\href}[2]{#2}
% fix spacing in \tableofcontents
\renewcommand\partnumberline[1]{#1\hspace{1em}}
% custom commands for use in the text of the book itself
\newcommand{\newterm}[1]{\textit{#1}}
\newcommand{\code}[1]{\mbox{\lstinline[basicstyle=\ttfamily]$#1$}}
\newcommand{\slurl}[1]{\href{https://#1}{\textsl{#1}}}
\newcommand{\codeblock}[2]{\label{foo#1#2}\hspace{1em}\lstinputlisting[linerange=ex#2-dex#2,autodedent]{examples-ch#1.cc}}
\newcommand{\codeblockref}[2]{\pageref{foo#1#2}}
\newcommand{\Csharp}{C\#}
\begin{document}
\frontmatter
\include{preface}
\tableofcontents
\mainmatter
% ...and so on...
Пример \codeblock
из основного текста:
Let's write a function to multiply each of the elements
in an array by 2.
\codeblock{1}{1}
Our function \code{double_each_element} works \emph{only} with objects of type
\code{array_of_int}...
И пример \codeblockref
:
Compare this version of the code to the version on page
\codeblockref{1}{1}.
К сожалению, если вы соберете только эти фрагменты в тестовом файле и запустите pdflatex test.tex; pdflatex test.tex
— все будет работать отлично! (За исключением того, что гиперссылка на цифру «1» на самом деле ведет к оглавлению, а не на страницу 1.) Но когда я делаю то же самое в масштабе многих глав, я получаю сообщения об ошибках, которые lstnumber.-14.11
вы видите в начале этого вопроса.
Я выяснил, что lstnumber.-<some number>
это формат меток, которые автоматически генерируются пакетом listings
, поэтому я предполагаю, что это какое-то плохое взаимодействие между listings
и hyperref
. Но что именно идет не так и что я могу сделать, чтобы это исправить?
В \hspace{1em}
моем \codeblock
макросе была моя наивная попытка обойти описанную ошибкуздесь, на случай, если проблема была именно в этом.
решение1
Вот пример того, как это следует делать, по моему мнению:
В \codeblock
команда \label
применяется до \lstinputlisting
использования макроса, т. е. до того, как счетчик listings
был увеличен для возможности ссылки с \refstepcounter
-- использование \label
before \refstepcounter
является очень распространенной ошибкой, и, как таковая, информация перекрестной ссылки, записанная с помощью, \label
используется из потенциально \refstepcounter
используемого before, который может быть связан с section
etc. counter, поэтому информация неверна, и координаты гиперякоря берутся из более старого экземпляра якоря before. Таким образом, ссылки указывают обратно на неправильную позицию.
На самом деле, \lstinputlisting
есть label=
возможность указать имя метки.
Ниже приведена очень сокращенная форма, предложенная не-MWE, но она работает.
\documentclass[ebook,10pt,oneside,final]{memoir}
\usepackage{microtype}
% support for code listings
\begin{filecontents}{examplehelloworld.c}
#include<stdio.h>
int main(int argc,char **argv)
{
printf("Hello World!\n");
return(0);
}
\end{filecontents}
\usepackage[final]{listings}
%\include{autodedent}
\lstset{
basicstyle=\ttfamily\footnotesize,
numberstyle=\footnotesize,
breaklines=true,
numbers=left,
firstnumber=1,
rangeprefix=//,
includerangemarker=false
}
% support for indexing
\usepackage{makeidx}
\makeindex
% make _ a non-special character
\usepackage{underscore}
% support for cross-references
\usepackage{hyperref}
% \newcommand{\href}[2]{#2}
% fix spacing in \tableofcontents
\renewcommand\partnumberline[1]{#1\hspace{1em}}
% custom commands for use in the text of the book itself
\newcommand{\newterm}[1]{\textit{#1}}
\newcommand{\code}[1]{\mbox{\lstinline[basicstyle=\ttfamily]$#1$}}
\newcommand{\slurl}[1]{\href{https://#1}{\textsl{#1}}}
\newcommand{\codeblock}[2]{\hspace{1em}\lstinputlisting[language={C},label={lst:#1-#2}]{examplehelloworld.c}}
\newcommand{\codeblockref}[2]{\pageref{lst:#1-#2}}
\newcommand{\Csharp}{C\#}
\usepackage{blindtext}
\begin{document}
\frontmatter
%\include{preface}
\tableofcontents
\mainmatter
\blindtext[3]
Reference: \codeblockref{1}{4}
\blindtext[5]
\codeblock{1}{4}
\end{document}
решение2
В процессе публикации этого вопроса я наткнулся на ответ! (Не то чтобы я его понял)почемуоно работает...)
Если вы соберете только эти фрагменты в тестовом файле и запустите
pdflatex test.tex; pdflatex test.tex
— все будет работать отлично! (За исключением того, что гиперссылка под номером «1» на самом деле ведет в оглавление, а не на страницу 1.)
Когда он выдавал ошибки, я не был удивлен, что hyperref попал не туда. Однако с сокращенным тестовым случаем я был озадачен. Поэтому я пошел читать очтопроблема, и обнаружил, что то, что я хотел, было \phantomsection
, например, таким:
\newcommand{\codeblock}[2]{\phantomsection\label{lst:#1-#2}\lstinputlisting[linerange=ex#2-dex#2,autodedent]{examples-ch#1.cc}}
\newcommand{\codeblockref}[2]{\pageref{lst:#1-#2}}
Обратите внимание, что мой карго-культ \hspace{1em}
больше не нужен (насколько мне известно); и я смог вернуть специальные символы, lst:#1-#2
которые заменил, foo#1#2
пытаясь сократить тестовый пример.
...И по волшебству мой гигантский проект внезапно компилируется отлично! Больше никаких загадочных сообщений об ошибках. Все гиперссылки ведут на нужные страницы и показывают правильный основной текст.
Понятия не имею, почему пропуск \phantomsection
вызвал такой хаос, но после его добавления все исправилось!