% \iffalse meta-comment
%
%% File: latex-lab-table.dtx (C) Copyright 2023 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{2023-10-30}
\def\ltlabtblversion{0.85d}
%<*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
% to produce a layout or because it is a not yet (fully) supported table structure, 
% the tagging can be disabled with
%  \verb|\tagpdfsetup{table-tagging=false}|.
%
% 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
% \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}|.
%
% \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 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_@@_rowtag_tl
%   ,\l_@@_cellattribute_tl
%   ,\l_@@_rowattribute_tl
%   ,\g_@@_missingcells_int
%   ,\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}
%    \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}
%    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}
% This will contain the number of missing cells used:
%    \begin{macrocode}
\int_new:N \g_@@_missing_cells_int
%    \end{macrocode}
% Temp variables
%    \begin{macrocode}
\clist_new:N \l_@@_tmpa_clist
\seq_new:N \l_@@_tmpa_seq
\tl_new:N \l_@@_tmpa_tl
%    \end{macrocode}
% \end{macro}
%
% \subsection{Sockets}
%
% The code uses a number of sockets to inject the tagging
% commands. These can be easily set to a noop-plug in case the
% automated tagging is not wanted At first sockets for the begin and
% end of cells and rows
%
% \begin{socketdecl}{tagsupport/tblcell/begin,
%                    tagsupport/tblcell/end,
%                    tagsupport/tblrow/begin,
%                    tagsupport/tblrow/end,
%                   }
%    \begin{macrocode}
\NewSocket{tagsupport/tblcell/begin}{0}
\NewSocket{tagsupport/tblcell/end}{0}
\NewSocket{tagsupport/tblrow/begin}{0}
\NewSocket{tagsupport/tblrow/end}{0}
%    \end{macrocode}
% \end{socketdecl}
%
% \begin{socketdecl}{tagsupport/tbl/init}
%    This socket should be at the begin of the table, inside a group.
%    It is meant for settings like disabling paratagging.  This socket
%    can perhaps be merged later into the begin-sockets when they are
%    no longer added as hooks but in the environment definitions.
%    \begin{macrocode}
\NewSocket{tagsupport/tbl/init}{0}
%    \end{macrocode}
% \end{socketdecl}
%
%
% \begin{socketdecl}{tagsupport/tbl/finalize}
%    To fine tune the structure (change cells to header cells, remove
%    unwanted structures, move a foot to the end, etc.) we also need a
%    socket that is executed at the end of the table but \emph{before}
%    all the variables are restored to the outer or default values.
%    The code in the socket can make assignments, but probably
%    shouldn't do typesetting and not write whatsits.
%    \begin{macrocode}
\NewSocket{tagsupport/tbl/finalize}{0}
%    \end{macrocode}
% \end{socketdecl}
%
% \begin{socketdecl}{tagsupport/tbl/finalize/longtable}
%    \env{longtable} needs its own socket to fine tune the structure.
%    Simply switching the plug in the previous socket interferes with
%    enabling/disabling the tagging.
%    \begin{macrocode}
\NewSocket{tagsupport/tbl/finalize/longtable}{0}
%    \end{macrocode}
% \end{socketdecl}
%
% \begin{socketdecl}{tagsupport/tblhmode/begin,
%                    tagsupport/tblhmode/end,
%                    tagsupport/tblvmode/begin,
%                    tagsupport/tblvmode/end
%                   }
%
%    These sockets are used in the begin and end code of environments,
%    to allow a fast enabling and disabling of the tagging. We
%    distinguish between tables that can be used inside paragraphs and
%    standalone tables like longtable.
%    \begin{macrocode}
\NewSocket{tagsupport/tblhmode/begin}{0}
\NewSocket{tagsupport/tblhmode/end}{0}
\NewSocket{tagsupport/tblvmode/begin}{0}
\NewSocket{tagsupport/tblvmode/end}{0}
%    \end{macrocode}
% \end{socketdecl}
%
% This are the standard plugs for tagging of cells and rows.
%
% \begin{plugdecl}{TD}
%    \begin{macrocode}
\NewSocketPlug{tagsupport/tblcell/begin}{TD}
  {
    \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/tblcell/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.
% tagging
% \begin{plugdecl}{TD}
%    \begin{macrocode}
\NewSocketPlug{tagsupport/tblcell/begin}{TDpbox}
  {
    \tag_struct_begin:n
      {
        tag             =\l__tbl_celltag_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}{TD}
%    \begin{macrocode}
\NewSocketPlug{tagsupport/tblcell/end}{TDpbox}
  {
    \tag_struct_end:
  }  
%    \end{macrocode}
% \end{plugdecl}
%
% \begin{plugdecl}{TR}
%    \begin{macrocode}
\NewSocketPlug{tagsupport/tblrow/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/tblrow/end}{TR}
  {
    \__tag_tbl_add_missing_cells:n { \g_@@_missing_cells_int }
    \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 }
      {
        \typeout
          {==>~
            stucture~stored~for~row~\int_use:N\g_@@_row_int :~
            \seq_use:Nn \g_@@_struct_cur_seq {,}
          }
      }
      { \ERROR } % 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}
% \end{plugdecl}
%
%
% \begin{plugdecl}{Table}
%  This plug will fine tune the structure.
%    \begin{macrocode}
\NewSocketPlug{tagsupport/tbl/finalize}{Table}
  {
    \@@_set_header_rows:
  }
%    \end{macrocode}
% \end{plugdecl}
%
% \begin{plugdecl}{Table}
%  This plug will fine tune the structure of longtable.
%    \begin{macrocode}
\NewSocketPlug{tagsupport/tbl/finalize/longtable}{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 }
                      {
                        \@@_struct_prop_gput:Vnn \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 }
                      {
                        \@@_struct_prop_gput:Vnn \l_@@_tmpa_tl {S}{/Artifact}
                      }
                   }
               }
           }
       }
  }
