% \iffalse meta-comment
%
%% File: tagpdf.dtx
%
% Copyright (C) 2019-2026 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{tagging=on}
\documentclass{l3in2edoc}
\usepackage{array,booktabs,caption}
\hypersetup{pdfauthor=Ulrike Fischer,
 pdftitle=tagpdf (main module)}
\input{tagpdf-docelements}
\begin{document}
  \DocInput{\jobname.dtx}
\end{document}
%</driver>
% \fi
% \title{^^A
%   The \pkg{tagpdf} main module
% }
%
% \author{^^A
%  Ulrike Fischer\thanks
%    {^^A
%      E-mail:
%        \href{mailto:fischer@troubleshooting-tex.de}
%          {fischer@troubleshooting-tex.de}^^A
%    }^^A
% }
%
% \date{Version 1.0c, released 2026-05-17}
% \maketitle%
%
% \begin{documentation}
% \begin{function}{\tag_suspend:n, \tag_resume:n,\tag_stop:n, \tag_start:n }%
% \begin{syntax}
% \cs{tag_suspend:n} \Arg{label}\\
% \cs{tag_resume:n} \Arg{label}\\
% \cs{tag_stop:n} \Arg{label} (\emph{deprecated})\\
% \cs{tag_start:n} \Arg{label} (\emph{deprecated})
% \end{syntax}
% We need commands to stop tagging in some places.
% They switches three local booleans and also stop the counting
% of paragraphs. If they are nested an inner \cs{tag_resume:n} will not
% restart tagging.
% \meta{label} is only used in debugging messages to allow to follow the nesting
% and to identify which code is disabling the tagging.
% The label is not expanded so can be a single token, e.g. |\caption|.
% |\tag_suspend:n| and |\tag_resume:n| are the l3-layer variants of
% |\SuspendTagging| and |\ResumeTagging| and will be provided by the kernel
% in the next release.
% \end{function}
%
% \begin{function}{
%  \tag_stop:, \tag_start:,
%  \tagstop, \tagstart }
% \emph{deprecated} These are variants of the above commands
% without the debuging level. They are now deprecated and it
% is recommended to use the kernel command
% |\SuspendTagging|, |\ResumeTagging|, |\tag_suspend:n| and |\tag_resume:n|
% instead.
% \end{function}
%
%
% \begin{setupkeydecl}{activate/spaces}
%  |activate/spaces| activates the additional parsing needed for
%  interword spaces. It replaces the deprecated key |interwordspace|.
% \end{setupkeydecl}
%
% \begin{setupkeydecl}{activate/mc,
%    activate-mc (deprecated),
%    }
% A key to activate the marked content code. It should be used
% only in special cases, e.g. for debugging.
% \end{setupkeydecl}
%
% \begin{setupkeydecl}{
%    activate/tree,
%    activate-tree (deprecated),
%    }
% This key activates the code that finalize the
% various trees. It should be used
% only in special cases, e.g. for debugging.
% \end{setupkeydecl}
% \begin{setupkeydecl}{
%    activate/struct,
%    activate-struct (deprecated),
%    }
% This key activates the code for structures.
% It should be used
% only in special cases, e.g. for debugging.
% \end{setupkeydecl}
%
% \begin{setupkeydecl}{
%    activate/all,
%    activate-all (deprecated),
%    }
% This is a meta key for the three previous keys
% and is normally what should be used to activate tagging.
% \end{setupkeydecl}
%
%\begin{setupkeydecl}{activate/struct-dest,no-struct-dest (deprecated)}
% The key allows to suppress the creation of structure destinations
% \end{setupkeydecl}
%
% \begin{setupkeydecl}{debug/log}
% The |debug/log| key takes currently the values |none|, |v|, |vv|, |vvv|, |all|.
% More details are in \texttt{tagpdf-checks}.
% \end{setupkeydecl}
%
% \begin{setupkeydecl}{activate/tagunmarked, tagunmarked (deprecated) }
% This key allows to set if (in luamode) unmarked text should be
% marked up as artifact. The initial value is true.
% \end{setupkeydecl}
%
% \begin{setupkeydecl}{activate/softhyphen}
% This key allows to activates automatic handling of hyphens inserted
% by hyphenation. It is only used in luamode.
% It accepts the following values:
% \begin{itemize}
% \item \texttt{false} and \texttt{off} deactivates the automatic
% handling.
% \item \texttt{char} Replaces the hyphens by U+00AD if the font contains this glyph.
% Note that in some newer fonts U+00AD is an invisible character and so this could led
% to disappearing hyphens.
% \item \texttt{artifact} (default value) This keeps the hard hyphen U+002D but
% surrounds it by an Artifact-MC. This only works if the document is tagged.
% \item \texttt{true}, \texttt{on}  do the same as \texttt{artifact}.
% \end{itemize}
%
% If tagging is not activate the softhyphen handling is not activated, by default. The hyphen
% stays a hard-hyphen. If is possible to use the \texttt{char} mode with
% \begin{verbatim}
% \DocumentMetadata{tagging=off,tagging-setup={activate/softhyphen=char},}
% \end{verbatim}
% As \cs{tagpdfsetup} is deactivated at the begin of the class,
% there is no interface to switch in the document.
%
% If tagging is active the default is \texttt{artifact} mode, this requires that \texttt{tagunmarked}
% is active too. The mode can be switched at any time with \cs{tagpdfsetup}.
% Switching on and off the softhyphen is a global operation,
% a switch between artifact and char more is a local operation.
% \end{setupkeydecl}
%
% \begin{setupkeydecl}{activate/collect-link-artifacts}
%  PDF/UA requires that link annotations are in a Link (or Reference) structure element even
%  if there are connected to text that is marked as artifact, as is the text in the
%  header and footer. Fulfilling this requirement
%  has sadly the bad side effect that a screen reader will, e.g.,
%  read suddenly \enquote{Link} between paragraphs when it encounters a header
%  at a page break. Leaving the annotations untagged gives a better reading but 
%  leads to complains of the UA-validators.
%  With lualatex it is possible to detect such untagged link annotations and to move them into
%  a structure at the end of the document, so that they at least no longer interrupt the reading.
%  The setting is on by default, but can be deactivated (in the preamble) with this key.
%  It knows currently only the value \texttt{off}. 
% \end{setupkeydecl}
%
%
% \begin{setupkeydecl}{page/tabsorder, tabsorder (deprecated)}
% This sets the tabsorder on a page. The values are |row|, |column|, |structure| (default)
% or |none|. Currently this is set more or less globally. More finer control can be
% added if needed.
% \end{setupkeydecl}
%
% \begin{function}{tagstruct,tagstructobj,tagabspage,tagmcabs,tagmcid}
% These are attributes used by the label/ref system.
% \end{function}
% \end{documentation}
% \begin{implementation}
% \section{Initialization and test if pdfmanagement is active.}
%    \begin{macrocode}
%<@@=tag>
%<*package>
\ProvidesExplPackage {tagpdf} {2026-05-17} {1.0c}
  { LaTeX kernel code for PDF tagging }

