summaryrefslogtreecommitdiff
path: root/macros/latex/base/lttagging.dtx
diff options
context:
space:
mode:
Diffstat (limited to 'macros/latex/base/lttagging.dtx')
-rw-r--r--macros/latex/base/lttagging.dtx985
1 files changed, 985 insertions, 0 deletions
diff --git a/macros/latex/base/lttagging.dtx b/macros/latex/base/lttagging.dtx
new file mode 100644
index 0000000000..6b68cd0e05
--- /dev/null
+++ b/macros/latex/base/lttagging.dtx
@@ -0,0 +1,985 @@
+% \iffalse meta-comment
+%
+% Copyright (C) 2023-2024
+% The LaTeX Project and any individual authors listed elsewhere
+% in this file.
+%
+% This file is part of the LaTeX base system.
+% -------------------------------------------
+%
+% It may be distributed and/or modified under the
+% conditions of the LaTeX Project Public License, either version 1.3c
+% of this license or (at your option) any later version.
+% The latest version of this license is in
+% https://www.latex-project.org/lppl.txt
+% and version 1.3c or later is part of all distributions of LaTeX
+% version 2008 or later.
+%
+% This file has the LPPL maintenance status "maintained".
+%
+% The list of all files belonging to the LaTeX base distribution is
+% given in the file `manifest.txt'. See also `legal.txt' for additional
+% information.
+%
+% The list of derived (unpacked) files belonging to the distribution
+% and covered by LPPL is defined by the unpacking scripts (with
+% extension .ins) which are part of the distribution.
+%
+% \fi
+%
+% \iffalse
+%%% From File: lttagging.dtx
+%
+%<*driver>
+% \fi
+\ProvidesFile{lttagging.dtx}
+ [2024/10/21 v1.0k LaTeX Kernel (tagging support)]
+% \iffalse
+\documentclass{l3doc}
+\GetFileInfo{lttagging.dtx}
+\title{\filename}
+\date{\filedate}
+\author{\LaTeX{} project}
+
+\begin{document}
+% \MaintainedByLaTeXTeam{latex} % should be added again the moment
+ % it is supported by l3doc
+ \maketitle
+ \DocInput{\filename}
+\end{document}
+%</driver>
+% \fi
+%
+% \providecommand\env[1]{\texttt{#1}}
+%
+% \providecommand\hook[1]{\texttt{#1\DescribeHook[noprint]{#1}}}
+% \providecommand\socket[1]{\texttt{#1\DescribeSocket[noprint]{#1}}}
+% \providecommand\plug[1]{\texttt{#1\DescribePlug[noprint]{#1}}}
+%
+% \ProvideDocElement[printtype=\textit{socket},idxtype=socket,idxgroup=Sockets]{Socket}{socketdecl}
+% \ProvideDocElement[printtype=\textit{hook},idxtype=hook,idxgroup=Hooks]{Hook}{hookdecl}
+% \ProvideDocElement[printtype=\textit{plug},idxtype=plug,idxgroup=Plugs]{Plug}{plugdecl}
+%
+%
+%
+% \begin{macrocode}
+%<*2ekernel|latexrelease>
+% \end{macrocode}
+%
+% \begin{macrocode}
+\ExplSyntaxOn
+% \end{macrocode}
+%
+%
+% \section{General support for tagged output}
+%
+% \DescribeMacro\SuspendTagging
+% \DescribeMacro\ResumeTagging
+% \DescribeMacro\tag_suspend:n
+% \DescribeMacro\tag_resume:n
+
+%
+% The are places in code where it is import top stop any tagging
+% activities, e.g., when we are doing trial typesetting that it is
+% done several times. In such a case one must tag only the final
+% version that is actually used, otherwise tagging structures are
+% generated which then do not end up in the PDF and confuse the
+% mechanism. For this we have two commands that can be used in
+% packages: \cs{SuspendTagging} and \cs{ResumeTagging} (with corresponding
+% L3 programming layer commands). They are
+% available as part of the \LaTeX{} kernel, so that they can be
+% safely used in packages whether or not tagging is requested. They
+% all take a string argument that is used for debugging to easily
+% identify why tagging was suspended or restarted, for example, in
+% \pkg{tabularx} you find \verb=\SuspendTagging{tabularx}=. By default
+% these four commands do nothing.
+%
+% The argument is used literally (in \cs{typeout} messages) without any
+% expansion when debugging is turned on and otherwise it is not used at all.
+% This means it is safe to write something like
+% \verb=\SuspendTagging{\foo}= or even \verb=\SuspendTagging\foo=
+% which means \LaTeX{} has to parse only
+% a single token instead of putting a string of characters into the
+% argument. This means a tiny speed improvement but with many such
+% debugging strings\ldots
+%
+%
+% \DescribeMacro\UseTaggingSocket
+% \DescribeMacro\tag_socket_use:n
+% \DescribeMacro\tag_socket_use:nn
+% \DescribeMacro\tag_socket_use:nnn
+% Given that we sometimes have to suspend tagging, it would be fairly
+% inefficient to put different plugs into these sockets whenever that
+% happens. We therefore offer \cs{UseTaggingSocket} which is like
+% \cs{UseSocket} except that is expects a socket starting with
+% \texttt{tagsupport/} but the socket name is specified without
+% this prefix, i.e.,
+% \begin{quote}
+% \verb=\UseTaggingSocket{foo}= $\to$
+% \verb=\UseSocket{tagsupport/foo}=
+% \end{quote}.
+%
+% Beside being slightly shorter, the big advantage is that this way
+% we can change \cs{UseTaggingSocket} to do nothing by switching a boolean
+% instead of changing the plugs of the tagging support sockets back and forth.
+%
+% Usually, these sockets have (beside the default plug defined for every socket)
+% one additional plug defined and directly assigned. This plug is used when
+% tagging is active.
+% There may be more plugs, e.g., tagging with special debugging or special behaviour
+% depending on the class or PDF version etc., but right now it is usually just on or off.
+%
+% When tagging is suspended they all have the same predefined behaviour:
+% The sockets with zero arguments do nothing. The sockets with one argument
+% gobble their argument. The sockets with two arguments
+% will drop their first argument and pass the second unchanged.
+%
+% It is possible to use the tagging support sockets with
+% \cs{UseSocket} directly, but in this case the socket remains active
+% if \cs{SuspendTagging} is in force. There may be reasons for doing
+% that but in general we expect to always use \cs{UseTaggingSocket}.
+%
+% \DescribeMacro\UseExpandableTaggingSocket
+% \DescribeMacro\tag_socket_use_expandable:n
+% For special cases like in some \cs{halign} contexts we need a fully expandable
+% version of the commend. For these cases, \cs{UseExpandableTaggingSocket} can be
+% used. To allow being expandable, it does not output any debugging information
+% if \cs{DebugSocketsOn} is in effect and therefore should be avoided whenever possible.
+%
+% The L3 programming layer versions \cs{tag_socket_use_expandable:n},
+% \cs{tag_socket_use:n}, \cs{tag_socket_use:nn}, and \cs{tag_socket_use:nnn}
+% are slightly more efficient than
+% \cs{UseTaggingSocket} because they do not have to determine how
+% many arguments the socket takes when disabling it.
+%
+%
+% \MaybeStop{}
+%
+%
+% \section{Implementation}
+%
+%
+%
+% \begin{macro}{\tag_suspend:n,\tag_resume:n,\SuspendTagging,\ResumeTagging}
+%
+% In the kernel, these commands get dummy definitions so that
+% they can be used without harm in packages. The real definition is
+% used when tagging gets enabled.
+% \begin{macrocode}
+\cs_new_eq:NN \tag_suspend:n \use_none:n
+\cs_new_eq:NN \tag_resume:n \use_none:n
+\cs_new_protected:Npn \SuspendTagging #1 { \tag_suspend:n {#1} }
+\cs_new_protected:Npn \ResumeTagging #1 { \tag_resume:n {#1} }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\tag_socket_use:n,
+% \tag_socket_use:nn,
+% \tag_socket_use:nnn,
+% \tag_socket_use_expandable:n,
+% \UseTaggingSocket,
+% \UseExpandableTaggingSocket,
+% }
+% Again this is not the final definition for the kernel; it is just
+% a version to get going while some parts of the kernel support are
+% still missing.
+% \begin{macrocode}
+\AddToHook{begindocument}[kernel]{
+ \cs_if_exist:NF \tag_if_active:T
+ {
+ \prg_new_conditional:Npnn \tag_if_active: { p , T , TF, F }
+ { \prg_return_false: }
+ }
+}
+% \end{macrocode}
+%
+% Dummy definitions in the kernel.
+% These definitions will get updated in \pkg{tagpdf}.
+% The default in the kernel is just to get rid of the first argument, the second is preserved if present:
+% \changes{v1.0k}{2024/10/21}{Changed behavior of two argument tagging sockets when disabled.}
+% \changes{v1.0k}{2024/10/21}{Added expandable variants}
+% \begin{macrocode}
+\cs_new:Npn \tag_socket_use_expandable:n #1 { }
+\cs_new_protected:Npn \tag_socket_use:n #1 { }
+\cs_new_protected:Npn \tag_socket_use:nn #1#2 { }
+\cs_new_protected:Npn \tag_socket_use:nnn #1#2#3 { #3 }
+\cs_new_protected:Npn \UseTaggingSocket #1 {
+ \int_case:nnF
+ { \int_use:c { c__socket_tagsupport/#1_args_int } }
+ {
+ 0 \prg_do_nothing:
+ 1 \use_none:n
+ 2 \use_ii:nn
+% \end{macrocode}
+% We do not expect tagging sockets with more than one or two
+% arguments, so for now we only provide those.
+% \begin{macrocode}
+ }
+ \ERRORusetaggingsocket % that should get a proper error message
+}
+% \end{macrocode}
+% The same as an expandable command:
+% \begin{macrocode}
+\cs_new:Npn \UseExpandableTaggingSocket #1 {
+ \int_case:nnF
+ { \int_use:c { c__socket_tagsupport/#1_args_int } }
+ {
+ 0 \prg_do_nothing:
+ 1 \use_none:n
+ 2 \use_ii:nn
+ }
+ \ERRORusetaggingsocket % that should get a proper error message
+}
+% \end{macrocode}
+%
+% \end{macro}
+%
+% \subsection{Tagging sockets}
+% This collects tagging sockets that should be generally available
+% so that they can also be used even if the tagging code is not loaded.
+%
+% \subsubsection{Tagging support for paragraph setup}
+%
+% Paragraphs are tagged through the code in the para/hooks. This code is sometimes
+% adjusted, e.g. to produce a \enquote{flattened} paragraph or to use a different tag.
+% Sockets related to such code parts are collected here.
+%
+% \begin{macro}{\l__tag_block_flattened_level_int}
+% The block code needs to know if they are nested blockenvs inside
+% a flattened environment. For this it uses a counter. Inside some contexts,
+% e.g. at the begin of a minipage or a footnote this counter must be reset.
+% We therefore define the counter here so that we can use it in the following
+% socket.
+% \begin{macrocode}
+\int_new:N \l__tag_block_flattened_level_int
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{socketdecl}{tagsupport/para/restore}
+% This socket restores the para related settings to their default. It
+% should be used in places where ``normal'' paragraph tagging must be ensured, for example
+% at the begin of a footnote.
+% \begin{macrocode}
+\NewSocket{tagsupport/para/restore}{0}
+% \end{macrocode}
+% \end{socketdecl}
+%
+% \begin{plugdecl}{default}
+% \changes{v1.0i}{2024/10/10}{Restore also paratagging (tagging/723)}
+% \begin{macrocode}
+\NewSocketPlug{tagsupport/para/restore}{default}
+ {
+ \tl_set:Nn \l__tag_para_main_tag_tl {text-unit}
+ \tl_set_eq:NN \l__tag_para_tag_tl\l__tag_para_tag_default_tl
+ \bool_set_false:N\l__tag_para_flattened_bool
+ \int_zero:N \l__tag_block_flattened_level_int
+ \bool_set_true:N \l__tag_para_bool
+ }
+\AssignSocketPlug{tagsupport/para/restore}{default}
+% \end{macrocode}
+% \end{plugdecl}
+%
+% \begin{socketdecl}{tagsupport/para/begin,tagsupport/para/end}
+% These sockets are currently defined in tagpdf. They overwrite
+% definitions in the latex-lab-block code. There is also a simpler
+% definition that probably should be a general socket too.
+% TODO: move this into lttagging.
+% \end{socketdecl}
+
+% \subsubsection{Tagging socket for targets}
+
+% \begin{socketdecl}{tagsupport/refstepcounter}
+% When tagging is active we want to track the current structure number
+% when targets are set. This will be mostly used in \cs{refstepcounter}
+% but also if targets are set manually.
+% \begin{macrocode}
+\NewSocket{tagsupport/recordtarget}{0}
+% \end{macrocode}
+% \end{socketdecl}
+%
+% \begin{plugdecl}{kernel (tagsupport/recordtarget)}
+% \begin{macrocode}
+%
+\NewSocketPlug{tagsupport/recordtarget}{kernel}
+ {
+ \tl_if_blank:VF \@currentHref
+ {
+ \prop_gput:Nee
+ \g__tag_struct_dest_num_prop
+ {\@currentHref}
+ {\tag_get:n{struct_num}}
+ }
+ }
+\AssignSocketPlug{tagsupport/recordtarget}{kernel}
+\ExplSyntaxOff
+% \end{macrocode}
+% \end{plugdecl}
+% \subsubsection{Tagging sockets for toc}
+
+% \begin{socketdecl}{tagsupport/toc/contentsline/before,
+% tagsupport/toc/contentsline/after}
+% Tagging sockets at the begin and end of contentsline.
+% They receive \emph{all} contentsline arguments as one argument
+% in four brace groups. The socket code should then use the parts it needs.
+% \begin{macrocode}
+\NewSocket{tagsupport/toc/contentsline/before}{1}
+\NewSocket{tagsupport/toc/contentsline/after}{1}
+% \end{macrocode}
+% \end{socketdecl}
+%
+% \begin{socketdecl}{tagsupport/toc/starttoc/before,
+% tagsupport/toc/starttoc/after}
+% Tagging sockets for the begin and end of start of \cs{@starttoc}.
+% They take one argument, the extension.
+% \begin{macrocode}
+\NewSocket{tagsupport/toc/starttoc/before}{1}
+\NewSocket{tagsupport/toc/starttoc/after}{1}
+% \end{macrocode}
+% \end{socketdecl}
+%
+% \begin{socketdecl}{tagsupport/toc/leaders/before,
+% tagsupport/toc/leaders/after}
+% Tagging sockets to make the dot leaders an artifact.
+% They do not take an argument.
+% \begin{macrocode}
+\NewSocket{tagsupport/toc/leaders/before}{0}
+\NewSocket{tagsupport/toc/leaders/after}{0}
+% \end{macrocode}
+% \end{socketdecl}
+%
+% \subsubsection{Tagging support for table/tabular packages}
+%
+% 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.
+%
+% \begin{socketdecl}{tagsupport/tbl/cell/begin,
+% tagsupport/tbl/cell/end,
+% tagsupport/tbl/pcell/end,
+% tagsupport/tbl/pcell/end,
+% tagsupport/tbl/row/begin,
+% tagsupport/tbl/row/end,
+% }
+% At first sockets for the begin and end of cells and table rows:
+% \begin{macrocode}
+\NewSocket{tagsupport/tbl/cell/begin}{0}
+\NewSocket{tagsupport/tbl/cell/end}{0}
+% \end{macrocode}
+%
+% \begin{macrocode}
+\NewSocket{tagsupport/tbl/row/begin}{0}
+\NewSocket{tagsupport/tbl/row/end}{0}
+% \end{macrocode}
+% Multi-line cells have their own sockets (as they start out in
+% vertical mode and need different treatment).
+% \begin{macrocode}
+\NewSocket{tagsupport/tbl/pcell/begin}{0}
+\NewSocket{tagsupport/tbl/pcell/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 used for settings such as disabling para-tagging inside the
+% table. This socket
+% can perhaps be merged later into the begin-sockets.
+% \begin{macrocode}
+\NewSocket{tagsupport/tbl/init}{0}
+% \end{macrocode}
+% \end{socketdecl}
+%
+% \begin{socketdecl}{tagsupport/tbl/init/celldata}
+% This socket is used in \cs{tbl_init_cell_data_for_table}, the command
+% that stores and initialize cell data to handle nested tables.
+% It can be used to restore similar tagging related values
+% \begin{macrocode}
+\NewSocket{tagsupport/tbl/init/celldata}{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/restore/celldata}
+% This socket is used in \cs{tbl_restore_outer_cell_data:}, the command
+% that restores cell data when quitting a nested table. It can be used to restore
+% similar tagging related values
+% \begin{macrocode}
+\NewSocket{tagsupport/tbl/restore/celldata}{0}
+% \end{macrocode}
+% \end{socketdecl}
+
+% \begin{socketdecl}{tagsupport/tbl/colspan}
+% This socket is used to manage spanning cells, e.g., a
+% \cs{multicolumn}. It expects one argument (the number of cells
+% spanned) and if tagging is enabled set appropriate tag attributes
+% in the background. We probably need a similar socket for row
+% spans eventually.
+% \begin{macrocode}
+\NewSocket{tagsupport/tbl/colspan}{1}
+% \end{macrocode}
+% \end{socketdecl}
+%
+
+
+% \begin{socketdecl}{tagsupport/tbl/hmode/begin,
+% tagsupport/tbl/hmode/end,
+% tagsupport/tbl/vmode/begin,
+% tagsupport/tbl/vmode/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 such as \env{longtable} that are always in
+% vertical mode.
+% \begin{macrocode}
+\NewSocket{tagsupport/tbl/hmode/begin}{0}
+\NewSocket{tagsupport/tbl/hmode/end}{0}
+\NewSocket{tagsupport/tbl/vmode/begin}{0}
+\NewSocket{tagsupport/tbl/vmode/end}{0}
+% \end{macrocode}
+% \end{socketdecl}
+%
+%
+%
+% \begin{socketdecl}{tagsupport/tbl/longtable/init,
+% tagsupport/tbl/longtable/finalize}
+% \env{longtable} needs its own sockets to fine tune the structure.
+% Simply switching the plug in the previous socket interferes with
+% enabling/disabling the tagging.
+% \begin{macrocode}
+\NewSocket{tagsupport/tbl/longtable/init}{0}
+\NewSocket{tagsupport/tbl/longtable/finalize}{0}
+% \end{macrocode}
+% \end{socketdecl}
+%
+% \begin{socketdecl}{tagsupport/tbl/longtable/head,
+% tagsupport/tbl/longtable/foot}
+% Header and footer boxes need special handling because they are repeatedly
+% used.
+% \begin{macrocode}
+\NewSocket{tagsupport/tbl/longtable/head}{0}
+\NewSocket{tagsupport/tbl/longtable/foot}{0}
+% \end{macrocode}
+% \end{socketdecl}
+%
+%
+% \begin{socketdecl}{tagsupport/tbl/leaders/begin,
+% tagsupport/tbl/leaders/end}
+% Sockets around leaders such as rules or dotted lines, that should
+% be tagged as artifacts, used, for example, in \cs{cline}.
+% \changes{v1.0c}{2024/07/13}{Sockets for \cs{cline} leaders added (tagging/134)}
+% \begin{macrocode}
+\NewSocket{tagsupport/tbl/leaders/begin}{0}
+\NewSocket{tagsupport/tbl/leaders/end}{0}
+% \end{macrocode}
+% \end{socketdecl}
+%
+% \subsubsection{Tagging Support for floats}
+%
+% \begin{socketdecl}{tagsupport/float/hmode/begin,
+% tagsupport/float/hmode/end}
+% These sockets are used if the float is called in
+% hmode.
+% \changes{v1.0h}{2024/09/13}{Sockets for floats added}
+% \begin{macrocode}
+\NewSocket{tagsupport/float/hmode/begin}{0}
+\NewSocket{tagsupport/float/hmode/end}{0}
+% \end{macrocode}
+% \end{socketdecl}
+%
+% \begin{socketdecl}{tagsupport/float/begin,
+% tagsupport/float/end}
+% These sockets start and stop the float structure.
+% \begin{macrocode}
+\NewSocket{tagsupport/float/begin}{0}
+\NewSocket{tagsupport/float/end}{0}
+% \end{macrocode}
+% \end{socketdecl}
+%
+%
+% \begin{socketdecl}{tagsupport/caption/begin,
+% tagsupport/caption/end}
+% These sockets are used in \cs{@makecaption}.
+% They open and close the \texttt{Caption} structure.
+% Their default plugs assume that they are used in
+% vmode. The argument of the begin socket is
+% the structure number of the parent float. If it is
+% empty the current structure number is used.
+% \begin{macrocode}
+\NewSocket{tagsupport/caption/begin}{1}
+\NewSocket{tagsupport/caption/end}{0}
+% \end{macrocode}
+% \end{socketdecl}
+%
+% \begin{socketdecl}{tagsupport/caption/label/begin,
+% tagsupport/caption/label/end}
+% These sockets are used in \cs{@makecaption} around the
+% label. Their default plugs ensure that
+% the label is outside the paragraph and that
+% the rest of the caption uses flattened para mode. If the
+% caption is not in a hbox, the \texttt{para/begin}
+% socket should follow to properly start the paragraph.
+% \begin{macrocode}
+\NewSocket{tagsupport/caption/label/begin}{0}
+\NewSocket{tagsupport/caption/label/end}{0}
+% \end{macrocode}
+% \end{socketdecl}
+%
+% \section{For lttab.dtx parked here for now}
+%
+%
+% \begin{macrocode}
+%<@@=tbl>
+\ExplSyntaxOn
+% \end{macrocode}
+%
+%
+% \subsection{Variables for row, column and span counting}
+%
+% This part needs a decision on names for various integer registers
+% as well as a decision if those should be also made available for
+% \LaTeXe{}-style packages in form of 2e names and or as
+% non-internals for the L3 programming layer.
+%
+% At the moment they are all internal but this probably has to change.
+%
+% \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 (or in case of longtable are just in front of the next
+% chunk to be processed). It is incremented by every \cs{cr}
+% including the one ending the table preamble.
+%
+% TODO: due to the gymnastics needed inside the longtable code the
+% row counter is directly exposed there rather than hidden by
+% interfaces. This needs changing when it is decided how to manage
+% these counters.
+%
+% \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 always use
+% 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_@@_missingcells_int}
+% This will contain the number of missing cells in a row:
+% \begin{macrocode}
+\int_new:N \g_@@_missing_cells_int
+% \end{macrocode}
+% \end{macro}
+%
+%
+%
+%
+%
+% \subsection{Tracing/debugging}
+%
+% \begin{macro}{\DebugTablesOn,\DebugTablesOff}
+%
+% \begin{macrocode}
+\def\DebugTablesOn{
+ \cs_set_eq:NN \@@_trace:n \typeout
+}
+\def\DebugTablesOff{
+ \cs_set_eq:NN \@@_trace:n \use_none:n
+}
+% \end{macrocode}
+%
+% \begin{macrocode}
+\cs_new_eq:NN \@@_trace:n \use_none:n
+% \end{macrocode}
+% \end{macro}
+%
+%
+%
+% \subsection{Interface commands}
+%
+% All interface commands for the cell number determination have to be
+% public on some level because they are needed in other packages as
+% well, e.g., longtable. We may or may not also want to provide 2e
+% style names for them.
+%
+% \begin{macro}{\tbl_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 \tbl_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}{\tbl_count_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
+% insertions made with \verb+!{}+ and \verb+@{}+.
+% So similar as does longtable for \cs{LT@cols} we count the
+% numbers of ampersands instead.
+% \begin{macrocode}
+\cs_new:Npn \tbl_count_table_cols: {
+ \seq_set_split:NnV\l_@@_tmpa_seq {&}\@preamble
+ \tl_gset:Ne \g_@@_table_cols_tl { \seq_count:N \l_@@_tmpa_seq }
+ \@@_trace:n { ==>~ Table~ has~ \g_@@_table_cols_tl \space columns }
+}
+% \end{macrocode}
+% \end{macro}
+%
+%
+% \begin{macro}{\l_@@_tmpa_seq}
+%
+% \begin{macrocode}
+\seq_new:N \l_@@_tmpa_seq
+% \end{macrocode}
+% \end{macro}
+%
+%
+%
+% \begin{macro}{\tbl_count_missing_cells:n}
+%
+% We might have the situation that some table package has not
+% implemented the \cs{tbl_count_table_cols:} in which case
+% \cs{g_@@_table_cols_tl} would always be zero and we would get an
+% error below when we try to determine the missing cells, so bypass
+% that calculation if we aren't doing tagging (there the packages
+% should have the proper code added). Recall that this is code,
+% that is called by \verb=\\= and an old table packagee might rely
+% on whatever the \LaTeX{} kernel offers here.
+% \begin{macrocode}
+\cs_new:Npn \tbl_count_missing_cells:n #1 {
+ \tag_if_active:T {
+ \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 \ERRORmissingcells % should not happen
+ \@@_trace:n{==>~
+ (#1)~
+ This~ row~ needs~
+ \int_use:N \g_@@_missing_cells_int \space
+ additional~ cell(s)
+ }
+ }
+ }
+}
+% \end{macrocode}
+% \end{macro}
+%
+%
+% \begin{macro}{\tbl_save_outer_table_cols:}
+%
+% \begin{macrocode}
+\cs_new_protected:Npn \tbl_save_outer_table_cols: {
+ \tl_set_eq:NN \l_@@_saved_table_cols_tl \g_@@_table_cols_tl
+}
+% \end{macrocode}
+% \end{macro}
+%
+%
+% \begin{macro}{\tbl_init_cell_data_for_table:}
+%
+% \begin{macrocode}
+\cs_new_protected:Npn \tbl_init_cell_data_for_table: {
+ \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
+%
+ \@@_trace:n { ==>~ 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}
+% Tagging has to initialize cell data too.
+% \begin{macrocode}
+ \UseTaggingSocket{tbl/init/celldata}
+% \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}
+}
+% \end{macrocode}
+% \end{macro}
+%
+%
+%
+%
+% \begin{macro}{\tbl_update_cell_data_for_next_row:}
+% \begin{macrocode}
+\cs_new_protected:Npn \tbl_update_cell_data_for_next_row: {
+ \int_gincr:N \g_@@_row_int % this row about to start
+ \int_gzero:N \g_@@_col_int % we are before first col
+}
+% \end{macrocode}
+% \end{macro}
+%
+%
+% \begin{macro}{\tbl_init_cell_data_for_row:}
+% If we start processing a cell in the first column we set
+% \cs{g_@@_col_int} to \texttt{1} as we are no longer "at" but "in"
+% the first column. We also set \cs{g_@@_span_tl} to its default
+% value (not spanning cells).
+% \begin{macrocode}
+\cs_new_protected:Npn \tbl_init_cell_data_for_row: {
+ \int_gset:Nn \g_@@_col_int {1}
+ \tl_gset:Nn \g_@@_span_tl {1}
+}
+% \end{macrocode}
+% \end{macro}
+%
+%
+% \begin{macro}{\tbl_if_row_was_started:T,
+% \tbl_if_row_was_started:TF}
+% We use \cs{g_@@_col_int} equal zero to indicate that we are just
+% after a TR (i.e.n between rows or at the very beginning of the
+% table). Using the row
+% count is not so good as longtable may split the table in chunks.
+%
+% These conditionals have to be expandable (i.e., unprotected) as
+% they are sometimes executed when \TeX{} is scanning inside a table.
+% \begin{macrocode}
+\cs_new:Npn \tbl_if_row_was_started:T {
+ \int_compare:nNnT \g_@@_col_int > 0
+}
+\cs_new:Npn \tbl_if_row_was_started:TF {
+ \int_compare:nNnTF \g_@@_col_int > 0
+}
+% \end{macrocode}
+% \end{macro}
+%
+%
+%
+% \begin{macro}{\tbl_gzero_row_count:,\tbl_gincr_row_count:,\tbl_gdecr_row_count:}
+% This here is basically a temporary interface. What it will be in
+% the end depends on what we decide concerning exposing row and
+% column counters, if they stay internal we need something like
+% this here (perhaps using \texttt{gincr} etc, or perhaps some
+% other names in the first place).
+% \begin{macrocode}
+\cs_new_protected:Npn \tbl_gzero_row_count: {
+ \int_gzero:N \g_@@_row_int
+}
+\cs_new_protected:Npn \tbl_gincr_row_count: {
+ \int_gincr:N \g_@@_row_int
+}
+\cs_new_protected:Npn \tbl_gdecr_row_count: {
+ \int_gdecr:N \g_@@_row_int
+}
+% \end{macrocode}
+% \end{macro}
+%
+%
+% \begin{macro}{\tbl_inbetween_rows:}
+% Again name is not really brilliant so far.
+% \begin{macrocode}
+\cs_new_protected:Npn \tbl_inbetween_rows: {
+ \int_gzero:N \g_@@_col_int
+}
+% \end{macrocode}
+% \end{macro}
+%
+%
+%
+%
+%
+%
+% \begin{macro}{\tbl_restore_outer_cell_data:}
+%
+% \begin{macrocode}
+\cs_new_protected:Npn \tbl_restore_outer_cell_data: {
+ \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
+ \UseTaggingSocket{tbl/restore/celldata}
+ \@@_trace:n { ==>~ 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 }
+ )
+ }
+}
+% \end{macrocode}
+% \end{macro}
+%
+%
+%
+% \begin{macro}{\tbl_update_multicolumn_cell_data:n}
+% This macro updates \cs{g_@@_col_int} and \cs{g_@@_span_tl} inside
+% a \cs{multicolumn} and possibly calls the tagging socket
+% \texttt{tbl/row/begin}.
+% \begin{macrocode}
+\cs_new_protected:Npn \tbl_update_multicolumn_cell_data:n #1 {
+% \end{macrocode}
+% We execute socket for tagging only if this \cs{multicolumn}
+% replaces the preamble of the first column. In that case we also have
+% to set \cs{g_@@_col_int} to 1 because this is no longer done in the
+% preamble for the cell either.
+% \begin{macrocode}
+ \int_compare:nNnTF \g_@@_col_int = 0
+ {
+ \UseTaggingSocket{tbl/row/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}
+% \end{macro}
+%
+%
+%
+% \begin{macro}{\tbl_crcr:n}
+% This macro is used instead of the usual \cs{crcr} at the end of a
+% table. It is deliberately defined without protection because it
+% may get expanded by the scanning mechanism of low-level \TeX{}
+% after a final \cs{cr} (aka \verb=\\=) in the table. In that case
+% it shouldn't stop the expansion and the conditional inside will
+% be false, thus it just vanishes without doing anything. If there
+% are missing cells (in which case we also haven't see \cs{cr} yet)
+% the macro \cs{tbl_count_missing_cells:n} is executed and
+% then the row is finished with a final \cs{cr}.
+% \begin{macrocode}
+\cs_new:Npn \tbl_crcr:n #1 {
+ \int_compare:nNnT \g_@@_col_int > 0
+ {
+ \tbl_count_missing_cells:n {#1}
+ }
+% \end{macrocode}
+% Even if we are at the start of a row we my have to do a \cs{cr},
+% so we do a \cs{crcr} always at the end.
+% \changes{v1.0b}{2024/06/10}
+% {Always issue a \cs{crcr} even if we are at the start of a
+% row to avoid problems with tabulary and similar code}
+% \begin{macrocode}
+ \crcr
+}
+% \end{macrocode}
+% \end{macro}
+%
+%
+%
+% \begin{macrocode}
+\ExplSyntaxOff
+%<@@=>
+% \end{macrocode}
+%
+% \changes{v1.0h}{2024/09/20}{moved \cs{@kernel@refstepcounter} into ltxref}
+% This is needed for \pkg{longtable} because \cs{refstepcounter} is
+% setting up a target when \pkg{hyperref} is loaded and we don't
+% want that in \pkg{longtable}.%%
+% Prevent longtable patching by hyperref until hyperref does so automatically:
+% \begin{macrocode}
+\def\hyper@nopatch@longtable{}
+% \end{macrocode}
+%
+%
+% Should there be a module?
+%
+% \begin{macrocode}
+%<latexrelease>\NewModuleRelease{2024/06/01}{lttagging}
+%<latexrelease> {Tagging support}
+% \end{macrocode}
+%
+%
+%
+%
+% \begin{macrocode}
+%<latexrelease>\IncludeInRelease{0000/00/00}{lttagging}%
+%<latexrelease> {Undo tagging support}
+%<latexrelease>
+%<latexrelease>
+%<latexrelease>
+%<latexrelease>\EndModuleRelease
+% \end{macrocode}
+%
+% \begin{macrocode}
+%</2ekernel|latexrelease>
+% \end{macrocode}
+%
+% \Finale
+%