%    \end{macrocode}
% \end{plugdecl}
%
% \begin{plugdecl}{Table}
%    \begin{macrocode}
\NewSocketPlug{tagsupport/tblhmode/begin}{Table}
  {
    \mode_leave_vertical:
    \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=Table}
    \tl_gset:Ne \g_@@_struct_table_tl { \tag_get:n {struct_num} }
  }
%    \end{macrocode}
% \end{plugdecl}
%
% \begin{plugdecl}{Table}
%    \begin{macrocode}
\NewSocketPlug{tagsupport/tblhmode/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/tblvmode/begin}{Table}
  {
    \tag_struct_begin:n {tag=Table}
    \tl_gset:Ne \g_@@_struct_table_tl { \tag_get:n {struct_num} }
  }
%    \end{macrocode}
% \end{plugdecl}
%
% \begin{plugdecl}{Table}
%    \begin{macrocode}
\NewSocketPlug{tagsupport/tblvmode/end}{Table}
  {
    \tag_struct_end:
    \par
  }
%    \end{macrocode}
% \end{plugdecl}
%
%
%
%
%
%\subsection{Environments}
%
% Currently only tabular, tabular*, tabularx and longtable.
% We must use the \texttt{before} and \texttt{after} hooks as the \texttt{end}
% hook is executed before the end of the last row and then MC are messed up.
% This means that this sockets should only contain code that doesn't needs to be
% grouped!
%    \begin{macrocode}
\AddToHook{env/tabular/before}  {\UseSocket{tagsupport/tblhmode/begin}}
\AddToHook{env/tabular/after}   {\UseSocket{tagsupport/tblhmode/end}}
\AddToHook{env/tabular*/before} {\UseSocket{tagsupport/tblhmode/begin}}
\AddToHook{env/tabular*/after}  {\UseSocket{tagsupport/tblhmode/end}}
\AddToHook{env/tabularx/before} {\UseSocket{tagsupport/tblhmode/begin}}
\AddToHook{env/tabularx/after}  {\UseSocket{tagsupport/tblhmode/end}}
\AddToHook{env/longtable/before}{\UseSocket{tagsupport/tblvmode/begin}}
\AddToHook{env/longtable/after} {\UseSocket{tagsupport/tblvmode/end}}
%    \end{macrocode}
%
% The \env{array} environment is math. So we disable table tagging for now.
%    \begin{macrocode}
\AddToHook{env/array/begin} {\__tag_tbl_disable:}
%    \end{macrocode}
%
%
% \subsection{Interfaces to tagging}
%
% \subsubsection{Tagging helper commands}
%
% \begin{macro}{\@@_set_colspan:n}
%    This commands takes a number, checks if is larger than one,
%    checks if the colspan attribute exists (we can't predefine an
%    arbitrary number), and updates \cs{l_@@_cellattribute_tl}.
%    \begin{macrocode}
\tag_if_active:T
  { \cs_generate_variant:Nn \__tag_attr_new_entry:nn {ee} }
\cs_new_protected:Npn \@@_set_colspan:n #1
  {
    \tag_if_active:T
      {
        \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}
% \end{macro}
%
% \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/tblcell/begin}{noop}
   \AssignSocketPlug{tagsupport/tblcell/end}{noop}
   \AssignSocketPlug{tagsupport/tblrow/begin}{noop}
   \AssignSocketPlug{tagsupport/tblrow/end}{noop}
   \AssignSocketPlug{tagsupport/tbl/init}{noop}
   \AssignSocketPlug{tagsupport/tbl/finalize}{noop}
   \AssignSocketPlug{tagsupport/tbl/finalize/longtable}{noop}
   \AssignSocketPlug{tagsupport/tblhmode/begin}{noop}
   \AssignSocketPlug{tagsupport/tblhmode/end}{noop}
   \AssignSocketPlug{tagsupport/tblvmode/begin}{noop}
   \AssignSocketPlug{tagsupport/tblvmode/end}{noop}
 }
%    \end{macrocode}
%  \end{macro}


%  \begin{macro}{\__tag_tbl_enable:}
%
%    \begin{macrocode}
\cs_new_protected:Npn \__tag_tbl_enable:
 {
   \AssignSocketPlug{tagsupport/tblcell/begin}{TD}
   \AssignSocketPlug{tagsupport/tblcell/end}{TD}
   \AssignSocketPlug{tagsupport/tblrow/begin}{TR}
   \AssignSocketPlug{tagsupport/tblrow/end}{TR}
   \AssignSocketPlug{tagsupport/tbl/init}{Table}
   \AssignSocketPlug{tagsupport/tbl/finalize}{Table}
   \AssignSocketPlug{tagsupport/tbl/finalize/longtable}{Table}
   \AssignSocketPlug{tagsupport/tblhmode/begin}{Table}
   \AssignSocketPlug{tagsupport/tblhmode/end}{Table}
   \AssignSocketPlug{tagsupport/tblvmode/begin}{Table}
   \AssignSocketPlug{tagsupport/tblvmode/end}{Table}
 }
%    \end{macrocode}
%  \end{macro}

%    \begin{macrocode}
% TODO decide about key name
\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 .default:n = true,
    table-tagging .initial:n = true
  }
%    \end{macrocode}
%
% Table tagging should be disabled in the head and foot.
%    \begin{macrocode}
\AddToHook{begindocument}
 {
  \cs_if_exist:NT \@kernel@before@head
   {
     \tl_put_right:Nn \@kernel@before@head {\__tag_tbl_disable:}
     \tl_put_right:Nn \@kernel@before@foot {\__tag_tbl_disable:}
   }
 }
