% \iffalse meta-comment % %% File: latex-lab-table.dtx (C) Copyright 2023-2024 LaTeX Project % % It may be distributed and/or modified under the conditions of the % LaTeX Project Public License (LPPL), either version 1.3c of this % license or (at your option) any later version. The latest version % of this license is in the file % % https://www.latex-project.org/lppl.txt % % % The development version of the bundle can be found below % % https://github.com/latex3/latex2e/required/latex-lab % % for those people who are interested or want to report an issue. % \def\ltlabtbldate{2024-08-10} \def\ltlabtblversion{0.85m} %<*driver> \documentclass{l3doc} \EnableCrossrefs \CodelineIndex \begin{document} \DocInput{latex-lab-table.dtx} \end{document} % % % \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. % % \subsection{Loading} % 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. % % 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. % % 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 work fully with the tagging, % but when it does, colors inside the table are currently ignored. If such a color % has a semantic meaning (like \enquote{important value}) this meaning will be lost. % % \subsection{Untagged and presentation tables} % If a table should not be tagged as table, for example because it is merely used % as a layout to ensure that the content is properly aligned % or because it is a not yet (fully) supported table structure, % the tagging can be disabled with % \verb|\tagpdfsetup{table/tagging=false}| or with \verb|\tagpdfsetup{table/tagging=presentation}|% % \footnote{The key has been renamed. The old name `table-tagging` still works but is % deprecated. The value \texttt{presentation} refers to the ARIA role \enquote{presentation}.} % The first option disables the table tagging code and the content of the tabular % is then treated more or less like running text. This works ok for simple tables using % only hmode-cells (l/c/r) with normal text content, but fails if the table uses vmode-cells % (p/m/b). In such cases the second option works better: it keeps the tagging code active % but changes the tag names to \texttt{Div} and the grouping structure \texttt{NonStruct}. It also % (re)sets the \texttt{table/header-rows} key to empty. % The key should currently only be used in a group as there is no key (yet) to reset to the default % tag names. % % \subsection{Header rows and columns} % There is some basic support\footnote{This is not meant to be the % final interface, though.} for headers. With\footnote{The old key name \texttt{table-header-rows} still % works but is deprecated.} % \begin{quote} % \verb|\tagpdfsetup{table/header-rows={|\meta{list of row numbers}\verb|}| % \verb|\tagpdfsetup{table/header-columns={|\meta{list of column numbers}\verb|}| % \end{quote} % you can % declare which (absolute) row and column numbers should be tagged as header rows and header columns. % It applies to all tables until it is changed to a different list of row or % columns numbers or undone by setting % the keys to \meta{empty}. % A row or column number can be % negative, then the counting starts from the end of the table. % 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. % % \subsection{Spanning of cells} % % \cs{multicolumn} is supported out of the box and will create a structure with a % suitable \texttt{ColSpan} attribute\footnote{The code uses actually an attribute \emph{class}. % The validator PAC doesn't handle this correctly currently and complains about a missing attribute.} % % For cells spanning rows some preliminary support exists\footnote{The interface is bound to change!}: % If you add % \begin{quote} % \verb|\tagpdfsetup{table/multirow={|\meta{number of rows}\verb|}| % \end{quote} % to a cell the code will add the suitable \texttt{RowSpan} attribute and suppress the tagging of affected % cells in the following rows. This will also work if the current cell is a \cs{multicolumn}, then % the cell will span both rows and columns. It is the duty of the author to % ensure that all cells below and covered by a \texttt{RowSpan} are empty! % The code neither checks that nor does % it tries to suppress content. % % 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 can only be added manually (the \pkg{multirow} package is untested). % % \item The \pkg{colortbl} package breaks tagging if there are nested tables. % % \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 fully tested, % that includes packages that change rules like \pkg{booktabs}, \pkg{hhline}, % \pkg{arydshln}, \pkg{hvdashln}. Some problems have been resolved, either % in the packages or through a firstaid which can be loaded the \verb+testphase=firstaid+. % % \item \env{longtable} currently only works with lualatex. % With other engines it breaks as its output % routine clashes with the code which closes open MC-chunks at pagebreaks and % if this is resolved there will probably be problems with the head and foot boxes % (but this can't be tested currently). % % \item Not every table should be tagged as a Table structure, often they are % only used as layout help, e.g. to align authors in a title pages. In such uses % the tagging of the table must be deactivated with \verb|\tagpdfsetup{table/tagging=false}| % or \verb|\tagpdfsetup{table/tagging=presentation}|. % % \item Only simple header rows and columns are currently supported. Complex headers with % subheaders will be handled later as that needs some syntax changes. Tables % with more than one header row or column are probably not pdf/UA as the headers array in % the cells is missing. % % \item A \pkg{longtable} \cs{caption} is currently simply formatted as a multicolumn and % not tagged as a \texttt{Caption} structure. % % \item The \pkg{caption} package will quite probably break the \pkg{longtable} caption. % % \item The setup for \pkg{longtable} requires lots of patches to internal \pkg{longtable} % commands and so can easily break if other packages try to patch \pkg{longtable} too. % % \item The \env{longtable} environment supports footnotes in p-type columns, but it hasn't been % tested yet if this works also with the tagging code. % % \item Vertical boxes (\cs{parbox}, \texttt{minipage}, \ldots) inside cells can be problematic. % % \item The code is quite noisy and fills the log with lots of % messages.\footnote{Helpful for us at this stage.} % \end{itemize} % % % % \section{Introduction} % % % \section{Technical details and problems} % % The implementation has to take care of various details. % % \subsection{TODOs} % % \begin{itemize} % \item % Test \texttt{array-006-longtable.lvt} and % \texttt{array-007-longtable.lvt} have errors with pdftex (para % tagging) % % \item % Instead of before/after hooks we should add sockets directly % into the code. % % \item Debugging code and messages must be improved. % % \item Cells need an \texttt{Headers} array. % % \item Row spans should be supported (but perhaps need syntax support) % % \item Longtable captions should be properly supported. % % % \item More packages must be tested. % \end{itemize} % % % \section{Implementation} % \begin{macrocode} %<@@=tbl> %<*package> % \end{macrocode} % \begin{macrocode} \ProvidesExplPackage {latex-lab-testphase-table} {\ltlabtbldate} {\ltlabtblversion} {Code related to the tagging of tables} % \end{macrocode} % This builds on \pkg{array} so we load it by default: % \begin{macrocode} \RequirePackage{array} % \end{macrocode} % % % \subsection{Variables} % \begin{macro} % { % \l_@@_celltag_tl % \l_@@_pcelltag_tl % ,\l_@@_rowtag_tl % ,\l_@@_table_tl % ,\l_@@_cellattribute_tl % ,\l_@@_rowattribute_tl % ,\l_@@_tmpa_clist % ,\l_@@_tmpa_tl % ,\l_@@_tmpa_str % } % This is for the celltag, e.g. TD or TH: % \begin{macrocode} \tl_new:N \l_@@_celltag_tl \tl_set:Nn \l_@@_celltag_tl {TD} \tl_new:N \l_@@_pcelltag_tl \tl_set:Nn \l_@@_pcelltag_tl {TD} % \end{macrocode} % For the rowtag, probably always TR: % \begin{macrocode} \tl_new:N \l_@@_rowtag_tl \tl_set:Nn \l_@@_rowtag_tl {TR} % \end{macrocode} % For the tabletag, probably always Table: % \begin{macrocode} \tl_new:N \l_@@_tabletag_tl \tl_set:Nn \l_@@_tabletag_tl {Table} % \end{macrocode} % And here cell and row attributes: % \begin{macrocode} \tl_new:N \l_@@_cellattribute_tl \tl_set:Nn \l_@@_cellattribute_tl {} \tl_new:N \l_@@_rowattribute_tl \tl_set:Nn \l_@@_rowattribute_tl {} % \end{macrocode} % Variable to store cell info like which cell must be skipped when tagging multirows. % \begin{macrocode} \prop_new:N\g_@@_untagged_cells_prop \prop_new:N\l_@@_saved_untagged_cells_prop % \end{macrocode} % Temp variables % \begin{macrocode} \clist_new:N \l_@@_tmpa_clist \tl_new:N \l_@@_tmpa_tl \str_new:N \l_@@_tmpa_str % \end{macrocode} % \end{macro} % % \subsection{Tagging support sockets} % % % This are the standard plugs for tagging of cells and rows. % % The following two sockets are defined in lttagging, but we check % for their existence until the release 11/2024. % \begin{macrocode} \str_if_exist:cF { l__socket_tagsupport/tbl/init/celldata_plug_str } { \NewSocket{tagsupport/tbl/init/celldata}{0} \NewSocket{tagsupport/tbl/restore/celldata}{0} } % \end{macrocode} % \begin{plugdecl}{default} % This socket is used inside \cs{tbl_init_cell_data_for_table} for the % case that this a nested table in a cell. % \begin{macrocode} \NewSocketPlug{tagsupport/tbl/init/celldata}{default} { \prop_set_eq:NN\l_@@_saved_untagged_cells_prop \g_@@_untagged_cells_prop \prop_gclear:N \g_@@_untagged_cells_prop } \AssignSocketPlug{tagsupport/tbl/init/celldata}{default} % \end{macrocode} % \end{plugdecl} % \begin{plugdecl}{default} % This socket is used inside \cs{tbl_restore_outer_cell_data:} and restores % values when a nested table is ended. % \begin{macrocode} \NewSocketPlug{tagsupport/tbl/restore/celldata}{default} { \prop_gset_eq:NN\g_@@_untagged_cells_prop \l_@@_saved_untagged_cells_prop } \AssignSocketPlug{tagsupport/tbl/restore/celldata}{default} % \end{macrocode} % \end{plugdecl} % % \begin{plugdecl}{TD} % \begin{macrocode} \NewSocketPlug{tagsupport/tbl/cell/begin}{TD} { % \end{macrocode} % Next line was previously outside of the plug, so if we want to execute it % always even if the noop plug is in force this needs a different solution. % \begin{macrocode} \@@_show_curr_cell_data: % \end{macrocode} % We test if the cell should be tagged at all. % \begin{macrocode} \@@_if_tag_cell:nnT {\g_@@_row_int } { \g_@@_col_int } { % \end{macrocode} % this sets row headers % \begin{macrocode} \clist_if_in:NVT \l_@@_header_columns_clist\g_@@_col_int { \tl_set:Nn \l_@@_celltag_tl {TH} \tl_set:Ne \l_@@_cellattribute_tl {\l_@@_cellattribute_tl,TH-row} } % \end{macrocode} % Here we handle negative value for row headers: % \begin{macrocode} \tl_set:Ne \l_@@_tmpa_tl {\int_eval:n { \g_@@_col_int - 1 - \g_@@_table_cols_tl }} \clist_if_in:NoT \l_@@_header_columns_clist { \l_@@_tmpa_tl } { \tl_set:Nn \l_@@_celltag_tl {TH} \tl_set:Ne \l_@@_cellattribute_tl {\l_@@_cellattribute_tl,TH-row} } \tag_struct_begin:n { tag =\l_@@_celltag_tl, attribute-class ={\l_@@_cellattribute_tl} } \seq_gput_right:Ne \g_@@_struct_cur_seq { \tag_get:n {struct_num} } % \end{macrocode} % we store the cells of multicolumns as negative number. This allow to skip them % or to use them as needed. % \begin{macrocode} \int_step_inline:nn { \g_@@_span_tl - 1 } { \seq_gput_right:Ne \g_@@_struct_cur_seq { -\tag_get:n {struct_num} } } \tag_mc_begin:n{} } } % \end{macrocode} % \end{plugdecl} % % \begin{plugdecl}{TD} % \begin{macrocode} \NewSocketPlug{tagsupport/tbl/cell/end}{TD} { \@@_if_tag_cell:nnT {\g__tbl_row_int } { \g__tbl_col_int } { \tag_mc_end: \tag_struct_end: } } % \end{macrocode} % \end{plugdecl} % % In p-columns we need a slightly different plug which reactivates the % paragraph tagging. % \begin{plugdecl}{TDpbox} % \begin{macrocode} \NewSocketPlug{tagsupport/tbl/pcell/begin}{TDpbox} { \@@_show_curr_cell_data: \@@_if_tag_cell:nnT {\g__tbl_row_int } { \g__tbl_col_int } { \clist_if_in:NVT \l_@@_header_columns_clist\g_@@_col_int { \tl_set:Nn \l_@@_celltag_tl {TH} \tl_set:Ne \l_@@_cellattribute_tl {\l_@@_cellattribute_tl,TH-row} } \tag_struct_begin:n { tag =\l_@@_pcelltag_tl, attribute-class ={\l_@@_cellattribute_tl} } \seq_gput_right:Ne \g_@@_struct_cur_seq { \tag_get:n {struct_num} } \int_step_inline:nn { \g_@@_span_tl - 1 } { \seq_gput_right:Ne \g_@@_struct_cur_seq { -\tag_get:n {struct_num} } } \tagpdfparaOn \tl_set:Nn \l__tag_para_main_tag_tl {Div} } } % \end{macrocode} % \end{plugdecl} % % \begin{plugdecl}{TDpbox} % \begin{macrocode} \NewSocketPlug{tagsupport/tbl/pcell/end}{TDpbox} { \@@_if_tag_cell:nnT {\g_@@_row_int } { \g_@@_col_int } { \tag_struct_end: \legacy_if:nT {@endpe}{\par} \mode_if_vertical:T{ \tagpdfparaOff } } } % \end{macrocode} % \end{plugdecl} % % \begin{plugdecl}{TR} % \begin{macrocode} \NewSocketPlug{tagsupport/tbl/row/begin}{TR} { \seq_gclear:N \g_@@_struct_cur_seq \tag_struct_begin:n { tag =\l_@@_rowtag_tl, attribute-class=\l_@@_rowattribute_tl } \seq_gput_right:Ne \g_@@_struct_rows_seq { \tag_get:n {struct_num} } } % \end{macrocode} % \end{plugdecl} % % \begin{plugdecl}{TR} % \begin{macrocode} \NewSocketPlug{tagsupport/tbl/row/end}{TR} { \tag_if_active:T { \@@_add_missing_cells: \seq_gput_right:Ne \g_@@_struct_cells_seq { \seq_use:Nn \g_@@_struct_cur_seq {,} } \int_compare:nNnTF { \g_@@_row_int } = { \seq_count:N \g_@@_struct_cells_seq } { \@@_trace:n {==>~ structure~stored~for~row~\int_use:N\g_@@_row_int :~ \seq_use:Nn \g_@@_struct_cur_seq {,} } } { \ERRORtbl/row } % should not happen ... \tag_struct_end: } } % \end{macrocode} % \end{plugdecl} % % And the plugs for the table as whole. The code can be different for % normal tables which can also be used inline and nested and % \enquote{vmode} tables like longtable. % % \begin{macro}{\l__tag_block_flattened_level_int} % Count the levels of nested blockenvs starting with the first that % is \enquote{flattened}. The counter is defined in lttagging.dtx, % but until the next release 11/24 we set it up here too so that we can % use it in the following socket % \begin{macrocode} \int_if_exist:NF \l__tag_block_flattened_level_int { \int_new:N \l__tag_block_flattened_level_int } % \end{macrocode} % \end{macro} % % \begin{plugdecl}{Table} % Inside a TD or TR Part is not allowed as child. For structures that restore % paragraph settings we therefore need a special plug that adjust the settings. % Currently we set the para main tag to Div. This leads to double Div-structures % but flattening the paragraph doesn't work, it errors if there is a list inside. % \begin{macrocode} \NewSocketPlug{tagsupport/para/restore}{Table} { \tl_set:Nn \l__tag_para_main_tag_tl {Div} \tl_set_eq:NN \l__tag_para_tag_tl\l__tag_para_tag_default_tl \bool_set_true:N \l__tag_para_bool \bool_set_false:N \l__tag_para_flattened_bool \int_zero:N \l__tag_block_flattened_level_int } % \end{macrocode} % \end{plugdecl} % % \begin{plugdecl}{Table} % Inside a table we currently only disable paratagging. We assume % that these sockets are called in a group, so there is no % need to reenable paratagging. % \begin{macrocode} \NewSocketPlug{tagsupport/tbl/init}{Table} { \bool_set_false:N \l__tag_para_bool \AssignSocketPlug{tagsupport/para/restore}{Table} % \end{macrocode} % We also initialize the structure data variables a this point. % \begin{macrocode} \@@_init_struct_data: } % \end{macrocode} % \end{plugdecl} % % % \begin{plugdecl}{Table} % This plug will fine tune the structure. % \begin{macrocode} \NewSocketPlug{tagsupport/tbl/finalize}{Table} { \@@_set_header_rows: % \end{macrocode} % Similarly, we restore the outer values of the structure data when % we leave the table. % \begin{macrocode} \@@_restore_struct_data: } % \end{macrocode} % \end{plugdecl} % % % % % \begin{plugdecl}{Table} % This plug will initialize the structure in longtable. % \begin{macrocode} \NewSocketPlug{tagsupport/tbl/longtable/init}{Table} { \seq_gclear:N\g_@@_struct_rows_seq \seq_gclear:N\g_@@_struct_cells_seq \seq_gclear:N\g_@@_struct_cur_seq \seq_gclear:N\g_@@_LT@firsthead_rows_seq \seq_gclear:N\g_@@_LT@head_rows_seq \seq_gclear:N\g_@@_LT@lastfoot_rows_seq \seq_gclear:N\g_@@_LT@foot_rows_seq } % \end{macrocode} % \end{plugdecl} % % % \begin{plugdecl}{Table} % This plug will fine tune the structure in longtable. % \begin{macrocode} \NewSocketPlug{tagsupport/tbl/longtable/finalize}{Table} { % \end{macrocode} % If neither \cs{endhead} nor \cs{endfirsthead} has been used % we use the standard header command: % \begin{macrocode} \bool_lazy_and:nnTF { \seq_if_empty_p:N \g_@@_LT@head_rows_seq } { \seq_if_empty_p:N \g_@@_LT@firsthead_rows_seq } { \@@_set_header_rows: } % \end{macrocode} % Otherwise, if firsthead has not been used we use head. For this % we simple retrieve the row numbers and then call the header % command. % \begin{macrocode} { \seq_if_empty:NTF \g_@@_LT@firsthead_rows_seq { \clist_set:Ne \l_@@_header_rows_clist {\seq_use:Nn \g_@@_LT@head_rows_seq {,}} \@@_set_header_rows: } % \end{macrocode} % In the other case we use firsthead. % \begin{macrocode} { \clist_set:Ne \l_@@_header_rows_clist { \seq_use:Nn \g_@@_LT@firsthead_rows_seq {,} } \@@_set_header_rows: % \end{macrocode} % Additionally we have to remove the head to avoid duplication. The % one option here is to remove the rows from the kid sequence of % the table (which will lead to orphaned structure elements), the % other to make them artifact. For now we use the first option for % pdf 1.7 and the second for pdf 2.0. % \begin{macrocode} \pdf_version_compare:NnTF < {2.0} { \seq_map_inline:Nn \g_@@_LT@head_rows_seq { \seq_gset_item:cnn {g__tag_struct_kids_ \g_@@_struct_table_tl _seq} { ##1 } {} % \end{macrocode} % Not sure if needed, but if needed we can remove also the P tag. % This is currently disabled as it produce warnings. TODO: This % needs also a tagpdf command which takes care of debug code. % \begin{macrocode} \tl_set:Ne \l_@@_tmpa_tl { \seq_item:Nn\g_@@_struct_rows_seq {##1} } \prop_if_exist:cT { g__tag_struct_ \l_@@_tmpa_tl _prop } { %\prop_gremove:cn {g__tag_struct_ \l_@@_tmpa_tl _prop} {P} } } } { \seq_map_inline:Nn \g_@@_LT@head_rows_seq { \tl_set:Ne \l_@@_tmpa_tl { \seq_item:Nn\g_@@_struct_rows_seq {##1} } \prop_if_exist:cT { g__tag_struct_ \l_@@_tmpa_tl _prop } { \__tag_struct_prop_gput:onn { \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 } { \__tag_struct_prop_gput:onn {\l_@@_tmpa_tl} {S}{/Artifact} } } } } } } % \end{macrocode} % \end{plugdecl} % % % % \begin{plugdecl}{Table} % % We must avoid that the reuse of the header foot box leads to duplicated % content, thus reset attribute of the box: % \begin{macrocode} \NewSocketPlug{tagsupport/tbl/longtable/head}{Table} { \tagmcbegin{artifact} \tag_mc_reset_box:N\LT@head \tagmcend } % \end{macrocode} % \end{plugdecl} % % \begin{plugdecl}{Table} % \begin{macrocode} \NewSocketPlug{tagsupport/tbl/longtable/foot}{Table} { \tagmcbegin{artifact} \tag_mc_reset_box:N \LT@foot \tagmcend } % \end{macrocode} % \end{plugdecl} % % % % % \begin{plugdecl}{Table} % \begin{macrocode} \NewSocketPlug{tagsupport/tbl/hmode/begin}{Table} { \tag_mc_end_push: % \end{macrocode} % Close the P-chunk. This assumes that para-tagging is active. % For nested tables that is not necessarily true, so we test for it. % \begin{macrocode} \bool_lazy_and:nnT { \bool_if_exist_p:N \l__tag_para_bool } { \l__tag_para_bool } { \tag_struct_end:n { text } } \tag_struct_begin:n {tag=\l_@@_tabletag_tl} \tl_gset:Ne \g_@@_struct_table_tl { \tag_get:n {struct_num} } } % \end{macrocode} % \end{plugdecl} % % \begin{plugdecl}{Table} % \begin{macrocode} \NewSocketPlug{tagsupport/tbl/hmode/end}{Table} { \tag_struct_end: % \end{macrocode} % reopen the P-chunk. This assumes that para-tagging is active. % For nested tables that is not necessarily true, so we test for it. % \begin{macrocode} \bool_lazy_and:nnT { \bool_if_exist_p:N \l__tag_para_bool } { \l__tag_para_bool } { \tag_struct_begin:n { tag=\l__tag_para_tag_tl } } \tag_mc_begin_pop:n{} } % \end{macrocode} % \end{plugdecl} % % \begin{plugdecl}{Table} % \begin{macrocode} \NewSocketPlug{tagsupport/tbl/vmode/begin}{Table} { \tag_struct_begin:n {tag=\l_@@_tabletag_tl} \tl_gset:Ne \g_@@_struct_table_tl { \tag_get:n {struct_num} } } % \end{macrocode} % \end{plugdecl} % % \begin{plugdecl}{Table} % \begin{macrocode} \NewSocketPlug{tagsupport/tbl/vmode/end}{Table} { \tag_struct_end: \par } % \end{macrocode} % \end{plugdecl} % % % \begin{plugdecl}{code} % This socket takes a number, checks if is larger than one, % checks if the colspan attribute already exists (we can't predefine an % arbitrary number), and updates \cs{l_@@_cellattribute_tl}. % \begin{macrocode} \NewSocketPlug{tagsupport/tbl/colspan}{code} { \int_compare:nNnT {#1}>{1} { \prop_get:NeNF \g__tag_attr_entries_prop {colspan-\int_eval:n{#1}} \l_@@_tmpa_tl { \__tag_attr_new_entry:ee {colspan-\int_eval:n{#1}} {/O /Table /ColSpan~\int_eval:n{#1}} } \tl_set:Ne \l_@@_cellattribute_tl {\l_@@_cellattribute_tl,colspan-\int_eval:n{#1}} } } % \end{macrocode} % \end{plugdecl} % % % % % \begin{plugdecl}{code} % The sockets will be in lttagging.dtx, but that may only happen in % the next main release, so for now we test if they are in the % format and if not define them now. % \begin{macrocode} \str_if_exist:cF { l__socket_tagsupport/tbl/leaders/end_plug_str } { \NewSocket{tagsupport/tbl/leaders/begin}{0} \NewSocket{tagsupport/tbl/leaders/end}{0} } % \end{macrocode} % % \begin{macrocode} \NewSocketPlug{tagsupport/tbl/leaders/begin}{code} { \tag_mc_begin:n{artifact} } \NewSocketPlug{tagsupport/tbl/leaders/end}{code} { \tag_mc_end: } % \end{macrocode} % \end{plugdecl} % % % %\subsection{Environments} % % Currently we support only tabular, tabular*, tabularx and longtable % (and possibly environments build directly on top of them). % % The \env{array} environment is math. So we disable table tagging for now. % We use the command hook to catch also cases where \cs{array} is used directly % in other environments like matrix environments. % Perhaps table tagging should be disable for math generally, but then % we have to handle text insertions. % \begin{macrocode} \AddToHook{cmd/array/before}{\__tag_tbl_disable:} % \end{macrocode} % % % \subsection{Interfaces to tagging} % % \subsubsection{Tagging helper commands} % \subsubsection{Disabling/enabling} % % For now we have only the option true/false but this will probably be extended % to allow different setups like first row header etc. % \begin{macro}{\__tag_tbl_disable:} % % \begin{macrocode} \cs_new_protected:Npn \__tag_tbl_disable: { \AssignSocketPlug{tagsupport/tbl/cell/begin}{noop} \AssignSocketPlug{tagsupport/tbl/cell/end}{noop} \AssignSocketPlug{tagsupport/tbl/pcell/begin}{noop} \AssignSocketPlug{tagsupport/tbl/pcell/end}{noop} \AssignSocketPlug{tagsupport/tbl/row/begin}{noop} \AssignSocketPlug{tagsupport/tbl/row/end}{noop} \AssignSocketPlug{tagsupport/tbl/init}{noop} \AssignSocketPlug{tagsupport/tbl/finalize}{noop} \AssignSocketPlug{tagsupport/tbl/longtable/init}{noop} \AssignSocketPlug{tagsupport/tbl/longtable/head}{noop} \AssignSocketPlug{tagsupport/tbl/longtable/foot}{noop} \AssignSocketPlug{tagsupport/tbl/longtable/finalize}{noop} \AssignSocketPlug{tagsupport/tbl/hmode/begin}{noop} \AssignSocketPlug{tagsupport/tbl/hmode/end}{noop} \AssignSocketPlug{tagsupport/tbl/vmode/begin}{noop} \AssignSocketPlug{tagsupport/tbl/vmode/end}{noop} \AssignSocketPlug{tagsupport/tbl/colspan}{noop} \AssignSocketPlug{tagsupport/tbl/leaders/begin}{noop} \AssignSocketPlug{tagsupport/tbl/leaders/end}{noop} } % \end{macrocode} % \end{macro} % \begin{macro}{\__tag_tbl_enable:} % % \begin{macrocode} \cs_new_protected:Npn \__tag_tbl_enable: { \AssignSocketPlug{tagsupport/tbl/cell/begin}{TD} \AssignSocketPlug{tagsupport/tbl/cell/end}{TD} \AssignSocketPlug{tagsupport/tbl/pcell/begin}{TDpbox} \AssignSocketPlug{tagsupport/tbl/pcell/end}{TDpbox} \AssignSocketPlug{tagsupport/tbl/row/begin}{TR} \AssignSocketPlug{tagsupport/tbl/row/end}{TR} \AssignSocketPlug{tagsupport/tbl/init}{Table} \AssignSocketPlug{tagsupport/tbl/finalize}{Table} \AssignSocketPlug{tagsupport/tbl/longtable/head}{Table} \AssignSocketPlug{tagsupport/tbl/longtable/foot}{Table} \AssignSocketPlug{tagsupport/tbl/longtable/init}{Table} \AssignSocketPlug{tagsupport/tbl/longtable/finalize}{Table} \AssignSocketPlug{tagsupport/tbl/hmode/begin}{Table} \AssignSocketPlug{tagsupport/tbl/hmode/end}{Table} \AssignSocketPlug{tagsupport/tbl/vmode/begin}{Table} \AssignSocketPlug{tagsupport/tbl/vmode/end}{Table} \AssignSocketPlug{tagsupport/tbl/colspan}{code} \AssignSocketPlug{tagsupport/tbl/leaders/begin}{code} \AssignSocketPlug{tagsupport/tbl/leaders/end}{code} } % \end{macrocode} % \end{macro} % \begin{macrocode} % See tagpdfsetup-keys.md in tagpdf/doc for the naming scheme. \keys_define:nn { __tag / setup } { table/tagging .choices:nn = { true, on } { \__tag_tbl_enable: }, table/tagging .choices:nn = { false, off } { \__tag_tbl_disable: }, table/tagging .choice:, table/tagging / presentation .code:n = { \__tag_tbl_enable: \tl_set:Nn\l_@@_rowtag_tl {NonStruct} \tl_set:Nn\l_@@_pcelltag_tl {NonStruct} \tl_set:Nn\l_@@_celltag_tl {text} \tl_set:Nn\l_@@_tabletag_tl {Div} \clist_clear:N \l_@@_header_rows_clist \clist_clear:N \l_@@_header_columns_clist }, table/tagging .default:n = true, table/tagging .initial:n = true } % \end{macrocode} % This are the old key names kept for now for % compatibility. They will got at some time. % \begin{macrocode} \keys_define:nn { __tag / setup } { table-tagging .meta:n = {table/tagging={#1}} } % \end{macrocode} % % % \subsubsection{Header support} % % Accessible table must have header cells declaring the meaning of the % data in a row or column. To allow a data cell to find it header cell(s) % a number of things must be done: % \begin{itemize} % \item every cell meant as a header should use the tag \texttt{TH}. % % \item header cells should have a \texttt{Scope} attribute with the % value \texttt{Column}, \texttt{Row} or \texttt{Both}. This is not % needed in the first row or column of a table. % % \item For more complex cases both \texttt{TD} and \texttt{TH} cell % can contain a \texttt{Headers} attribute, that is an array of % \texttt{ID}s of \texttt{TH} cell. % \end{itemize} % % For now we support only header rows. % % At first we define attributes for the three standard cases: % We delay to begin document % as we can't know if tagpdf is already loaded. % \begin{macrocode} \AddToHook{begindocument} { \tag_if_active:T { \tagpdfsetup { role/new-attribute = {TH-col}{/O /Table /Scope /Column}, role/new-attribute = {TH-row}{/O /Table /Scope /Row}, role/new-attribute = {TH-both}{/O /Table /Scope /Both}, } % \end{macrocode} % % And we put all three into the class map (perhaps the next tagpdf % should do that directly with role/new-attribute): % % \begin{macrocode} \seq_gput_left:Ne\g__tag_attr_class_used_seq {\pdf_name_from_unicode_e:n{TH-col}} \seq_gput_left:Ne\g__tag_attr_class_used_seq {\pdf_name_from_unicode_e:n{TH-row}} \seq_gput_left:Ne\g__tag_attr_class_used_seq {\pdf_name_from_unicode_e:n{TH-both}} } } % \end{macrocode} % % \begin{variable}{\l_@@_header_rows_clist,\l_@@_header_columns_clist} % This holds the numbers of the header rows and columns. Negative numbers are % possible and count from the last column backwards. % \begin{macrocode} \clist_new:N \l_@@_header_rows_clist \clist_new:N \l_@@_header_columns_clist % \end{macrocode} % \end{variable} % % \begin{macro}{\@@_set_header_rows:} % \begin{macrocode} \cs_new_protected:Npn \@@_set_header_rows: { \clist_map_inline:Nn \l_@@_header_rows_clist { \clist_set:Ne\l_@@_tmpa_clist { \seq_item:Nn \g_@@_struct_cells_seq {##1} } \clist_map_inline:Nn \l_@@_tmpa_clist { % \end{macrocode} % We can have negative numbers in the list from the multicolumn. % \begin{macrocode} \prop_if_exist:cT { g__tag_struct_####1_prop } { \__tag_struct_prop_gput:nnn{ ####1 }{S}{/TH} % \end{macrocode} % This sets the scope class. If the header has already a row attribute % we replace by TH-both. % \begin{macrocode} \prop_get:cnNTF { g__tag_struct_####1_prop } { C } \l_@@_tmpa_tl { \str_set:Ne \l_@@_tmpa_str {\l_@@_tmpa_tl} \str_remove_once:Nn \l_@@_tmpa_str {[} \str_remove_once:Nn \l_@@_tmpa_str {]} \str_if_in:NnTF\l_@@_tmpa_str{/TH-row} { \str_replace_once:Nnn \l_@@_tmpa_str {/TH-row}{/TH-both} \__tag_struct_prop_gput:nne{ ####1 }{C}{[\l_@@_tmpa_str]} } { \__tag_struct_prop_gput:nne{ ####1 }{C}{[/TH-col~\l_@@_tmpa_str]} } } {\__tag_struct_prop_gput:nnn{ ####1 }{C}{/TH-col}} } } } } % \end{macrocode} % \end{macro} % % And some key support: % (See tagpdfsetup-keys.md for the naming scheme.) % \begin{macrocode} \keys_define:nn { __tag / setup } { table/header-rows .clist_set:N = \l_@@_header_rows_clist, table/header-columns .clist_set:N = \l_@@_header_columns_clist, % \end{macrocode} % obsolete older name: % \begin{macrocode} table-header-rows .meta:n = {table/header-rows={#1}} } % \end{macrocode} % % \subsection{Multirow support} % % \begin{macro}{\@@_multirow:n} % This command makes the current cell into a multirow cell: % it creates, if needed, an RowSpan-attribute, adds it to the attributes % of the cell structure, and marks all following cells spanned by the multirow % as cells that should not be tagged. The argument is the number of spanned row % (including the current row). % \begin{macrocode} \cs_if_exist:NT \__tag_struct_prop_gput:nnn { \cs_generate_variant:Nn \__tag_struct_prop_gput:nnn {one} } \cs_new_protected:Npn \@@_multirow:n #1 { % \end{macrocode} % Create an attribute if needed: % \begin{macrocode} \prop_get:NeNF \g__tag_attr_entries_prop {rowspan-\int_eval:n{#1}} \l_@@_tmpa_tl { \__tag_attr_new_entry:ee {rowspan-\int_eval:n{#1}} {/O /Table /RowSpan~\int_eval:n{#1}} } % \end{macrocode} % ensure that the attribute is marked as used: % \begin{macrocode} \seq_gput_left:Ne\g__tag_attr_class_used_seq {\pdf_name_from_unicode_e:n{rowspan-\int_eval:n{#1}}} % \end{macrocode} % Get the structure number of the current cell % \begin{macrocode} \seq_get_right:NN\g_@@_struct_cur_seq \l_@@_tmpb_tl % \end{macrocode} % If we are in a multicolumn the number can be negative and % this must be changed % \begin{macrocode} \tl_set:Ne \l_@@_tmpb_tl { \int_abs:n{\l_@@_tmpb_tl} } % \end{macrocode} % no we must update an existing attribute. % TODO: simplify this ... (see also colspan handling). % \begin{macrocode} \prop_get:cnNTF { g__tag_struct_\l_@@_tmpb_tl _prop } { C } \l_@@_tmpa_tl { \tl_remove_once:Nn \l_@@_tmpa_tl {[} \tl_remove_once:Nn \l_@@_tmpa_tl {]} \__tag_struct_prop_gput:one{ \l_@@_tmpb_tl } {C} {[/rowspan-\int_eval:n{#1}~\l_@@_tmpa_tl]} } { \__tag_struct_prop_gput:one{ \l_@@_tmpb_tl } {C} {[/rowspan-\int_eval:n{#1}]} } % \end{macrocode} % Now mark the spanned cells that should be ignored. % \begin{macrocode} \@@_gset_untagged_row_cells:nn {#1-1}{\g_@@_span_tl} } % \end{macrocode} % \end{macro} % % \begin{macrocode} \keys_define:nn{ __tag / setup } { table/multirow .code:n = {\@@_multirow:n {#1} } ,table/multirow .default:n = 1 } % \end{macrocode} % % \begin{macro}{\@@_gset_untagged_row_cells:nn} % This command stores the row and column numbers of the cells in the following row(s) % that should not be tagged as they are a part of a rowspan. % \begin{macrocode} \cs_new_protected:Npn \@@_gset_untagged_row_cells:nn #1 #2 % #1 number of rows, #2 number of columns { \int_step_inline:nn {#1} { \int_step_inline:nn {#2} { \prop_gput:Nee \g_@@_untagged_cells_prop { \int_eval:n {\g_@@_row_int + ##1},\int_eval:n{\g_@@_col_int + ####1 -1 } }{} } } } % \end{macrocode} % \end{macro} % % \begin{macro}{\@@_if_tag_cell:nn} % We must be able to detect if a cell should be tagged. For this % we define a conditional --- if more options are needed it can be extended. % \begin{macrocode} \prg_new_protected_conditional:Npnn\@@_if_tag_cell:nn #1 #2 %#1 row, #2 col { T,TF } { \prop_get:NeNTF \g_@@_untagged_cells_prop {\int_eval:n{#1},\int_eval:n{#2}}\l__tbl_tmpa_tl { \prg_return_false:} { \prg_return_true: } } % \end{macrocode} % \end{macro} % % \subsection{Misc stuff} % % \begin{macro}{\@@_show_curr_cell_data:} % Show the row/column index and span count for current table cell % for debugging. % \begin{macrocode} \cs_new_protected:Npn \@@_show_curr_cell_data: { \@@_trace:n { ==>~ current~cell~data:~ \int_use:N \g_@@_row_int , \int_use:N \g_@@_col_int , \g_@@_span_tl } } % \end{macrocode} % \end{macro} % % % % \begin{macro}{\@@_add_missing_cells:} % The storing and use of the number of missing cells must happen at % different places as the testing happens at the end of the last % cell of a row, but still inside that cell, so we use two % commands. The one adding is used in the row/end socket. % \begin{macrocode} \cs_new:Npn \@@_add_missing_cells: { % \end{macrocode} % The TD-socket messages are issued after the message about the % end-row socket, but the structure is ok, so better issue a % message for now to avoid confusion: % \begin{macrocode} \int_compare:nNnT \g_@@_missing_cells_int > 0 { \@@_trace:n {==>~ ~Inserting~\int_use:N \g_@@_missing_cells_int \space additional~cell(s)~into~previous~row:} \int_step_inline:nn { \g_@@_missing_cells_int } { \int_gincr:N\g_@@_col_int \UseTaggingSocket{tbl/cell/begin} \UseTaggingSocket{tbl/cell/end} } } } % \end{macrocode} % \end{macro} % % % % % % % \begin{macro} % { % \g_@@_struct_table_tl, \l_@@_saved_struct_table_tl, % \g_@@_struct_rows_seq,\l_@@_saved_struct_rows_seq, % \g_@@_struct_cells_seq,\l_@@_saved_struct_cells_seq, % \g_@@_struct_cur_seq,\l_@@_saved_struct_cur_seq % } % We need to store the structure numbers for the fine tuning in the % finalize socket. For now we use a rather simple system: A % sequence that hold the numbers for the row structures, and one % that holds comma lists for the cells. % % \cs{g_@@_struct_table_tl} will hold the structure number of the % table, \cs{g_@@_struct_rows_seq} will hold at index i the % structure number of row i, \cs{g_@@_struct_cells_seq} will hold % at index i a comma list of the cell structure numbers of row i. % \cs{g_@@_struct_cur_seq} is used as a temporary store for the % cell structures of the current row. We need also local version % to store and restore the values. % % \begin{macrocode} \tl_new:N \g_@@_struct_table_tl \tl_new:N \l_@@_saved_struct_table_tl \seq_new:N \g_@@_struct_rows_seq \seq_new:N \l_@@_saved_struct_rows_seq \seq_new:N \g_@@_struct_cells_seq \seq_new:N \l_@@_saved_struct_cells_seq \seq_new:N \g_@@_struct_cur_seq \seq_new:N \l_@@_saved_struct_cur_seq % \end{macrocode} % \end{macro} % \begin{macro}{\@@_init_struct_data:} % Save the global structure data variables locally so the we can % restore them when the table ends (important in case of nested % tables). % % This is also the right point to initialize % them. \cs{g_@@_struct_table_tl} is set elsewhere. % \begin{macrocode} \cs_new_protected:Npn \@@_init_struct_data: { \tl_set_eq:NN \l_@@_saved_struct_table_tl \g_@@_struct_table_tl \seq_set_eq:NN \l_@@_saved_struct_rows_seq \g_@@_struct_rows_seq \seq_set_eq:NN \l_@@_saved_struct_cells_seq \g_@@_struct_cells_seq \seq_set_eq:NN \l_@@_saved_struct_cur_seq \g_@@_struct_cur_seq % \seq_gclear:N\g_@@_struct_rows_seq \seq_gclear:N\g_@@_struct_cells_seq \seq_gclear:N\g_@@_struct_cur_seq } % \end{macrocode} % \end{macro} % \begin{macro}{\@@_restore_struct_data:} % % \begin{macrocode} \cs_new_protected:Npn \@@_restore_struct_data: { \tl_gset_eq:NN \g_@@_struct_table_tl \l_@@_saved_struct_table_tl \seq_gset_eq:NN \g_@@_struct_rows_seq \l_@@_saved_struct_rows_seq \seq_gset_eq:NN \g_@@_struct_cells_seq\l_@@_saved_struct_cells_seq \seq_gset_eq:NN \g_@@_struct_cur_seq \l_@@_saved_struct_cur_seq } % \end{macrocode} % \end{macro} % % % \subsection{longtable} % % % % % % \begin{macro}{\@@_patch_\LT@makecaption} % This patch is quite similar to the one for LaTeX's \cs{@makecaption} % we also have to change the parbox sockets. % \begin{macrocode} \def\@@_patch_LT@makecaption#1#2#3{% \LT@mcol\LT@cols c{% % test can go after merge \str_if_exist:cT { l__socket_tagsupport/parbox/before_plug_str } { \AssignSocketPlug{tagsupport/parbox/before}{noop} \AssignSocketPlug{tagsupport/parbox/after}{noop} } \hbox to\z@{\hss\parbox[t]\LTcapwidth{% \reset@font \tag_stop:n{caption} \sbox\@tempboxa{#1{#2:~}#3}% \tag_start:n{caption} \ifdim\wd\@tempboxa>\hsize #1{#2:~}#3% \else \hbox to\hsize{\hfil#1{#2:~}#3\hfil}% \fi \endgraf\vskip\baselineskip}% \hss}}} % \end{macrocode} % \end{macro} % % % % Overwrite the longtable definition. That will probably break somewhere as % they are various package which patch too. % \begin{macrocode} \AddToHook{package/longtable/after} { \cs_set_eq:NN \LT@makecaption\@@_patch_LT@makecaption } % \end{macrocode} % \begin{macrocode} % % \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} % % \end{macrocode} % \begin{macrocode} %<*latex-lab-alias> \ProvidesFile{tabular-latex-lab-testphase.ltx} [\ltlabtbldate\space v\ltlabtblversion\space latex-lab wrapper tabular] \RequirePackage{latex-lab-testphase-table} % % \end{macrocode}