\IfPDFManagementActiveF
  {
    \PackageError{tagpdf}
     {
       PDF~resource~management~is~no~active!\MessageBreak
       tagpdf~will~no~work.
     }
     {
       Activate~it~with \MessageBreak
       \string\DocumentMetadata{<options>}\MessageBreak
       before~\string\documentclass
     }
  }
%</package>
%    \end{macrocode}
%<*debug>
%    \begin{macrocode}
\ProvidesExplPackage {tagpdf-debug} {2026-05-17} {1.0c}
  { debug code for tagpdf }
\@ifpackageloaded{tagpdf}{}{\PackageWarning{tagpdf-debug}{tagpdf~not~loaded,~quitting}\endinput}
%    \end{macrocode}
%</debug>
% We map the internal module name \enquote{tag} to \enquote{tagpdf} in messages.
%    \begin{macrocode}
%<*package>
\prop_gput:Nnn \g_msg_module_name_prop { tag }{ tagpdf }
%</package>
%    \end{macrocode}
% Debug mode has its special mapping:
%    \begin{macrocode}
%<*debug>
\prop_gput:Nnn \g_msg_module_type_prop { tag / debug} {}
\prop_gput:Nnn \g_msg_module_name_prop { tag / debug }{tagpdf~DEBUG}
%</debug>
%    \end{macrocode}
%\section{base package}
% To avoid to have to test everywhere if tagpdf has been loaded and is active,
% we define a base package with dummy functions
%    \begin{macrocode}
%<*base>
\ProvidesExplPackage {tagpdf-base} {2026-05-17} {1.0c}
  {part of tagpdf - provide base, no-op versions of the user commands }