%    \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
         {
           newattribute =
            {TH-col}{/O /Table /Scope /Column},
           newattribute =
             {TH-row}{/O /Table /Scope /Row},
           newattribute =
             {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 newattribute):
%
%    \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 back.
%    \begin{macrocode}
\clist_new:N  \l_@@_header_rows_clist
%    \end{macrocode}
% \end{variable}
%
% \begin{macro}{\@@_set_header_rows:}
%    TEMP: Next tagpdf will have the right command which also updates
%    the debug info.
%    For now a temporary command:
%    \begin{macrocode}
\cs_if_free:NTF \__tag_struct_prop_gput:nnn
   {
     \cs_new_protected:Npn \@@_struct_prop_gput:nnn #1#2#3
       { \prop_gput:cnn { g__tag_struct_#1_prop }{#2}{#3} }
   }
   { \cs_new_protected:Npn  \@@_struct_prop_gput:nnn #1#2#3
       { \__tag_struct_prop_gput:nnn {#1}{#2}{#3} }
   }
\cs_generate_variant:Nn \@@_struct_prop_gput:nnn {nne,Vnn}
%    \end{macrocode}
%
%    \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 }
              {
                \@@_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
                  {\@@_struct_prop_gput:nne{ ####1 }{C}{[/TH-col~\l_@@_tmpa_tl]} }
                  {\@@_struct_prop_gput:nnn{ ####1 }{C}{/TH-col}}
             }
          }
      }
  }
%    \end{macrocode}
% \end{macro}
%  
% And some key support:
%    \begin{macrocode}
% TODO decide about key name
\keys_define:nn { __tag / setup }
  {
    table-header-rows .clist_set:N = \l_@@_header_rows_clist
  }
%    \end{macrocode}
%
% \subsection{Changes to \pkg{array} commands}
%
%
%
%  \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: {
   \typeout { ==>~ current~cell~data:~
              \int_use:N \g_@@_row_int ,
              \int_use:N \g_@@_col_int ,
              \g_@@_span_tl
            }
}
%    \end{macrocode}
%  \end{macro}
%
%
%
%
% \begin{macro}{\insert@column}
%    \cs{insert@column} is defined in \pkg{array}, here only the two
%    sockets are inserted.
%    \begin{macrocode}
\def\insert@column{%
  \@@_show_curr_cell_data:
  \UseSocket{tagsupport/tblcell/begin}%
  \the@toks \the \@tempcnta
  \ignorespaces \@sharp \unskip
  \the@toks \the \count@ \relax
  \UseSocket{tagsupport/tblcell/end}%
}
%    \end{macrocode}
% \end{macro}
%
%
% \begin{macro}{\@classz}
%    \begin{macrocode}
\def\@classz{\@classx
   \@tempcnta \count@
   \prepnext@tok
   \@addtopreamble{\ifcase \@chnum
      \hfil
      \hskip1sp%
      \d@llarbegin
      \insert@column
      \d@llarend \do@row@strut \hfil \or
      \hskip1sp\d@llarbegin \insert@column \d@llarend \do@row@strut \hfil \or
      \hfil\hskip1sp\d@llarbegin \insert@column \d@llarend \do@row@strut \or
    \setbox\ar@mcellbox\vbox
    \@startpbox{\@nextchar}
    \AssignSocketPlug{tagsupport/tblcell/begin}{TDpbox}
    \AssignSocketPlug{tagsupport/tblcell/end}{TDpbox}
    \insert@column \@endpbox
    \ar@align@mcell
    \do@row@strut \or
   \vtop \@startpbox{\@nextchar}
    \AssignSocketPlug{tagsupport/tblcell/begin}{TDpbox}
    \AssignSocketPlug{tagsupport/tblcell/end}{TDpbox}
    \insert@column \@endpbox\do@row@strut \or
   \vbox \@startpbox{\@nextchar}
    \AssignSocketPlug{tagsupport/tblcell/begin}{TDpbox}
    \AssignSocketPlug{tagsupport/tblcell/end}{TDpbox}
    \insert@column \@endpbox\do@row@strut
  \fi}\prepnext@tok}
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}{\@array,\@@@@array}
%    We modificate the \cs{@array} from \pkg{array}.
%    \begin{macrocode}
\def\@array[#1]#2{%
  \@tempdima \ht \strutbox
  \advance \@tempdima by\extrarowheight
  \setbox \@arstrutbox \hbox{\vrule
             \@height \arraystretch \@tempdima
             \@depth \arraystretch \dp \strutbox
             \@width \z@}%
%    \end{macrocode}
%    The total number of table columns of the current table is
%    determined in \cs{@@_determine_table_cols:} but this is called in
%    a group, so local settings do not survive. Thus, to save away the
%    outer value of \cs{g_@@_table_cols_tl} we do it before the group.
%    \begin{macrocode}
  \tl_set_eq:NN \l_@@_saved_table_cols_tl  \g_@@_table_cols_tl
%    \end{macrocode}
%
%    \begin{macrocode}
  \begingroup
  \@mkpream{#2}
%    \end{macrocode}
%    Next call has to happen immediately after \cs{@mkpream} because
%    it uses implementation details from that.
%    \begin{macrocode}
  \@@_determine_table_cols:
%    \end{macrocode}
%
%    \begin{macrocode}
  \xdef\@preamble{%
    \noexpand
%    \end{macrocode}
%    \cs{ialign} in the original definition is replaced by
%    \cs{ar@ialign} defined below.
%    \begin{macrocode}
     \ar@ialign
     \@halignto
     \bgroup \@arstrut
%    \end{macrocode}
%    A socket is inserted
%    \begin{macrocode}
      \UseSocket{tagsupport/tblrow/begin}%
%    \end{macrocode}
%    At the start of the preamble for the first column we set
%    \cs{g_@@_col_int} to \texttt{1} as we are no longer "at" but "in"
%    the first column. This is done in \cs{@@_init_cell_data:}. In
%    later columns this data is updated via \cs{@@_update_cell_data:}.
%    \begin{macrocode}
      \@@_init_cell_data:
      \@preamble
      \tabskip \z@ \cr}%
  \endgroup
  \@arrayleft
%    \end{macrocode}
%    Another socket for tagging. TODO: what about \cs{arrayleft}?
%    \begin{macrocode}
  \UseSocket{tagsupport/tbl/init}
  \if #1t\vtop \else \if#1b\vbox \else \vcenter \fi \fi
  \bgroup
  \let \@sharp ##\let \protect \relax
  \lineskip \z@
  \baselineskip \z@
  \m@th
  \let\\\@arraycr \let\tabularnewline\\\let\par\@empty
%\show\@preamble
  \@preamble}
%    \end{macrocode}
%    Finally, also set \cs{@@@@array} to the new definition:
%    \begin{macrocode}
\let\@@@@array\@array
%    \end{macrocode}
% \end{macro}
%
%
%  \begin{macro}{\@@_init_cell_data:}
%
%    \begin{macrocode}
\cs_new_protected:Npn \@@_init_cell_data: {
        \int_gset:Nn \g_@@_col_int {1}
        \tl_gset:Nn  \g_@@_span_tl {1}
}
%    \end{macrocode}
%  \end{macro}
%
%  \begin{macro}{\@@_update_cell_data:}
%    Updating cell data in columns after the first means we have to
%    increment the \cs{g_@@_col_int} by the span count of the previous
%    cell (in case it was a \cs{multicolumn} and then reset the
%    \cs{g_@@_span_tl} to one (as the default).
%    \begin{macrocode}
\cs_new_protected:Npn \@@_update_cell_data: {
        \int_gadd:Nn \g_@@_col_int { \g_@@_span_tl }
        \tl_gset:Nn  \g_@@_span_tl {1}
}
%    \end{macrocode}
%  \end{macro}
%
%
%
%  \begin{macro}{\@@_determine_table_cols:}
%    Current implementation of \cs{@mkpream} uses the scratch counter
%    \cs{count@} to keep track of the number of toks registers it needs
%    (2 per column), but this can't be used as it counts also
%    insertings made with \verb+!{}+ and \verb+@{}+.
%    So similar as does longtable for \cs{LT@cols} we count the
%    numbers of ambersands instead.
%    \begin{macrocode}
\cs_new:Npn \@@_determine_table_cols:  {
  \seq_set_split:NnV\l_@@_tmpa_seq {&}\@preamble
  \tl_gset:Ne \g_@@_table_cols_tl { \seq_count:N \l_@@_tmpa_seq }
  \typeout{ ==>~ Table~ has~ \g_@@_table_cols_tl \space columns }
}
%    \end{macrocode}
%  \end{macro}
%
%
%  \begin{macro}{\@arraycr}
%    Add code that figures out if the current table row is incomplete
%    (not enough \verb=&=s). It can then do extra actions, such as
%    inserting missing cell tags.
%    \begin{macrocode}
\protected\def\@arraycr{
  \relax
  \@@_store_missing_cells:n{@arraycr}
  %
  \iffalse{\fi\ifnum 0=`}\fi
  \@ifstar \@xarraycr \@xarraycr}
%    \end{macrocode}
%  \end{macro}
%
%
%
%  \begin{macro}{\@@_store_missing_cells:n,
%                \__tag_tbl_add_missing_cells:n}
%    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 second is used in
%    the endrow socket.
%    \begin{macrocode}
\cs_new:Npn \@@_store_missing_cells:n #1 {
  \int_compare:nNnT \g_@@_col_int > 0
      {
        \int_gset:Nn \g_@@_missing_cells_int
            {
              \g_@@_table_cols_tl
            - \g_@@_col_int
            - \g_@@_span_tl
            + 1
            }
        \int_compare:nNnT \g_@@_missing_cells_int < 0 \ERROR % should not happen
        \typeout{==>~
          (#1)~
          This~ row~ needs~
          \int_use:N \g_@@_missing_cells_int \space
          additional~ cell(s)
        }
      }
}
%    \end{macrocode}
%
%    \begin{macrocode}
\cs_new:Npn \__tag_tbl_add_missing_cells:n #1
  {
%    \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 {#1}>{0}
      {
       \typeout{==>~
          ~Inserting~\int_eval:n{#1}~additional~cell(s)~into~previous~row:}
      }
    \int_step_inline:nn { #1 }
      {
        \UseSocket{tagsupport/tblcell/begin}
        \UseSocket{tagsupport/tblcell/end}
      }
  }
%    \end{macrocode}
%  \end{macro}
%
%
%
% \begin{macro}{\endarray}
%    If tables are nested into another then  it is necessary to
%    restore information about the cell the inner table started
%    in. Otherwise, \cs{g_@@_row_int}, \cs{g_@@_col_int}, and
%    \cs{g_@@_span_tl} reflect the status in the outer table as they
%    are globally manipulated. We restore in all cases even if we are
%    not in a nesting situation as that makes the code simpler (and
%    probably faster).
%
%    \cs{endtabular} and \cs{endtabular*} inherit from \cs{endarray}
%    so we only need to change that. \texttt{tabularx} is handled
%    below.
%    \begin{macrocode}
\def\endarray{
  \@@_store_missing_cells:n{endarray}
  \crcr \egroup
  \UseSocket{tagsupport/tbl/finalize}
  \int_gset:Nn \g_@@_col_int { \l_@@_saved_col_tl }
  \int_gset:Nn \g_@@_row_int { \l_@@_saved_row_tl }
  \tl_gset_eq:NN \g_@@_span_tl \l_@@_saved_span_tl
  \tl_gset_eq:NN \g_@@_table_cols_tl   \l_@@_saved_table_cols_tl
  \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
  \typeout{==>~ restored~cell~data:~
                \int_use:N \g_@@_row_int,
                \int_use:N \g_@@_col_int,
                \l_@@_saved_span_tl \space
                (
                \int_compare:nNnTF \g_@@_table_cols_tl = 0
                    { outer~ level }
                    { max:~ \g_@@_table_cols_tl }
                )
          }
  \egroup
  \@arrayright \gdef\@preamble{}%
}
%    \end{macrocode}
% \end{macro}
%
%
%  \begin{macro}{\@addamp}
%    If we are after the first column we have to insert a \verb=&= and
%    also update the cell data.
%    \begin{macrocode}
\def\@addamp {
  \if@firstamp
    \@firstampfalse
  \else
    \edef\@preamble{\@preamble &
        \@@_update_cell_data:
    }
  \fi
}
%    \end{macrocode}
%  \end{macro}
%
%
%
%  \begin{macro}{
%     \g_@@_col_int,
%     \g_@@_row_int,
%     \g_@@_span_tl,
%     \g_@@_table_cols_tl}
%    \cs{g_@@_row_int} holds the current row number in the table. The value
%    \texttt{0} means we haven't yet processed the table preamble. It
%    is incremented by every \cs{cr} including the one ending the
%    table preamble.
%
%    \cs{g_@@_col_int} holds the current column number. The value
%    \texttt{0} means we have not yet started the table or just finished a table row
%    (with \verb=\\= typically); any other positive value means we
%    are currently typesetting a cell in that column in some row
%    (denoted by the \cs{g_@@_row_int}.
%
%    In a \cs{multicolumn} it holds the column number of the first
%    spanned column and \cs{g_@@_span_tl} the info how many cells are
%    spanned.
%
%    \cs{g_@@_span_tl} is normally \texttt{1} except in a
%    \cs{multicolumn} cell.
%    \begin{macrocode}
\int_new:N \g_@@_col_int
\int_new:N \g_@@_row_int
\tl_new:N  \g_@@_span_tl
\tl_new:N  \g_@@_table_cols_tl

\tl_gset:Nn \g_@@_span_tl {1}
\tl_gset:Nn \g_@@_table_cols_tl {0}  % indicates outer level
%    \end{macrocode}
%  \end{macro}
%
%
%  \begin{macro}{\l_@@_saved_col_tl,\l_@@_saved_row_tl,
%                \l_@@_saved_span_tl,\l_@@_saved_table_cols_tl}
%
%    Saving the outer values if we are nesting tables is necessary (as
%    the above variables are globally altered. For this we use always
%    token lists because they don't change and we do not need to blow
%    additional integer registers.
%    \begin{macrocode}
\tl_new:N \l_@@_saved_col_tl
\tl_new:N \l_@@_saved_row_tl
\tl_new:N \l_@@_saved_span_tl
\tl_new:N \l_@@_saved_table_cols_tl

\tl_set:Nn \l_@@_saved_col_tl{0}
\tl_set:Nn \l_@@_saved_row_tl{0}
\tl_set:Nn \l_@@_saved_span_tl{1}
\tl_set:Nn \l_@@_saved_table_cols_tl{0}  % indicates outer level
%    \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}{\ar@ialign}
%    A new command that replaces \cs{ialign} above.  \cs{everycr} is
%    also applied to the \cs{cr} ending the preamble so we have to
%    program around that.
%    \begin{macrocode}
\def\ar@ialign{%
%    \end{macrocode}
%    Before starting a table we locally stored the information related
%    to the current cell (if any) so that we can restore it once the
%    table is finished.
%    \begin{macrocode}
  \tl_set:No \l_@@_saved_col_tl {\int_use:N \g_@@_col_int }
  \tl_set:No \l_@@_saved_row_tl {\int_use:N \g_@@_row_int }
  \tl_set_eq:NN \l_@@_saved_span_tl  \g_@@_span_tl
  \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
%
  \typeout{==>~ saved~cell~data:~
                \l_@@_saved_row_tl,
                \l_@@_saved_col_tl,
                \l_@@_saved_span_tl \space
                (
                \int_compare:nNnTF \l_@@_saved_table_cols_tl = 0
                    { outer~ level }
                    { max:~ \l_@@_saved_table_cols_tl }
                )
          }
%    \end{macrocode}
%    These are the initial values when starting a table:
%    \begin{macrocode}
  \int_gzero:N \g_@@_row_int
  \int_gzero:N \g_@@_col_int
  \tl_gset:Nn  \g_@@_span_tl {1}
  \seq_gclear:N\g_@@_struct_rows_seq
  \seq_gclear:N\g_@@_struct_cells_seq
  \seq_gclear:N\g_@@_struct_cur_seq
%    \end{macrocode}
%
%    \begin{macrocode}
  \everycr{%
    \noalign{%
%    \end{macrocode}
%    We use \cs{g_@@_col_int} equal zero to indicate that we are just
%    after a TR (or at the very beginning of the table). Using the row
%    count is not so good as longtable may split the table in chunks.
%    \begin{macrocode}
      \int_compare:nNnT \g_@@_col_int > 0
         { \UseSocket{tagsupport/tblrow/end} }
      \int_gincr:N \g_@@_row_int          % this row about to start
      \int_gzero:N \g_@@_col_int          % we are before first col
    }%
  }%
  \tabskip\z@skip\halign}
%    \end{macrocode}
% \end{macro}
%
%
% \begin{macro}{\multicolumn}
%
%    \cs{multicolumn} is also defined in \pkg{array}. The redefinition
%    has to solve two problems: it must handle the row begin if it is
%    used there, and it must save the numbers of cells it spans so
%    that we can add a suitable ColSpan attribute.\footnote{FMi: This can
%    now perhaps cleaned up somewhat}
%    \begin{macrocode}
\long\def\multicolumn#1#2#3{%
  % alternative: determine first col with vmode test ...
  %  \ifvmode
  %    \multispan{#1}\typeout{A==> vmode}%
  %  \else
  %    \multispan{#1}\typeout{A==> not vmode}
  %  \fi
  % but this makes the \crcr handling really complicated which would
  % then need to become something like
  %    \ifvmode \expandafter \@gobble
  %    \else \expandafter \@iden \fi {\cr\noalign{do something}}%
  % so not used.
  % Instead:
   \multispan{#1}\begingroup
%    \end{macrocode}
%    Insert rowbegin socket only if this multicolumn
%    replaces the preamble of the first column. In that case we have
%    to set \cs{g_@@_col_int} to 1 since this is no longer done in the
%    preamble for the cell.
%    \begin{macrocode}
   \int_compare:nNnTF \g_@@_col_int = 0
       {
         \UseSocket{tagsupport/tblrow/begin}
         \int_gset:Nn \g_@@_col_int {1}
       }
%    \end{macrocode}
%    If we are in a later column we use \cs{g_@@_span_tl} from the
%    previous column to update.
%    \begin{macrocode}
       {
         \int_gadd:Nn \g_@@_col_int { \g_@@_span_tl }
       }
%    \end{macrocode}
%    Then we set the span value so that it can be use in the next column.
%    \begin{macrocode}
   \tl_gset:Nn \g_@@_span_tl {#1}
%    \end{macrocode}
%
%    \begin{macrocode}
  \def\@addamp{\if@firstamp\@firstampfalse \else
                \@preamerr 5\fi}%
   \@mkpream{#2}\@addtopreamble\@empty
   \endgroup
%    \end{macrocode}
%    Now we update the colspan attribute.  This needs setting after
%    the group as it is hidden inside the plug in \cs{insert@column}.
%    \begin{macrocode}
   \@@_set_colspan:n {#1}
%    \end{macrocode}
%    \begin{macrocode}
   \def\@sharp{#3}%
   \@arstrut \@preamble
   \null
   \ignorespaces}
%    \end{macrocode}
% \end{macro}
%
% \subsection{longtable}
%
% Longtable is complicated. When at the begin the \cs{endhead},
% \cs{endfirsthead}, \cs{endfoot} and \cs{endlastfoot} are used to
% setup head and foot they create each a structure subtree with one or
% more rows. From this structures we want to keep at most two (head
% and foot) and move the foot to the end of the table. When the head
% and foot boxes are (re)inserted on following pages we want to mark
% them up as artifact with the exception of the head at the begin and
% the foot box at the end.
%
% TODO: When a line is killed the structure subtree is there already
% too and must be removed.
% 
% Hyperref patches longtable. This must be disabled and replace with
% genuine code
%    \begin{macrocode}
\let\@kernel@refstepcounter\refstepcounter
\def\hyper@nopatch@longtable{}
%    \end{macrocode}
%    \begin{macrocode}
\def\@@_patch_LT@array[#1]#2{%
%    \end{macrocode}
% \cs{LT@array} is executed in a group, so we can disable para-tagging here.
%    \begin{macrocode}
  \UseSocket{tagsupport/tbl/init}%
  \@kernel@refstepcounter{table}\stepcounter{LT@tables}%
%    \end{macrocode}
% The target is created rather late and a \cs{label} can come earlier, 
% so we have to define \cs{@currentHref} explicitly. We can't currently
% assume that \cs{theHtable} is defined always.   
%    \begin{macrocode}
  \tl_gset:Ne \@currentHref {table.\cs_if_exist_use:N\theHtable}
  \int_gzero:N \g_@@_row_int
  \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
  \if l#1%
    \LTleft\z@ \LTright\fill
  \else\if r#1%
    \LTleft\fill \LTright\z@
  \else\if c#1%
    \LTleft\fill \LTright\fill
  \fi\fi\fi
  \let\LT@mcol\multicolumn
  \let\LT@@@@tabarray\@tabarray
  \let\LT@@@@hl\hline
  \def\@tabarray{%
    \let\hline\LT@@@@hl
    \LT@@@@tabarray}%
  \let\\\LT@tabularcr
  \let\tabularnewline\\%
  \def\newpage{\noalign{\break}}%
  \def\pagebreak{\noalign{\ifnum`}=0\fi\@testopt{\LT@no@pgbk-}4}%
  \def\nopagebreak{\noalign{\ifnum`}=0\fi\@testopt\LT@no@pgbk4}%
  \let\hline\LT@hline \let\kill\LT@kill\let\caption\LT@caption
  \@tempdima\ht\strutbox
  \let\@endpbox\LT@endpbox
  \ifx\extrarowheight\@undefined
    \let\@acol\@tabacol
    \let\@classz\@tabclassz \let\@classiv\@tabclassiv
    \def\@startpbox{\vtop\LT@startpbox}%
    \let\@@@@startpbox\@startpbox
    \let\@@@@endpbox\@endpbox
    \let\LT@LL@FM@cr\@tabularcr
  \else
    \advance\@tempdima\extrarowheight
    \col@sep\tabcolsep
    \let\@startpbox\LT@startpbox\let\LT@LL@FM@cr\@arraycr
  \fi
  \setbox\@arstrutbox\hbox{\vrule
    \@height \arraystretch \@tempdima
    \@depth \arraystretch \dp \strutbox
    \@width \z@}%
  \let\@sharp##\let\protect\relax
   \begingroup
    \@mkpream{#2}%
    \@@_determine_table_cols:
    \xdef\LT@bchunk{%
%    \end{macrocode}
%    At the start of a chunk we set \cs{g_@@_col_int} to zero to make
%    sure that we aren't generating /TR with the \cs{cr} ending the
%    chunk preamble.
%    \begin{macrocode}
       \int_gzero:N \g_@@_col_int
       \global\advance\c@LT@chunks\@ne
       \global\LT@rows\z@\setbox\z@\vbox\bgroup
       \LT@setprevdepth
       \tabskip\LTleft \noexpand\halign to\hsize\bgroup
       \tabskip\z@ \@arstrut
%    \end{macrocode}
% Insert the socket and the setting of the conditional
%    \begin{macrocode}
       \UseSocket{tagsupport/tblrow/begin}%
       \@@_init_cell_data:
%    \end{macrocode}
%    \begin{macrocode}
       \@preamble \tabskip\LTright \cr}%
  \endgroup
  \expandafter\LT@nofcols\LT@bchunk&\LT@nofcols
  \LT@make@row
  \m@th\let\par\@empty
%    \end{macrocode}
% Socket and conditional
%    \begin{macrocode}
  \everycr{%
    \noalign{%
%    \end{macrocode}
%    In \pkg{longtable} we have a bunch of extra \cs{cr}s that are
%    executed whenever a chunk ends. In that case they should not
%    increment the main row counter, sigh.
%    \begin{macrocode}
      \typeout{--longtable-->~chunk~row:~ \the\LT@rows \space
               row:~ \the\g_@@_row_int   \space
               column:~ \the\g_@@_col_int
      }
      \int_compare:nNnT \g_@@_col_int > 0
          {
            \UseSocket{tagsupport/tblrow/end}
          }
%    \end{macrocode}
%    This prevents any of the additional \cs{cr}s at the end of the
%    chunk to add another /TR. Then once we really start a new chunk
%    it gets incremented so\ldots
%    \begin{macrocode}
      \int_gzero:N \g_@@_col_int           % before first col
%    \end{macrocode}
%    And for the same reason such \cs{cr}s should not increment the
%    main row counter (but it has to be incremented after the preamble
%    of a chunk), so here we test against \cs{LT@rows} which is
%    \cs{LTchunksize} at the end of a chunk.
%    \begin{macrocode}
      \int_compare:nNnT \LT@rows < \LTchunksize
         { \int_gincr:N \g_@@_row_int  }   % this row about to start
    }%
  }%
%    \end{macrocode}
%    \begin{macrocode}
  \lineskip\z@\baselineskip\z@
  \LT@bchunk}
%    \end{macrocode}
% The end code most stop to insert the endrow too.
%    \begin{macrocode}
\def\@@_patch_endlongtable{%
  \@@_store_missing_cells:n{endlongtable}
  \crcr
  \noalign{%
    \UseSocket{tagsupport/tbl/finalize/longtable}
    \int_gzero:N \g_@@_row_int      % this prevents considering the next
                                    % \crcr as another row end.
    \let\LT@entry\LT@entry@chop
    \xdef\LT@save@row{\LT@save@row}}%
  \LT@echunk
  \LT@start
  \unvbox\z@
  \LT@get@widths
  \if@filesw
    {\let\LT@entry\LT@entry@write\immediate\write\@auxout{%
      \gdef\expandafter\noexpand
        \csname LT@\romannumeral\c@LT@tables\endcsname
          {\LT@save@row}}}%
  \fi
  \ifx\LT@save@row\LT@@@@save@row
  \else
    \LT@warn{Column~\@width s~have~changed\MessageBreak
             in~table~\thetable}%
    \LT@final@warn
  \fi
  \endgraf\penalty -\LT@end@pen
  \ifvoid\LT@foot\else
    \global\advance\vsize\ht\LT@foot
    \global\advance\@colroom\ht\LT@foot
    \dimen@\pagegoal\advance\dimen@\ht\LT@foot\pagegoal\dimen@
  \fi
  \endgroup
  \global\@mparbottom\z@
  \endgraf\penalty\z@\addvspace\LTpost
  \ifvoid\footins\else\insert\footins{}\fi}
%    \end{macrocode}
%
%
%
%  \begin{macro}{\@@_patch_LT@t@bularcr}
%
%    \begin{macrocode}
\def\@@_patch_LT@t@bularcr{%
  \global\advance\LT@rows\@ne
  \ifnum\LT@rows=\LTchunksize
%    \end{macrocode}
%    At the end of the chunk \verb=\\= is doing something special and
%    so we loose \cs{@@_store_missing_cells:n}. Below is about the
%    right place to add it do this code branch.
%    \begin{macrocode}
    \@@_store_missing_cells:n{echunk}
    \gdef\LT@setprevdepth{%
      \prevdepth\z@
      \global\let\LT@setprevdepth\relax}%
    \expandafter\LT@xtabularcr
  \else
    \ifnum0=`{}\fi
    \expandafter\LT@LL@FM@cr
  \fi}

%    \end{macrocode}
%  \end{macro}
%
%
%
%  \begin{macro}{\@@_patch_LT@end@hd@ft}
%    This command is used to store the head and foot boxes.
%    We need to retrieve and store the row so that we can clean
%    up the structure in the finalize code.
%    \begin{macrocode}
\def\@@_patch_LT@end@hd@ft#1{%
%    \end{macrocode}
%    To handle missing columns in the header we need this:
%    \begin{macrocode}
  \__tbl_store_missing_cells:n{head/foot}
  \int_step_inline:nn
   { \LT@rows + 1 }
   {
     \seq_gput_left:ce
       {g_@@_\cs_to_str:N #1 _rows_seq }
       { \int_eval:n {\g_@@_row_int + 1 - ##1 } }
   }
%    \end{macrocode}
%    We also have to set the chunk rows to its max value before
%    calling \cs{LTechunk} so that we don't get extra increments of
%    the main row counter due to \cs{everycr}.
%    \begin{macrocode}
  \int_gset:Nn \LT@rows { \LTchunksize }
  \LT@echunk
  \ifx\LT@start\endgraf
    \LT@err
     {Longtable head or foot not at start of table}%
     {Increase LTchunksize}%
  \fi
  \setbox#1\box\z@
  \LT@get@widths
  \LT@bchunk}
%    \end{macrocode}
%  \end{macro}

% \begin{macro}{\@@_patch_LT@start}
%    \begin{macrocode}
\def\@@_patch_LT@start{%
  \let\LT@start\endgraf
  \endgraf\penalty\z@\vskip\LTpre\endgraf
   \ifdim \pagetotal<\pagegoal \else
      \dimen@=\pageshrink
      \advance \dimen@ 1sp %
      \kern\dimen@\penalty 9999\endgraf \kern-\dimen@
   \fi
  \dimen@\pagetotal
  \advance\dimen@ \ht\ifvoid\LT@firsthead\LT@head\else\LT@firsthead\fi
  \advance\dimen@ \dp\ifvoid\LT@firsthead\LT@head\else\LT@firsthead\fi
  \advance\dimen@ \ht\LT@foot
  \edef\LT@reset@vfuzz{\vfuzz\the\vfuzz\vbadness\the\vbadness\relax}%
  \vfuzz\maxdimen
  \vbadness\@M
  \setbox\tw@\copy\z@
  \setbox\tw@\vsplit\tw@ to \ht\@arstrutbox
  \setbox\tw@\vbox{\unvbox\tw@}%
  \LT@reset@vfuzz
  \advance\dimen@ \ht
        \ifdim\ht\@arstrutbox>\ht\tw@\@arstrutbox\else\tw@\fi
  \advance\dimen@\dp
        \ifdim\dp\@arstrutbox>\dp\tw@\@arstrutbox\else\tw@\fi
  \advance\dimen@ -\pagegoal
  \ifdim \dimen@>\z@
    \vfil\break
  \else
    \ifdim\pageshrink>\z@\pageshrink\z@\fi
  \fi
      \global\@colroom\@colht
  \ifvoid\LT@foot\else
    \global\advance\vsize-\ht\LT@foot
    \global\advance\@colroom-\ht\LT@foot
    \dimen@\pagegoal\advance\dimen@-\ht\LT@foot\pagegoal\dimen@
    \maxdepth\z@
  \fi
  \MakeLinkTarget{table}
  \ifvoid\LT@firsthead\copy\LT@head\else\box\LT@firsthead\fi\nobreak
%    \end{macrocode}
% Avoid that following uses of the box add content:
%    \begin{macrocode}
  \tagmcbegin{artifact}
   \tag_mc_reset_box:N\LT@head
  \tagmcend
  \output{\LT@output}}
%    \end{macrocode}
% \end{macro}
%
%
% \begin{macro}{\@@_patch_LT@output}
%    We must also avoid that the reuse of the foot box leads to
%    duplicated content:
%    \begin{macrocode}
\def\@@_patch_LT@output{%
  \ifnum\outputpenalty <-\@Mi
    \ifnum\outputpenalty > -\LT@end@pen
      \LT@err{floats and marginpars not allowed in a longtable}\@ehc
    \else
      \setbox\z@\vbox{\unvbox\@cclv}%
      \ifdim \ht\LT@lastfoot>\ht\LT@foot
        \dimen@\pagegoal
        \advance\dimen@\ht\LT@foot
        \advance\dimen@-\ht\LT@lastfoot
        \ifdim\dimen@<\ht\z@
          \setbox\@cclv\vbox{\unvbox\z@\copy\LT@foot\vss}%
          \@makecol
          \@outputpage
          \global\vsize\@colroom
          \setbox\z@\vbox{\box\LT@head}%
        \fi
      \fi
        \unvbox\z@\box\ifvoid\LT@lastfoot\LT@foot\else\LT@lastfoot\fi
%    \end{macrocode}
%    Reset attribute of foot box:
%    \begin{macrocode}
        \tagmcbegin{artifact}
        \tag_mc_reset_box:N \LT@foot
        \tagmcend
    \fi
  \else
    \setbox\@cclv\vbox{\unvbox\@cclv\copy\LT@foot\vss}%
%    \end{macrocode}
%    Reset attribute of foot box:
%    \begin{macrocode}
    \tagmcbegin{artifact}
    \tag_mc_reset_box:N \LT@foot
    \tagmcend
    \@makecol
    \@outputpage
      \global\vsize\@colroom
    \copy\LT@head\nobreak
  \fi}
%    \end{macrocode}
% \end{macro}
%
% \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}
  {
   \seq_new:N \g_@@_LT@firsthead_rows_seq
   \seq_new:N \g_@@_LT@head_rows_seq
   \seq_new:N \g_@@_LT@lastfoot_rows_seq
   \seq_new:N \g_@@_LT@foot_rows_seq
   \cs_set_eq:NN \LT@array\@@_patch_LT@array
   \cs_set_eq:NN \endlongtable\@@_patch_endlongtable
   \cs_set_eq:NN \LT@start\@@_patch_LT@start
   \cs_set_eq:NN \LT@output\@@_patch_LT@output
   \cs_set_eq:NN \LT@t@bularcr\@@_patch_LT@t@bularcr
   \cs_set_eq:NN \LT@end@hd@ft\@@_patch_LT@end@hd@ft
   \cs_set_eq:NN \LT@makecaption\@@_patch_LT@makecaption
  }
%    \end{macrocode}





% \subsection{tabularx}
%
% In tabularx we mainly need to ensure that no tagging is done during
% the trial.
%
%    \begin{macrocode}
\def\@@_patch_TX@endtabularx{%
   \expandafter\expandafter\expandafter
     \TX@find@endtabularxa\csname end\TX@\endcsname
     \endtabularx\TX@\endtabularx\TX@find@endtabularxa
  \expandafter\TX@newcol\expandafter{\tabularxcolumn{\TX@col@width}}%
  \let\verb\TX@verb
  \def\@elt##1{\global\value{##1}\the\value{##1}\relax}%
  \edef\TX@ckpt{\cl@@@@ckpt}%
  \let\@elt\relax
  \TX@old@table\maxdimen
  \TX@col@width\TX@target
  \global\TX@cols\@ne
  \TX@typeout@
    {\@spaces Table Width\@spaces Column Width\@spaces X Columns}%
%    \end{macrocode}
%    Here we stop tagging:
%    \begin{macrocode}
  \tag_stop:n{tabularx}
  \TX@trial{\def\NC@rewrite@X{%
          \global\advance\TX@cols\@ne\NC@find p{\TX@col@width}}}%
  \loop
    \TX@arith
    \ifTX@
    \TX@trial{}%
  \repeat
%    \end{macrocode}
%    And now we restart it again.
%    \begin{macrocode}
  \tag_start:n{tabularx}
  {\let\@footnotetext\TX@ftntext\let\@xfootnotenext\TX@xftntext
    \csname tabular*\expandafter\endcsname\expandafter\TX@target
      \the\toks@
    \csname endtabular*\endcsname}%
  \global\TX@ftn\expandafter{\expandafter}\the\TX@ftn
  \ifnum0=`{\fi}%
   \expandafter\expandafter\expandafter
   \TX@find@endtabularxbb
    \expandafter\end\expandafter{\TX@}%
    \endtabularx\TX@\endtabularx\TX@find@endtabularxb
}

\AddToHook{package/tabularx/after}
  {\cs_set_eq:NN \TX@endtabularx\@@_patch_TX@endtabularx }
%    \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}
