summaryrefslogtreecommitdiff
path: root/macros/latex/contrib/hyperref/hyperref-linktarget.dtx
diff options
context:
space:
mode:
Diffstat (limited to 'macros/latex/contrib/hyperref/hyperref-linktarget.dtx')
-rw-r--r--macros/latex/contrib/hyperref/hyperref-linktarget.dtx584
1 files changed, 584 insertions, 0 deletions
diff --git a/macros/latex/contrib/hyperref/hyperref-linktarget.dtx b/macros/latex/contrib/hyperref/hyperref-linktarget.dtx
new file mode 100644
index 0000000000..6ebd482295
--- /dev/null
+++ b/macros/latex/contrib/hyperref/hyperref-linktarget.dtx
@@ -0,0 +1,584 @@
+% \iffalse meta-comment
+%
+%% File: hyperref-linktarget.dtx
+%
+% Copyright (C) 2022-2022 The LaTeX Project
+%
+% It may be distributed and/or modified under the conditions of the
+% LaTeX Project Public License (LPPL), either version 1.3c of this
+% license or (at your option) any later version. The latest version
+% of this license is in the file
+%
+% http://www.latex-project.org/lppl.txt
+%
+% This file is part of the "Hyperref bundle" (The Work in LPPL)
+% and all files in that bundle must be distributed together.
+%
+% -----------------------------------------------------------------------
+%
+% The development version of the bundle can be found at
+%
+% https://github.com/latex3/hyperref
+%
+% for those people who are interested.
+%
+%<*driver>
+\RequirePackage{pdfmanagement-testphase}
+\DocumentMetadata{pdfstandard=A-2b}
+\documentclass[full]{l3doc}
+\usepackage{array,booktabs,hyperxmp}
+\hypersetup{pdfauthor=The LaTeX Project,
+ pdftitle=Make link targets (hyperref bundle)}
+\usepackage[skins]{tcolorbox}
+\definecolor{myblue}{RGB}{00,33,99}
+\newtcolorbox{doccomment}[1][]{enhanced,frame hidden,colback=myblue!10,fontupper=\footnotesize,#1}
+%\newtcbox{smalldoccomment}{size=small,on line,enhanced,frame hidden,colback=myblue!10,fontupper=\footnotesize}
+\begin{document}
+ \DocInput{\jobname.dtx}
+\end{document}
+%</driver>
+% \fi
+% \title{^^A
+% Make link targets ^^A
+% \\
+% hyperref bundle
+% }
+%
+% \author{^^A
+% The \LaTeX{} Project\thanks
+% {^^A
+% E-mail:
+% \href{mailto:latex-team@latex-project.org}
+% {latex-team@latex-project.org}^^A
+% }^^A
+% }
+%
+% \date{Version v7.00o, released 2022-05-13}
+%
+% \maketitle
+% \begin{documentation}
+% \section{Commands to create and adapt targets}
+% This module provides commands to create targets.
+% Their goal is to unify and replace
+% a number of user and internal hyperref commands and to provide a clear
+% interface for package authors and users.
+% \subsection{The main command}
+% \begin{function}{\MakeLinkTarget}
+% \begin{syntax}
+% \cs{MakeLinkTarget}\oarg{prefix}\Arg{counter}\\
+% \cs{MakeLinkTarget}\oarg{prefix}\{\} \\
+% \cs{MakeLinkTarget}*\Arg{manual target}
+% \end{syntax}
+% \end{function}
+%
+% \cs{MakeLinkTarget} creates a target for an internal link, (called a destination
+% if a PDF is created). In vertical mode the target is created where the command is issued,
+% in horizontal mode it is typically (in a PDF) raised by the current \cs{normalbaselineskip}
+% (similar to the targets created by \cs{refstepcounter}).
+%
+% The arguments allow to control the name of the target/anchor/destination. While technically
+% any unique name (e.g. some number) would work, names related to the actual counter both
+% simplifies the debugging and referencing destinations from external documents. Also \cs{autoref}
+% makes use of the name to identify the type of a label: it splits off the part until the first period and
+% then looks up for a defined name. So if you create a target name \texttt{ABC.CDE.1.2}, \cs{autoref} tries
+% to use \cs{ABCautorefname}.
+%
+% The name is then stored \emph{globally}\footnote{This means that the deprecated \pkg{hyperref}
+% option \texttt{localanchorname} is ignored.} in \cs{@currentHref} and used for example in the
+% next \cs{label}.
+% A target name must be unique across a document. It is up to the users and package
+% authors to ensure this by following the advices given below.
+%
+% \begin{doccomment}
+% A global \cs{@currentHref} means that the target name and
+% the label name can get out of sync: if
+% for example a numbered equation is used between a section and a \cs{label},
+% a reference will show the section number but a link will jump to the equation.
+%
+% \pkg{hyperref} allows to switch to local assignment of \cs{@currentHref}
+% and so to align with the other \cs{label} values with the
+% (so-called experimental) option \texttt{localanchorname}. But while on a document
+% level this can work, it makes it difficult for package authors
+% to add reliable link targets that can be referenced if it is unclear
+% if the assignment is local or global.
+%
+% So the choice was made to set \cs{@currentHref} globally always
+% (and to deprecate \texttt{localanchorname}):
+% This is the default in \pkg{hyperref} since ever and it didn't lead to
+% major problems. It has the advantage to
+% allow to put an anchor in a box and move it around. E.g. in the following
+% example the two \cs{ref} commands
+% reference the section but the second jumps to the top of the rule:
+%
+% \begin{verbatim}
+% \section{An example}\label{sec:title}
+% \newpage \raisebox{3cm}[0pt][0pt]{\phantomsection}%
+% \rule{1cm}{3cm}\label{sec:page2}
+% page two
+% \newpage
+% \ref{sec:title}, \ref{sec:page2}
+% \end{verbatim}
+%
+% \end{doccomment}
+%
+%
+%
+% \subsection*{Target names from counters}
+% \begin{syntax}
+% \cs{MakeLinkTarget}\oarg{prefix}\Arg{counter}
+% \end{syntax}
+%
+% If the mandatory argument is not empty it is interpreted as a \LaTeX{} counter name.
+% The counter is not stepped by the command.
+% If the \meta{counter} doesn't exist, a warning is issued and no target is created.
+%
+% The target name is created as expansion of
+% \texttt{\meta{prefix}.\cs{theH\meta{counter}}} (and so both the prefix
+% and \cs{theH\meta{counter}} should be expandable).
+% The default prefix is the counter name \meta{counter}.
+%
+% For example:
+%
+% \medskip
+% \noindent
+% {\ttfamily
+% \begin{tabular}{@{}l@{}ll}
+% \cs{MakeLinkTarget}\{section\} &$\Rightarrow$ section.\cs{theHsection} & $\Rightarrow$ section.1.1\\
+% \cs{MakeLinkTarget}[sec]\{section\} &$\Rightarrow$ sec.\cs{theHsection}
+% \end{tabular}
+% }
+% \medskip
+%
+% \cs{theH\meta{counter}} must be defined:
+% \cs{MakeLinkTarget} does not try like other \pkg{hyperref} commands to
+% construct the command on-the-fly but warns and creates no
+% target if it doesn't exist.
+% Be aware that if \pkg{hyperref} is used with the option \texttt{implicit=false} it
+% does not predefine and create \cs{theH\meta{counter}} representations
+% so targets could be missing!\footnote{It is planed that \LaTeX{} directly defines
+% the \cs{theH\meta{counter}} for all counters created with \cs{newcounter}.}
+%
+%
+% Typically \cs{theH\meta{counter}} should expand to numbers
+% and periods and make use of parent counters to give a unique representation.
+% The prefix can be a more or less arbitrary string. Spaces are allowed (but not
+% really recommended).
+% A star at the end should be avoided to prevent clashes with
+% the names created when the internal counter is used.
+% The use of non-ASCII chars with pdflatex depends on the \LaTeX{} version: With newer
+% version many of them are safe and with a format 2022-06-01 or newer
+% it should be even possible to use chars in a target name which
+% are undeclared and can't be typeset in the document.
+% But despite the fact that it works, it is recommended to stick to ASCII
+% and to avoid spaces: It is unclear if all PDF viewers and editors can handle
+% them, also they give rather unreadable names in the PDF like |(\360\237\246\206)|
+% and so make debugging harder.
+%
+%
+% Using a special prefix can be useful if the actual counter
+% has an internal name like e.g. \texttt{tcb@cnt@example}.
+% \texttt{\cs{MakeLinkTarget}[tcbexample]\{tcb@cnt@example\}} then gives
+% a nicer looking target name. Care should be taken not to clash
+% with existing counter names and names for \cs{autoref}
+% should be adjusted if needed.
+%
+%
+% \subsection*{Targets using the internal counter}
+% \begin{syntax}
+% \cs{MakeLinkTarget}\oarg{prefix}\{\}
+% \end{syntax}
+%
+% If the mandatory argument is empty a target name based on
+% an internal absolute counter is created.
+% The counter is stepped at every call (and also by other \pkg{hyperref} commands).
+% The prefix is added with a star and a period (see above for
+% the allowed chars). The default prefix is \texttt{page}.
+%
+%
+% \begin{syntax}
+% \begin{tabular}{@{}ll}
+% \cs{MakeLinkTarget}\texttt{\{\}} &$\Rightarrow$ \texttt{page*.1}\\
+% \cs{MakeLinkTarget}\texttt{\{\}} &$\Rightarrow$ \texttt{page*.2}\\
+% \cs{MakeLinkTarget}[section]\texttt{\{\}} &$\Rightarrow$ \texttt{section*.3}\\
+% \end{tabular}
+% \end{syntax}
+%
+% As an example the
+% \pkg{hyperref} command \cs{phantomsection} is equivalent to
+% \texttt{\cs{MakeLinkTarget}[section]\{\}}.
+%
+% \subsection*{Manual target names}
+% \begin{syntax}
+% \cs{MakeLinkTarget}\texttt{*}\Arg{manual target name}
+% \end{syntax}
+%
+% If the starred variant is used a manual target name is created.
+%
+% \texttt{\cs{MakeLinkTarget}*\{destname\}} is roughly equivalent to
+% \texttt{\cs{hypertarget}\{destname\}\{\}} but unlike the
+% latter it also raises the destination in a PDF, there is no text argument,
+% and---most importantly--it updates \cs{@currentHref}, that means the target
+% name is stored by the next \cs{label}, something that \cs{hypertarget} doesn't do).
+% The target name is expanded, see the remarks about prefixes above regarding
+% the allowed chars.
+%
+% When creating manually targets care should be taken to avoid
+% clashes with automatic target names.
+% As all automatic targets contain at least one period,
+% names without a period are recommended.
+%
+% \begin{doccomment}
+% The second (text) argument of \cs{hypertarget} is only used
+% if the option \texttt{nesting} is set to true---but this option isn't used
+% anywhere (in PDF the idea of a text in the anchor or nesting of anchors
+% makes no sense anyway but also when html is produced e.g. with \texttt{make4ht}
+% it is not used). So probably the option will be deprecated and removed.
+% \end{doccomment}
+%
+% \subsection{Manipulate the next target name}
+%
+% Targets are sometimes created in places where it is difficult to inject
+% a label to retrieve the target name for use e.g. in a bookmark.
+% The next command allows to change the next target name:
+%
+% \begin{function}{\NextLinkTarget}
+% \begin{syntax}
+% \cs{NextLinkTarget}\Arg{manual target}
+% \end{syntax}
+%
+% The command changes the next target name to \meta{manual target}.
+% It does the same as the \cs{hypersetup} key
+% \texttt{next-anchor} and also affects targets created by \cs{refstepcounter}.
+%\end{function}
+%
+% A use case are bookmarks for the table of contents
+% where you know that the heading will create
+% a target:
+%
+% \begin{verbatim}
+% \documentclass{book}
+% \usepackage{bookmark}
+%
+% \begin{document}
+% \bookmark[dest=toc]{Table of Contents}
+% \NextLinkTarget{toc}
+% \tableofcontents
+% \chapter{A}
+% \end{document}
+% \end{verbatim}
+%
+%
+% \subsection*{Hooks}
+%
+% \begin{function}{makelinktarget}
+% The hook \texttt{makelinktarget}\footnote{The hook uses a plain name
+% without reference to the \pkg{hyperref} package in anticipation of the move
+% of this code into the \LaTeX{} kernel.}
+% is executed at the begin of the commands. It is inside a group and
+% so can be used to locally change settings. See below for an example.
+% \end{function}
+%
+%
+% \subsection*{Suppressing the target}
+%
+% \begin{function}{\LinkTargetOn,\LinkTargetOff}
+% \begin{syntax}
+% \cs{LinkTargetOn}\\
+% \cs{LinkTargetOff}
+% \end{syntax}
+% \end{function}
+%
+%
+% This commands allows to switch on and off locally the creation of a target with
+% \cs{MakeLinkTarget}.
+% The switches are also honored by \cs{refstepcounter}\footnote{currently
+% \cs{refstepcounter} doesn't use \cs{MakeLinkTarget} itself but this
+% will probably change.}.
+% This allows to suppress the target
+% from an internal \cs{refstepcounter} and replace it by some manual version
+% by using grouping:
+%
+% \begin{verbatim}
+% \LinkTargetOff %suppress anchor in internal refstepcounter
+% ...
+% \refstepcounter{...}
+% ...
+% {\LinkTargetOn\MakeLinkTarget*{mytarget}} %create manual anchor
+% ...
+% \LinkTargetOn
+% \end{verbatim}
+%
+%
+% \subsection*{Raising the target}
+%
+% In horizontal mode the target is raised by the current value of \cs{normalbaselineskip}.
+%
+% To change this the hook can be used e.g. to double the value everywhere:
+%
+% \begin{verbatim}
+% \AddToHook{cmd/MakeLinkTarget/before}
+% {\setlength\normalbaselineskip{2\normalbaselineskip}}
+% \leavevmode\MakeLinkTarget{section}
+% \end{verbatim}
+%
+%
+% \subsection{Changing all target names}
+%
+% \begin{function}{\SetLinkTargetFilter}
+% \begin{syntax}
+% \cs{SetLinkTargetFilter}\Arg{filter code using \#1}
+% \end{syntax}
+% \end{function}
+%
+% \pkg{hyperref} provides the command \cs{HyperDestNameFilter} to change all target names.
+% It is applied\footnote{In the backend code, so it depends actually on
+% the driver if it is honored or not}
+% to every target name and is also used in references,
+% but it doesn't change \cs{@currentHref} itself. So after
+%
+% \begin{verbatim}
+% \renewcommand*{\HyperDestNameFilter}[1]{docA-#1}
+% \end{verbatim}
+%
+% you would get in the PDF everywhere the prefix \texttt{docA}
+%
+% \begin{verbatim}
+% %destination names:
+% <<
+% /Names [(docA-Doc-Start) 7 0 R (docA-chapter.1) 8 0 R (docA-page.1) 6 0 R]
+% /Limits [(docA-Doc-Start) (docA-page.1)]
+% >>
+% %link to a chapter
+% /A << /S /GoTo /D (docA-chapter.1) >>
+% %link from the bookmark
+% << /S /GoTo /D (docA-chapter.1) >>
+% \end{verbatim}
+%
+% but the label info in the \texttt{.aux} would show only \texttt{chapter.1}:
+%
+% \begin{verbatim}
+% \newlabel{chap}{{1}{1}{Title}{chapter.1}{}}
+% \end{verbatim}
+%
+% and so \cs{autoref} is still able to extract the counter name.
+%
+% \cs{MakeLinkTarget} uses this filter too: it would break internal link
+% commands if it would ignore it.
+% To stay compatible with future development the filter should not be redefined
+% directly but be set with \cs{SetLinkTargetFilter}.
+% The command can only be used in the preamble.
+%
+% \begin{verbatim}
+% \SetLinkTargetFilter{docA-#1}
+% \end{verbatim}
+% \end{documentation}
+%
+% \begin{implementation}
+% \section{Implementation}
+% \begin{macrocode}
+%<@@=hyp>
+%<*header>
+\ProvidesExplPackage{hyperref-linktarget}{2022-05-13}{v7.00o}
+ {Making targets, destinations and anchors -- module of hyperref}
+%</header>
+% \end{macrocode}
+%<package-include>\ExplSyntaxOn
+% \subsection{Variables}
+% \begin{macrocode}
+%<*package>
+% \end{macrocode}
+% \begin{macro}{\l_@@_target_create_bool}
+% This boolean decides if a target is created at all.
+% (it will replace \cs{@skiphyperref} long term)
+% \begin{macrocode}
+\bool_new:N \l_@@_target_create_bool
+\bool_set_true:N \l_@@_target_create_bool
+% \end{macrocode}
+% \end{macro}
+% \begin{macro}{makelinktarget}
+% This hook is used to adapt for example the raising
+% \begin{macrocode}
+\hook_new:n {makelinktarget}
+% \end{macrocode}
+% \end{macro}
+% \subsection{Helper commands}
+% \begin{macro}{\_@@_target_raise:n}
+% We need a command to raise the targets.
+% It is mostly a copy from the hyperref command
+% but we removed the hooks and use \cs{normalbaselineskip}.
+% TODO: The code to save/restore the space factor should
+% be replaced by kernel methods.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_target_raise:n #1
+ {
+ \mode_if_vertical:TF
+ { #1 }
+ {
+ \Hy@SaveSpaceFactor
+ \penalty\@M
+ \smash
+ {
+ \box_move_up:nn
+ { \normalbaselineskip }
+ {
+ \hbox:n
+ {
+ \Hy@RestoreSpaceFactor
+ #1
+ \Hy@SaveSpaceFactor
+ }
+ }
+ }
+ \Hy@RestoreSpaceFactor
+ }
+ }
+% \end{macrocode}
+% \end{macro}
+% \subsection{Providing the commands}
+% In anticipation of the addition of the main commands to the
+% kernel as no-ops we provide them:
+% \begin{macrocode}
+\ProvideDocumentCommand\LinkTargetOn{}{}
+\ProvideDocumentCommand\LinkTargetOff{}{}
+\ProvideDocumentCommand\MakeLinkTarget{sO{}m}{}
+\ProvideDocumentCommand\NextLinkTarget{m}{}
+% \end{macrocode}
+%
+% \subsection{Target on and off switch}
+% \begin{macro}{\LinkTargetOn,\LinkTargetOff}
+% \begin{macrocode}
+\RenewDocumentCommand\LinkTargetOn {}
+ {
+ \bool_set_true:N \l_@@_target_create_bool
+ }
+
+
+\RenewDocumentCommand\LinkTargetOff {}
+ {
+ \bool_set_false:N \l_@@_target_create_bool
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\MakeLinkTarget}
+% This is the main command. To keep it simple
+% we allow an optional argument also for the manual
+% command but ignore it for now.
+% \begin{macrocode}
+\RenewDocumentCommand\MakeLinkTarget {s O{} m}
+ {
+ \bool_if:NT \l_@@_target_create_bool
+ {
+ \group_begin:
+ \hook_use:n { makelinktarget }
+ \IfBooleanTF {#1}
+ {
+ \@@_target_manual:nn {#2}{#3}
+ }
+ {
+ \@@_target_counter:nn {#2}{#3}
+ }
+ \group_end:
+ }
+ }
+% \end{macrocode}
+% \end{macro}
+% \begin{macro}{\@@_target_manual:nn}
+% This is the code for the manual target name. The prefix is simply ignored.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_target_manual:nn #1 #2 %#1 prefix, #2 name
+ {
+ \tl_gset:Nx \@currentHref {#2}
+ \hook_use:n {__hyp/target/setname}
+ \@onelevel@sanitize\@currentHref
+ \@@_target_raise:n {\hyper@anchorstart{\@currentHref}\hyper@anchorend}
+ }
+% \end{macrocode}
+% \end{macro}
+% \begin{macro}{\@@_target_counter:nn}
+% The code for counter related targets must be split into the case
+% where the internal counter is used, and where a user counter is used
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_target_counter:nn #1 #2 %#1 prefix, #2 counter or empty
+ {
+ \tl_if_blank:nTF {#2}
+ {
+ \@@_target_counter_anon:n {#1}
+ }
+ {
+ \@@_target_counter_doc:nn {#1}{#2}
+ }
+ }
+
+% \end{macrocode}
+%\end{macro}
+%\begin{macro}{\@@_target_counter_anon:n}
+% This creates the target with the internal count.
+% We use the same (tex) count \cs{Hy@linkcounter} than the other
+% hyperref commands.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_target_counter_anon:n #1
+ {
+ \int_gincr:N\Hy@linkcounter
+ \tl_gset:Nx \@currentHref
+ {\tl_if_blank:nTF{#1}{page}{#1}*.\int_use:N\Hy@linkcounter}
+ \hook_use:n {__hyp/target/setname}
+ \@onelevel@sanitize\@currentHref
+ \@@_target_raise:n {\hyper@anchorstart{\@currentHref}\hyper@anchorend}
+ }
+% \end{macrocode}
+% \end{macro}
+
+% \begin{macro}{\@@_target_counter_doc:nn}
+% And now the target with the user counter. We warn if
+% the counter or the representation doesn't exist
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_target_counter_doc:nn #1 #2
+ {
+ \bool_lazy_and:nnTF { \cs_if_free_p:c {c@#2} } { \cs_if_free_p:c {theH#2} }
+ {
+ \PackageWarning {hyperref}{Counter~'#2'~or~the~representation~'\string\theH#2`\MessageBreak
+ don't~exist.~No~target~created.}{}
+ }
+ {
+ \tl_gset:Nx \@currentHref {\tl_if_blank:nTF{#1}{#2}{#1}.\use:c{theH#2}}
+ \hook_use:n {@@/target/setname}
+ \@onelevel@sanitize\@currentHref
+ \@@_target_raise:n {\hyper@anchorstart{\@currentHref}\hyper@anchorend}
+ }
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\NextLinkTarget}
+% we rely on the internal hook to set the next target name:
+% \begin{macrocode}
+\RenewDocumentCommand\NextLinkTarget {m}
+ {
+ \hook_gput_next_code:nn {@@/target/setname}
+ {
+ \tl_gset:Nx \@currentHref {#1}
+ }
+ }
+
+% \end{macrocode}
+% \end{macro}
+% \begin{macro}{\SetLinkTargetFilter}
+% This is an interface to \cs{HyperDestNameFilter}
+% \begin{macrocode}
+\NewDocumentCommand\SetLinkTargetFilter {m}
+ {
+ \cs_set:Npn \HyperDestNameFilter ##1 {#1}
+ }
+\@onlypreamble \SetLinkTargetFilter
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macrocode}
+%</package>
+%<@@=>.
+% \end{macrocode}
+%<package-include>\ExplSyntaxOff
+%\end{implementation}
+% \Finale
+\endinput