%</base>
%    \end{macrocode}
%\section{Package options}
% The boolean is kept for now for compatibility, can go in 2026.
%    \begin{macrocode}
%<*package>
\bool_new:N\g_@@_mode_lua_bool
\sys_if_engine_luatex:T {\bool_gset_true:N \g_@@_mode_lua_bool}
\DeclareOption {luamode}  { }
\DeclareOption {genericmode}{ }
\ProcessOptions
%    \end{macrocode}
% \section{Packages}
% To be on the safe side for now, load also the base definitions
%    \begin{macrocode}
\RequirePackage{tagpdf-base}
%</package>
%    \end{macrocode}
% The no-op version should behave a near enough to the real code as
% possible, so we define a command which a special in the relevant backends:
%    \begin{macrocode}
%<*base>
\cs_new_protected:Npn \@@_whatsits: {}
\AddToHook{begindocument}
 {
  \str_case:onF { \c_sys_backend_str }
   {
    { luatex  } { \cs_set_protected:Npn \@@_whatsits: {} }
    { dvisvgm } { \cs_set_protected:Npn \@@_whatsits: {} }
   }
   {
     \cs_set_protected:Npn \@@_whatsits: {\tex_special:D {} }
   }
 }
%</base>
%    \end{macrocode}
%
% \subsection{a LastPage label}
% With LaTeX 2025-06-01 we no longer need a special version as the label is now
% written directly. To avoid problems with the xr package, we undefine the label command
% before reading the aux-file.
%    \begin{macrocode}
%<*package>
\AddToHook{begindocument/before}{\cs_undefine:N\r@@@@tag@LastPage}
\AddToHook{enddocument/afterlastpage}
 {\property_record:nn{@tag@LastPage}{abspage,tagmcabs,tagstruct}}
%    \end{macrocode}
%
% \section{Variables}
% \begin{variable}
%  {
%    \l_@@_tmpa_tl,
%    \l_@@_tmpb_tl,
%    \l_@@_tmpc_tl,
%    \l_@@_tmp_unused_tl
%    \l_@@_Ref_tmpa_tl,
%    \l_@@_get_tmpc_tl,
%    \l_@@_get_parent_tmpa_tl,
%    \l_@@_get_parent_tmpb_tl,
%    \l_@@_get_parent_tmpc_tl,
%    \l_@@_get_child_tmpa_tl,
%    \l_@@_get_child_tmpb_tl,
%    \l_@@_get_child_tmpc_tl,
%    \l_@@_tmpa_str,
%    \l_@@_tmpa_prop,
%    \l_@@_tmpa_seq,
%    \l_@@_tmpb_seq,
%    \l_@@_tmpa_clist,
%    \l_@@_tmpa_int,
%    \l_@@_tmpa_box,
%    \l_@@_tmpb_box
%  }
% A few temporary variables
%    \begin{macrocode}
\tl_new:N    \l_@@_tmpa_tl
\tl_new:N    \l_@@_tmpb_tl
\tl_new:N    \l_@@_tmpc_tl
\tl_new:N    \l_@@_tmp_unused_tl
\tl_new:N    \l_@@_Ref_tmpa_tl
\tl_new:N    \l_@@_get_tmpc_tl
\tl_new:N    \l_@@_get_parent_tmpa_tl
\tl_new:N    \l_@@_get_parent_tmpb_tl
\tl_new:N    \l_@@_get_parent_tmpc_tl
\tl_new:N    \l_@@_get_child_tmpa_tl
\tl_new:N    \l_@@_get_child_tmpb_tl
\tl_new:N    \l_@@_get_child_tmpc_tl
\str_new:N   \l_@@_tmpa_str
\prop_new:N  \l_@@_tmpa_prop
\seq_new:N   \l_@@_tmpa_seq
\seq_new:N   \l_@@_tmpb_seq
\clist_new:N \l_@@_tmpa_clist
\int_new:N   \l_@@_tmpa_int
\box_new:N   \l_@@_tmpa_box
\box_new:N   \l_@@_tmpb_box
%    \end{macrocode}
% \end{variable}
% Attribute lists for the label command. We have a list for
% mc-related labels, and one for structures.
% \begin{macro}{\c_@@_property_mc_clist,\c_@@_property_struct_clist}
%    \begin{macrocode}
\clist_const:Nn \c_@@_property_mc_clist     {tagabspage,tagmcabs,tagmcid}
\clist_const:Nn \c_@@_property_struct_clist {tagstruct,tagstructobj}
%    \end{macrocode}
% \end{macro}
%
% \begin{variable}{\l_@@_loglevel_int}
% This integer hold the log-level and so allows to
% control the messages.
% TODO: a list which log-level shows what is needed. The current behaviour
% is quite ad-hoc.
%    \begin{macrocode}
\int_new:N  \l_@@_loglevel_int
%    \end{macrocode}
% \end{variable}
%
% \begin{variable}
%  {
%    \g_@@_active_space_bool,
%    \g_@@_active_mc_bool,
%    \g_@@_active_tree_bool,
%    \g_@@_active_struct_bool,
%    \g_@@_active_struct_dest_bool
%  }
% These booleans should help to control the global behaviour of tagpdf.
% Ideally it should more or less do nothing if all are false.
% The space-boolean controls the interword space code,
% the mc-boolean activates \cs{tag_mc_begin:n},
% the tree-boolean activates writing the finish code and the pdfmanagement related
% commands, the struct-boolean activates the storing of the structure data.
% In a normal document all should be active, the split is only there for
% debugging purpose.
% Structure destination will be activated automatically,
% but with the boolean struct-dest-boolean one can suppress them.
% Also we assume currently that they are set only at begin document. But
% if some control passing over groups are needed they could be perhaps
% used in a document too.
% TODO: check if they are used everywhere as needed and as wanted.
%    \begin{macrocode}
\bool_new:N \g_@@_active_space_bool
\bool_new:N \g_@@_active_mc_bool
\bool_new:N \g_@@_active_tree_bool
\bool_new:N \g_@@_active_struct_bool
\bool_new:N \g_@@_active_struct_dest_bool
\bool_gset_true:N \g_@@_active_struct_dest_bool
%    \end{macrocode}
% \end{variable}

