% \iffalse meta-comment
%
%% File: tagpdf-user.dtx
%
% Copyright (C) 2019-2024 Ulrike Fischer
%
% 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
%
% https://www.latex-project.org/lppl.txt
%
% This file is part of the "tagpdf 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/tagpdf
%
% for those people who are interested.
%<*driver>
\DocumentMetadata{}
\documentclass{l3doc}
\usepackage{array,booktabs,caption}
\hypersetup{pdfauthor=Ulrike Fischer,
pdftitle=tagpdf-user module (tagpdf)}
\begin{document}
\DocInput{\jobname.dtx}
\end{document}
%
% \fi
% \title{^^A
% The \pkg{tagpdf-user} module\\
% Code related to \LaTeX2e user commands and document commands ^^A
% \\ Part of the tagpdf package
% }
%
% \author{^^A
% Ulrike Fischer\thanks
% {^^A
% E-mail:
% \href{mailto:fischer@troubleshooting-tex.de}
% {fischer@troubleshooting-tex.de}^^A
% }^^A
% }
%
% \date{Version 0.99i, released 2024-11-19}
% \maketitle
% \begin{documentation}
% \section{Setup commands}
% \begin{function}{\tagpdfsetup}
% \begin{syntax}
% \cs{tagpdfsetup}\Arg{key val list}
% \end{syntax}
% This is the main setup command to adapt the behaviour of tagpdf.
% It can be used in the preamble and in the document (but not all keys make sense there).
% \end{function}
%
% \begin{function}{activate (setup-key)}
% And additional setup key which combine the other activate keys
% |activate/mc|, |activate/tree|, |activate/struct| and additionally
% adds a document structure.
% \end{function}
%
% \begin{function}{\tag_tool:n,\tagtool}
% \begin{syntax}
% \cs{tag_tool:n}\Arg{key val}
% \end{syntax}
% The tagging of basic document elements will require a variety of small commands
% to configure and adapt the tagging. This command will collect them under a command
% interface. The argument is \emph{one} key-value like string. This is work in progress
% and both syntax, known arguments and implementation can change!
% \end{function}
%
% \section{Commands related to mc-chunks}
% \begin{function}{\tagmcbegin, \tagmcend,\tagmcuse}
% \begin{syntax}
% \cs{tagmcbegin} \Arg{key-val}\\
% \cs{tagmcend}\\
% \cs{tagmcuse}\Arg{label}
% \end{syntax}
% These are wrappers around |\tag_mc_begin:n|, |\tag_mc_end:| and |\tag_mc_use:n|.
% The commands and their argument are documentated in the \pkg{tagpdf-mc} module.
% In difference to the expl3 commands, \cs{tagmcbegin} issues also an \cs{ignorespaces},
% and \cs{tagmcend} will issue in horizontal mode an \cs{unskip}.
% \end{function}
%
% \begin{function}{\tagmcifinTF}
% \begin{syntax}
% \cs{tagmcifin} \Arg{true code}\Arg{false code}
% \end{syntax}
% This is a wrapper around |\tag_mc_if_in:TF|.
% and tests if an mc is open or not. It is mostly of
% importance for pdflatex as lualatex doesn't mind much if a mc tag is not
% correctly closed. Unlike the expl3 command it is not expandable.
%
% The command is probably not of much use and will perhaps disappear in future
% versions. It normally makes more sense to push/pop an mc-chunk.
% \end{function}
%
% \section{Commands related to structures}
% \begin{function}{\tagstructbegin, \tagstructend,\tagstructuse}
% \begin{syntax}
% \cs{tagstructbegin} \Arg{key-val}\\
% \cs{tagstructend}\\
% \cs{tagstructuse}\Arg{label}
% \end{syntax}
% These are direct wrappers around |\tag_struct_begin:n|,
% |\tag_struct_end:| and |\tag_struct_use:n|.
% The commands and their argument are documented in the \pkg{tagpdf-struct} module.
% \end{function}
% \section{Debugging}
% \begin{function}{\ShowTagging}
% \begin{syntax}
% \cs{ShowTagging} \Arg{key-val}
% \end{syntax}
% This is a generic function to output various debugging helps. It not
% necessarily stops the compilation. The keys and their function are described below.
% \end{function}
%
% \begin{function}{mc-data (show-key)}
% \begin{syntax}
% |mc-data| = \meta{number}
% \end{syntax}
% This key is (currently?) relevant for lua mode only.
% It shows the data of all mc-chunks created so far.
% It is accurate only after shipout (and perhaps a second compilation),
% so typically should be issued after a
% newpage. The value is a positive integer and sets the first mc-shown. If no value
% is given, 1 is used and so all mc-chunks created so far are shown.
% \end{function}
% \begin{function}{mc-current (show-key)}
% \begin{syntax}
% |mc-current|
% \end{syntax}
% This key shows the number and the tag of the currently open mc-chunk. If no
% chunk is open it shows only the state of the abs count. It works in all mode,
% but the output in luamode looks different.
% \end{function}
% \begin{function}{mc-marks (show-key)}
% \begin{syntax}
% |mc-marks| = |show|\verb+|+|use|
% \end{syntax}
% This key helps to debug the page marks. It should only be used at shipout in
% header or footer.
% \end{function}
% \begin{function}{struct-stack (show-key)}
% \begin{syntax}
% |struct-stack| = |log|\verb+|+|show|
% \end{syntax}
% This key shows the current structure stack. With |log| the info is only
% written to the log-file, |show| stops the compilation and shows on the terminal.
% If no value is used, then the default is |show|.
% \end{function}
%
% \begin{function}{debug/structures (show-key)}
% \begin{syntax}
% |debug/structures| = \meta{structure number}
% \end{syntax}
% This key is available only if the tagpdf-debug package is loaded
% and shows all structures starting with the one with the number
% given by the key.
% \end{function}
% \section{Extension commands}
% The following commands and code parts are not core commands of tagpdf.
% They either provide work-arounds for missing functionality elsewhere,
% or do a first step to apply tagpdf commands to document commands.
%
% The commands and keys should be view as experimental!
%
% This part will be regularly revisited to check if the code should go to a
% better place or can be improved and so can change easily.
%
% \subsection{Fake space}
% \begin{function}{\pdffakespace}
% (lua-only) This provides a lua-version of the |\pdffakespace| primitive of
% pdftex.
% \end{function}
%
% \subsection{Tagging of paragraphs}
%
% This makes use of the paragraph hooks in LaTeX to
% automate the tagging of paragraph. It requires sane paragraph nesting,
% faulty code, e.g. a missing |\par| at the end of a low-level vbox can highly
% confuse the tagging. The tags should be carefully checked if this is used.
%
% \begin{function}{para/tagging (setup-key),paratagging-show (deprecated),paratagging (deprecated)}
% \begin{syntax}
% |para/tagging| = |true|\verb+|+|false|\\
% |debug/show=para|\\
% |debug/show=paraOff|
% \end{syntax}
% The |para/tagging| key can be used in |\tagpdfsetup| and enable/disables tagging
% of paragraphics.
% |debug/show=para| puts small colored numbers at the begin and end of a paragraph.
% This is meant as a debugging help. The number are boxes and have a (tiny) height,
% so they can affect typesetting.
% \end{function}
%
% \begin{function}{\tagpdfparaOn,\tagpdfparaOff}
% These commands allow to enable/disable para tagging too and are a bit
% faster then |\tagpdfsetup|. But I'm not sure if the names are good.
% \end{function}
%
% \begin{function}{\tagpdfsuppressmarks}
% This command allows to suppress the creation of the marks. It takes an argument
% which should normally be one of the mc-commands, puts a group around it and
% suppress the marks creation in this group. This command should be used if
% the begin and end command are at different boxing levels. E.g.
% \begin{verbatim}
% \@hangfrom
% {
% \tagstructbegin{tag=H1}%
% \tagmcbegin {tag=H1}%
% #2
% }
% {#3\tagpdfsuppressmarks{\tagmcend}\tagstructend}%
% \end{verbatim}
% \end{function}
%
% \subsection{Header and footer}
% Header and footer are automatically tagged as artifact:
% They are surrounded by an artifact-mc and inside tagging is stopped.
% If some real content is in the header and
% footer, tagging must be restarted there explicitly.
% The behaviour can be changed with the following key.
% The key accepts the values |true| (the default),
% |false| which disables the header tagging code. This can be useful
% if the page style is empty (it then avoids empty mc-chunks) or
% if the head and foot should be tagged in some special way.
% The last value, |pagination|, is like |true| but
% additionally adds an artifact structure with an pagination attribute.
%
% \begin{function}{page/exclude-header-footer (setup-key)}
% \begin{syntax}
% |page/exclude-header-footer| = |true|\verb+|+|false|\verb+|+pagination
% \end{syntax}%
% \end{function}
% \subsection{Link tagging}
% Links need a special structure and cross reference system.
% This is added through hooks of the l3pdfannot module and will work
% automatically if tagging is activated.
%
% Links should (probably) have an alternative text in the Contents
% key. It is unclear which text this should be and
% how to get it. Currently the code simply adds the fix texts |url| and |ref|.
% Another text can be added by changing the dictionary value:
% \begin{verbatim}
% \pdfannot_dict_put:nnn
% { link/GoTo }
% { Contents }
% { (ref) }
% \end{verbatim}
%
% \section{Socket support}
%
% \begin{function}{\tag_socket_use:n,\tag_socket_use:nnn,\UseTaggingSocket}
% \begin{syntax}
% \cs{tag_socket_use:n} \Arg{socket name}\\
% \cs{tag_socket_use:nn} \Arg{socket name} \Arg{socket argument}\\
% \cs{tag_socket_use:nnn} \Arg{socket name} \Arg{socket argument} \Arg{socket argument}\\
% \cs{tag_socket_use_expandable:n} \Arg{socket name}\\
% \cs{UseTaggingSocket} \Arg{socket name} \\
% \cs{UseTaggingSocket} \Arg{socket name} \Arg{socket argument}\\
% \cs{UseTaggingSocket} \Arg{socket name} \Arg{socket argument}\Arg{socket argument}\\
% \end{syntax}
% \end{function}
%
% Given that we sometimes have to suspend tagging, it would be fairly
% inefficient to put different plugs into these sockets whenever that
% happens. We therefore offer \cs{UseTaggingSocket} which is like
% \cs{UseSocket} except that is expects a socket starting with
% \texttt{tagsupport/} but the socket name is specified without
% this prefix, i.e.,
% \begin{quote}
% \verb=\UseTaggingSocket{foo}= $\to$
% \verb=\UseSocket{tagsupport/foo}=
% \end{quote}.
%
% Beside being slightly shorter, the big advantage is that this way
% we can change \cs{UseTaggingSocket} to do nothing by switching a boolean
% instead of changing the plugs of the tagging support sockets back and forth.
%
% Usually, these sockets have (beside the default plug defined for every socket)
% one additional plug defined and directly assigned. This plug is used when
% tagging is active.
% There may be more plugs, e.g., tagging with special debugging or special behaviour
% depending on the class or PDF version etc., but right now it is usually just on or off.
%
% When tagging is suspended they all have the same predefined behaviour:
% The sockets with zero arguments do nothing. The sockets with one argument
% gobble their argument. The sockets with two arguments
% will drop their first argument and pass the second unchanged.
%
% It is possible to use the tagging support sockets with
% \cs{UseSocket} directly, but in this case the socket remains active
% if e.g. \cs{SuspendTagging} is in force. There may be reasons for doing
% that but in general we expect to always use \cs{UseTaggingSocket}.
%
% For special cases like in some \cs{halign} contexts we need a fully expandable
% version of the commend. For these cases, \cs{UseExpandableTaggingSocket} can be
% used. To allow being expandable, it does not output any debugging information
% if \cs{DebugSocketsOn} is in effect and therefore should be avoided whenever possible.
%
% The L3 programming layer versions \cs{tag_socket_use_expandable:n}, \cs{tag_socket_use:n}, and
% \cs{tag_socket_use:nn}, \cs{tag_socket_use:nnn}
% are slightly more efficient than
% \cs{UseTaggingSocket} because they do not have to determine how
% many arguments the socket takes when disabling it.
% \end{documentation}
%
% \begin{implementation}
% \section{User commands and extensions of document commands}
% \begin{macrocode}
%<@@=tag>
%<*header>
\ProvidesExplPackage {tagpdf-user} {2024-11-19} {0.99i}
{tagpdf - user commands}
%
% \end{macrocode}
% \section{Setup and preamble commands}
% \begin{macro}{\tagpdfsetup}
% \begin{macrocode}
%\NewDocumentCommand \tagpdfsetup { m }{}
%<*package>
\RenewDocumentCommand \tagpdfsetup { m }
{
\keys_set:nn { @@ / setup } { #1 }
}
%
% \end{macrocode}
% \end{macro}
%
% \begin{macro}{\tag_tool:n,\tagtool}
% This is a first definition of the tool command.
% Currently it uses key-val, but this should be probably
% be flattened to speed it up.
% \begin{macrocode}
%\cs_new_protected:Npn\tag_tool:n #1 {}
%\cs_set_eq:NN\tagtool\tag_tool:n
%<*package>
\cs_set_protected:Npn\tag_tool:n #1
{
\tag_if_active:T { \keys_set:nn {tag / tool}{#1} }
}
\cs_set_eq:NN\tagtool\tag_tool:n
%
% \end{macrocode}
% \end{macro}
% \section{Commands for the mc-chunks}
% \begin{macro}{\tagmcbegin,\tagmcend,\tagmcuse }
% \begin{macrocode}
%<*base>
\NewDocumentCommand \tagmcbegin { m }
{
\tag_mc_begin:n {#1}
}
\NewDocumentCommand \tagmcend { }
{
\tag_mc_end:
}
\NewDocumentCommand \tagmcuse { m }
{
\tag_mc_use:n {#1}
}
%
% \end{macrocode}
% \end{macro}
% \begin{macro}{\tagmcifinTF}
% This is a wrapper around |\tag_mc_if_in:| and tests if an mc is open or not. It is mostly of
% importance for pdflatex as lualatex doesn't mind much if a mc tag is not
% correctly closed. Unlike the expl3 command it is not expandable.
% \begin{macrocode}
%<*package>
\NewDocumentCommand \tagmcifinTF { m m }
{
\tag_mc_if_in:TF { #1 } { #2 }
}
%
% \end{macrocode}
% \end{macro}
%
% \section{Commands for the structure}
% \begin{macro}{\tagstructbegin,\tagstructend,\tagstructuse }
%
% These are structure related user commands. There are direct wrapper around the
% expl3 variants.
% \begin{macrocode}
%<*base>
\NewDocumentCommand \tagstructbegin { m }
{
\tag_struct_begin:n {#1}
}
\NewDocumentCommand \tagstructend { }
{
\tag_struct_end:
}
\NewDocumentCommand \tagstructuse { m }
{
\tag_struct_use:n {#1}
}
%
% \end{macrocode}
% \end{macro}
%
% \section{Socket support}
% Until we can be sure that the kernel defines the commands we provide them before
% redefining them:
% The expandable version will only work correctly after the 2024-11-01 release.
% \begin{macrocode}
%<*base>
\providecommand\tag_socket_use:n[1]{}
\providecommand\tag_socket_use:nn[2]{}
\providecommand\tag_socket_use:nnn[3]{}
\providecommand\tag_socket_use_expandable:n[1]{}
\providecommand\socket_use_expandable:nw [1] {
\use:c { __socket_#1_plug_ \str_use:c { l__socket_#1_plug_str } :w }
}
\providecommand\UseTaggingSocket[1]{}
\providecommand\UseExpandableTaggingSocket[1]{}
%
% \end{macrocode}
%
% \begin{macro}{\tag_socket_use:n,\tag_socket_use:nn,\tag_socket_use:nnn,\UseTaggingSocket,
% \tag_socket_use_expandable:n,\UseExpandableTaggingSocket}
% \begin{macrocode}
%<*package>
\cs_set_protected:Npn \tag_socket_use:n #1
{
\bool_if:NT \l_@@_active_socket_bool
{ \socket_use:n {tagsupport/#1} }
}
% \end{macrocode}
%
% \begin{macrocode}
\cs_set_protected:Npn \tag_socket_use:nn #1#2
{
\bool_if:NT \l_@@_active_socket_bool
{ \socket_use:nn {tagsupport/#1} {#2} }
}
% \end{macrocode}
%
% \begin{macrocode}
\cs_set_protected:Npn \tag_socket_use:nnn #1#2#3
{
\bool_if:NTF \l_@@_active_socket_bool
{ \socket_use:nnn {tagsupport/#1} {#2} {#3} }
{ #3 }
}
% \end{macrocode}
% \begin{macrocode}
\cs_set:Npn \tag_socket_use_expandable:n #1
{
\bool_if:NT \l_@@_active_socket_bool
{ \socket_use_expandable:n {tagsupport/#1} }
}
% \end{macrocode}
% \begin{macrocode}
\cs_set_protected:Npn \UseTaggingSocket #1
{
\bool_if:NTF \l_@@_active_socket_bool
{ \socket_use:nw {tagsupport/#1} }
{
\int_case:nnF
{ \int_use:c { c__socket_tagsupport/#1_args_int } }
{
0 \prg_do_nothing:
1 \use_none:n
2 \use_ii:nn
% \end{macrocode}
% We do not expect tagging sockets with more than one or two
% arguments, so for now we only provide those.
% \begin{macrocode}
}
\ERRORusetaggingsocket
}
}
% \end{macrocode}
% \begin{macrocode}
\cs_set:Npn \UseExpandableTaggingSocket #1
{
\bool_if:NTF \l_@@_active_socket_bool
{ \socket_use_expandable:nw {tagsupport/#1} }
{
\int_case:nnF
{ \int_use:c { c__socket_tagsupport/#1_args_int } }
{
0 \prg_do_nothing:
1 \use_none:n
2 \use_ii:nn
% \end{macrocode}
% We do not expect tagging sockets with more than one or two
% arguments, so for now we only provide those.
% \begin{macrocode}
}
\ERRORusetaggingsocket
}
}
%
% \end{macrocode}
% \end{macro}
%
% \section{Debugging}
% \begin{macro}{\ShowTagging}
% This is a generic command for various show commands.
% It takes a keyval list, the various keys are implemented below.
% \begin{macrocode}
%<*package>
\NewDocumentCommand\ShowTagging { m }
{
\keys_set:nn { @@ / show }{ #1}
}
% \end{macrocode}
% \end{macro}
%
% \begin{macro}{mc-data (show-key)}
% This key is (currently?) relevant for lua mode only.
% It shows the data of all mc-chunks created so far.
% It is accurate only after shipout, so typically should be issued after a
% newpage. With the optional argument the minimal number can be set.
% \begin{macrocode}
\keys_define:nn { @@ / show }
{
mc-data .code:n =
{
\sys_if_engine_luatex:T
{
\lua_now:e{ltx.@@.trace.show_all_mc_data(#1,\@@_get_mc_abs_cnt:,0)}
}
}
,mc-data .default:n = 1
}
% \end{macrocode}
% \end{macro}
% \begin{macro}{mc-current (show-key)}
% This shows some info about the current mc-chunk. It works in generic and lua-mode.
% \begin{macrocode}
\keys_define:nn { @@ / show }
{ mc-current .code:n =
{
\bool_if:NTF \g_@@_mode_lua_bool
{
\sys_if_engine_luatex:T
{
\int_compare:nNnTF
{ -2147483647 }
=
{
\lua_now:e
{
tex.print
(tex.getattribute
(luatexbase.attributes.g_@@_mc_cnt_attr))
}
}
{
\lua_now:e
{
ltx.@@.trace.log
(
"mc-current:~no~MC~open,~current~abscnt
=\@@_get_mc_abs_cnt:"
,0
)
texio.write_nl("")
}
}
{
\lua_now:e
{
ltx.@@.trace.log
(
"mc-current:~abscnt=\@@_get_mc_abs_cnt:=="
..
tex.getattribute(luatexbase.attributes.g_@@_mc_cnt_attr)
..
"~=>tag="
..
tostring
(ltx.@@.func.get_tag_from
(tex.getattribute
(luatexbase.attributes.g_@@_mc_type_attr)))
..
"="
..
tex.getattribute
(luatexbase.attributes.g_@@_mc_type_attr)
,0
)
texio.write_nl("")
}
}
}
}
{
\msg_note:nn{ tag }{ mc-current }
}
}
}
% \end{macrocode}
% \end{macro}
% \begin{macro}{mc-marks (show-key)}
% It maps the mc-marks into the sequences and then shows them.
% This allows to inspect the first and last mc-Mark on a page.
% It should only be used in the shipout (header/footer).
% \begin{macrocode}
\keys_define:nn { @@ / show }
{
mc-marks .choice: ,
mc-marks / show .code:n =
{
\@@_mc_get_marks:
\@@_check_if_mc_in_galley:TF
{
\iow_term:n {Marks~from~this~page:~}
}
{
\iow_term:n {Marks~from~a~previous~page:~}
}
\seq_show:N \l_@@_mc_firstmarks_seq
\seq_show:N \l_@@_mc_botmarks_seq
\@@_check_if_mc_tmb_missing:T
{
\iow_term:n {BDC~missing~on~this~page!}
}
\@@_check_if_mc_tme_missing:T
{
\iow_term:n {EMC~missing~on~this~page!}
}
},
mc-marks / use .code:n =
{
\@@_mc_get_marks:
\@@_check_if_mc_in_galley:TF
{ Marks~from~this~page:~}
{ Marks~from~a~previous~page:~}
\seq_use:Nn \l_@@_mc_firstmarks_seq {,~}\quad
\seq_use:Nn \l_@@_mc_botmarks_seq {,~}\quad
\@@_check_if_mc_tmb_missing:T
{
BDC~missing~
}
\@@_check_if_mc_tme_missing:T
{
EMC~missing
}
},
mc-marks .default:n = show
}
% \end{macrocode}
% \end{macro}
% \begin{macro}{struct-stack (show-key)}
% \begin{macrocode}
\keys_define:nn { @@ / show }
{
struct-stack .choice:
,struct-stack / log .code:n = \seq_log:N \g_@@_struct_tag_stack_seq
,struct-stack / show .code:n = \seq_show:N \g_@@_struct_tag_stack_seq
,struct-stack .default:n = show
}
%
% \end{macrocode}
% \end{macro}
% \begin{macro}{debug/structures (show-key)}
% The following key is available only if the tagpdf-debug package is loaded
% and shows all structures starting with the one with the number
% given by the key.
% \begin{macrocode}
%<*debug>
\keys_define:nn { @@ / show }
{
,debug/structures .code:n =
{
\int_step_inline:nnn{#1}{\c@g_@@_struct_abs_int}
{
\msg_term:nneeee
{ tag/debug } { show-struct }
{ ##1 }
{
\prop_map_function:cN
{g_@@_struct_debug_##1_prop}
\msg_show_item_unbraced:nn
}
{ } { }
\msg_term:nneeee
{ tag/debug } { show-kids }
{ ##1 }
{
\seq_map_function:cN
{g_@@_struct_debug_kids_##1_seq}
\msg_show_item_unbraced:n
}
{ } { }
}
}
,debug/structures .default:n = 1
}
%
% \end{macrocode}
% \end{macro}
%
% \section{Commands to extend document commands}
% The following commands and code parts are not core commands of tagpdf.
% They either provide work-arounds for missing functionality elsewhere,
% or do a first step to apply tagpdf commands to document commands.
% This part should be regularly revisited to check if the code should go to a
% better place or can be improved.
%
% \begin{macrocode}
%<*package>
% \end{macrocode}
% \subsection{Document structure}
% \begin{macro}{\g_@@_root_default_tl,activate (setup-key),activate/socket (setup-key)}
% \begin{macrocode}
\tl_new:N\g_@@_root_default_tl
\tl_gset:Nn\g_@@_root_default_tl {Document}
\hook_gput_code:nnn{begindocument}{tagpdf}{\tagstructbegin{tag=\g_@@_root_default_tl}}
\hook_gput_code:nnn{tagpdf/finish/before}{tagpdf}{\tagstructend}
\keys_define:nn { @@ / setup}
{
activate/socket .bool_set:N = \l_@@_active_socket_bool,
activate .code:n =
{
\keys_set:nn { @@ / setup }
{ activate/mc,activate/tree,activate/struct,activate/socket }
\tl_gset:Nn\g_@@_root_default_tl {#1}
},
activate .default:n = Document
}
% \end{macrocode}
% \end{macro}
%
% \subsection{Structure destinations}
% Since TeXlive 2022 pdftex and luatex offer support for structure destinations
% and the pdfmanagement has backend support for. We activate them if
% structures are actually created.
% Structure destinations are actually PDF 2.0 only but they don't harm in
% older PDF and can improve html export.
% \begin{macrocode}
\AddToHook{begindocument/before}
{
\bool_lazy_and:nnT
{ \g_@@_active_struct_dest_bool }
{ \g_@@_active_struct_bool }
{
\tl_set:Nn \l_pdf_current_structure_destination_tl
{ {@@/struct}{\g_@@_struct_stack_current_tl }}
\pdf_activate_indexed_structure_destination:
}
}
% \end{macrocode}
% \subsection{Fake space}
% \begin{macro}{\pdffakespace}
% We need a luatex variant for |\pdffakespace|. This should probably go into
% the kernel at some time. We also provide a no-op version for dvi mode
% \begin{macrocode}
\sys_if_engine_luatex:T
{
\NewDocumentCommand\pdffakespace { }
{
\@@_fakespace:
}
}
\providecommand\pdffakespace{}
% \end{macrocode}
% \end{macro}
%
% \subsection{Paratagging}
% The following are some simple commands to enable/disable paratagging.
% Probably one should add some checks if we are already in a paragraph.
%
%
% \begin{macro}{
% \l_@@_para_bool,
% \l_@@_para_flattened_bool,
% \l_@@_para_show_bool,
% \g_@@_para_begin_int,
% \g_@@_para_end_int,
% \g_@@_para_main_begin_int,
% \g_@@_para_main_end_int,
% \g_@@_para_main_struct_tl,
% \l_@@_para_tag_default_tl,
% \l_@@_para_tag_tl,
% \l_@@_para_main_tag_tl,
% \l_@@_para_attr_class_tl,
% \l_@@_para_main_attr_class_tl,
% }
% At first some variables.
% \begin{macrocode}
%
%\bool_new:N \l_@@_para_flattened_bool
%\bool_new:N \l_@@_para_bool
%<*package>
\int_new:N \g_@@_para_begin_int
\int_new:N \g_@@_para_end_int
\int_new:N \g_@@_para_main_begin_int
\int_new:N \g_@@_para_main_end_int
% \end{macrocode}
% this will hold the structure number of the current text-unit.
% \begin{macrocode}
\tl_new:N \g_@@_para_main_struct_tl
\tl_gset:Nn \g_@@_para_main_struct_tl {1}
\tl_new:N \l_@@_para_tag_default_tl
\tl_set:Nn \l_@@_para_tag_default_tl { text }
\tl_new:N \l_@@_para_tag_tl
\tl_set:Nn \l_@@_para_tag_tl { \l_@@_para_tag_default_tl }
\tl_new:N \l_@@_para_main_tag_tl
\tl_set:Nn \l_@@_para_main_tag_tl {text-unit}
% \end{macrocode}
% this is perhaps already defined by the block code
% \begin{macrocode}
\tl_if_exist:NF \l_@@_para_attr_class_tl
{\tl_new:N \l_@@_para_attr_class_tl }
\tl_new:N \l_@@_para_main_attr_class_tl
% \end{macrocode}
% \end{macro}
% \begin{macro}
% {
% \@@_gincr_para_main_begin_int:,
% \@@_gincr_para_main_end_int:,
% \@@_gincr_para_begin_int:,
% \@@_gincr_para_end_int:
% }
% The global para counter should be set through commands so
% that \cs{tag_stop:} can stop them.
% \begin{macrocode}
\cs_new_protected:Npn \@@_gincr_para_main_begin_int:
{
\int_gincr:N \g_@@_para_main_begin_int
}
\cs_new_protected:Npn \@@_gincr_para_begin_int:
{
\int_gincr:N \g_@@_para_begin_int
}
\cs_new_protected:Npn \@@_gincr_para_main_end_int:
{
\int_gincr:N \g_@@_para_main_end_int
}
\cs_new_protected:Npn \@@_gincr_para_end_int:
{
\int_gincr:N \g_@@_para_end_int
}
% \end{macrocode}
% \end{macro}
% \begin{macro}{\@@_start_para_ints:,\@@_stop_para_ints:}
% \begin{macrocode}
\cs_new_protected:Npn \@@_start_para_ints:
{
\cs_set_protected:Npn \@@_gincr_para_main_begin_int:
{
\int_gincr:N \g_@@_para_main_begin_int
}
\cs_set_protected:Npn \@@_gincr_para_begin_int:
{
\int_gincr:N \g_@@_para_begin_int
}
\cs_set_protected:Npn \@@_gincr_para_main_end_int:
{
\int_gincr:N \g_@@_para_main_end_int
}
\cs_set_protected:Npn \@@_gincr_para_end_int:
{
\int_gincr:N \g_@@_para_end_int
}
}
\cs_new_protected:Npn \@@_stop_para_ints:
{
\cs_set_eq:NN \@@_gincr_para_main_begin_int:\prg_do_nothing:
\cs_set_eq:NN \@@_gincr_para_begin_int: \prg_do_nothing:
\cs_set_eq:NN \@@_gincr_para_main_end_int: \prg_do_nothing:
\cs_set_eq:NN \@@_gincr_para_end_int: \prg_do_nothing:
}
% \end{macrocode}
% \end{macro}
%
% We want to be able to inspect the current para main structure, so
% we need a command to store its structure number
% \begin{macro}{\@@_para_main_store_struct:}
% \begin{macrocode}
\cs_new:Npn \@@_para_main_store_struct:
{
\tl_gset:Ne \g_@@_para_main_struct_tl {\int_use:N \c@g_@@_struct_abs_int }
}
% \end{macrocode}
% \end{macro}
% TEMPORARY FIX (2023-11-17). Until latex-lab is updated we must adapt a sec command:
% \begin{macrocode}
\AddToHook{package/latex-lab-testphase-sec/after}
{
\cs_set_protected:Npn \@kernel@tag@hangfrom #1
{
\tagstructbegin{tag=\l_@@_para_tag_tl}
\@@_gincr_para_begin_int:
\tagstructbegin{tag=Lbl}
\setbox\@tempboxa
\hbox
{
\bool_lazy_and:nnT
{\tag_if_active_p:}
{\g_@@_mode_lua_bool}
{\tagmcbegin{tag=Lbl}}
{#1}
}
\tag_suspend:n{hangfrom}
\hangindent \wd\@tempboxa\noindent
\tag_resume:n{hangfrom}
\tagmcbegin{}\box\@tempboxa\tagmcend\tagstructend\tagmcbegin{}
}
}
% \end{macrocode}
% and one temporary adaptions for the block module:
% \begin{macrocode}
\AddToHook{package/latex-lab-testphase-block/after}
{
\tl_if_exist:NT \l_tag_para_attr_class_tl
{
\tl_set:Nn \l_@@_para_attr_class_tl { \l_tag_para_attr_class_tl }
}
}
% \end{macrocode}
%\begin{macro}
% {
% para/tagging (setup-key),
% para/tag (setup-key),
% para/maintag (setup-key),
% para/tagging (tool-key),
% para/tag (tool-key),
% para/maintag (tool-key),
% para/flattened (tool-key),
% unittag (deprecated),
% para-flattened (deprecated),
% paratagging (deprecated),
% paratagging-show (deprecated),
% paratag (deprecated),
% }
% These keys enable/disable locally paratagging.
% Paragraphs are typically tagged with two structure: A main structure
% around the whole paragraph, and inner structures around the various chunks.
% Debugging can be
% activated locally with |debug/show=para|, this can affect the typesetting
% as the small numbers
% are boxes and they have a (small) height. Debugging can be deactivated
% with |debug/show=paraOff|
% The |para/tag| key sets the tag used by
% the inner structure, |para/maintag| the tag of the outer structure,
% both can also be changed with |\tag_tool:n|
% \begin{macrocode}
\keys_define:nn { @@ / setup }
{
para/tagging .bool_set:N = \l_@@_para_bool,
debug/show/para .code:n = {\bool_set_true:N \l_@@_para_show_bool},
debug/show/paraOff .code:n = {\bool_set_false:N \l_@@_para_show_bool},
para/tag .tl_set:N = \l_@@_para_tag_tl,
para/maintag .tl_set:N = \l_@@_para_main_tag_tl,
para/flattened .bool_set:N = \l_@@_para_flattened_bool
}
\keys_define:nn { tag / tool}
{
para/tagging .bool_set:N = \l_@@_para_bool,
para/tag .tl_set:N = \l_@@_para_tag_tl,
para/maintag .tl_set:N = \l_@@_para_main_tag_tl,
para/flattened .bool_set:N = \l_@@_para_flattened_bool
}
% \end{macrocode}
% the deprecated names
% \begin{macrocode}
\keys_define:nn { @@ / setup }
{
paratagging .bool_set:N = \l_@@_para_bool,
paratagging-show .bool_set:N = \l_@@_para_show_bool,
paratag .tl_set:N = \l_@@_para_tag_tl
}
\keys_define:nn { tag / tool}
{
para .bool_set:N = \l_@@_para_bool,
paratag .tl_set:N = \l_@@_para_tag_tl,
unittag .tl_set:N = \l_@@_para_main_tag_tl,
para-flattened .bool_set:N = \l_@@_para_flattened_bool
}
% \end{macrocode}
% \end{macro}
% Helper command for debugging:
% \begin{macrocode}
\cs_new_protected:Npn \@@_check_para_begin_show:nn #1 #2
%#1 color, #2 prefix
{
\bool_if:NT \l_@@_para_show_bool
{
\tag_mc_begin:n{artifact}
\llap{\color_select:n{#1}\tiny#2\int_use:N\g_@@_para_begin_int\ }
\tag_mc_end:
}
}
\cs_new_protected:Npn \@@_check_para_end_show:nn #1 #2
%#1 color, #2 prefix
{
\bool_if:NT \l_@@_para_show_bool
{
\tag_mc_begin:n{artifact}
\rlap{\color_select:n{#1}\tiny\ #2\int_use:N\g_@@_para_end_int}
\tag_mc_end:
}
}
% \end{macrocode}
%
% The para/begin and para/end code.
% We have two variants here: a simpler one, which must be used
% if the block code is not used (and so probably will disappear
% at some time) and a more sophisticated one that must
% be used if the block code is used.
% It is possible that we will need more variants, so
% we setup a socket so that the code can be easily switched.
% This code should move into lttagging, so we add a test for
% the transition.
% \begin{macrocode}
\str_if_exist:cF { l__socket_tagsupport/para/begin_plug_str }
{
\socket_new:nn {tagsupport/para/begin}{0}
\socket_new:nn {tagsupport/para/end}{0}
\socket_new_plug:nnn{tagsupport/para/begin}{plain}
{
\bool_if:NT \l_@@_para_bool
{
\bool_if:NF \l_@@_para_flattened_bool
{
\@@_gincr_para_main_begin_int:
\tag_struct_begin:n
{
tag=\l_@@_para_main_tag_tl,
}
\@@_para_main_store_struct:
}
\@@_gincr_para_begin_int:
\tag_struct_begin:n {tag=\l_@@_para_tag_tl}
\@@_check_para_begin_show:nn {green}{}
\tag_mc_begin:n {}
}
}
\socket_new_plug:nnn{tagsupport/para/begin}{block}
{
\bool_if:NT \l_@@_para_bool
{
\legacy_if:nF { @inlabel }
{
\@@_check_typeout_v:n
{==>~ @endpe = \legacy_if:nTF { @endpe }{true}{false} \on@line }
\legacy_if:nF { @endpe }
{
\bool_if:NF \l_@@_para_flattened_bool
{
\@@_gincr_para_main_begin_int:
\tag_struct_begin:n
{
tag=\l_@@_para_main_tag_tl,
attribute-class=\l_@@_para_main_attr_class_tl,
}
\@@_para_main_store_struct:
}
}
\@@_gincr_para_begin_int:
\@@_check_typeout_v:n {==>~increment~ P \on@line }
\tag_struct_begin:n
{
tag=\l_@@_para_tag_tl
,attribute-class=\l_@@_para_attr_class_tl
}
\@@_check_para_begin_show:nn {green}{\PARALABEL}
\tag_mc_begin:n {}
}
}
}
% \end{macrocode}
% there was no real difference between the original and
% in the block variant, only a debug message. We therefore
% define only a plain variant.
% \begin{macrocode}
\socket_new_plug:nnn{tagsupport/para/end}{plain}
{
\bool_if:NT \l_@@_para_bool
{
\@@_gincr_para_end_int:
\@@_check_typeout_v:n {==>~increment~ /P \on@line }
\tag_mc_end:
\@@_check_para_end_show:nn {red}{}
\tag_struct_end:
\bool_if:NF \l_@@_para_flattened_bool
{
\@@_gincr_para_main_end_int:
\tag_struct_end:
}
}
}
}
% \end{macrocode}
% By default we assign the plain plug:
% \begin{macrocode}
\socket_assign_plug:nn { tagsupport/para/begin}{plain}
\socket_assign_plug:nn { tagsupport/para/end}{plain}
% \end{macrocode}
% And use the sockets in the hooks. Once tagging sockets exist, this
% can be adapted.
% \begin{macrocode}
\AddToHook{para/begin}{ \socket_use:n { tagsupport/para/begin }
}
\AddToHook{para/end} { \socket_use:n { tagsupport/para/end } }
% \end{macrocode}
%
% If the block code is loaded we must ensure that it doesn't overwrite
% the hook again. And we must reassign the para/begin plug.
% This can go once the block code no longer tries to adapt the hooks.
% \begin{macrocode}
\AddToHook{package/latex-lab-testphase-block/after}
{
\RemoveFromHook{para/begin}[tagpdf]
\RemoveFromHook{para/end}[latex-lab-testphase-block]
\AddToHook{para/begin}[tagpdf]
{
\socket_use:n { tagsupport/para/begin }
}
\AddToHook{para/end}[tagpdf]
{
\socket_use:n { tagsupport/para/end }
}
\socket_assign_plug:nn { tagsupport/para/begin}{block}
}
% \end{macrocode}
%
% We check the para count at the end. If tagging is not active it is not a error,
% but we issue a warning as it perhaps indicates that the testphase code didn't guard
% everything correctly.
% \begin{macrocode}
\AddToHook{enddocument/info}
{
\tag_if_active:F
{
\msg_redirect_name:nnn { tag } { para-hook-count-wrong } { warning }
}
\int_compare:nNnF {\g_@@_para_main_begin_int}={\g_@@_para_main_end_int}
{
\msg_error:nneee
{tag}
{para-hook-count-wrong}
{\int_use:N\g_@@_para_main_begin_int}
{\int_use:N\g_@@_para_main_end_int}
{text-unit}
}
\int_compare:nNnF {\g_@@_para_begin_int}={\g_@@_para_end_int}
{
\msg_error:nneee
{tag}
{para-hook-count-wrong}
{\int_use:N\g_@@_para_begin_int}
{\int_use:N\g_@@_para_end_int}
{text}
}
}
% \end{macrocode}
% We need at least the new-or-1 code.
% In generic mode we also must insert the code to finish the MC-chunks
% \begin{macrocode}
\@ifpackageloaded{footmisc}
{\PackageWarning{tagpdf}{tagpdf~has~been~loaded~too~late!}} %
{\RequirePackage{latex-lab-testphase-new-or-1}}
\AddToHook{begindocument/before}
{
\providecommand\@kernel@tagsupport@@@@makecol{}
\providecommand\@kernel@before@cclv{}
\bool_if:NF \g_@@_mode_lua_bool
{
\cs_if_exist:NT \@kernel@before@footins
{
\tl_put_right:Nn \@kernel@before@footins
{ \tag_mc_add_missing_to_stream:Nn \footins {footnote} }
\tl_put_right:Nn \@kernel@before@cclv
{
\@@_check_typeout_v:n {====>~In~\token_to_str:N \@makecol\c_space_tl\the\c@page}
\tag_mc_add_missing_to_stream:Nn \@cclv {main}
}
\tl_put_right:Nn \@kernel@tagsupport@@@@makecol
{
\@@_check_typeout_v:n {====>~In~\token_to_str:N \@makecol\c_space_tl\the\c@page}
\tag_mc_add_missing_to_stream:Nn \@outputbox {main}
}
\tl_if_exist:NT \@mult@ptagging@hook
{
\tl_put_right:Nn \@mult@ptagging@hook
{
\@@_check_typeout_v:n {====>~In~\string\page@sofar}
\process@cols\mult@firstbox
{
\tag_mc_add_missing_to_stream:Nn \count@ {multicol}
}
\tag_mc_add_missing_to_stream:Nn \mult@rightbox {multicol}
}
}
}
}
}
%
% \end{macrocode}
% \begin{macro}
% {
% \tagpdfparaOn,
% \tagpdfparaOff
% }
% This two command switch para mode on and off. |\tagpdfsetup| could be used
% too but is longer. An alternative is |\tag_tool:n{para/tagging=false}|
% \begin{macrocode}
%\newcommand\tagpdfparaOn {}
%\newcommand\tagpdfparaOff{}
%<*package>
\renewcommand\tagpdfparaOn {\bool_set_true:N \l_@@_para_bool}
\renewcommand\tagpdfparaOff{\bool_set_false:N \l_@@_para_bool}
% \end{macrocode}
% \end{macro}
%
% \begin{macro}
% {
% \tagpdfsuppressmarks
% }
% This command allows to suppress the creation of the marks. It takes an argument
% which should normally be one of the mc-commands, puts a group around it and
% suppress the marks creation in this group. This command should be used if
% the begin and end command are at different boxing levels. E.g.
% \begin{verbatim}
% \@hangfrom
% {
% \tagstructbegin{tag=H1}%
% \tagmcbegin {tag=H1}%
% #2
% }
% {#3\tagpdfsuppressmarks{\tagmcend}\tagstructend}%
% \end{verbatim}
% \begin{macrocode}
\NewDocumentCommand\tagpdfsuppressmarks{m}
{{\use:c{@@_mc_disable_marks:} #1}}
% \end{macrocode}
% \end{macro}
% \subsection{Language support}
%
% With the following key the lang variable is set. All structures in the current group will
% then set this lang variable.
% \begin{macro}{test/lang (setup-key)}
% \begin{macrocode}
\keys_define:nn { @@ / setup }
{
text / lang .tl_set:N = \l_@@_struct_lang_tl
}
% \end{macrocode}
% \end{macro}
% \subsection{Header and footer}
% Header and footer should normally be tagged as artifacts. The following code
% requires the new hooks.
% For now we allow to disable this function, but probably the code should always there at
% the end.
% TODO check if Pagination should be changeable.
% \begin{macrocode}
\cs_new_protected:Npn\@@_hook_kernel_before_head:{}
\cs_new_protected:Npn\@@_hook_kernel_after_head:{}
\cs_new_protected:Npn\@@_hook_kernel_before_foot:{}
\cs_new_protected:Npn\@@_hook_kernel_after_foot:{}
\AddToHook{begindocument}
{
\cs_if_exist:NT \@kernel@before@head
{
\tl_put_right:Nn \@kernel@before@head {\@@_hook_kernel_before_head:}
\tl_put_left:Nn \@kernel@after@head {\@@_hook_kernel_after_head:}
\tl_put_right:Nn \@kernel@before@foot {\@@_hook_kernel_before_foot:}
\tl_put_left:Nn \@kernel@after@foot {\@@_hook_kernel_after_foot:}
}
}
\bool_new:N \g_@@_saved_in_mc_bool
\cs_new_protected:Npn \@@_exclude_headfoot_begin:
{
\bool_set_false:N \l_@@_para_bool
\bool_if:NTF \g_@@_mode_lua_bool
{
\tag_mc_end_push:
}
{
\bool_gset_eq:NN \g_@@_saved_in_mc_bool \g_@@_in_mc_bool
\bool_gset_false:N \g_@@_in_mc_bool
}
\tag_mc_begin:n {artifact}
\tag_suspend:n{headfoot}
}
\cs_new_protected:Npn \@@_exclude_headfoot_end:
{
\tag_resume:n{headfoot}
\tag_mc_end:
\bool_if:NTF \g_@@_mode_lua_bool
{
\tag_mc_begin_pop:n{}
}
{
\bool_gset_eq:NN \g_@@_in_mc_bool\g_@@_saved_in_mc_bool
}
}
% \end{macrocode}
% This version allows to use an Artifact structure
% \begin{macrocode}
\@@_attr_new_entry:nn {@@/attr/pagination}{/O/Artifact/Type/Pagination}
\cs_new_protected:Npn \@@_exclude_struct_headfoot_begin:n #1
{
\bool_set_false:N \l_@@_para_bool
\bool_if:NTF \g_@@_mode_lua_bool
{
\tag_mc_end_push:
}
{
\bool_gset_eq:NN \g_@@_saved_in_mc_bool \g_@@_in_mc_bool
\bool_gset_false:N \g_@@_in_mc_bool
}
\tag_struct_begin:n{tag=Artifact,attribute-class=@@/attr/#1}
\tag_mc_begin:n {artifact=#1}
\tag_suspend:n{headfoot}
}
\cs_new_protected:Npn \@@_exclude_struct_headfoot_end:
{
\tag_resume:n{headfoot}
\tag_mc_end:
\tag_struct_end:
\bool_if:NTF \g_@@_mode_lua_bool
{
\tag_mc_begin_pop:n{}
}
{
\bool_gset_eq:NN \g_@@_in_mc_bool\g_@@_saved_in_mc_bool
}
}
% \end{macrocode}
% And now the keys
% \begin{macro}{page/exclude-header-footer (setup-key),exclude-header-footer (deprecated)}
% \begin{macrocode}
\keys_define:nn { @@ / setup }
{
page/exclude-header-footer .choice:,
page/exclude-header-footer / true .code:n =
{
\cs_set_eq:NN \@@_hook_kernel_before_head: \@@_exclude_headfoot_begin:
\cs_set_eq:NN \@@_hook_kernel_before_foot: \@@_exclude_headfoot_begin:
\cs_set_eq:NN \@@_hook_kernel_after_head: \@@_exclude_headfoot_end:
\cs_set_eq:NN \@@_hook_kernel_after_foot: \@@_exclude_headfoot_end:
},
page/exclude-header-footer / pagination .code:n =
{
\cs_set:Nn \@@_hook_kernel_before_head: { \@@_exclude_struct_headfoot_begin:n {pagination} }
\cs_set:Nn \@@_hook_kernel_before_foot: { \@@_exclude_struct_headfoot_begin:n {pagination} }
\cs_set_eq:NN \@@_hook_kernel_after_head: \@@_exclude_struct_headfoot_end:
\cs_set_eq:NN \@@_hook_kernel_after_foot: \@@_exclude_struct_headfoot_end:
},
page/exclude-header-footer / false .code:n =
{
\cs_set_eq:NN \@@_hook_kernel_before_head: \prg_do_nothing:
\cs_set_eq:NN \@@_hook_kernel_before_foot: \prg_do_nothing:
\cs_set_eq:NN \@@_hook_kernel_after_head: \prg_do_nothing:
\cs_set_eq:NN \@@_hook_kernel_after_foot: \prg_do_nothing:
},
page/exclude-header-footer .default:n = true,
page/exclude-header-footer .initial:n = true,
% \end{macrocode}
% deprecated name
% \begin{macrocode}
exclude-header-footer .meta:n = { page/exclude-header-footer = {#1} }
}
% \end{macrocode}
% \end{macro}
% \subsection{Links}
% We need to close and reopen mc-chunks around links.
% Currently we handle URI and GoTo (internal) links.
% Links should have an alternative text in the Contents
% key. It is unclear which text this should be and
% how to get it.
% \begin{macrocode}
\hook_gput_code:nnn
{pdfannot/link/URI/before}
{tagpdf}
{
\tag_mc_end_push:
\tag_struct_begin:n { tag=Link }
\tag_mc_begin:n { tag=Link }
\pdfannot_dict_put:nne
{ link/URI }
{ StructParent }
{ \tag_struct_parent_int: }
}
\hook_gput_code:nnn
{pdfannot/link/URI/after}
{tagpdf}
{
\tag_struct_insert_annot:ee {\pdfannot_link_ref_last:}{\tag_struct_parent_int:}
\tag_mc_end:
\tag_struct_end:
\tag_mc_begin_pop:n{}
}
\hook_gput_code:nnn
{pdfannot/link/GoTo/before}
{tagpdf}
{
\tag_mc_end_push:
\tag_struct_begin:n{tag=Link}
\tag_mc_begin:n{tag=Link}
\pdfannot_dict_put:nne
{ link/GoTo }
{ StructParent }
{ \tag_struct_parent_int: }
}
\hook_gput_code:nnn
{pdfannot/link/GoTo/after}
{tagpdf}
{
\tag_struct_insert_annot:ee {\pdfannot_link_ref_last:}{\tag_struct_parent_int:}
\tag_mc_end:
\tag_struct_end:
\tag_mc_begin_pop:n{}
}
% "alternative descriptions " for PAX3. How to get better text here??
\pdfannot_dict_put:nnn
{ link/URI }
{ Contents }
{ (url) }
\pdfannot_dict_put:nnn
{ link/GoTo }
{ Contents }
{ (ref) }
% \end{macrocode}
%
% \end{implementation}
% \PrintIndex