% \iffalse meta-comment
%
%% File: latex-lab-toc.dtx (C) Copyright 2022-2026 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
%
%    https://www.latex-project.org/lppl.txt
%
%
% The latex-lab bundle is developed in the LaTeX2e GitHub.
% Issues may be reported at
%
%    https://github.com/latex3/latex2e/issues
%
\def\ltlabtocdate{2026-04-28}
\def\ltlabtocversion{0.85k}
%<*driver>
\DocumentMetadata{tagging=on,pdfstandard=ua-2}
\documentclass[kernel]{l3in2edoc}

\EnableCrossrefs
\CodelineIndex
\begin{document}
  \DocInput{latex-lab-toc.dtx}
\end{document}
%</driver>
%
% \fi
%
%
% \title{The \textsf{latex-lab-toc} package\\
% Changes related to the tagging of toc and similar lists}
% \author{\LaTeX{} Project\thanks{Initial implementation done by Ulrike Fischer}}
% \date{v\ltlabtocdate\ \ltlabtocversion}
%
% \maketitle
%
% \newcommand{\xt}[1]{\textsl{\textsf{#1}}}
% \newcommand{\TODO}[1]{\textbf{[TODO:} #1\textbf{]}}
% \newcommand{\docclass}{document class \marginpar{\raggedright document class
% customizations}}
%
%
%
% \begin{abstract}
% \end{abstract}
% 
% Header for the testphase package
%    \begin{macrocode}
%<*header>
\ProvidesExplPackage {latex-lab-testphase-toc} {\ltlabtocdate} {\ltlabtocversion}
  { Code related to the tagging of toc-like lists}
%</header>  
%    \end{macrocode}
% \begin{documentation}
% \section{Introduction}
%
% The followings contains various functions related to the tagging of the
% table of contents and similar list. 
% 
% The structure of tocs consist of nested TOC and TOCI structures. 
% The code uses the first argument of the \cs{contentsline} command to detect the
% level and to decided if a structure should be closed. The structure that 
% should be used in \texttt{/Ref} key to link to the heading is detected from the
% target name in the fourth argument -- because of this with this code such a target 
% name is created and stored also if hyperref is not loaded. 
% 
% \subsection{Manual toc additions}
% As the \texttt{/Ref} key relies on the target name, 
% manual \cs{addcontentsline} commands must ensure that they reference the right 
% structure. If an unnumbered heading command is used before this is normally the case, 
% so the following should work fine:
% 
%  \begin{verbatim}
%  \chapter*{Unnumbered}
%  \addcontentsline{toc}{chapter}{Unnumbered}
%  \end{verbatim}
%  
%  If there is no heading command a target name must be created manually \emph{inside the
%  right structure} with \cs{MakeLinkTarget}:
%  
%  \begin{verbatim}
%  \noindent % start e.g. P-structure
%  \MakeLinkTarget*{unnumbered}% target inside the P-structure
%  Unnumbered
%  \addcontentsline{toc}{chapter}{Unnumbered}
%  \end{verbatim}   
% 
% \end{documentation}
% \begin{implementation}
%    \begin{macrocode}
%<*package>
%<@@=tag>
%    \end{macrocode}
% \section{Temporary variables}
% \begin{macro}{\l_@@_toc_tmpa_tl}
%    \begin{macrocode}
\tl_new:N \l_@@_toc_tmpa_tl 
%    \end{macrocode}
% \end{macro}
%
% \section{General struct commands}
% The following variables and commands are not restricted to toc, but
% probably will be need in other places too.
% \begin{variable}{\g_@@_struct_dest_num_prop}
% This variable records for (some or all, not clear yet)
% destination names the related structure number to allow
% to reference them in a Ref. The key is the destination.
% Defined by tagpdf.
% \end{variable}
%
% \changes{0.85h}{2025-07-02}{Removed patches for hyperref to record targets, is now in the kernel.}
% 
% \section{Toc code}
% \begin{variable}{\g_@@_toc_level_int,\g_@@_toc_stack_seq}
% |\g_@@_toc_level_int| records in a toc the current absolute level.
% We must close open structures at the end of the toc, for this
% we maintain a stack |\g_@@_toc_stack_seq|.
%    \begin{macrocode}
\int_new:N \g_@@_toc_level_int
\seq_new:N \g_@@_toc_stack_seq
%    \end{macrocode}
% \end{variable}

% \begin{macro}[no-user-doc]{\_tag_toc_starttoc_init:n}
% The init code clears the stack, and set the level to -100
% and start to TOC structure. We also disable paratagging.
% The |/Title| is currently simply the type, but this could be done nicer.
% The argument is the argument of \cs{@starttoc}, so, e.g., \texttt{toc}.
% \changes{0.85i}{2026-04-27}{Use symbolic names}
%    \begin{macrocode}
\cs_new_protected:Npn \@@_toc_starttoc_init:n #1
 {
    \bool_set_false:N \l_@@_para_bool
    \seq_gclear:N \g_@@_toc_stack_seq
    \int_gset:Nn  \g_@@_toc_level_int {-100}
    \tag_struct_begin:n{tag=\UseStructureName{toc},title=#1}
 }
%    \end{macrocode}
% \end{macro}
% Now define the tagging plug and assign it to the socket.
% \begin{plugdecl}{default}
%    \begin{macrocode}
\NewTaggingSocketPlug{toc/starttoc/before}{default}
 {
   \@@_toc_starttoc_init:n{#1}
 }
\AssignTaggingSocketPlug{toc/starttoc/before}{default}
%    \end{macrocode}
% \end{plugdecl}
% \begin{macro}{\@@_toc_starttoc_finalize:}
%    \begin{macrocode}
\cs_new_protected:Npn \@@_toc_starttoc_finalize:
 {
    \int_step_inline:nn
      {\seq_count:N \g_@@_toc_stack_seq }
      {\tag_struct_end:}
    \tag_struct_end:
    \seq_gclear:N \g_@@_toc_stack_seq
 }
%    \end{macrocode}
% \end{macro}
% Now define the tagging plug and assign it to the socket.
% \begin{plugdecl}{default}
%    \begin{macrocode}
\NewTaggingSocketPlug{toc/starttoc/after}{default}
 {
   \@@_toc_starttoc_finalize:
 }
\AssignTaggingSocketPlug{toc/starttoc/after}{default}
%    \end{macrocode}
% \end{plugdecl}
%
% \begin{macro}{\@@_toc_end:n}
% This commands ends all TOC on the stack with a level higher than the argument
%    \begin{macrocode}
\cs_new_protected:Npn \@@_toc_end:n #1 
 {
   \seq_get:NNT\g_@@_toc_stack_seq \l_@@_toc_tmpa_tl 
     {
       \bool_lazy_and:nnT
         {
           \str_if_eq_p:ee{\tl_head:N\l_@@_toc_tmpa_tl}{TOC}
         }
         {
           \int_compare_p:nNn {#1}<{\tl_tail:N \l_@@_toc_tmpa_tl}
         }
         {
           \seq_gpop:NN\g_@@_toc_stack_seq \l_@@_toc_tmpa_tl 
           \tag_struct_end:
           \@@_toc_end:n{#1}
         }
     }
 }
\cs_generate_variant:Nn \@@_toc_end:n {e} 
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}{\@@_toc_contentsline_begin:nnnn}
% This is main command executed at the begin of a |\contentsline|.
% \changes{0.85h}{2025-07-02}{Removed unneeded \cs{tag_if_active:T}, command is used in tagging socket.}   
%    \begin{macrocode}
\cs_new_protected:Npn \@@_toc_contentsline_begin:nnnn #1 #2 #3 #4 
  %#1 level, #2 content, #3 page number (unused) #4 destination
  {
%    \end{macrocode}
% We detect the intended level by checking the value of |toclevel@...|
% (currently only provided by hyperref, but should be there always).
% To be on the safe side we set it to 1 if not defined.
%    \begin{macrocode}
     \ExpandArgs{c}\providecommand { toclevel@#1 }{ 1 } %  just in case ...
     \int_compare:nNnF { \use:c{toclevel@#1} } > {\use:c{c@tocdepth}}
      {
%    \end{macrocode}
% if level goes up, start new sub TOC unless we are at the begin
%    \begin{macrocode}
        \bool_lazy_and:nnT
          { \int_compare_p:nNn { \g_@@_toc_level_int } > {-100} }
          { \int_compare_p:nNn { \use:c{toclevel@#1} }   > { \g_@@_toc_level_int } }
          {
            \seq_gpush:Ne \g_@@_toc_stack_seq {{TOC}\use:c{toclevel@#1}}
            \tag_struct_begin:n{tag=\UseStructureName{toc}}
          }
%    \end{macrocode}
% if level goes down close all TOC's with a higher level
%    \begin{macrocode}
        \int_compare:nNnT
          { \use:c{toclevel@#1} } < { \g_@@_toc_level_int }
          {
            \@@_toc_end:e { \use:c{toclevel@#1} }
          }
%    \end{macrocode}
%if same level do nothing
% update toclevel to the current level.
%    \begin{macrocode}
        \int_gset:Nn \g_@@_toc_level_int { \use:c{toclevel@#1} }
%    \end{macrocode}
% now open the TOCI, the tagging of the
% inner structure is left to the |\l@xxx| commands.
% setting the title is not strictly necessary but looks nicer
% but we have to remove the |\numberline|
% \changes{v0.85k}{2026-04-28}{Use \cs{pdf_purify:nN} for the title of the structure.}
% \begin{NOTE}{UF}
% perhaps keep the number? How to insert a space then
% \end{NOTE}
%    \begin{macrocode}
        \group_begin:
         \text_declare_expand_equivalent:Nn \numberline \use_none:n
         \pdf_purify:nN {#2}\l_@@_sec_tmpa_tl 
         \tag_struct_begin:n{tag=\UseStructureName{toc/item},title-o=\l_@@_sec_tmpa_tl}           
%    \end{macrocode}
% The TOCI structure should get a /Ref, we use a destination
% to retrieve it.
% \begin{NOTE}{UF}
% This only works with hyperref currently. Without hyperref we
% need to store fake names.
% \end{NOTE}
%    \begin{macrocode}
         \tag_struct_gput:nnn { \tag_get:n {struct_num} }{ref_dest}{#4}
         \seq_gpush:Ne \g_@@_toc_stack_seq {{\UseStructureName{toc/item}}\use:c{toclevel@#1}}
        \group_end:
     }    
  }
%    \end{macrocode}
% \end{macro}
% Now define the tagging plug and assign it to the socket.
% \begin{plugdecl}{default}
%    \begin{macrocode}
\NewTaggingSocketPlug{toc/contentsline/before}{default}
  {
    \@@_toc_contentsline_begin:nnnn #1
  }  
\AssignTaggingSocketPlug{toc/contentsline/before}{default}
%    \end{macrocode}
% \end{plugdecl}
%
% \begin{macro}{\@@_toc_contentsline_end:nnnn}
% This is the closing code of a |\contentsline|.
% If the contentsline was actually printed, the code has to
% close the TOCI structure and to update the stack.
%    \begin{macrocode}
\msg_new:nnn {tag}{toc-no-TOCI}{Missing~TOCI~structure~on~toc~stack}

\cs_new_protected:Npn \@@_toc_contentsline_end:nnnn #1 #2 #3 #4 
% #1 level, #2 content (unused), #3 page number (unused) #4 destination (unused)
  {
    \int_compare:nNnF { \use:c{toclevel@#1} } > {\use:c{c@tocdepth}}
      {
        \seq_gpop:NNT \g_@@_toc_stack_seq\l_@@_tmpa_tl
          {
            \str_if_eq:eeTF{\tl_head:N\l_@@_tmpa_tl}{\UseStructureName{toc/item}}
             {
               \tag_struct_end:
             }
             {
               \msg_warning:nn{tag}{toc-no-TOCI}
             }  
          }
      }
  }
%    \end{macrocode}
% \end{macro}
% Now define the tagging plug and assign it to the socket.
% \begin{plugdecl}{default} 
%    \begin{macrocode}
\NewTaggingSocketPlug{toc/contentsline/after}{default}
  {
    \@@_toc_contentsline_end:nnnn #1
  }  
\AssignTaggingSocketPlug{toc/contentsline/after}{default}
%    \end{macrocode}
% \end{plugdecl}
%
% \subsection{Tagging of the content}
% This needs discussion.
%
%    \begin{macrocode}
\AddToHook{contentsline/text/before}[tagpdf]{%
  \tag_struct_begin:n{tag=\UseStructureName{toc/item/text}}%
  \tag_mc_begin:n{}}
\AddToHook{contentsline/text/after}[tagpdf]{%
  \tag_mc_end:}
\AddToHook{contentsline/page/before}[tagpdf]{%
  \tag_mc_begin:n{}}
\AddToHook{contentsline/page/after}[tagpdf]{%
  \tag_mc_end:
  \tag_struct_end:} %Reference
\AddToHook{contentsline/number/before}[tagpdf]{%
  \tag_mc_end:
  \tag_struct_begin:n{tag=\UseStructureName{toc/item/label}}%
  \tag_mc_begin:n{}}
\AddToHook{contentsline/number/after}[tagpdf]{%
  \tag_mc_end:
  \tag_struct_end:
  \tag_mc_begin:n{}}
%    \end{macrocode}
% \begin{plugdecl}{artifact}
%    \begin{macrocode}
\NewTaggingSocketPlug{toc/leaders/before}{artifact}
 {\tag_mc_begin:n{artifact}\nobreak}
\NewTaggingSocketPlug{toc/leaders/after}{artifact}
 {\nobreak\tag_mc_end:}
\AssignTaggingSocketPlug{toc/leaders/before}{artifact}
\AssignTaggingSocketPlug{toc/leaders/after}{artifact}
%    \end{macrocode}
% \end{plugdecl}
%
%    \begin{macrocode}
%</package>
%    \end{macrocode}
% \end{implementation}