% \begin{variable}
%  {
%    \l_@@_active_mc_bool,
%    \l_@@_active_struct_bool,
%    \l_@@_active_socket_bool
%  }
% These booleans should help to control the \emph{local} behaviour of tagpdf.
% In some cases it could e.g. be necessary to stop tagging completely.
% As local booleans they respect groups.
% TODO: check if they are used everywhere as needed and as wanted.
%    \begin{macrocode}
\bool_new:N \l_@@_active_mc_bool
\bool_set_true:N \l_@@_active_mc_bool
\bool_new:N \l_@@_active_struct_bool
\bool_set_true:N \l_@@_active_struct_bool
\bool_new:N \l_@@_active_socket_bool
%    \end{macrocode}
% \end{variable}
%
% \begin{variable}{\g_@@_tagunmarked_bool}
% This boolean controls if the code should try to automatically
% tag parts not in mc-chunk. It is currently only used in luamode.
% It would be possible to used it in generic mode, but this would create
% quite a lot empty artifact mc-chunks.
%    \begin{macrocode}
\bool_new:N \g_@@_tagunmarked_bool
%    \end{macrocode}
% \end{variable}
%
% \begin{variable}{\g_@@_softhyphen_bool}
% This boolean controls if the code should try to automatically
% handle hyphens from hyphenation. It is currently only used in luamode.
%    \begin{macrocode}
\bool_new:N \g_@@_softhyphen_bool
\bool_gset_true:N \g_@@_softhyphen_bool
%    \end{macrocode}
% \end{variable}
%
% \begin{variable}{\g_@@_unique_cnt_int}
% If tagpdf has to create unique names (e.g. for object names when embedding files)
% it can use this integer to get an unique name. At every use it should be increased
%    \begin{macrocode}
\int_new:N \g_@@_unique_cnt_int
%    \end{macrocode}
% \end{variable}

