% \iffalse meta-comment
%
%% File: latex-lab-table.dtx (C) Copyright 2023-2024 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 development version of the bundle can be found below
%
%    https://github.com/latex3/latex2e/required/latex-lab
%
% for those people who are interested or want to report an issue.
%
\def\ltlabtbldate{2024-05-25}
\def\ltlabtblversion{0.85i}
%<*driver>
\documentclass{l3doc}
\EnableCrossrefs
\CodelineIndex
\begin{document}
  \DocInput{latex-lab-table.dtx}
\end{document}
%</driver>
%
% \fi
%
% \providecommand\hook[1]{\texttt{#1\DescribeHook[noprint]{#1}}}
% \providecommand\socket[1]{\texttt{#1\DescribeSocket[noprint]{#1}}}
% \providecommand\plug[1]{\texttt{#1\DescribePlug[noprint]{#1}}}
%
% \NewDocElement[printtype=\textit{socket},idxtype=socket,idxgroup=Sockets]{Socket}{socketdecl}
% \NewDocElement[printtype=\textit{hook},idxtype=hook,idxgroup=Hooks]{Hook}{hookdecl}
% \NewDocElement[printtype=\textit{plug},idxtype=plug,idxgroup=Plugs]{Plug}{plugdecl}
%
%
%
%
% \title{The \textsf{latex-lab-table} package\\
%        Changes related to the tagging of tables}
% \author{Frank \& Ulrike, \LaTeX{} Project\thanks{Initial implementation done by Frank Mittelbach}}
% \date{v\ltlabtblversion\ \ltlabtbldate}
%
% \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}
%   The following code implements a first draft for the tagging of
%   tables. It still has a large number of limitations and restrictions!
% \end{abstract}
%
% \tableofcontents
%
% \section{Documentation}
%
% In \LaTeX{} the word \texttt{table} is used as the name of the float environment that can contain
% a data table\footnote{But it does not really have to, you can put
% other material into such environments.} along with a caption and some additional text. The environments for
% actual data tables have various names like \texttt{tabular}, \texttt{tabular*},
% \texttt{tabularx} and \texttt{longtable}---the last should not be used inside a float
% and supports its own caption command.
%
% In this documentation \enquote{table} always means such data tables and not the float
% environment.
%
% Tagging of tables is on one side doesn't look very difficult: one only has to
% surround rows and cells by TR and TH or TD structures.
% But there are difficulties:
% \begin{itemize}
% \item
%   One is that over the years various packages related to tables have
%   been written that all change some of the internals. Inserting the
%   tagging commands and testing all the variants and various nestings
%   is not trivial.
%
% \item
%   The other difficulty is that most of the existing environments to
%   create tables do not know the concept of headers as a semantic
%   structures.
%
% \item
%   Headers are only produced through visual formatting, e.g., by
%   making them bold or by underlying some color. But accessible
%   tables need headers (and the PDF/UA standards requires them) and
%   this means that additional syntax to declare headers (when they
%   can't be guessed) must be developed. This is still an area for
%   research.
% \end{itemize}
%
% Right now, this module therefore does implement only some basic
% support for the tagging of tables. A list of the known limitations
% is shown below.
%
% The module is not loaded automatically (i.e., not yet integrated
% into any \texttt{phase-XX})  and by itself it doesn't activate
% tagging. For experimenting with table tagging it is therefore best
% to load it in combination
% with phase-III in \cs{DocumentMetadata}, i.e.:
%
% \begin{verbatim}
% \DocumentMetadata{testphase={phase-III,table}}
% \end{verbatim}
%
% It will then automatically tag all table environments it already supports with
% the exception of tables in the header and footer of the page (where tagging is disabled).
% Such tables can be nested. 
%
% If a table should not be tagged as table, for example because it is merely used
% as a layout to ensure that the content is properly aligned
% or because it is a not yet (fully) supported table structure, 
% the tagging can be disabled with
% \verb|\tagpdfsetup{table/tagging=false}| or with \verb|\tagpdfsetup{table/tagging=presentation}|%
% \footnote{The key has been renamed. The old name `table-tagging` still works but is 
% deprecated. The value \texttt{presentation} refers to the ARIA role \enquote{presentation}.}
% The first option disables the table tagging code and the content of the tabular
% is then treated more or less like running text. This works ok for simple tables using
% only hmode-cells (l/c/r) with normal text content, but fails if the table uses vmode-cells
% (p/m/b). In such cases the second option works better: it keeps the tagging code active
% but changes the tag names to \texttt{Div} and the grouping structure \texttt{NonStruct}. It also
% (re)sets the \texttt{table/header-rows} key to empty. 
% The key should currently only be used in a group as there is no key (yet) to reset to the default 
% tag names.
%
% Inside cells the automatic tagging of paragraphs is disabled with the exception of
% p/m/b-type cells. 
%
% Rows do not need to contain a full number of \&, missing cells are automatically
% added with an empty TD-structure.
%
% There is some basic support\footnote{This is not meant to be the
% final interface, though.} for headers. With\footnote{The old key name \texttt{table-header-rows} still
% works but is deprecated.}
% \begin{quote}
%   \verb|\tagpdfsetup{table/header-rows={|\meta{list of row numbers}\verb|}|
% \end{quote}
% you can
% declare which (absolute) row numbers should be tagged as header rows.
% It applies to all tables until it is changed to a different list of row numbers or undone by setting
% the key to \meta{empty}.
% A row number can be
% negative, then the counting starts from the end of the table. There is no support
% for header columns yet. In a \env{longtable} the code will currently use the \cs{endhead} or
% \cs{endfirsthead} rows as header if one of these commands has been
% used and in that case the code
% ignores a \texttt{table/header-rows} setting.
%
% You should not insert meaningful text with \verb+!{...}+ or \verb+@{...}+ or \cs{noalign}
% between the rows or columns of the table.
% With pdflatex such text will be unmarked, with lualatex it will be marked as artifact.
% Either case means that it will be currently ignored in the
% structure.\footnote{While it is theoretically possible
% to collect such text and move it into a structure it would require manual markup
% from the author to clarify where this text belongs too.}
% 
% As mentioned below the \pkg{colortbl} doesn't yet work properly with the tagging,
% but once it does, then colors inside the table will probably be
% simply ignored (at least initially). If such a color
% has a semantic meaning (like \enquote{important value}) this meaning will be lost.
% 
% Feedback and problems with the code can be reported at
% \url{https://github.com/latex3/tagging-project} either in form of
% explicit issues or as a \enquote{discussion topic}, whatever works best.
%
%
% \section{Limitations}
%
% \begin{itemize}
% \item The code loads the \pkg{array} package and so does not work without it (that is
% not really a limitation, but can affect existing tables).
%
% \item It supports only a restricted number of tables types. Currently
% \env{tabular}, \env{tabular*}, \env{tabularx}, and \env{longtable}.
%
% \item the \env{array} environment is assumed to be part of math and tagging as a table is disabled for
% it.
%
% \item Row spans are not yet supported (and the \pkg{multirow} package is untested).
%
% \item The \pkg{colortbl} package breaks tagging if there are nested tables. It also breaks
% the filling up of incomplete rows.
% 
% \item The \pkg{tabularray} package use a completed different
% method to create tables and will not be supported by this code.
% 
% \item The \pkg{nicematrix} package is currently incompatible. 
% 
% \item Most other packages related to tables in \LaTeX{} are not yet tested,
% that includes packages that change rules like \pkg{booktabs}, \pkg{hhline},
% \pkg{arydshln}, \pkg{hvdashln}.
%
% \item \env{longtable} currently only works with lualatex.
%  With other engines it breaks as its output
%  routine clashes with the code which closes open MC-chunks at pagebreaks and
%  if this is resolved there will probably be problems with the head and foot boxes
%  (but this can't be tested currently).
%
% \item Not every table should be tagged as a Table structure, often they are
% only used as layout help, e.g. to align authors in a title pages. In such uses
% the tagging of the table must be deactivated with \verb|\tagpdfsetup{table/tagging=false}|
% or \verb|\tagpdfsetup{table/tagging=presentation}|.
%
% \item Only simple header rows are currently supported. Columns and complex headers with
% subheaders will be handled later as that needs some syntax changes. Tables
% with more than one header row are probably not pdf/UA as the headers array in
% the cells is missing.
%
% \item A \pkg{longtable} \cs{caption} is currently simply formatted as a multicolumn and
% not tagged as a \texttt{Caption} structure.
%
% \item The \pkg{caption} package will quite probably break the \pkg{longtable} caption.
%
% \item The setup for \pkg{longtable} requires lots of patches to internal \pkg{longtable}
% commands and so can easily break if other packages try to patch \pkg{longtable} too.
% 
% \item The \env{longtable} environment supports footnotes in p-type columns, but it hasn't been
% tested yet if this works also with the tagging code. 
% 
% \item Vertical boxes (\cs{parbox}, \texttt{minipage}, \ldots) inside cells can be problematic. 
%
% \item The code is quite noisy and fills the log with lots of
%   messages.\footnote{Helpful for us at this stage.}
% \end{itemize}
%
%
%
% \section{Introduction}
%
%
% \section{Technical details and problems}
%
%  The implementation has to take care of various details.
%
%  \subsection{TODOs}
%
%  \begin{itemize}
%  \item
%     Test \texttt{array-006-longtable.lvt} and
%     \texttt{array-007-longtable.lvt} have errors with pdftex (para
%    tagging)
%
%  \item
%     Instead of before/after hooks we should add sockets directly
%     into the code.
%
%  \item Debugging code and messages must be improved.
%
%  \item Cells need an \texttt{Headers} array.
%
%  \item Row spans should be supported (but perhaps need syntax support)
%
%  \item Longtable captions should be properly supported.
%
%  \item Handle p-cells better. para-tagging should probably be enabled,
%  but Part can't be a child of TD, so this should probably be changed to Div here.
%  Also there is a stray MC somewhere.
%
%  \item More packages must be tested.
%  \end{itemize}
%
%
% \section{Implementation}
%    \begin{macrocode}
%<@@=tbl>
%<*package>
%    \end{macrocode}
%    \begin{macrocode}
\ProvidesExplPackage {latex-lab-testphase-table} {\ltlabtbldate} {\ltlabtblversion}
  {Code related to the tagging of tables}
%    \end{macrocode}
% This builds on \pkg{array} so we load it by default:
%    \begin{macrocode}
\RequirePackage{array}
%    \end{macrocode}
%
%
% \subsection{Variables}
% \begin{macro}
%  {
%    \l_@@_celltag_tl
%    \l_@@_pcelltag_tl
%   ,\l_@@_rowtag_tl
%   ,\l_@@_table_tl
%   ,\l_@@_cellattribute_tl
%   ,\l_@@_rowattribute_tl
%   ,\l_@@_tmpa_clist
%   ,\l_@@_tmpa_seq
%   ,\l_@@_tmpa_tl
%  }
%    This is for the celltag, e.g. TD or TH:
%    \begin{macrocode}
\tl_new:N  \l_@@_celltag_tl
\tl_set:Nn \l_@@_celltag_tl {TD}
\tl_new:N  \l_@@_pcelltag_tl
\tl_set:Nn \l_@@_pcelltag_tl {TD}
%    \end{macrocode}
%    For the rowtag, probably always TR:
%    \begin{macrocode}
\tl_new:N  \l_@@_rowtag_tl
\tl_set:Nn \l_@@_rowtag_tl {TR}
%    \end{macrocode}
%    For the tabletag, probably always Table:
%    \begin{macrocode}
\tl_new:N  \l_@@_tabletag_tl
\tl_set:Nn \l_@@_tabletag_tl {Table}
%    \end{macrocode}
%    And here cell and row attributes:
%    \begin{macrocode}
\tl_new:N  \l_@@_cellattribute_tl
\tl_set:Nn \l_@@_cellattribute_tl {}
\tl_new:N  \l_@@_rowattribute_tl
\tl_set:Nn \l_@@_rowattribute_tl {}
%    \end{macrocode}
% Temp variables
%    \begin{macrocode}
\clist_new:N \l_@@_tmpa_clist
\tl_new:N \l_@@_tmpa_tl
%    \end{macrocode}
% \end{macro}
%
% \subsection{Tagging support sockets}
%
%
% This are the standard plugs for tagging of cells and rows.
%
% \begin{plugdecl}{TD}
%    \begin{macrocode}
\NewSocketPlug{tagsupport/tbl/cell/begin}{TD}
  {
%    \end{macrocode}
%    Next line was previously outside of the plug, so if we want to execute it
%    always even if the noop plug is in force this needs a different solution.
%    \begin{macrocode}
    \@@_show_curr_cell_data:
    \tag_struct_begin:n
      {
        tag             =\l_@@_celltag_tl,
        attribute-class =\l_@@_cellattribute_tl
      }
    \seq_gput_right:Ne \g_@@_struct_cur_seq { \tag_get:n {struct_num} }
%    \end{macrocode}
%   we store the cells of multicolumns as negative number. This allow to skip them
%   or to use them as needed.
%    \begin{macrocode}
    \int_step_inline:nn { \g_@@_span_tl - 1 }
      {
        \seq_gput_right:Ne \g_@@_struct_cur_seq { -\tag_get:n {struct_num} }
      }
    \tag_mc_begin:n{}
  }
%    \end{macrocode}
% \end{plugdecl}
%
% \begin{plugdecl}{TD}
%    \begin{macrocode}
\NewSocketPlug{tagsupport/tbl/cell/end}{TD}
  {
    \tag_mc_end:
    \tag_struct_end:
  }
%    \end{macrocode}
% \end{plugdecl}
% 
%    In p-columns we need a slightly different plug which reactivates the
%    paragraph tagging.
% \begin{plugdecl}{TDpbox}
%    \begin{macrocode}
\NewSocketPlug{tagsupport/tbl/pcell/begin}{TDpbox}
  {
    \@@_show_curr_cell_data:
    \tag_struct_begin:n
      {
        tag             =\l__tbl_pcelltag_tl,
        attribute-class =\l__tbl_cellattribute_tl
      }
    \seq_gput_right:Ne \g__tbl_struct_cur_seq { \tag_get:n {struct_num} }
    \int_step_inline:nn { \g__tbl_span_tl - 1 }
      {
        \seq_gput_right:Ne \g__tbl_struct_cur_seq { -\tag_get:n {struct_num} }
      }
    \tagpdfparaOn
    \tl_set:Nn \l__tag_para_main_tag_tl {Div}
  }
%    \end{macrocode}
% \end{plugdecl}
% 
% \begin{plugdecl}{TDpbox}
%    \begin{macrocode}
\NewSocketPlug{tagsupport/tbl/pcell/end}{TDpbox}
  {
    \tag_struct_end:
  }  
%    \end{macrocode}
% \end{plugdecl}
%
% \begin{plugdecl}{TR}
%    \begin{macrocode}
\NewSocketPlug{tagsupport/tbl/row/begin}{TR}
  {
    \seq_gclear:N \g_@@_struct_cur_seq
    \tag_struct_begin:n
     {
       tag            =\l_@@_rowtag_tl,
       attribute-class=\l_@@_rowattribute_tl
     }
   \seq_gput_right:Ne \g_@@_struct_rows_seq { \tag_get:n {struct_num} }
  }
%    \end{macrocode}
% \end{plugdecl}
%
% \begin{plugdecl}{TR}
%    \begin{macrocode}
\NewSocketPlug{tagsupport/tbl/row/end}{TR}
  {
    \tag_if_active:T
      {
    \@@_add_missing_cells:
    \seq_gput_right:Ne \g_@@_struct_cells_seq
      {
        \seq_use:Nn \g_@@_struct_cur_seq {,}
      }
    \int_compare:nNnTF { \g_@@_row_int } =
                       { \seq_count:N \g_@@_struct_cells_seq }
      {
        \@@_trace:n
          {==>~
            stucture~stored~for~row~\int_use:N\g_@@_row_int :~
            \seq_use:Nn \g_@@_struct_cur_seq {,}
          }
      }
      { \ERRORtbl/row } % should not happen ...
    \tag_struct_end:
  }
  }
%    \end{macrocode}
% \end{plugdecl}
%
% And the plugs for the table as whole. The code can be different for
% normal tables which can also be used inline and nested and
% \enquote{vmode} tables like longtable.
%
% \begin{plugdecl}{Table}
%    Inside a table we currently only disable paratagging. We assume
%    that these sockets are in an environment group, so there is no
%    need to reenable paratagging.
%    \begin{macrocode}
\NewSocketPlug{tagsupport/tbl/init}{Table}
  {
    \tag_if_active:T
      {
        \bool_set_false:N \l__tag_para_bool
%    \end{macrocode}
%    We also initialize the structure data variables a this point.
%    \begin{macrocode}
        \@@_init_struct_data:
      }
  }
%    \end{macrocode}
% \end{plugdecl}
%
%
% \begin{plugdecl}{Table}
%  This plug will fine tune the structure.
%    \begin{macrocode}
\NewSocketPlug{tagsupport/tbl/finalize}{Table}
  {
    \@@_set_header_rows:
%    \end{macrocode}
%    Similarly, we restore the outer values of the structure data when
%    we leave the table.
%    \begin{macrocode}
    \@@_restore_struct_data:
  }
%    \end{macrocode}
% \end{plugdecl}
%
%
%
%
% \begin{plugdecl}{Table}
%  This plug will initialize the structure in longtable.
%    \begin{macrocode}
\NewSocketPlug{tagsupport/tbl/longtable/init}{Table}
 {
   \seq_gclear:N\g_@@_struct_rows_seq
   \seq_gclear:N\g_@@_struct_cells_seq
   \seq_gclear:N\g_@@_struct_cur_seq
   \seq_gclear:N\g_@@_LT@firsthead_rows_seq
   \seq_gclear:N\g_@@_LT@head_rows_seq
   \seq_gclear:N\g_@@_LT@lastfoot_rows_seq
   \seq_gclear:N\g_@@_LT@foot_rows_seq
 }
%    \end{macrocode}
% \end{plugdecl}
%
%


% \begin{plugdecl}{Table}
%  This plug will fine tune the structure in longtable.
%    \begin{macrocode}
\NewSocketPlug{tagsupport/tbl/longtable/finalize}{Table}
  {
%    \end{macrocode}
%    If neither \cs{endhead} nor \cs{endfirsthead} has been used
%    we use the standard header command:
%    \begin{macrocode}
     \bool_lazy_and:nnTF
       { \seq_if_empty_p:N \g_@@_LT@head_rows_seq  }
       { \seq_if_empty_p:N \g_@@_LT@firsthead_rows_seq }
       { \@@_set_header_rows: }
%    \end{macrocode}
%    Otherwise, if firsthead has not been used we use head. For this
%    we simple retrieve the row numbers and then call the header
%    command.
%    \begin{macrocode}
       {
         \seq_if_empty:NTF \g_@@_LT@firsthead_rows_seq
           {
             \clist_set:Ne \l_@@_header_rows_clist
               {\seq_use:Nn \g_@@_LT@head_rows_seq {,}}
             \@@_set_header_rows:
           }
%    \end{macrocode}
%    In the other case we use firsthead.
%    \begin{macrocode}
           {
             \clist_set:Ne \l_@@_header_rows_clist
               { \seq_use:Nn \g_@@_LT@firsthead_rows_seq {,} }
             \@@_set_header_rows:
%    \end{macrocode}
%    Additionally we have to remove the head to avoid duplication. The
%    one option here is to remove the rows from the kid sequence of
%    the table (which will lead to orphaned structure elements), the
%    other to make them artifact.  For now we use the first option for
%    pdf 1.7 and the second for pdf 2.0.
%    \begin{macrocode}
             \pdf_version_compare:NnTF < {2.0}
              {
                \seq_map_inline:Nn \g_@@_LT@head_rows_seq
                  {
                    \seq_gset_item:cnn
                      {g__tag_struct_kids_ \g_@@_struct_table_tl _seq}
                      { ##1 }
                      {}
%    \end{macrocode}
%    Not sure if needed, but if needed we can remove also the P tag.
%    This is currently disabled as it produce warnings.  TODO: This
%    needs also a tagpdf command which takes care of debug code.
%    \begin{macrocode}
                    \tl_set:Ne \l_@@_tmpa_tl
                      { \seq_item:Nn\g_@@_struct_rows_seq {##1} }
                    \prop_if_exist:cT
                      { g__tag_struct_ \l_@@_tmpa_tl _prop }
                      {
                        %\prop_gremove:cn {g__tag_struct_ \l_@@_tmpa_tl _prop} {P}
                      }
                  }
              }
              {
                \seq_map_inline:Nn \g_@@_LT@head_rows_seq
                  {
                    \tl_set:Ne \l_@@_tmpa_tl
                      { \seq_item:Nn\g_@@_struct_rows_seq {##1} }
                    \prop_if_exist:cT
                      { g__tag_struct_ \l_@@_tmpa_tl _prop }
                      {
                        \exp_args:No \__tag_struct_prop_gput:nnn {\l_@@_tmpa_tl} {S}{/Artifact}
                      }
                   }
               }
            }
       }
%    \end{macrocode}
%    The foot is handled similar, the difference is
%    that we have to move it to the end one of them
%    is not empty, but do nothing if they aren't there.
%    \begin{macrocode}
     \bool_lazy_and:nnF
       { \seq_if_empty_p:N \g_@@_LT@foot_rows_seq  }
       { \seq_if_empty_p:N \g_@@_LT@lastfoot_rows_seq }
       {
%    \end{macrocode}
%    If lastfoot is empty move foot to the end.
%    \begin{macrocode}
         \seq_if_empty:NTF \g_@@_LT@lastfoot_rows_seq
           {
             \seq_map_inline:Nn \g_@@_LT@foot_rows_seq
               {
                 \tl_set:Ne \l_@@_tmpa_tl
                   {
                     \seq_item:cn
                       {g__tag_struct_kids_ \g_@@_struct_table_tl _seq}
                       {##1}
                   }
                 \seq_gset_item:cnn
                   {g__tag_struct_kids_ \g_@@_struct_table_tl _seq}
                   { ##1 }
                   {}
                 \seq_gput_right:cV
                   {g__tag_struct_kids_ \g_@@_struct_table_tl _seq}
                   \l_@@_tmpa_tl
               }
           }
%    \end{macrocode}
%    If lastfoot is not empty we move that.
%    \begin{macrocode}
           {
             \seq_map_inline:Nn \g_@@_LT@lastfoot_rows_seq
               {
                 \tl_set:Ne \l_@@_tmpa_tl
                   {
                     \seq_item:cn
                       {g__tag_struct_kids_ \g_@@_struct_table_tl _seq}
                       {##1}
                   }
                 \seq_gset_item:cnn
                   {g__tag_struct_kids_ \g_@@_struct_table_tl _seq}
                   { ##1 }
                   {}
                 \seq_gput_right:cV
                   {g__tag_struct_kids_ \g_@@_struct_table_tl _seq}
                   \l_@@_tmpa_tl
               }
%    \end{macrocode}
%    and we hide foot
%    \begin{macrocode}
            \pdf_version_compare:NnTF < {2.0}
              {
                \seq_map_inline:Nn \g_@@_LT@foot_rows_seq
                  {
                    \seq_gset_item:cnn
                      {g__tag_struct_kids_ \g_@@_struct_table_tl _seq}
                      { ##1 }
                      {}
%    \end{macrocode}
%    Not sure if needed, but if needed we can remove also the P tag.
%    This is currently disabled as it produce warnings.
%    TODO: This needs also
%    a tagpdf command which takes care of debug code.
%    \begin{macrocode}
                    \tl_set:Ne \l_@@_tmpa_tl
                      { \seq_item:Nn\g_@@_struct_rows_seq {##1} }
                    \prop_if_exist:cT
                      { g__tag_struct_ \l_@@_tmpa_tl _prop }
                      {
                        %\prop_gremove:cn {g__tag_struct_ \l_@@_tmpa_tl _prop} {P}
                      }
                  }
              }
              {
                \seq_map_inline:Nn \g_@@_LT@foot_rows_seq
                  {
                    \tl_set:Ne \l_@@_tmpa_tl
                      { \seq_item:Nn\g_@@_struct_rows_seq {##1} }
                    \prop_if_exist:cT
                      { g__tag_struct_ \l_@@_tmpa_tl _prop }
                      {
                        \exp_args:No\__tag_struct_prop_gput:nnn {\l_@@_tmpa_tl} {S}{/Artifact}
                      }
                   }
               }
           }
       }
  }
%    \end{macrocode}
% \end{plugdecl}
%
%
%
% \begin{plugdecl}{Table}
%  
%    We must avoid that the reuse of the header foot box leads to duplicated
%    content, thus reset attribute of the box:
%    \begin{macrocode}
\NewSocketPlug{tagsupport/tbl/longtable/head}{Table}
 {
  \tagmcbegin{artifact}
   \tag_mc_reset_box:N\LT@head
  \tagmcend
 }
%    \end{macrocode}
% \end{plugdecl}
%
% \begin{plugdecl}{Table}
%    \begin{macrocode}
\NewSocketPlug{tagsupport/tbl/longtable/foot}{Table}
 {
   \tagmcbegin{artifact}
   \tag_mc_reset_box:N \LT@foot
   \tagmcend
 }
%    \end{macrocode}
% \end{plugdecl}
%
%
%
%
% \begin{plugdecl}{Table}
%    \begin{macrocode}
\NewSocketPlug{tagsupport/tbl/hmode/begin}{Table}
  {
    \tag_mc_end_push:
%    \end{macrocode}
%    Close the P-chunk. This assumes that para-tagging is active.
%    For nested tables that is not necessarly true, so we test for it.
%    \begin{macrocode}
     \bool_lazy_and:nnT
      { \bool_if_exist_p:N \l__tag_para_bool } { \l__tag_para_bool }
      { \tag_struct_end:n { text } }
    \tag_struct_begin:n {tag=\l_@@_tabletag_tl}
    \tl_gset:Ne \g_@@_struct_table_tl { \tag_get:n {struct_num} }
  }
%    \end{macrocode}
% \end{plugdecl}
%
% \begin{plugdecl}{Table}
%    \begin{macrocode}
\NewSocketPlug{tagsupport/tbl/hmode/end}{Table}
  {
    \tag_struct_end:
%    \end{macrocode}
% reopen the P-chunk. This assumes that para-tagging is active.
% For nested tables that is not necessarly true, so we test for it.
%    \begin{macrocode}
    \bool_lazy_and:nnT
      { \bool_if_exist_p:N \l__tag_para_bool } { \l__tag_para_bool }
      { \tag_struct_begin:n { tag=\l__tag_para_tag_tl } }
    \tag_mc_begin_pop:n{}
  }
%    \end{macrocode}
% \end{plugdecl}
%
% \begin{plugdecl}{Table}
%    \begin{macrocode}
\NewSocketPlug{tagsupport/tbl/vmode/begin}{Table}
  {
    \tag_struct_begin:n {tag=\l_@@_tabletag_tl}
    \tl_gset:Ne \g_@@_struct_table_tl { \tag_get:n {struct_num} }
  }
%    \end{macrocode}
% \end{plugdecl}
%
% \begin{plugdecl}{Table}
%    \begin{macrocode}
\NewSocketPlug{tagsupport/tbl/vmode/end}{Table}
  {
    \tag_struct_end:
    \par
  }
%    \end{macrocode}
% \end{plugdecl}
%
%
% \begin{plugdecl}{code}
%    This socket takes a number, checks if is larger than one,
%    checks if the colspan attribute already exists (we can't predefine an
%    arbitrary number), and updates \cs{l_@@_cellattribute_tl}.
%    \begin{macrocode}
\NewSocketPlug{tagsupport/tbl/colspan}{code}
  {
    \int_compare:nNnT {#1}>{1}
     {
       \prop_get:NeNF \g__tag_attr_entries_prop
          {colspan-\int_eval:n{#1}}
          \l_@@_tmpa_tl
          {
            \__tag_attr_new_entry:ee
              {colspan-\int_eval:n{#1}}
              {/O /Table /ColSpan~\int_eval:n{#1}}
          }
       \tl_set:Ne \l_@@_cellattribute_tl
          {colspan-\int_eval:n{#1}}
     }
  }
%    \end{macrocode}
%    TODO: move to tagpdf
%    \begin{macrocode}
\tag_if_active:T
  { \cs_generate_variant:Nn \__tag_attr_new_entry:nn {ee} }

%    \end{macrocode}
% \end{plugdecl}
%
%
%
%\subsection{Environments}
%
% Currently we support only tabular, tabular*, tabularx and longtable
% (and possibly environments build directly on top of them). 
%
% The \env{array} environment is math. So we disable table tagging for now.
% We use the command hook to catch also cases where \cs{array} is used directly
% in other environments like matrix environments.
% Perhaps table tagging should be disable for math generally, but then
% we have to handle text insertions. 
%    \begin{macrocode}
\AddToHook{cmd/array/before}{\__tag_tbl_disable:}
%    \end{macrocode}
%
%
% \subsection{Interfaces to tagging}
%
% \subsubsection{Tagging helper commands}
%
%
% \subsubsection{Disabling/enabling}
%
%  For now we have only the option true/false but this will probably be extended
%  to allow different setups like first row header etc.

%  \begin{macro}{\__tag_tbl_disable:}
%
%    \begin{macrocode}
\cs_new_protected:Npn \__tag_tbl_disable:
 {
   \AssignSocketPlug{tagsupport/tbl/cell/begin}{noop}
   \AssignSocketPlug{tagsupport/tbl/cell/end}{noop}
   \AssignSocketPlug{tagsupport/tbl/pcell/begin}{noop}
   \AssignSocketPlug{tagsupport/tbl/pcell/end}{noop}
   \AssignSocketPlug{tagsupport/tbl/row/begin}{noop}
   \AssignSocketPlug{tagsupport/tbl/row/end}{noop}
   \AssignSocketPlug{tagsupport/tbl/init}{noop}
   \AssignSocketPlug{tagsupport/tbl/finalize}{noop}
   \AssignSocketPlug{tagsupport/tbl/longtable/init}{noop}
   \AssignSocketPlug{tagsupport/tbl/longtable/head}{noop}
   \AssignSocketPlug{tagsupport/tbl/longtable/foot}{noop}
   \AssignSocketPlug{tagsupport/tbl/longtable/finalize}{noop}
   \AssignSocketPlug{tagsupport/tbl/hmode/begin}{noop}
   \AssignSocketPlug{tagsupport/tbl/hmode/end}{noop}
   \AssignSocketPlug{tagsupport/tbl/vmode/begin}{noop}
   \AssignSocketPlug{tagsupport/tbl/vmode/end}{noop}
   \AssignSocketPlug{tagsupport/tbl/colspan}{noop}
 }
%    \end{macrocode}
%  \end{macro}


%  \begin{macro}{\__tag_tbl_enable:}
%
%    \begin{macrocode}
\cs_new_protected:Npn \__tag_tbl_enable:
 {
   \AssignSocketPlug{tagsupport/tbl/cell/begin}{TD}
   \AssignSocketPlug{tagsupport/tbl/cell/end}{TD}
   \AssignSocketPlug{tagsupport/tbl/pcell/begin}{TDpbox}
   \AssignSocketPlug{tagsupport/tbl/pcell/end}{TDpbox}
   \AssignSocketPlug{tagsupport/tbl/row/begin}{TR}
   \AssignSocketPlug{tagsupport/tbl/row/end}{TR}
   \AssignSocketPlug{tagsupport/tbl/init}{Table}
   \AssignSocketPlug{tagsupport/tbl/finalize}{Table}
   \AssignSocketPlug{tagsupport/tbl/longtable/head}{Table}
   \AssignSocketPlug{tagsupport/tbl/longtable/foot}{Table}
   \AssignSocketPlug{tagsupport/tbl/longtable/init}{Table}
   \AssignSocketPlug{tagsupport/tbl/longtable/finalize}{Table}
   \AssignSocketPlug{tagsupport/tbl/hmode/begin}{Table}
   \AssignSocketPlug{tagsupport/tbl/hmode/end}{Table}
   \AssignSocketPlug{tagsupport/tbl/vmode/begin}{Table}
   \AssignSocketPlug{tagsupport/tbl/vmode/end}{Table}
   \AssignSocketPlug{tagsupport/tbl/colspan}{code}
 }
%    \end{macrocode}
%  \end{macro}

%    \begin{macrocode}
% See tagpdfsetup-keys.md in tagpdf/doc for the naming scheme.
\keys_define:nn { __tag / setup }
  {
    table/tagging .choices:nn = { true, on }
      { \__tag_tbl_enable: },
    table/tagging .choices:nn = { false, off }
      { \__tag_tbl_disable: },
    table/tagging .choice:,
    table/tagging / presentation .code:n = 
     {
       \__tag_tbl_enable:
       \tl_set:Nn\l_@@_rowtag_tl   {NonStruct}
       \tl_set:Nn\l_@@_pcelltag_tl {NonStruct}
       \tl_set:Nn\l_@@_celltag_tl  {text}
       \tl_set:Nn\l_@@_tabletag_tl {Div} 
       \clist_clear:N \l_@@_header_rows_clist 
     },  
    table/tagging .default:n = true,
    table/tagging .initial:n = true
  }
%    \end{macrocode}
% This are the old key names kept for now for 
% compability. They will got at some time.
%    \begin{macrocode}
\keys_define:nn { __tag / setup }
  {
    table-tagging .meta:n = {table/tagging={#1}}
  }
%    \end{macrocode}
%
%
% \subsubsection{Header support}
%
% Accessible table must have header cells declaring the meaning of the
% data in a row or column. To allow a data cell to find it header cell(s)
% a number of things must be done:
% \begin{itemize}
% \item every cell meant as a header should use the tag \texttt{TH}.
%
% \item header cells should have a \texttt{Scope} attribute with the
%   value \texttt{Column}, \texttt{Row} or \texttt{Both}.  This is not
%   needed in the first row or column of a table.
% 
% \item For more complex cases both \texttt{TD} and \texttt{TH} cell
%   can contain a \texttt{Headers} attribute, that is an array of
%   \texttt{ID}s of \texttt{TH} cell.
% \end{itemize}
%
% For now we support only header rows.
%
% At first we define attributes for the three standard cases:
% We delay to begin document
% as we can't know if tagpdf is already loaded.
%    \begin{macrocode}
\AddToHook{begindocument}
  {
   \tag_if_active:T
     {
       \tagpdfsetup
         {
           role/new-attribute =
            {TH-col}{/O /Table /Scope /Column},
           role/new-attribute =
             {TH-row}{/O /Table /Scope /Row},
           role/new-attribute =
             {TH-both}{/O /Table /Scope /Both},
         }
%    \end{macrocode}
%
% And we put all three into the class map (perhaps the next tagpdf
% should do that directly with role/new-attribute):
%
%    \begin{macrocode}
         \seq_gput_left:Ne\g__tag_attr_class_used_seq
           {\pdf_name_from_unicode_e:n{TH-col}}
         \seq_gput_left:Ne\g__tag_attr_class_used_seq
           {\pdf_name_from_unicode_e:n{TH-row}}
         \seq_gput_left:Ne\g__tag_attr_class_used_seq
           {\pdf_name_from_unicode_e:n{TH-both}}
     }
  }

%    \end{macrocode}
%
% \begin{variable}{\l_@@_header_rows_clist}
%    This holds the numbers of the header rows. Negative numbers are
%    possible and count from the last column backwards. 
%    \begin{macrocode}
\clist_new:N  \l_@@_header_rows_clist
%    \end{macrocode}
% \end{variable}
%
% \begin{macro}{\@@_set_header_rows:}
%    \begin{macrocode}
\cs_new_protected:Npn \@@_set_header_rows:
  {
    \clist_map_inline:Nn \l_@@_header_rows_clist
      {
        \clist_set:Ne\l_@@_tmpa_clist
          {
            \seq_item:Nn \g_@@_struct_cells_seq {##1}
          }
        \clist_map_inline:Nn \l_@@_tmpa_clist
          {
%    \end{macrocode}
%    We can have negative numbers in the list from the multicolumn.
%    \begin{macrocode}
            \prop_if_exist:cT { g__tag_struct_####1_prop }
              {
                \__tag_struct_prop_gput:nnn{ ####1 }{S}{/TH}
%    \end{macrocode}
%    This need refinement once row headers (and perhaps other attributes)
%    are used too, but for now it should be ok.
%    \begin{macrocode}
                \prop_get:cnNTF
                  { g__tag_struct_####1_prop }
                  { C }
                  \l_@@_tmpa_tl
                  {\__tag_struct_prop_gput:nne{ ####1 }{C}{[/TH-col~\l_@@_tmpa_tl]} }
                  {\__tag_struct_prop_gput:nnn{ ####1 }{C}{/TH-col}}
             }
          }
      }
  }
%    \end{macrocode}
% \end{macro}
%  
% And some key support:
% (See tagpdfsetup-keys.md for the naming scheme.) 
%    \begin{macrocode}
\keys_define:nn { __tag / setup }
  {
    table/header-rows .clist_set:N = \l_@@_header_rows_clist,
%    \end{macrocode}
% obsolete older name:
%    \begin{macrocode}
    table-header-rows .meta:n = {table/header-rows={#1}}
  }
%    \end{macrocode}
%
%
%
% \subsection{Misc stuff}
%
%  \begin{macro}{\@@_show_curr_cell_data:}
%    Show the row/column index and span count for current table cell
%    for debugging.
%    \begin{macrocode}
\cs_new_protected:Npn \@@_show_curr_cell_data: {
   \@@_trace:n { ==>~ current~cell~data:~
                 \int_use:N \g_@@_row_int ,
                 \int_use:N \g_@@_col_int ,
                 \g_@@_span_tl
               }
}
%    \end{macrocode}
%  \end{macro}
%
%
%
%  \begin{macro}{\@@_add_missing_cells:}
%    The storing and use of the number of missing cells must happen at
%    different places as the testing happens at the end of the last
%    cell of a row, but still inside that cell, so we use two
%    commands. The one adding is used in the row/end socket.
%    \begin{macrocode}
\NewSocket{tbl/celldata/missingcount}{1}
\NewSocketPlug{tbl/celldata/missingcount}{code}{\tbl_count_missing_cells:n{#1}}
\AssignSocketPlug{tbl/celldata/missingcount}{code}

%    \end{macrocode}
%
%    \begin{macrocode}
\cs_new:Npn \@@_add_missing_cells:
  {
%    \end{macrocode}
%    The TD-socket messages are issued after the message about the
%    end-row socket, but the structure is ok, so better issue a
%    message for now to avoid confusion:
%    \begin{macrocode}
    \int_compare:nNnT \g_@@_missing_cells_int > 0
      {
       \@@_trace:n {==>~
         ~Inserting~\int_use:N \g_@@_missing_cells_int \space
          additional~cell(s)~into~previous~row:}
       \int_step_inline:nn { \g_@@_missing_cells_int }
           {
             \UseTaggingSocket{tbl/cell/begin}
             \UseTaggingSocket{tbl/cell/end}
           }
      }
  }
%    \end{macrocode}
%  \end{macro}
%
%
%
%
%
%
% \begin{macro}
%   {
%     \g_@@_struct_table_tl, \l_@@_saved_struct_table_tl,
%     \g_@@_struct_rows_seq,\l_@@_saved_struct_rows_seq,
%     \g_@@_struct_cells_seq,\l_@@_saved_struct_cells_seq,
%     \g_@@_struct_cur_seq,\l_@@_saved_struct_cur_seq
%   }
%    We need to store the structure numbers for the fine tuning in the
%    finalize socket.  For now we use a rather simple system: A
%    sequence that hold the numbers for the row structures, and one
%    that holds comma lists for the cells.
%
%    \cs{g_@@_struct_table_tl} will hold the structure number of the
%    table, \cs{g_@@_struct_rows_seq} will hold at index i the
%    structure number of row i, \cs{g_@@_struct_cells_seq} will hold
%    at index i a comma list of the cell structure numbers of row i.
%    \cs{g_@@_struct_cur_seq} is used as a temporary store for the
%    cell structures of the current row.  We need also local version
%    to store and restore the values.
%
%    \begin{macrocode}
\tl_new:N  \g_@@_struct_table_tl
\tl_new:N  \l_@@_saved_struct_table_tl
\seq_new:N \g_@@_struct_rows_seq
\seq_new:N \l_@@_saved_struct_rows_seq
\seq_new:N \g_@@_struct_cells_seq
\seq_new:N \l_@@_saved_struct_cells_seq
\seq_new:N \g_@@_struct_cur_seq
\seq_new:N \l_@@_saved_struct_cur_seq
%    \end{macrocode}
% \end{macro}



%  \begin{macro}{\@@_init_struct_data:}
%    Save the global structure data variables locally so the we can
%    restore them when the table ends (important in case of nested
%    tables).
%
%    This is also the right point to initialize
%    them. \cs{g_@@_struct_table_tl} is set elsewhere.
%    \begin{macrocode}
\cs_new_protected:Npn \@@_init_struct_data: {
  \tl_set_eq:NN  \l_@@_saved_struct_table_tl \g_@@_struct_table_tl
  \seq_set_eq:NN \l_@@_saved_struct_rows_seq \g_@@_struct_rows_seq
  \seq_set_eq:NN \l_@@_saved_struct_cells_seq \g_@@_struct_cells_seq
  \seq_set_eq:NN \l_@@_saved_struct_cur_seq \g_@@_struct_cur_seq
%
  \seq_gclear:N\g_@@_struct_rows_seq
  \seq_gclear:N\g_@@_struct_cells_seq
  \seq_gclear:N\g_@@_struct_cur_seq
}
%    \end{macrocode}
%  \end{macro}




%  \begin{macro}{\@@_restore_struct_data:}
%    
%    \begin{macrocode}
\cs_new_protected:Npn \@@_restore_struct_data: {
  \tl_gset_eq:NN  \g_@@_struct_table_tl \l_@@_saved_struct_table_tl
  \seq_gset_eq:NN \g_@@_struct_rows_seq \l_@@_saved_struct_rows_seq
  \seq_gset_eq:NN \g_@@_struct_cells_seq\l_@@_saved_struct_cells_seq
  \seq_gset_eq:NN \g_@@_struct_cur_seq  \l_@@_saved_struct_cur_seq
}
%    \end{macrocode}
%  \end{macro}


%
%
% \subsection{longtable}
%
%
%
%
%
% \begin{macro}{\@@_patch_\LT@makecaption}
%     This patch is quite similar to the one for LaTeX's \cs{@makecaption}
%     we also have to change the parbox sockets.
%    \begin{macrocode}
\def\@@_patch_LT@makecaption#1#2#3{%
  \LT@mcol\LT@cols c{%
  % test can go after merge
    \str_if_exist:cT { l__socket_tagsupport/parbox/before_plug_str }
      {
        \AssignSocketPlug{tagsupport/parbox/before}{noop}
        \AssignSocketPlug{tagsupport/parbox/after}{noop}
      }
    \hbox to\z@{\hss\parbox[t]\LTcapwidth{%
    \reset@font
    \tag_stop:n{caption}
    \sbox\@tempboxa{#1{#2:~}#3}%
    \tag_start:n{caption}
    \ifdim\wd\@tempboxa>\hsize
      #1{#2:~}#3%
    \else
      \hbox to\hsize{\hfil#1{#2:~}#3\hfil}%
    \fi
    \endgraf\vskip\baselineskip}%
  \hss}}}
%    \end{macrocode}
% \end{macro}
%
%
%
% Overwrite the longtable definition. That will probably break somewhere as
% they are various package which patch too.
%    \begin{macrocode}
\AddToHook{package/longtable/after}
  {
   \cs_set_eq:NN \LT@makecaption\@@_patch_LT@makecaption
  }
%    \end{macrocode}


%    \begin{macrocode}
%</package>
%    \end{macrocode}
%
%
%    \begin{macrocode}
%<*latex-lab>
\ProvidesFile{table-latex-lab-testphase.ltx}
        [\ltlabtbldate\space v\ltlabtblversion\space latex-lab wrapper table]
\RequirePackage{latex-lab-testphase-table}
%</latex-lab>
%    \end{macrocode}

%    \begin{macrocode}
%<*latex-lab-alias>
\ProvidesFile{tabular-latex-lab-testphase.ltx}
        [\ltlabtbldate\space v\ltlabtblversion\space latex-lab wrapper tabular]
\RequirePackage{latex-lab-testphase-table}
%</latex-lab-alias>
%    \end{macrocode}
