可能的好的解决方案

可能的好的解决方案

(有点相关为链接和索引部分标题定义命令)。

我试图让章节标题链接到目录中的条目,以便获得“双向”导航,例如:用户在目录中查找章节标题,跳转到那里,阅读几行,再次单击章节标题并再次被带到目录中该章节的条目。

我原来的问题是这里,从那里的讨论中我得到了以下代码:

\newcounter{tocBackrefCount}
\newcommand{\Chapter}[3]{%
\chapter%
(#1)%
[\protect\raisebox{\baselineskip}{\protect\hypertarget{toc:backref:\thetocBackrefCount}{}}#2]%
{\hyperlink{toc:backref:\thetocBackrefCount}{#3}}\stepcounter{tocBackrefCount}%
}

(注:括号中的论据\chapter是由hypbmsec包裹) 这个宏的调用方式如下\Chapter{bookmark}{short}{long},其中bookmark是您希望它出现在查看器中的书签名称,short是目录条目,long是实际的章节标题(在文本中,将被链接的标题)。

我目前使用此方法面临三个问题:

  1. 需要 3 到 5 次编译才能将所有引用放到位(也许hypdestopt 包与此有关系……?)。
  2. 它是丑陋的,我宁愿重新定义标准\chapter\chapter*命令,或者至少让它采用像标准一样默认的可选参数。

您愿意分享您在这个问题上的专业知识吗?

PS:该\raisebox命令是为了避免hyperref将目标的锚点设置为 ToC 条目的左下角(试一试不用它,你就会明白我的意思)。

PPS:这里有一个 MWE 供您欣赏:

\documentclass{book}
\usepackage[colorlinks,linktocpage]{hyperref}
\usepackage{hypbmsec}
\newcounter{tocBackrefCount}
\newcommand{\Chapter}[3]{%
\chapter(#1)%
[\protect\raisebox{\baselineskip}{\protect\hypertarget{toc:backref:\thetocBackrefCount}{}}#2]%
{\hyperlink{toc:backref:\thetocBackrefCount}{#3}}%
\stepcounter{tocBackrefCount}}
\begin{document}
  \tableofcontents
  \Chapter{Bookmark 1}{Short 1}{Long 1}
    \noindent\ldots
  \Chapter{Bookmark 2}{Short 2}{Long 2}
    \noindent\ldots
  \Chapter{Bookmark 3}{Short 3}{Long 3}
    \noindent\ldots
  \Chapter{Bookmark 4}{Short 4}{Long 4}
    \noindent\ldots
  \Chapter{Bookmark 5}{Short 5}{Long 5}
    \noindent\ldots
  \Chapter{Bookmark 6}{Short 6}{Long 6}
    \noindent\ldots
  \Chapter{Bookmark 7}{Short 7}{Long 7}
    \noindent\ldots
\end{document}

可能的好的解决方案

在周末的大部分时间里,我都在摆弄这个问题,得到了@egreg代码的以下“最终”版本:

\documentclass{book}
\usepackage{xparse}
\usepackage{etoolbox}
\usepackage[colorlinks,linktocpage,plainpages=false,pdfpagelabels]{hyperref}
\usepackage{bookmark}
\usepackage{hypbmsec}
\usepackage{hypdestopt}

\makeatletter
\newcounter{tocBackrefCount}

\def\MPR@command#1{%
  \AtBeginDocument{\csletcs{HYPBMSEC@#1}{#1}\csletcs{#1}{MPR@#1}}%
  \expandafter\DeclareDocumentCommand\csname MPR@#1\endcsname{s d() o m}{
    \stepcounter{tocBackrefCount}
    \IfBooleanTF{##1}
      {\IfNoValueTF{##2}
        {\IfNoValueTF{##3}
          {\addtocounter{tocBackrefCount}{-1}\@nameuse{HYPBMSEC@#1}*{##4}}
          {\@nameuse{HYPBMSEC@#1}*{\phantomsection{}\mpr@mand{##4}%
            \addcontentsline{toc}{#1}{\texorpdfstring{\mpr@opt{##3}}{##3}}}}
        }
        {\IfNoValueTF{##3}
          {\@nameuse{HYPBMSEC@#1}*{\phantomsection{}\mpr@mand{##4}%
            \addcontentsline{toc}{#1}{\texorpdfstring{\mpr@opt{##4}}{##2}}}}
          {\@nameuse{HYPBMSEC@#1}*{\phantomsection{}\mpr@mand{##4}%
            \addcontentsline{toc}{#1}{\texorpdfstring{\mpr@opt{##3}}{##2}}}}
        }
      }
      {\IfNoValueTF{##2}
        {\IfNoValueTF{##3}
          {\@nameuse{HYPBMSEC@#1}(##4)[\mpr@opt{##4}]{\mpr@mand{##4}}}
          {\@nameuse{HYPBMSEC@#1}(##3)[\mpr@opt{##3}]{\mpr@mand{##4}}}
        }
        {\IfNoValueTF{##3}
          {\@nameuse{HYPBMSEC@#1}(##2)[\mpr@opt{##4}]{\mpr@mand{##4}}}
          {\@nameuse{HYPBMSEC@#1}(##2)[\mpr@opt{##3}]{\mpr@mand{##4}}}
        }
      }
  }
}
\def\mpr@opt#1{\protect\raisebox{1.6ex}{%
  \protect\hypertarget{toc:backref:\thetocBackrefCount}{}}#1}
\def\mpr@mand#1{\hyperlink{toc:backref:\thetocBackrefCount}{#1}}

\MPR@command{chapter}
\MPR@command{section}
\makeatother

\begin{document}
  \tableofcontents

  \chapter{No optional argument}
    \noindent\ldots

  \section{ABC}
    \noindent\ldots

  \chapter[opt]{Optional argument}
    \noindent\ldots

  \chapter(book){No optional argument + bookmark}
    \noindent\ldots

  \chapter(book)[opt]{Both optional arguments}
    \noindent\ldots

  \chapter*{No optional argument}
    \noindent\ldots

  \section*{ABC}
    \noindent\ldots

  \chapter*[opt]{Optional argument}
    \noindent\ldots

  \chapter*(book){No optional argument + bookmark}
    \noindent\ldots

  \chapter*(book)[opt]{Both optional arguments}
    \noindent\ldots

\end{document}

这很好地处理了带星号的分段命令版本。请注意添加hypdestopt包裹:如果没有它,您会收到有关重复hyperref目标的虚假警告。

请注意,我们不再\reisebox使用整体\bsaelineskip:这会在 ToC 中产生某些不良影响;相反,1.6ex使用经验合理的值。我敢打赌,这将不是是一个万无一失的解决方案,我愿意听取有关此事的建议。

这个解决方案对我来说最有效,但我也想听听您的想法,我相信在这个问题上还有更多可以说的!

答案1

\documentclass{book}
\usepackage{xparse}
\usepackage[colorlinks,linktocpage]{hyperref}
\usepackage{bookmark}
\usepackage{hypbmsec}

\newcounter{tocBackrefCount}

\makeatletter
\def\MPR@chapter{\@ifstar{\HYPBMSEC@chapter*}\MPR@chapter@}

\DeclareDocumentCommand{\MPR@chapter@}{d() o m}{
  \stepcounter{tocBackrefCount}
  \IfNoValueTF{#1}
    {\IfNoValueTF{#2}
      {\HYPBMSEC@chapter(#3)[\mpr@opt{#3}]{\mpr@mand{#3}}}
      {\HYPBMSEC@chapter(#2)[\mpr@opt{#2}]{\mpr@mand{#3}}}
    }
    {\IfNoValueTF{#2}
      {\HYPBMSEC@chapter(#1)[\mpr@opt{#3}]{\mpr@mand{#3}}}
      {\HYPBMSEC@chapter(#1)[\mpr@opt{#2}]{\mpr@mand{#3}}}
    }
}
\def\mpr@opt#1{\protect\raisebox{\baselineskip}{%
  \protect\hypertarget{toc:backref:\thetocBackrefCount}{}}#1}
\def\mpr@mand#1{\hyperlink{toc:backref:\thetocBackrefCount}{#1}}

\AtBeginDocument{
  \let\HYPBMSEC@chapter\chapter
  \let\chapter\MPR@chapter
}
\makeatother

\begin{document}
  \tableofcontents

  \chapter{No optional argument}
    \noindent\ldots
  \chapter[opt]{Optional argument}
    \noindent\ldots
  \chapter(book){No optional argument + bookmark}
    \noindent\ldots
  \chapter(book)[opt]{Both optional arguments}
    \noindent\ldots
\end{document}

加载中书签通常会节省 LaTeX 运行的次数。

通用定义

以下是如何扩展以前的方法来重新定义所有所需的分段命令

\documentclass{book}
\usepackage{xparse,etoolbox}
\usepackage[colorlinks,linktocpage]{hyperref}
\usepackage{bookmark}
\usepackage{hypbmsec}

\makeatletter

\newcounter{tocBackrefCount}

\def\MPR@command#1{%
  \AtBeginDocument{\csletcs{HYPBMSEC@#1}{#1}\csletcs{#1}{MPR@#1}}%
  \@namedef{MPR@#1}{\@ifstar{\@nameuse{HYPBMSEC@#1}*}{\@nameuse{MPR@#1@}}}%
  % define \MPR@<command>@
  \expandafter\DeclareDocumentCommand\csname MPR@#1@\endcsname{d() o m}{
    \stepcounter{tocBackrefCount}
    \IfNoValueTF{##1}
      {\IfNoValueTF{##2}
        {\@nameuse{HYPBMSEC@#1}(##3)[\mpr@opt{##3}]{\mpr@mand{##3}}}
        {\@nameuse{HYPBMSEC@#1}(##2)[\mpr@opt{##2}]{\mpr@mand{##3}}}
      }
      {\IfNoValueTF{##2}
        {\@nameuse{HYPBMSEC@#1}(##1)[\mpr@opt{##3}]{\mpr@mand{##3}}}
        {\@nameuse{HYPBMSEC@#1}(##1)[\mpr@opt{##2}]{\mpr@mand{##3}}}
      }
  }
}
\def\mpr@opt#1{\protect\raisebox{\baselineskip}{%
  \protect\hypertarget{toc:backref:\thetocBackrefCount}{}}#1}
\def\mpr@mand#1{\hyperlink{toc:backref:\thetocBackrefCount}{#1}}

\MPR@command{chapter}
\MPR@command{section}

\makeatother

\begin{document}

\tableofcontents

\chapter{No optional argument}
\noindent\ldots
\section{ABC}
\noindent\ldots

\chapter[opt]{Optional argument}
\noindent\ldots

\chapter(book){No optional argument + bookmark}
\noindent\ldots

\chapter(book)[opt]{Both optional arguments}
\noindent\ldots

\end{document}

现在只需列出要重新定义的命令,在此示例中仅为\chapter\section,通过\MPR@command{<section level>}

我未能成功重新定义方便\chapter*和类似以允许可选参数。仍在考虑。

答案2

以下是将某个命令包装到另一个命令中的常用模式。以本章为例:

\let\chapterOriginal\chapter
\renewcommand{\chapter}{...\chapterOriginal ...}

“let”为原始命令创建了另一个名称,当原始命令被重新定义时,该名称仍然有效。

相关内容