% \section{Variants of l3 commands}
%     \begin{macrocode}
\prg_generate_conditional_variant:Nnn \pdf_object_if_exist:n {e}{T,F,TF}
\cs_generate_variant:Nn \pdf_object_ref:n {e}
\cs_generate_variant:Nn \pdfannot_dict_put:nnn {nne}
\cs_generate_variant:Nn \pdffile_embed_stream:nnn {nee,oee}
\cs_generate_variant:Nn \prop_gput:Nnn {Nee,Nen} %** unneeded
\cs_generate_variant:Nn \prop_put:Nnn  {Nee}     %** unneeded
\cs_generate_variant:Nn \prop_item:Nn {No,Ne}    %**  unneeded
\cs_generate_variant:Nn \seq_set_split:Nnn{Nno}
\cs_generate_variant:Nn \str_set_convert:Nnnn {Nonn, Noon, Nnon }
\cs_generate_variant:Nn \clist_map_inline:nn {on}
\cs_generate_variant:Nn \pdffile_embed_file:nnn {eee}
%    \end{macrocode}
%
% \section{Label and Reference commands}
% The code uses mostly the kernel properties but need a few local variants.
% \begin{macro}{\@@_property_record:nn}
% The command to record a property while preserving the spaces similar to the standard
% \cs{label}.
%    \begin{macrocode}
    \cs_new_protected:Npn \@@_property_record:nn #1#2
      {
        \@bsphack
        \property_record:nn{#1}{#2}
        \@esphack
      }

%    \end{macrocode}
% And a few variants
%    \begin{macrocode}
\cs_generate_variant:Nn \property_ref:nnn {enn}
\cs_generate_variant:Nn \property_ref:nn  {en}
\cs_generate_variant:Nn \@@_property_record:nn {en,eo}
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}{\@@_property_ref_lastpage:nn}
% A command to retrieve the lastpage label, this will be adapted when there
% is a proper, kernel lastpage label.
%    \begin{macrocode}
\cs_new:Npn \@@_property_ref_lastpage:nn #1 #2
  {
    \property_ref:nnn {@tag@LastPage}{#1}{#2}
  }
%    \end{macrocode}
%\end{macro}

% \section{Setup label attributes}
% \begin{macro}{tagstruct,tagstructobj,tagabspage,tagmcabs,tagmcid}
% This are attributes used by the label/ref system.
% With structures we store the structure number |tagstruct|
% and the object reference |tagstructobj|.
% The second is needed to be able to reference a structure which hasn't been
% created yet. The alternative would be to create the object in such cases,
% but then we would have to check the object existence all the time.
%
% With mc-chunks we store the absolute page number |tagabspage|,
% the absolute id |tagmcabc|, and the id on the page |tagmcid|.
%    \begin{macrocode}
\property_new:nnnn
  { tagstruct } { now }
  {1} { \int_use:N \c@g_@@_struct_abs_int }
\property_new:nnnn  { tagstructobj } { now }  {}
  {
     \pdf_object_ref_indexed:nn { @@/struct } { \c@g_@@_struct_abs_int }
  }
\property_new:nnnn
  { tagabspage } { shipout }
  {0} { \int_use:N \g_shipout_readonly_int }
\property_new:nnnn  { tagmcabs } { now }
  {0} { \int_use:N \c@g_@@_MCID_abs_int }

\flag_new:n { @@/mcid }
\property_new:nnnn  {tagmcid } { shipout }
   {0} { \flag_height:n { @@/mcid } }

%    \end{macrocode}
% \end{macro}
%

%\section{Commands to fill seq and prop}
% With most engines these are simply copies of the expl3 commands,
% but luatex will overwrite them, to store the data also in lua tables.
% \begin{macro}
%  {
%    \@@_prop_new:N
%    ,\@@_prop_new_linked:N
%    ,\@@_seq_new:N
%    ,\@@_prop_gput:Nnn
%    ,\@@_seq_gput_right:Nn
%    ,\@@_seq_item:cn
%    ,\@@_prop_item:cn
%    ,\@@_seq_show:N
%    ,\@@_prop_show:N
%  }
%    \begin{macrocode}
\cs_set_eq:NN \@@_prop_new:N        \prop_new:N
\cs_set_eq:NN \@@_prop_new_linked:N \prop_new_linked:N
\cs_set_eq:NN \@@_seq_new:N         \seq_new:N
\cs_set_eq:NN \@@_prop_gput:Nnn     \prop_gput:Nnn
\cs_set_eq:NN \@@_seq_gput_right:Nn \seq_gput_right:Nn
\cs_set_eq:NN \@@_seq_gput_left:Nn  \seq_gput_left:Nn
\cs_set_eq:NN \@@_seq_item:cn       \seq_item:cn
\cs_set_eq:NN \@@_prop_item:cn      \prop_item:cn
\cs_set_eq:NN \@@_seq_show:N        \seq_show:N
\cs_set_eq:NN \@@_prop_show:N       \prop_show:N
% cnx temporary needed for latex-lab-graphic code
\cs_generate_variant:Nn \@@_prop_gput:Nnn      { Nen, Nee, Nne, Nno, cnn, cen, cne, cno, cnx}
\cs_generate_variant:Nn \@@_seq_gput_right:Nn  { Ne  , No, cn, ce }
\cs_generate_variant:Nn \@@_seq_gput_left:Nn   { ce }
\cs_generate_variant:Nn \@@_prop_new:N   { c }
\cs_generate_variant:Nn \@@_seq_new:N    { c }
\cs_generate_variant:Nn \@@_seq_show:N   { c }
\cs_generate_variant:Nn \@@_prop_show:N  { c }
%</package>
%    \end{macrocode}
% \end{macro}
%
% \section{General tagging commands}
%
% \begin{macro}{
%   \tag_suspend:n,\tag_resume:n,
%   \tag_stop:,\tag_start:,
%   \tag_stop:n,\tag_start:n}
% We need commands to stop tagging in some places.
% They switch local booleans and also stop the counting of paragraphs.
% The commands keep track of the nesting with a local
% counter. Tagging only is only restarted at the outer level,
% if the current level is 1.
% The commands with argument allow to give a label. This is only used
% in debugging messages to allow to follow the nesting. The label is
% not expand so can e.g. be a single command token.
%
% \begin{variable}{\l_@@_tag_stop_int}
% When stop/start pairs are nested we do not want the inner
% start command to restart tagging. To control this we use a
% local int: The stop command will increase it. The starting
% will decrease it and only restart tagging, if it is zero.
% This will replace the label version.
%    \begin{macrocode}
%<*package|debug>
%<package>\int_new:N \l_@@_tag_stop_int
%    \end{macrocode}
% \end{variable}
%
%    \begin{macrocode}
\cs_set_protected:Npn \tag_stop:
  {
%<debug>     \msg_note:nne {tag / debug }{tag-suspend}{ \int_use:N \l_@@_tag_stop_int }
    \int_incr:N \l_@@_tag_stop_int
    \bool_set_false:N \l_@@_active_struct_bool
    \bool_set_false:N \l_@@_active_mc_bool
    \bool_set_false:N \l_@@_active_socket_bool
    \@@_stop_para_ints:
  }
\cs_set_protected:Npn \tag_start:
  {
    \int_if_zero:nF { \l_@@_tag_stop_int } { \int_decr:N \l_@@_tag_stop_int }
    \int_if_zero:nT { \l_@@_tag_stop_int }
      {
        \bool_set_true:N \l_@@_active_struct_bool
        \bool_set_true:N \l_@@_active_mc_bool
        \bool_set_true:N \l_@@_active_socket_bool
        \@@_start_para_ints:
      }
%<debug>    \msg_note:nne {tag / debug }{tag-resume}{ \int_use:N \l_@@_tag_stop_int }
  }
\cs_set_eq:NN\tagstop\tag_stop:
\cs_set_eq:NN\tagstart\tag_start:
%    \end{macrocode}
%    \begin{macrocode}
\cs_set_protected:Npn \tag_suspend:n #1
  {
%<debug>    \msg_note:nnee {tag / debug }{tag-suspend}
%<debug>       { \int_use:N \l_@@_tag_stop_int }{\exp_not:n{#1}}
    \int_incr:N \l_@@_tag_stop_int
    \bool_set_false:N \l_@@_active_struct_bool
    \bool_set_false:N \l_@@_active_mc_bool
    \bool_set_false:N \l_@@_active_socket_bool
    \@@_stop_para_ints:
  }
\cs_set_eq:NN \tag_stop:n \tag_suspend:n
\cs_set_protected:Npn \tag_resume:n #1
  {
    \int_if_zero:nF { \l_@@_tag_stop_int } { \int_decr:N \l_@@_tag_stop_int }
    \int_if_zero:nT { \l_@@_tag_stop_int }
      {
        \bool_set_true:N \l_@@_active_struct_bool
        \bool_set_true:N \l_@@_active_mc_bool
        \bool_set_true:N \l_@@_active_socket_bool
        \@@_start_para_ints:
      }
%<debug>    \msg_note:nnee {tag / debug }{tag-resume}
%<debug>       { \int_use:N \l_@@_tag_stop_int }{\exp_not:n{#1}}
  }
\cs_set_eq:NN \tag_start:n \tag_resume:n
%</package|debug>
%<*base>
\cs_new_protected:Npn \tag_stop:{}
\cs_new_protected:Npn \tag_start:{}
\cs_new_protected:Npn \tagstop{}
\cs_new_protected:Npn \tagstart{}
\cs_new_protected:Npn \tag_stop:n  #1 {}
\cs_new_protected:Npn \tag_start:n #1 {}
%    \end{macrocode}
% Until the commands are provided by the kernel we provide them here too
%    \begin{macrocode}
\cs_set_eq:NN \tag_suspend:n \tag_stop:n
\cs_set_eq:NN \tag_resume:n  \tag_start:n
%</base>
%    \end{macrocode}
% \end{macro}
%
% \section{Handling link artifacts}
% Links that should be artifacts, e.g. in header or an overlay frame,
% can not be simply kept untagged as the validators then complain about the link
% annotations that are not in a Link structure.
% But tagging them gives a bad reading experience as they can interrupt the text.
% Probably long term the best is not to interrupt tagging inside an Artifact
% structure and to move these structures to the end of the structure tree to keep them
% out of the way.
% But with luatex we have an easy option to detect untagged link annotations and so
% can move them to the end of the structure tree too and this is done here.
%
%    \begin{macrocode}
%<*package>
\cs_new_protected:Npn \@@_activate_linkbin:
  {
   \sys_if_engine_luatex:T
    {
      \tagstructbegin{tag=\UseStructureName{link},stash}
      \tl_const:Ne \c__tag_struct_link_bin_tl {\int_use:N\c@g__tag_struct_abs_int}%
      \tagstructend
      \AddToHook{tagpdf/finish/before}[tagpdf/linkbin]
       {
         \par
         \int_compare:nNnT {\directlua{tex.sprint(ltx.__tag.func.linkbin())}}>{0}
          {
            \tagstructbegin{tag=Artifact,title=Ignored~link~annotations}         
            \tag_struct_use_num:o{\c__tag_struct_link_bin_tl}
            \tagstructend
          }  
       }
     }
  }
\AddToHook{begindocument/end}[tagpdf/linkbin]{\@@_activate_linkbin:}
%</package>
%    \end{macrocode}
%
% \section{Keys for tagpdfsetup}
%
% TODO: the log-levels must be sorted
%
% \begin{setupkeydecl}
%  {
%    activate/mc,
%    activate/tree,
%    activate/struct,
%    activate/all,
%    activate/struct-dest,
%  }
%  Keys to (globally) activate tagging.
%  |activate/spaces| activates the additional parsing needed for
%  interword spaces. It is defined in tagpdf-space.
%  |activate/struct-dest| allows to activate or suppress structure destinations.
%    \begin{macrocode}
%<*package>
\keys_define:nn { @@ / setup }
  {
    activate/mc     .bool_gset:N = \g_@@_active_mc_bool,
    activate/tree   .bool_gset:N = \g_@@_active_tree_bool,
    activate/struct .bool_gset:N = \g_@@_active_struct_bool,
    activate/all    .meta:n =
      {activate/mc={#1},activate/tree={#1},activate/struct={#1}},
    activate/all  .default:n = true,
    activate/struct-dest .bool_gset:N = \g_@@_active_struct_dest_bool,
%    \end{macrocode}
% old, deprecated names
%    \begin{macrocode}
    activate-mc     .bool_gset:N = \g_@@_active_mc_bool,
    activate-tree   .bool_gset:N = \g_@@_active_tree_bool,
    activate-struct .bool_gset:N = \g_@@_active_struct_bool,
    activate-all    .meta:n =
      {activate/mc={#1},activate/tree={#1},activate/struct={#1}},
    activate-all  .default:n = true,
    no-struct-dest .bool_gset_inverse:N = \g_@@_active_struct_dest_bool,
%    \end{macrocode}
% \end{setupkeydecl}
% \begin{setupkeydecl}{debug/show}
% Subkeys/values are defined in various other places.
%    \begin{macrocode}
    debug/show            .choice:,
%    \end{macrocode}
% \end{setupkeydecl}
% \begin{setupkeydecl}{debug/log, debug/uncompress, log (deprecated), uncompress (deprecated)}
% The |log| takes currently the values |none|, |v|, |vv|, |vvv|, |all|.
% The description of the log levels is in tagpdf-checks.
%    \begin{macrocode}
    debug/log             .choice:,
    debug/log / none      .code:n = {\int_set:Nn \l_@@_loglevel_int { 0 }},
    debug/log / v         .code:n =
      {
        \int_set:Nn \l_@@_loglevel_int { 1 }
        \cs_set_protected:Nn \@@_check_typeout_v:n { \iow_term:e {##1} }
      },
    debug/log / vv        .code:n = {\int_set:Nn \l_@@_loglevel_int { 2 }},
    debug/log / vvv       .code:n = {\int_set:Nn \l_@@_loglevel_int { 3 }},
    debug/log / all       .code:n = {\int_set:Nn \l_@@_loglevel_int { 10 }},
    debug/uncompress .code:n = { \pdf_uncompress:  },
%    \end{macrocode}
% deprecated but still needed as the documentmetadata key argument uses it.
%    \begin{macrocode}
    log             .meta:n = {debug/log={#1}},
    uncompress      .code:n = { \pdf_uncompress:  },
%    \end{macrocode}
% \end{setupkeydecl}
% \begin{setupkeydecl}{activate/tagunmarked,tagunmarked (deprecated)}
% This key allows to set if (in luamode) unmarked text should be
% marked up as artifact. The initial value is true.
%    \begin{macrocode}
    activate/tagunmarked     .bool_gset:N = \g_@@_tagunmarked_bool,
    activate/tagunmarked     .initial:n  = true,
%    \end{macrocode}
% deprecated name
%    \begin{macrocode}
    tagunmarked .bool_gset:N = \g_@@_tagunmarked_bool,
%    \end{macrocode}
% \end{setupkeydecl}
% \begin{setupkeydecl}{activate/softhyphen}
% This key activates (in luamode) the handling of soft hyphens.
%    \begin{macrocode}
    activate/softhyphen .choice:,
    activate/softhyphen/false .code:n = {\bool_gset_false:N \g_@@_softhyphen_bool},
    activate/softhyphen/off .code:n = {\bool_gset_false:N \g_@@_softhyphen_bool},
    activate/softhyphen/char  .code:n =
     {
       \bool_gset_true:N \g_@@_softhyphen_bool
       \sys_if_engine_luatex:T
        {
         \lua_now:e
          {
            tex.setattribute
             (
               luatexbase.attributes.l_@@_softhyphen_attr,
               1
             )
          }
        }
     },
    activate/softhyphen/artifact  .code:n =
     {
       \bool_gset_true:N \g_@@_softhyphen_bool
       \sys_if_engine_luatex:T
        {
         \lua_now:e
          {
            tex.setattribute
             (
               luatexbase.attributes.l_@@_softhyphen_attr,
               -2147483647
             )
          }
        }
     },
    activate/softhyphen/true  .code:n =
     {
       \bool_gset_true:N \g_@@_softhyphen_bool
       \sys_if_engine_luatex:T
        {
         \lua_now:e
          {
            tex.setattribute
             (
               luatexbase.attributes.l_@@_softhyphen_attr,
               -2147483647
             )
          }
        }
     },
    activate/softhyphen/on  .code:n =
     {
       \bool_gset_true:N \g_@@_softhyphen_bool
       \sys_if_engine_luatex:T
        {
         \lua_now:e
          {
            tex.setattribute
             (
               luatexbase.attributes.l_@@_softhyphen_attr,
               -2147483647
             )
          }
        }
     },
    activate/softhyphen     .default:n  = artifact,
%    \end{macrocode}
% \end{setupkeydecl}
% \begin{setupkeydecl}{activate/collect-link-artifacts}
%    \begin{macrocode}
    activate/collect-link-artifacts/off .code:n =
      {\RemoveFromHook{begindocument/end}[tagpdf/linkbin]},
%    \end{macrocode}
% \end{setupkeydecl}
% \begin{setupkeydecl}{page/tabsorder,tabsorder (deprecated)}
% This sets the tabsorder on a page. The values are |row|, |column|, |structure| (default)
% or |none|. Currently this is set more or less globally. More finer control can be
% added if needed.
%    \begin{macrocode}
    page/tabsorder       .choice:,
    page/tabsorder / row       .code:n =
      \pdfmanagement_add:nnn { Page } {Tabs}{/R},
    page/tabsorder / column    .code:n =
      \pdfmanagement_add:nnn { Page } {Tabs}{/C},
    page/tabsorder / structure .code:n =
      \pdfmanagement_add:nnn { Page } {Tabs}{/S},
    page/tabsorder / none      .code:n =
      \pdfmanagement_remove:nn {Page} {Tabs},
    page/tabsorder       .initial:n = structure,
%    \end{macrocode}
% deprecated name
%    \begin{macrocode}
    tabsorder .meta:n = {page/tabsorder={#1}},
  }
%    \end{macrocode}
% \end{setupkeydecl}
% \section{loading of engine/more dependent code}
%    \begin{macrocode}
\sys_if_engine_luatex:T
  {
    \file_input:n {tagpdf-luatex.def}
  }
%</package>
%    \end{macrocode}
%    \begin{macrocode}
%<*mcloading>
\bool_if:NTF \g_@@_mode_lua_bool
  {
   \RequirePackage {tagpdf-mc-code-lua}
  }
  {
   \RequirePackage {tagpdf-mc-code-generic} %
  }
%</mcloading>
%<*debug>
\bool_if:NTF \g_@@_mode_lua_bool
  {
   \RequirePackage {tagpdf-debug-lua}
  }
  {
   \RequirePackage {tagpdf-debug-generic} %
  }
%</debug>
%    \end{macrocode}
% \end{implementation}
% \PrintIndex
