% \iffalse meta-comment % %% File: latex-lab-sec.dtx (C) Copyright 2022-2025 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\ltlabsecdate{2025-10-20} \def\ltlabsecversion{0.84k} % \end{macrocode} %<*driver> \DocumentMetadata{tagging=on,pdfstandard=ua-2} \documentclass[kernel]{l3doc} \usepackage{latex-lab-testphase-l3doc} \EnableCrossrefs \CodelineIndex \begin{document} \DocInput{latex-lab-sec.dtx} \end{document} % % % \fi % % % \title{The \textsf{latex-lab-sec} package\\ % Changes related to the tagging of sectioning commands} % \author{\LaTeX{} Project\thanks{Initial implementation done by Ulrike Fischer}} % \date{v\ltlabsecversion\ \ltlabsecdate} % % \maketitle % % \newcommand{\xt}[1]{\textsl{\textsf{#1}}} % \newcommand{\TODO}[1]{\textbf{[TODO:} #1\textbf{]}} % \newcommand{\docclass}{document class \marginpar{\raggedright document class % customizations}} % % \providecommand\hook[1]{\texttt{#1}} % % \begin{abstract} % The following code implements a first draft for the tagging of sectioning commands. % \end{abstract} % % \section{Limitations} % % Sectioning commands are in general not defined by the format but by the classes. % Their implementation vary: some are defined with the help of \cs{@startsection}, % some are like \cs{chapter} handcrafted, % some build with the help of extension packages or as in the KOMA classes % with class code that extends the \cs{@startsection} functionality. % % The following code can therefore currently be used \emph{only} with the standard classes % or with classes which do not overwrite the changed definitions. % % % % \section{Introduction} % % Tagging of sectioning commands consist of two parts: % % \begin{itemize} % \item The heading/title text of the section should be surrounded by a % heading tag, typically \texttt{Hn} with some value of \texttt{n}. % In theory, one could put the number of the section command in an \texttt{Lbl}. % However, current AT doesn't handle this well, so % we use a tag \texttt{section-number} that is mapped to \texttt{Span}. % The number of the \texttt{Hn} tag should reflect the \enquote{natural} level. % So in an article \cs{section} will use \texttt{H1}, in a book \cs{chapter} will use % \texttt{H1} and \cs{section} \texttt{H2}. % Titles of \cs{part} are a bit out of this system as they are normally % not part of the hierarchy: often only some chapters are grouped under a part. % Their title is therefore tagged as \texttt{Title}. % \item % The whole section should normally be surrounded by % a \texttt{Sect} tag. Parts should be surrounded by \texttt{Part}. % It is a bit unclear if the headings should be inside or outside of these % structures---the best practice guide puts them outside---but on the whole % it sounds more logical to group the heading with the text inside the \texttt{Sect}. % For the part this is actually required, as there can be only one \texttt{Title} % in a structure, so the part title can't be at the same level as the % document \texttt{Title}. % % Starting such an enclosing \texttt{Sect} structure is rather easy, % but closing it requires code in various place, % for example the commands \cs{mainmatter}, \cs{backmatter}, % \cs{frontmatter} and \cs{appendix} should typically close everything. % Following sectioning commands should close all previous structures % with a level equal or higher than their own level. % \end{itemize} % % \section{Technical details and problems} % % The implementation has to take care of various details. % % \begin{itemize} % % \item As sections in \LaTeX{} are not environments, the % \texttt{} structures can be wrongly nested with other structures. For example % if a document puts a sectioning command into a list or a trivlist or % a minipage then it can no longer close previous \texttt{} structures correctly. % The problem can be detected by checking the structure stack % and a warning can be issued, but the author then has to close the structures % manually before the list or minipage. % % Thus there have to be user interfaces to handle such cases. % It should also be possible not to create all the \texttt{} structures % automatically but to tag only the headings so that the author can handle special % cases manually. % % \item If hyperref is used, targets for links should be inserted, either with % \cs{refstepcounter} or manually with \cs{MakeLinkTarget}. These targets must be % in the correct structure for the structure destinations. They replace some % of the current patches in hyperref. % % \end{itemize} % % \subsection{Functions and keys} % % \begin{function}{\tag_tool:n,\tagtool} % \emph{deprecated}, use tagging sockets instead. % \end{function} % % \subsection{TODO} % % \begin{itemize} % \item A dedicated command to close a sectioning unit should be provided. % % \item A dedicated command to open a sectioning unit should be provided too. % % \item It should also be possible to suppress the sectioning unit in sectioning commands % to allow e.g. to put an epigraph or similar in front. % % \item The number in \cs{part} and \cs{chapter} is currently not correctly % tagged as a \texttt{section-number} as this requires to redefine the internal (class dependent) % commands too. % % \end{itemize} % % \begin{macrocode} %<*package> % \end{macrocode} % % \section{Implementation} % \begin{macrocode} \ProvidesExplPackage {latex-lab-testphase-sec} {\ltlabsecdate} {\ltlabsecversion} {Code related to the tagging of sectioning commands} % \end{macrocode} % \changes{0.84k}{2025-10-20}{Switched to tagging sockets and various corrections}. % \subsection{Temporary fix} % Until tagpdf correctly sets the symbolic name (2025-10-06) % \begin{macrocode} \tl_set:Nn \l__tag_para_tag_default_tl { \UseStructureName {para/textblock} } \tl_set:Nn \l__tag_para_main_tag_tl { \UseStructureName {para/semantic} } % \end{macrocode} % % \subsection{Surrounding by \texttt{Sect} structures} % We use a stack to record the levels of the open \texttt{Sect}. The first item % has level -100. A sectioning command will take a record from the stack. If its level is % greater or equal it closes this structure and takes the next record from the stack. % If the record has a smaller level then it puts it back and stops. % The stack is compared with the main structure stack, if they don't match % it means we can't safely close the \texttt{Sect} and so we issue a warning % and do nothing. % % \begin{macrocode} % % \end{macrocode} % \subsubsection{Glyphtounicode improvements} % % As lualatex runs with legacy encodings in the test files, we enable and % load glyphtounicode. For the math we load additional definitions. % % \begin{macrocode} %<*kernelchange> \ifdefined\directlua \ifnum\outputmode > 0 \pdfvariable gentounicode =1 \protected\def\pdfglyphtounicode {\pdfextension glyphtounicode } \protected\edef\pdfgentounicode {\pdfvariable gentounicode} \input{glyphtounicode} \fi \fi \ifdefined\pdfglyphtounicode \input{glyphtounicode-cmex} \fi % % \end{macrocode} % % \begin{macrocode} %<*package> %<@@=tag> % \end{macrocode} % % \subsubsection{Tagging commands} % % % \begin{variable}{\g_@@_sec_stack_seq} % The stack holds the tag, the level and the structure number. % \begin{macrocode} \seq_new:N \g_@@_sec_stack_seq \seq_gpush:Nn\g_@@_sec_stack_seq {{Document}{-100}{2}} % \end{macrocode} % \end{variable} % % \begin{macro}{\@@_get_data_current_Sect:} % This allows to retrieve the number of the current Sect structure (or % Document if we are outside any Sect) with |\tag_get:n{current_Sect}| % \begin{macrocode} \cs_new:Npn \@@_get_data_current_Sect: { \exp_last_unbraced:Ne\use_iii:nnn{\seq_item:Nn\g_@@_sec_stack_seq{1}} } % \end{macrocode} % \end{macro} % \begin{variable}{\l_@@_sec_Sect_bool} % This boolean controls if a Sect structure is opened. % \begin{macrocode} \bool_new:N \l_@@_sec_Sect_bool \bool_set_true:N\l_@@_sec_Sect_bool % \end{macrocode} % \end{variable} % % \begin{macro}{\@@_sec_begin:nn} % This starts a sectioning structure (the „Sect environment“). % Currently the default tag is either Sect or Part, depending on the level, % but this can be changed by adapting the symbolic structure names which are % built from the level. % The second argument allows to add more options but is currently unused. % \begin{macrocode} \cs_new_protected:Npn\@@_sec_begin:nn #1 #2 %#1 level #2 keyval { \tag_struct_begin:n { tag= \UseStructureName{sec/#1} ,#2 } \seq_gpush:Ne \g_@@_sec_stack_seq {{\g_@@_struct_tag_tl}{\int_eval:n{#1}}{\g_@@_struct_stack_current_tl}} } \cs_generate_variant:Nn \@@_sec_begin:nn {en} % \end{macrocode} % \end{macro} % % \begin{macro}{\@@_sec_end:n} % \begin{macrocode} \msg_new:nnn { tag } {wrong-sect-nesting} { The~structure~#1~can~not~be~closed.\\ It~is~not~equal~to~the~current~structure~#2~on~the~main~stack } \cs_new_protected:Npn\@@_sec_end:n #1 % #1 level { \seq_get:NN \g_@@_sec_stack_seq \l_@@_tmpa_tl \int_compare:nNnT {#1}<{\exp_last_unbraced:NV\use_ii:nnn\l_@@_tmpa_tl+1} { \seq_get:NN\g_@@_struct_tag_stack_seq \l_@@_tmpb_tl \exp_args:Nee \tl_if_eq:nnTF {\exp_last_unbraced:NV\use_i:nnn\l_@@_tmpa_tl} {\exp_last_unbraced:NV\use_i:nn\l_@@_tmpb_tl} { \seq_gpop:NN \g_@@_sec_stack_seq \l_@@_tmpa_tl \tag_struct_end: \@@_sec_end:n {#1} } { \msg_warning:nnee {tag}{wrong-sect-nesting} { \exp_last_unbraced:NV\use_i:nnn \l_@@_tmpa_tl } { \exp_last_unbraced:NV\use_i:nn \l_@@_tmpb_tl } } } } % \end{macrocode} % \end{macro} % \begin{macro}{\@@_sec_title_split:,\@@_sec_restore_para:} % Runin-sectioning command must separate the heading from the following text. % The code is in an \cs{everypar} which is perhaps executed in a group (e.g. when % a list follows), we have to ensure that the restoring of the para can escape. % \begin{macrocode} \cs_new_protected:Npn\@@_sec_restore_para: { \UseTaggingSocket {para/restore} \if_int_compare:w \tex_currentgrouptype:D =14 % semi-simple group \group_insert_after:N \@@_sec_restore_para: \else: \if_int_compare:w \tex_currentgrouptype:D =\c_one_int % simple group \group_insert_after:N \@@_sec_restore_para: \fi: \fi: } \cs_new_protected:Npn \@@_sec_title_split: { % \end{macrocode} % This ends the title structure. As the begin is from the % automatic (flattened) para-tagging we have to increase the counter. % \begin{macrocode} \tag_mc_end: \tag_struct_end: \@@_gincr_para_end_int: % \end{macrocode} % In case something (e.g. a list) did reset the boolean we need to close also a semantic % paragraph. % \begin{macrocode} \bool_if:NF\l__tag_para_flattened_bool {\UseTaggingSocket{para/semantic/end}{}} % \end{macrocode} % Now restore the para-tagging and start a normal paragraph: % \begin{macrocode} \@@_sec_restore_para: \UseTaggingSocket{para/begin} } % \end{macrocode} % \end{macro} % % \begin{macro}{\@@_sec_title_begin:nn} % This command is used in the socket at the begin of display sectioning commands % like part and chapter. It takes two arguments: the level and the title. % \begin{macrocode} \cs_new_protected:Npn \@@_sec_title_begin:nn #1 #2 %level, title { \tag_struct_begin:n{tag=\UseStructureName{sec/#1/title},title={#2}} \bool_set_true:N\l_@@_para_flattened_bool \tl_set:Nn\l_@@_para_tag_tl {\UseStructureName{sec/#1/titleline}} } % \end{macrocode} % \end{macro} % \begin{macro}{\@@_sec_title_end:} % \begin{macrocode} \cs_new_protected:Npn \@@_sec_title_end: { \tag_struct_end: %P = Hn \UseTaggingSocket{para/restore} } % \end{macrocode} % \end{macro} % % \begin{macro}{\@@_set_title_hang:nnn} % To be able to correctly tag the number and insert the link target in the % title of a sectioning command created with \cs{@startsection} % we need a special\cs{@hangfrom} variant. This is a bit tricky: % The argument contains the link target and for a correct structure % destination it should be typeset \emph{after} the structure has been opened. % But to measure the hangindent it must be typeset \emph{before} the paragraph is started. % This means that we have to open the title structure manually % and then have to suppress the para-tagging. % Additionally there is an engine difference: with pdftex the literals for the mc are inserted % with the box after the paragraph has started but luatex sets the attributes before % and we have to reset them. Hiding all this in a tagging socket is non-trivial. % The code assumes that we are in vmode! % Attention: The code opens a structure that it doesn't close (it is closed by the \cs{par}). % It therefore does not handle the full tagging of the title. In a new implementation % of the sectioning command this will perhaps have to change. % % \begin{macrocode} \cs_new_protected:Npn \@@_set_title_hang:nNnn #1 #2 #3 #4 %#1 level, %#2 boolean: nonumber? (will be later \l__head_nonumber_bool) %#3 formated number /hang space %#4 title % \end{macrocode} % The handling of the title is not perfect. It would be better to pass it through % something like \cs{GetTitleString}. TODO. % \begin{macrocode} { \tagstructbegin{tag=\UseStructureName{sec/#1/title},title-o={#4}} \cs_if_exist_use:N \@@_gincr_para_begin_int: \bool_if:NF #2 { \tagstructbegin{tag=\UseStructureName{sec/#1/number}} } \setbox\@tempboxa\hbox{{#3}} % \end{macrocode} % We stop paratagging now, to avoid that the \cs{noindent} creates a structure. % \begin{macrocode} \bool_set_false:N \l_@@_para_bool \hangindent \wd\@tempboxa\noindent % \end{macrocode} % Restart paratagging and insert the box. If the box has a real content (if there is a % number) we have to add mc-chunks and reset the attribute of the box. % \begin{macrocode} \bool_set_true:N \l_@@_para_bool \bool_if:NTF #2 { \box\@tempboxa } { \tagmcbegin{} % \end{macrocode} % In lua mode we have to reset the attributes inside the box! % \begin{macrocode} \tag_mc_reset_box:N\@tempboxa \box\@tempboxa \tagmcend \tagstructend } \tagmcbegin{} } % \end{macrocode} % \end{macro} % % % \begin{macro}{\@@_sec_title_runin_number:nn} % \begin{macrocode} \cs_new_protected:Npn \@@_sec_title_runin_number:nNn #1 #2 #3 %#1 level, #2 boolean no number, #3 content { \bool_if:NTF #2 { #3 } { \tag_mc_end_push: \tag_struct_begin:n{tag=\UseStructureName{sec/#1/number}} \tag_mc_begin:n{} #3 \tag_mc_end: \tag_struct_end: \tag_mc_begin_pop:n{} } } % \end{macrocode} % \end{macro} % Open sec structures should be closed at the end of the document. This should % be done before tagpdf closes the Document structure. % \begin{macrocode} \hook_gput_code:nnn {tagpdf/finish/before} {tagpdf/sec} {\AssignTaggingSocketPlug{sec/end}{kernel}\UseTaggingSocket{sec/end}{-10}} \hook_gset_rule:nnnn {tagpdf/finish/before}{tagpdf/sec}{before}{tagpdf} % \end{macrocode} % % The commands \cs{mainmatter}, \cs{backmatter}, \cs{frontmatter} and % \cs{appendix} close all \texttt{Sect} and \texttt{Part} structures. % \changes{v0.84g}{2025/01/05}{ensure that paragraph is ended (tagging/777)} % \begin{macrocode} \AddToHook{cmd/frontmatter/before}{\par\UseTaggingSocket{sec/end}{-10}} \AddToHook{cmd/mainmatter/before} {\par\UseTaggingSocket{sec/end}{-10}} \AddToHook{cmd/backmatter/before} {\par\UseTaggingSocket{sec/end}{-10}} \AddToHook{cmd/appendix/before} {\par\UseTaggingSocket{sec/end}{-10}} % \end{macrocode} % % \subsection{Tagging Sockets} % First the sockets that handle the Sect structures. % % The argument of the begin socket consists of two brace groups, % in the first brace is the level, in the second the keys for the structure. % \begin{macrocode} \NewTaggingSocketPlug{sec/begin}{kernel} { \@@_sec_begin:en #1 } \AssignTaggingSocketPlug{sec/begin}{kernel} % \end{macrocode} % The end socket takes as argument only the level to close. % \begin{macrocode} \NewTaggingSocketPlug{sec/end}{kernel} { \@@_sec_end:n {#1} } \AssignTaggingSocketPlug{sec/end}{kernel} % \end{macrocode} % % These two sockets handle the tagging of headings with special formatting % like part and chapter. % The argument of the begin socket is two brace groups containing the level and % the title. % \begin{macrocode} \NewTaggingSocketPlug{sec/title/begin}{kernel} { \@@_sec_title_begin:nn #1 } \AssignTaggingSocketPlug{sec/title/begin}{kernel} % \end{macrocode} % The end socket does not take an argument. It only closes the structures % and restores the para settings. % \begin{macrocode} \NewTaggingSocketPlug{sec/title/end}{kernel} { \@@_sec_title_end: } \AssignTaggingSocketPlug{sec/title/end}{kernel} % \end{macrocode} % % % The \texttt{sec/title/hang} socket is used to typeset the heading of a title using \cs{@hangfrom}. % It is the most tricky one. It takes two argument. % The second argument of this socket will pass the normal \cs{@hangfrom} command % if tagging is not active. It is not used with tagging. % The first argument passes four brace groups: % the level, a boolean for \enquote{nonumber}, % the actual content of the number and the title. % % Attention: The socket opens a structure that it doesn't close (it is closed by the \cs{par}). % It therefore does not handle the full tagging of the title. In a new implementation % of the sectioning command this will perhaps have to change. % \begin{macrocode} \NewTaggingSocketPlug{sec/title/hang}{kernel} { \@@_set_title_hang:nNnn #1 } \AssignTaggingSocketPlug{sec/title/hang}{kernel} % \end{macrocode} % % The \texttt{sec/title/init} socket is used to do some initialization % for sectioning commands that are setup with \cs{@startsection}. It sets % the tag name and flattens the para-tagging. It is mostly needed for run-in % headings which set the heading inside \cs{everypar}. % It takes 1 argument, the level. % \begin{macrocode} \NewTaggingSocketPlug{sec/title/init}{kernel} { \tl_set:Ne\l_@@_para_tag_tl{\UseStructureName{sec/#1/title}} \bool_set_true:N \l_@@_para_flattened_bool } \AssignTaggingSocketPlug{sec/title/init}{kernel} % \end{macrocode} % % This socket handles the tagging between a run-in heading and the following text. % It takes no argument. % \begin{macrocode} \NewTaggingSocketPlug{sec/title/split}{kernel} { \@@_sec_title_split: } \AssignTaggingSocketPlug{sec/title/split}{kernel} % \end{macrocode} % % The following tagging socket command is used to handle the tagging % of the number in the title of run-in headings. Similar to the hang variant it does not % handle the full structure but relies in part on the paragraph % tagging. This again can change if sectioning commands are reimplemented. % It takes as first argument the level and a boolean for the numbering. % The second argument contains the formatted number (if numbered) and the destination. % \begin{macrocode} \NewTaggingSocketPlug{sec/title/runin/number}{kernel} { \@@_sec_title_runin_number:nNn #1 {#2} } \AssignTaggingSocketPlug{sec/title/runin/number}{kernel} % \end{macrocode} % % The following tagging socket command simply tags a number. % It is not used here, as the legacy code needs a more complicated setup. % % It takes the level as first argument. % The second argument contains the formatted number. % \begin{macrocode} \NewTaggingSocketPlug{sec/title/number}{kernel} { \tag_mc_end_push: \tag_struct_begin:n{tag=\UseStructureName{sec/#1/number}} \tag_mc_begin:n{} #2 \tag_mc_end: \tag_struct_end: \tag_mc_begin_pop:n{} } \AssignTaggingSocketPlug{sec/title/number}{kernel} % \end{macrocode} % % \section{Sectioning commands} % % \subsection{\cs{part} and \cs{chapter}} % % \cs{part} and \cs{chapter} are defined by the classes. % To tag them we redefine the user commands. % This will probably break with various classes and with titlesec. % The tagging inside relies on the para tagging. % We do not yet use keyval in the optional argument, as this requires latex-dev % and the naming of the keys and their key family is unclear. % \changes{v0.84f}{2024/10/04}{Added braces around optional arg (tagging/725)} % \begin{macrocode} \AddToHook{class/after} { \@ifundefined{chapter} { % \end{macrocode} % This redefines \cs{part} in article class. % \begin{macrocode} \@ifundefined{part}{} { \RenewDocumentCommand\part{ s O{#3} m } { \if@noskipsec \leavevmode \fi \par \addvspace{4ex}% \@afterindentfalse % \end{macrocode} % This are the tagging commands needed at the begin. They open a Part structure % and the structure for the title of the heading. % \begin{macrocode} % tagging start commands \UseTaggingSocket{sec/end}{-1} \UseTaggingSocket{sec/begin}{{-1}{tag=\UseStructureName{sec/-1}}} \UseTaggingSocket{sec/title/begin}{{-1}{#2}} % end tagging start commands % \end{macrocode} % This adds a manual target if the part is unnumbered or starred. % It replaces the hyperref patches. % \begin{macrocode} \bool_lazy_any:nT { { #1 } { \int_compare_p:nNn {\c@secnumdepth}<{-1} } } { \MakeLinkTarget[part]{} } % \end{macrocode} % The main call to the underlying commands. % \begin{macrocode} \IfBooleanTF {#1} { \@spart {#3} } { \@part [{#2}]{#3} } % \end{macrocode} % and now the closing command for the tagging of the title. % \begin{macrocode} \UseTaggingSocket{sec/title/end} } } } % \end{macrocode} % Redefinitions for book and report % \begin{macrocode} { \RenewDocumentCommand\chapter{ s O{#3} m } { \if@openright\cleardoublepage\else\clearpage\fi \thispagestyle{plain}% \global\@topnum\z@ \@afterindentfalse % \end{macrocode} % This are the tagging commands needed at the begin. They open a Sect structure % and the structure for the title of the heading. % \begin{macrocode} \UseTaggingSocket{sec/end}{0} \UseTaggingSocket{sec/begin}{{0}{tag=\UseStructureName{sec/0}}} \UseTaggingSocket{sec/title/begin}{{0}{#2}} % \end{macrocode} % This adds a manual target if the chapter is unnumbered or starred. % It replaces the hyperref patches. % \begin{macrocode} \bool_lazy_any:nT { { #1 } { \int_compare_p:nNn {\c@secnumdepth}<{0} } { %in book target also needed in frontmatter \bool_lazy_and_p:nn { \cs_if_exist_p:c { @mainmattertrue } } { ! \legacy_if_p:n { @mainmatter } } } } { % \end{macrocode} % The relation target-struct is stored internally by the MakeLinkTarget commands % \begin{macrocode} \MakeLinkTarget[chapter]{} } % \end{macrocode} % The main call to the underlying commands. % \begin{macrocode} \IfBooleanTF {#1} { \@schapter {#3} } { \@chapter [{#2}]{#3} } % \end{macrocode} % and now the closing command for the tagging of the title. % \begin{macrocode} \UseTaggingSocket{sec/title/end} } % \end{macrocode} % and similar for \cs{part} % \begin{macrocode} \RenewDocumentCommand\part{ s O{#3} m } { \if@openright \cleardoublepage \else \clearpage \fi \thispagestyle{plain}% \if@twocolumn \onecolumn \@tempswatrue \else \@tempswafalse \fi \null\vfil % \end{macrocode} % These are the tagging commands needed at the begin. They open a Part structure % and the structure for the title of the heading. % \begin{macrocode} \UseTaggingSocket{sec/end}{-1} \UseTaggingSocket{sec/begin}{{-1}{tag=\UseStructureName{sec/-1}}} \UseTaggingSocket{sec/title/begin}{{-1}{#2}} % \end{macrocode} % This adds a manual target if the part is unnumbered or starred. % It replaces the hyperref patches. % \begin{macrocode} \bool_lazy_any:nT { { #1 } { \int_compare_p:nNn {\c@secnumdepth}<{-1} } { %in book target also needed in frontmatter \bool_lazy_and_p:nn { \cs_if_exist_p:c { @mainmattertrue } } { ! \legacy_if_p:n { @mainmatter } } } } { \MakeLinkTarget[part]{} } % \end{macrocode} % The main call to the underlying commands. % \begin{macrocode} \IfBooleanTF {#1} { \@spart {#3} } { \@part [{#2}]{#3} } % \end{macrocode} % and now the closing command for the tagging of the title. % \begin{macrocode} \UseTaggingSocket{sec/title/end} } } } % \end{macrocode} % % \subsection{Sectioning commands based on \cs{@startsection}} % % The tagging relies again on the para tagging: % we simply exchange the tag name by the one given as \#1. % This assumes that a tag with the name of the sectioning type is defined. % We don't try to pass the title, this will be done together with % the new keyval handling in the user command. % % \subsubsection{Hyperref code} % hyperref has to insert anchors. If the sectioning is numbered this is done by % \cs{refstepcounter} (and so in vmode). For unnumbered section hyperref % injects the anchor in hmode before the text, it also inserts a % kern to compensate the indent. % % This means that the target of numbered and unnumbered sectioning commands % differ, both regarding the location and in relation to the % tagging structure: The anchor from the \cs{refstepcounter} is outside of % the structure created by the heading title if the para tags are used, % while the other anchors are inside and so the structure destinations are different. % % We unify this by suppressing the anchor from the refstepcounter. % Also we only go back if the indent is positive. % % At first suppress all hyperref patches related to sectioning: % \begin{macrocode} \def\hyper@nopatch@sectioning{} % \end{macrocode} % % \begin{variable}{\l__kernel_sec_nonumber_bool} % A boolean to keep track if a sectioning command should be numbered or not. % \begin{macrocode} \bool_new:N\l__kernel_sec_nonumber_bool % \end{macrocode} % \end{variable} % % \begin{macro}[no-user-doc]{\@hyp@section@target@nnn} % A simple internal command. There is no need for something public, % as packages defining their own version of \cs{@startsection} will % probably need something slightly different based on \cs{MakeLinkTarget}. % \changes{v0.84i}{2025/02/13}{Wrapped all use inside \cs{NoCaseChange}, % tagging-project issue \#787} % \begin{macrocode} \cs_new_protected:Npn \@hyp@section@target@nnn #1 #2 #3 %#1 optarg #2 name/counter, #3 indent { \makebox[0pt][l] { \skip_set:Nn \@tempskipa {#3} \dim_compare:nNnF {\@tempskipa}<{0pt}{\kern-\@tempskipa} \MakeLinkTarget#1{#2} } } % \end{macrocode} % \end{macro} % % \begin{macro}[no-user-doc]{\@@_makecurrentHref:n} % In run-in heading we have to set the name of the anchor before it is actually % created in a everypar. If hyperref is loaded we could use \cs{Hy@MakeCurrentHrefAuto} % but without it we need a similar command. % \begin{macrocode} \cs_new_protected:Npn \@kernel@makecurrentHref #1 %#1 prefix { \int_gincr:N\g__kernel_target_int \tl_gset:Ne \@currentHref {#1.\int_use:N\g__kernel_target_int} } % \end{macrocode} % hyperref uses a different counter so we need to use a different command, % TODO: merge that. % % \begin{macrocode} \AddToHook{package/hyperref/after} { \cs_set_eq:NN \@kernel@makecurrentHref \Hy@MakeCurrentHrefAuto } % \end{macrocode} % \end{macro} % \subsection{Adaption of the heading commands} % We add to \cs{@startsection} the commands to open the \texttt{Sect} % structure and to change the para tag. % % We save the current level so that unnumbered sections can use it too. % \begin{macro}{\@currentseclevel} % \begin{macrocode} \newcommand\@currentseclevel{-2} % \end{macrocode} % \end{macro} % \begin{macrocode} \def\@startsection#1#2#3#4#5#6{% \def\@currentseclevel{#2} \if@noskipsec \leavevmode \fi \par \@tempskipa #4\relax \@afterindenttrue \ifdim \@tempskipa <\z@ \@tempskipa -\@tempskipa \@afterindentfalse \fi \if@nobreak \everypar{}% \else \addpenalty\@secpenalty\addvspace\@tempskipa \fi \UseTaggingSocket{sec/end} {#2} \UseTaggingSocket{sec/begin} { {#2} {tag=\cs_if_exist_use:cF{g_tag_role_#1_tl}{Sect}} } % \end{macrocode} % \changes{0.84j}{2025-10-07}{Moved para-flattened setting} % This tagging sockets changes the para-tagging: it is flattened and % the tag is changed. This is actually used only by runin headings, % but nevertheless this looks like the best place to use it as we can % not make the changes inside some \cs{everypar} command. % \begin{macrocode} \UseTaggingSocket{sec/title/init}{#2} \@ifstar {\@ssect{#3}{#4}{#5}{#6}}% {\@dblarg{\@sect{#1}{#2}{#3}{#4}{#5}{#6}}}} % \end{macrocode} % % \cs{@sect} is only changed to replace the hyperref patches % and to use the new \cs{@kernel@tag@hangfrom}. % \begin{macrocode} %<@@=> \def\@sect#1#2#3#4#5#6[#7]#8{% % #1= name, #2= level, #3= indent #4 unused #5 after vspace #6 formatting #7=short title, #8=title \ifnum #2>\c@secnumdepth \bool_set_true:N\l__kernel_sec_nonumber_bool \@kernel@makecurrentHref {#1*} \def\@svsec{\NoCaseChange{\@hyp@section@target@nnn{*}{\@currentHref}{#3}}} \else \bool_set_false:N\l__kernel_sec_nonumber_bool \LinkTargetOff \refstepcounter{#1}% \tl_gset:Ne\@currentHref{#1.\use:c{theH#1}} \LinkTargetOn \protected@edef\@svsec{\NoCaseChange{\@hyp@section@target@nnn{}{#1}{#3}}\@seccntformat{#1}\relax}% \fi \@tempskipa #5\relax \ifdim \@tempskipa>\z@ \begingroup #6{% % \end{macrocode} % The formatting can contain a \cs{MakeUppercase} % so we must protect the name of the socket: % \begin{macrocode} \NoCaseChange {\UseTaggingSocket{sec/title/hang} {{#2}\l__kernel_sec_nonumber_bool{\hskip #3\relax\@svsec}{#7}} {\@hangfrom {\hskip #3\relax\@svsec}}} \interlinepenalty \@M #8\@@par}% \endgroup \csname #1mark\endcsname{#7}% \addcontentsline{toc}{#1}{% \ifnum #2>\c@secnumdepth \else \protect\numberline{\csname the#1\endcsname}% \fi #7}% \else \def\@svsechd{% #6{\hskip #3\relax \NoCaseChange{ \UseTaggingSocket{sec/title/runin/number}{{#2}\l__kernel_sec_nonumber_bool}{\@svsec}} #8}% \csname #1mark\endcsname{#7}% \addcontentsline{toc}{#1}{% \ifnum #2>\c@secnumdepth \else \protect\numberline{\csname the#1\endcsname}% \fi #7}}% \fi \@xsect{#5}} % \end{macrocode} % similar for \cs{@ssect} % \begin{macrocode} \def\@ssect#1#2#3#4#5{% \@tempskipa #3\relax \ifdim \@tempskipa>\z@ \begingroup #4{ \NoCaseChange { \UseTaggingSocket{sec/title/hang} { {\@currentseclevel} \c_true_bool {\hskip #1\relax\NoCaseChange{\@hyp@section@target@nnn{[section]}{}{#1}}} {#5} } {\@hangfrom{\hskip #1\relax\NoCaseChange{\@hyp@section@target@nnn{[section]}{}{#1}}}} } \interlinepenalty \@M #5\@@par}% \endgroup \else \@kernel@makecurrentHref{section*} \def\@svsechd{#4{\hskip #1\relax\NoCaseChange{\@hyp@section@target@nnn{*}{\@currentHref}{#3}}\relax #5}}% \fi \@xsect{#3}} % \end{macrocode} % At last \cs{@xsect} needs code in two places. For display headings it has to % restore the default para code, for run in headings it has to separated the % heading from the following text. % \begin{macrocode} \def\@xsect#1{% \@tempskipa #1\relax \ifdim \@tempskipa>\z@ \par \nobreak \vskip \@tempskipa \UseTaggingSocket {para/restore} \@afterheading \else \@nobreakfalse \global\@noskipsectrue \everypar{% \if@noskipsec \global\@noskipsecfalse {\setbox\z@\lastbox}% \clubpenalty\@M \begingroup \@svsechd \endgroup \unskip \UseTaggingSocket{sec/title/split} \@tempskipa #1\relax \hskip -\@tempskipa \else \clubpenalty \@clubpenalty \everypar{}% \fi}% \fi \ignorespaces} % \end{macrocode} % % \subsection{Keys for \cs{tagpdfsetup}} % We need to provide user and package level commands % \changes{0.84k}{2025/04/09}{Add braces around key values, tagging issue \#830} % \changes{0.84j}{2025-10-11}{add \cs{tagpdfsetup} keys for two user facing keys} % % \begin{macrocode} \keys_define:nn{__tag / setup} { ,sec/end .code:n = { \par \UseTaggingSocket{sec/end}{\int_eval:n{\cs_if_exist_use:c{toclevel@#1}+0}} } ,sec/end .value_required:n = true ,sec/grouping .choice:, ,sec/grouping / true .code:n = { \AssignTaggingSocketPlug{sec/begin}{kernel} \AssignTaggingSocketPlug{sec/end}{kernel} } ,sec/grouping / false .code:n = { \AssignTaggingSocketPlug{sec/begin}{noop} \AssignTaggingSocketPlug{sec/end}{noop} } ,sec/grouping .default:n = true } % \end{macrocode} % % \subsubsection{Tagging tools (deprecated)} % \cs{tag_tool:n} is deprecated. % \begin{macrocode} \cs_if_free:NT \tag_tool:n { \cs_new_protected:Npn \tag_tool:n #1 { \tag_if_active:T { \keys_set:nn {tag / tool}{#1} } } \cs_set_eq:NN\tagtool\tag_tool:n } \keys_define:nn { tag / tool} { ,sec-start-part .code:n = { \UseTaggingSocket{sec/end}{-1} \UseTaggingSocket{sec/begin}{{-1}{tag=\UseStructureName{sec/-1}}} \UseTaggingSocket{sec/title/begin}{{-1}{#1}} % \end{macrocode} % We remap here the text-unit from the paragraph to NonStruct. % It would be better to suppress it completely as with the other % sectioning commands, but this would require to redefine \cs{@spart} % and \cs{@part}, as there is the grouping, and these commands are % all slightly different in the standard classes. So this is delayed % to the time when sectioning commands are redefined with templates. % \begin{macrocode} } ,sec-stop-part .code:n = {\UseTaggingSocket{sec/title/end}} ,sec-start-chapter .code:n = { \UseTaggingSocket{sec/end}{0} \UseTaggingSocket{sec/begin}{{0}{tag=\UseStructureName{sec/0}}} \UseTaggingSocket{sec/title/begin}{{0}{#1}} } ,sec-stop-chapter .meta:n = { sec-stop-part} ,sec-start .code:n = % #1 is a name like "section" { \UseTaggingSocket{sec/end} {\int_eval:n{\cs_if_exist_use:c{toclevel@#1}+0}} \UseTaggingSocket{sec/begin} { {\int_eval:n{\cs_if_exist_use:c{toclevel@#1}+0}} {tag=\cs_if_exist_use:cF{g_tag_role_#1_tl}{Sect}} } \tl_set:Nn\l_@@_para_tag_tl{#1} } ,sec-start .value_required:n = true ,sec-split-para .code:n = {\UseTaggingsocket{sec/title/split}} ,restore-para .code:n = {\UseTaggingSocket{para/restore}}% ,sec-stop .code:n = { \par \UseTaggingSocket{sec/end}{\int_eval:n{\cs_if_exist_use:c{toclevel@#1}+0}} } ,sec-stop .value_required:n = true ,sec-add-grouping .choice:, ,sec-add-grouping / true .code:n = { \AssignTaggingSocketPlug{sec/begin}{kernel} \AssignTaggingSocketPlug{sec/end}{kernel} } ,sec-add-grouping / false .code:n = { \AssignTaggingSocketPlug{sec/begin}{noop} \AssignTaggingSocketPlug{sec/end}{noop} } ,sec-add-grouping .default:n = true } % % \end{macrocode} % \begin{macrocode} %<*latex-lab> \ProvidesFile{sec-latex-lab-testphase.ltx} [\ltlabsecdate\space v\ltlabsecversion\space latex-lab wrapper sec] \RequirePackage{latex-lab-testphase-sec} % % \end{macrocode}