% \iffalse meta-comment % %% File: latex-lab-sec-template.dtx % % Copyright (C) 2026 The 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 latex-lab bundle is developed in the LaTeX2e GitHub. % Issues may be reported at % % https://github.com/latex3/latex2e/issues % \def\ltlabsecIIdate{2026-09-22} \def\ltlabsecIIversion{0.9q} %<*driver> \DocumentMetadata{tagging=on,pdfstandard=ua-2} \documentclass[kernel]{l3in2edoc} \usepackage{amstext} \usepackage{xcolor} \EnableCrossrefs \CodelineIndex \usepackage{todonotes} \begin{document} \DocInput{latex-lab-sec-template.dtx} \end{document} % % % \fi % % % \newcommand\key[1]{\texttt{#1}} % \newcommand\keyvalue[1]{\texttt{#1}} % % % \newcommand\insttype[1]{\texttt{#1}} % % \newcommand\instname[1]{\texttt{#1}} % % \NewDocumentCommand\fmi{sO{}m} % {\IfBooleanTF{#1}{\todo[inline,#2]{#3}}^^A % {\todo[#2]{#3}}} % % \NewDocumentCommand\ufi{sO{}m} % {\IfBooleanTF{#1}{\todo[inline,#2]{UFi:#3}}^^A % {\todo[#2]{UFi:#3}}} % % % \title{Reimplementation of \LaTeXe{}'s heading commands using templates} % \author{\LaTeX{} Project\thanks{Initial implementation by Frank Mittelbach.}} % \date{v\ltlabsecIIversion\ \ltlabsecIIdate} % % \maketitle % % % % \begin{abstract} % \end{abstract} % % % \tableofcontents % \medskip % % % \begin{documentation} % % % % \section{Introduction} % This module reimplements tagging aware heading commands with templates. % The new implementation is used automatically if \cs{DocumentMetadata} is used % and replaces patches and redefinitions done in the \texttt{latex-lab-sec}. % In a later step both modules will be merged. % % Most of the documentation is of interest only for class and package authors % who want to setup their own heading commands but the new implementation changes % also the user interface. This is documented first. % % \section{Changes to the user interface} % % \subsection{New user commands} % \DescribeMacro\theheading % This command expands inside a heading to the current number. % % \DescribeMacro\partmark % This replaces the fix \verb+\markboth{}{}+ or similar in the standard \cs{part} % definition. % % % \subsection{The optional argument} % The standard heading commands (re)defined by this module use the standard 2e syntax, e.g., % \begin{verbatim} % \section[toc]{title} % \end{verbatim} % The star-form \verb+\section*{Title}+ % stops the numbering, toc, bookmark and running header % as it was the way for the last 30-odd years. % New is that the optional argument is handled as a key/val list if an equal sign at the outer level % is detected, otherwise the argument is taken to be the value of the key \key{shorttitle} % and passed to the table of contents, the running headers, the bookmarks and used % with \cs{nameref}. Note that this means, that % \begin{verbatim} % \section*[Title]{Title} % \end{verbatim} % will create a toc entry and a bookmark! % % The following keys can be used % \begin{description} % % \item[\key{toc}] % % This sets the text for the table of % contents. It also forces that the entry is created. So % \begin{verbatim} % \section*[toc=Introduction]{Introduction to this document} % \end{verbatim} % will create an unnumbered entry \enquote{Introduction} in the % table of contents. An empty value (i.e., \texttt{toc=}) will % suppress the toc entry. % % \item[\key{running}] % % This sets the text for the running header and forces the use % of the mark command, so even with a starred section the % running header will be updated. An empty value will suppress % the call of the mark commands, so a header from a % previous section will prevail. % % \item[\key{bookmark}] % % This sets the text for the bookmarks (the PDF outline) if, % e.g., hyperref is loaded. As with the previous keys an empty % value suppresses the bookmark entry. % % \item[\key{nameref}] % % This allows to set the text used for a cross reference % with \cs{nameref}. % % \item[\key{label}] % % This sets a label, so it is an alternative to using a % \cs{label} command. % % \item[\key{subtitle}, \key{quote}] % % These keys will allow to pass a subtitle and a quote to the % underlying code, but currently they are not yet used by the % implementations of the standard \LaTeX{} heading commands. % % \item[\key{shorttitle}] % % This is the key which is used if the optional argument is not % of key/val form. So, % \begin{verbatim} % \section[short]{long title} % \end{verbatim} % is the same as % \begin{verbatim} % \section[shortitle=short]{long title} % \end{verbatim} % The key sets the toc, running, bookmark and nameref text. If % \key{shorttitle} is used together with any of the individual % keys (\key{toc}, \key{running}, \key{bookmark}, % \key{nameref}) then they overwrite the default value provided % by \key{shorttitle}. % % \item[\key{numbered}, \key{unnumbered}] % % This allow to control the numbering without stopping toc, % running head or bookmarks as it would happen with the starred % version of the heading command. % % \item[template keys] % % Any other key present is assumed to be a key that should be % passed to the heading instance to overwrite some of its % settings. So, e.g., % \begin{verbatim} % \section[placement=top,number-format=\fbox{\theheading}]{Title} % \end{verbatim} % will force the section to start a new page and put the number % into an \cs{fbox}. For a list of supported keys, check the % following parts of the documentation. % \end{description} % % \subsection{Class support} % % The module redefines all heading commands of the three standard % classes, \pkg{article}, \pkg{report} and \pkg{book}, to use the % new implementation. % % Heading commands of other classes that are defined with % \cs{@startsection} are supported (with some restrictions) through % a compatibility layer described in the next section. % % The heading commands \cs{part} and \cs{chapter} are typically % defined with \cs{secdef} and the internal commands \cs{@part} and % \cs{@chapter}. The module redefines \cs{secdef} to use the % standard instances for these commands---this adds tagging support % but \emph{changes the layout} until the class provides its own % instances. % % Heading commands in other classes defined with \cs{secdef} will % likely error as the code has no real chance to know how to handle % such a heading. % % Heading commands which use neither \cs{@startsection} nor % \cs{secdef} (e.g, the \cs{chapter} command of \pkg{memoir}, or % the heading commands of \pkg{KOMA} classes) are not changed by % this module and so will not be tagged correctly unless they add % explicit tagging support. % % For the AMS-classes, \texttt{latex-lab-firstaid} contains % instances for \cs{part}, \cs{chapter}, \cs{section}, and all % other headings used by these classes. The current set of % instances is now assumed to reproduce the original % layout. Setting them up revealed a number of interesting % differences between these classes---not all of them intentional % (I think). % % % % \subsection{Changing heading commands}\label{sec:startsection} % % It is possible to change heading commands by editing the instance % they use. E.g., in the standard classes one could frame every % heading number with % \begin{verbatim} % \EditInstance{heading}{section} % {number-format=\fbox{\theheading}} % \end{verbatim} % The name of the instance (here \texttt{section}) is the same as % the heading command. % % To support other classes which still setup their heading commands % with \cs{@startsection} the code has a legacy compatibility % layer: the \cs{@startsection} command has been redefined to setup % instances on-the-fly at the first use of a heading command. These % instances have names using the the first argument of % \cs{@startsection} with an attached \texttt{-@startsection}. As % they are created on-the-fly these internal instances can be only % edited \emph{after} the first use of the heading command or by % declaring (instead of editing) an instance with this name in the % preamble. Also as the names of the instances are built from the % first argument of \cs{@startsection} it is not possible to define % two different heading commands of the same level with % \cs{@startsection} as that would require two instances with % different names. The next section describes how to lift this % restriction by converting such sectioning commands to use the % new interfaces. % % \subsection[label=sec:convert] % {Converting heading commands defined with \cs{@startsection}} % % To convert a heading command defined with \cs{@startsection} to % the new interface, suitable instances must be declared. The same % needs to happen if one wants to alter the layout of a heading % that was defined with \cs{@startsection} in the document % preamble. How this can be done is demonstrated here with the % \cs{section} command of the \pkg{ltugboat} class. % % \subsubsection{Step 1: Getting the template name and the instance values} % % The sectioning commands use two template \emph{types}, % \insttype{heading} and \insttype{headformat}. % Compile the following document % % \begin{verbatim} % \DocumentMetadata{} % \documentclass{ltugboat} % % \begin{document} % % \section{test} % <--- needed so that the instances get defined % \ShowInstanceValues{heading}{section-@startsection} % \ShowInstanceValues{headformat}{section-@startsection} % % \end{document} % \end{verbatim} % % This will show the instance values in the log-file: % \begin{verbatim} % The instance 'section-@startsection' of type 'heading' has values: % > level => 1 % > force-unnumbered => false % > placement => normal % > column-spanning => false % > mark-cmd => \sectionmark {##1} % > para-indent => false % > before-vspace => 8.0pt plus 2.0pt minus 2.0pt % > penalty => \c_max_int % > after-penalty-vspace => 0pt % > after-vspace => 4.0pt % > placement-start-code => % > placement-end-code => % > prefix => \NoValue % > punct => % > heading-decls => \tubsecfmt % > prefix-decls => % > number-decls => % > title-decls => % > subtitle-decls => % > quote-decls => % > heading-format => ##1 % > prefix-format => ##1 % > number-format => ##1 % > punct-format => ##1 % > title-format => \use:n {##1} \par % > subtitle-format => ##1 % > quote-format => ##1 % > number-tag => heading-number % > headformat-instance => section-@startsection % > contents-extra => % > name => section % > from template => display. % % The instance 'section-@startsection' of type 'headformat' has values: % > heading-indent => 0pt % > order-hang => prefix,separator-a,?,number,punct,separator-b,? % > order => title,separator-c,subtitle % > separator-a => \nobreakspace % > separator-b => \hspace {1em} % > separator-c => \ % > separator-d => % > from template => hang. % \end{verbatim} % % \subsubsection{Step 2: Get the default template values} % % The last line in both lists show after the \texttt{from template} % the names of the templates of each type. That can be used to get % the default values of these templates. Compile the following % document: % \begin{verbatim} % \DocumentMetadata{} % \documentclass{ltugboat} % % \begin{document} % \ShowTemplateDefaults{heading}{display} % \ShowTemplateDefaults{headformat}{hang} % \end{document} % \end{verbatim} % % This gives in the log and terminal: % \begin{verbatim} % The template 'display' of type 'heading' has default values: % > level => 0 % > force-unnumbered => false % > placement => normal % > column-spanning => false % > mark-cmd => % > para-indent => false % > before-vspace => 0pt % > penalty => \c_max_int % > after-penalty-vspace => 0pt % > after-vspace => 0pt % > placement-start-code => % > placement-end-code => % > prefix => \NoValue % > punct => % > heading-decls => \normalfont % > prefix-decls => % > number-decls => % > title-decls => % > subtitle-decls => % > quote-decls => % > heading-format => ##1 % > prefix-format => ##1 % > number-format => ##1 % > punct-format => ##1 % > title-format => ##1\par % > subtitle-format => ##1 % > quote-format => ##1 % > number-tag => heading-number % > headformat-instance => std % > contents-extra => . % % The template 'hang' of type 'headformat' has default values: % > heading-indent => 0pt % > order-hang => prefix,separator-a,?,number,punct,separator-b,? % > order => title,separator-c,subtitle % > separator-a => \nobreakspace % > separator-b => \hspace {1em} % > separator-c => \ % > separator-d => . % \end{verbatim} % % \subsubsection{Step 3: Declare the instances} % % By comparing the instance values with the default values one can % see which values should be changed in the instance. The names of % the new instances can be freely chosen; best practice is to use % the name of the heading command (without a backslash). % % This leads then to these declarations: % \begin{verbatim} % \DeclareInstance{heading}{section}{display} % { % level = 1, % mark-cmd = \sectionmark{#1}, % remove second hash % before-vspace = 8.0pt plus 2.0pt minus 2.0pt, % after-vspace = 4.0pt, % heading-decls = \tubsecfmt, % headformat-instance = section, % new name % } % \end{verbatim} % The value for key \key{title-format} seems to have changed as % well but \verb=\use:n {##1} \par= is really only a fancy way to % write \verb=##1 \par= so we can keep the default. % Note, however, that the \verb=##1= have to replaced by \verb=#1= % when you reenter them. % % In case of the \insttype{headformat} instance only default values % have been used so all that is necessary is: % \begin{verbatim} % \DeclareInstance{headformat}{section}{hang}{} % \end{verbatim} % % Of course, if you do not want to rely on default values you can (for % clarity) specify all keys and their values in these instances. % % % \subsubsection{Step 4 Redefine the \cs{section} command} % % Finally, the heading command should be defined to use the new % interface: % \begin{verbatim} % \DeclareDocumentCommand \section {s ={shorttitle}o m} % { \ParseLaTeXeHeading {section} {#1} {#2} {#3} } % \end{verbatim} % What \cs{ParseLaTeXeHeading} does is explained in the next % section. % % % \section{Setting up \LaTeXe{} heading commands in classes} % % Heading commands are managed by defining an instance of a % \insttype{heading} template which is then used in % \cs{ParseLaTeXeHeading}, e.g., % \begin{verbatim} % \DeclareDocumentCommand \part {s ={shorttitle}o m} % { \ParseLaTeXeHeading {part} {#1} {#2} {#3} } % \end{verbatim} % The name of the \insttype{heading} instance is given in the first % argument of \cs{ParseLaTeXeHeading} and it is recommended that % classes use the same name as the heading command, to make it % easier for users to identify the instance to edit if they want to % adjust the layout of the heading. Examples of instances that mimic the behavior % of the heading commands in the standard classes can be found % below. Section~\ref{sec:convert} shows how to get instances from % commands previously defined with \cs{@startsection}. % % Legacy setup using \cs{@startsection} remains supported albeit % not that performant and with some restrictions for the users, see % section \ref{sec:startsection} above and in the documentation % below. % % In contrast, \cs{secdef} is only rudimentarily supported. It % delegates the layout to two commands which can contain arbitrary % code (usually hardwired) so that there is no realistic chance to % automatically take this apart and figure out what values should % be used for what template parameters. We therefore redefine % \cs{secdef} and map the standard commands \cs{part} and % \cs{chapter} to the standard instance and error if other uses are % detected. Classes using \cs{secdef} should setup suitable % instances that mimic their current layout, an example can be % found for the ams-classes in \texttt{latex-lab-firstaid.dtx}. % % % \section{Open points} % % \begin{itemize} % \item % % There is no good interface yet to change e.g. the font family of % all heading commands in one go. % % \item % % Support for \pkg{titlesec}, see section \ref{sec:titlesec}. % % \end{itemize} % % % % % % \section{Template types and templates for headings} % % The template(s) for headings expect(s) a large number of % positional arguments containing document user data. The % document-level command, e.g., \cs{section}, may not offer all of % them directly, but may do so via a key value interface or not at % all. If no interface is provided then the template is passed % \cs{NoNalue}. If it is offered via a key value interface, the % template receives whatever is set by the user (and \cs{NoNalue} % otherwise). % % In my initial implementation I had only the main data as % positional arguments, but I came to the conclusion that this % scheme here is better going forward. % % \subsection{Template types} % % The template type \insttype{heading} has 10 data arguments, which % is more than can be specified as positional arguments. For that % reason argument \#9 holds 2 brace groups. The alternative would % be to specify such arguments as keys, but for a number of reasons % I think the approach to put seldom used data arguments all % in the last positional argument is actually better (besides being % faster). % % \begin{TemplateInterfaceDescription}{heading} % \TemplateArgument{1}{key/value list to alter the default heading parameters} % \TemplateArgument{2}{unnumbered heading?} % \TemplateArgument{3}{main title of the heading} % \TemplateArgument{4}{toc title} % \TemplateArgument{5}{running title} % \TemplateArgument{6}{bookmark title} % \TemplateArgument{7}{nameref text} % \TemplateArgument{8}{label for the heading in the form \cs{label}\{\meta{string}\}} % \TemplateArgument{9}{\{ sub title \} \{ quotation \}} % \TemplateSemantics % % Handles the layout and processing aspects of a heading. This % includes doing all the work necessary for tagging. % % Whether or not the heading is numbered is governed through a % boolean, expecting the result of an \texttt{s} specification of % \cs{NewDocumentCommand} or equivalent, i.e., expects % \cs{BooleanTrue} or \cs{BooleanFalse}. % % If the title data is also used for bookmarks, toc, running % header, and cross references then it has to be given several times which % can be arranged for by the parsing interface command that calls % the template instance. % % If the bookmark, toc, or running argument is set to % \enquote{empty} then the bookmark, toc or running header should % be suppressed by the template. This enables the user on document % level to explicitly specify, for example, \texttt{[bookmark=]} to % suppress the bookmark. % % The \texttt{nameref} argument is not expected to be empty. Its value % should always be used as given if named references are to be % generated. % % The label argument either holds a \cs{label} command as provided % by the user (or several, see implementation of % \cs{ParseLaTeXeHeading}) or it is empty. I.e., it can be directly % executed by a template at the right point where the reference % counter (if any) has been set up without the need to check its % content. % % Argument \#9 holds further user data (in brace groups) which are % seldom implemented, but if they are the data is available in a % positional argument, which then needs to be taken apart using % \cs{@firstoftwo} (for subtitle) or \cs{@secondoftwo} (for a % quotation). If no data is provided then \cs{NoValue} is used to % indicate that. % % \end{TemplateInterfaceDescription} % % % \changes{0.9g}{2026-05-24}{Add 'fixed prefix text' as a further % argument to the headformat template type} % % \begin{TemplateInterfaceDescription}{headformat} % \TemplateArgument{1}{key/value list to alter the default headformat parameters} % \TemplateArgument{2}{fixed prefix text} % \TemplateArgument{3}{formatted heading number} % \TemplateArgument{4}{main title of the heading} % \TemplateArgument{5}{subtitle} % \TemplateArgument{6}{quotation} % \TemplateSemantics % % Handles the layout of just the heading label (prefix and number), % main title, subtitle, and quotation but not the spatial relation % to previous and following text. % % Whether or not the heading is numbered is governed through a % boolean, e.g., result of an \texttt{s} specification in % \cs{NewDocumentCommand} or equivalent. % % If there is no prefix, number, subtitle or quotation then this is indicated % with \cs{NoValue}. % % The templates are currently % implemented to mimic \LaTeXe{} behavior so if a % \insttype{heading} template should not number a heading it calls % a \insttype{headformat} template with both prefix and number set % to \cs{NoValue}. % % The \meta{subtitle} and \meta{quotation} are always % ignored by the currently defined templates but that will probably % change soon, either by extending them or by providing additional % ones that support these mandatory argument. % % \end{TemplateInterfaceDescription} % % % % \subsection{Templates} % % % \subsubsection{Templates of type \insttype{heading}} % % % There are quite a number of keys that are expected to be % recognized by all heading templates (though they may choose not % to make use of them). These are listed below instead of being % repeated on the actual templates. % % All other keys are either attached to the \insttype{heading} or % to the \insttype{headformat} templates. Those that typically vary % from heading instance to the next (e.g., \key{heading-decls}) are % all declared in the \insttype{heading} templates even if they are % actually only used within \insttype{headformat} templates. In % other words, \insttype{headformat} templates have a number of % implicit variables that they expect to be set.\footnote{Maybe % questionable} % % \changes{0.9h}{2026-07-04}{Support a \key{force-unnumbered} key % (tagging/1473)} % \changes{0.9q}{2026-09-22}{Renamed \key{start-code} key to % \key{placement-start-code} (tagging/1617)} % \changes{0.9q}{2026-09-22}{Renamed \key{final-code} key to % \key{placement-end-code} (tagging/1617)} % % \begin{TemplateDescription}{heading}{\meta{all}} % % \changes{0.9h}{2026-06-21}{Support \texttt{column-spanning} key (tagging/1436)} % % \TemplateKey{name}{tokenlist} % {Referenceable name of the heading instance. String that % is acceptable in csnames for use in building counter % names, etc.}{} % \TemplateKey{parent-name}{tokenlist} % {Name of the next higher heading instance. If not % given, then the internal heading level of the heading % instance is set to \texttt{0} --- not yet % implemented/may vanish}{} % \TemplateKey{reset-counter}{tokenlist} % {Name of the heading instance that should reset the % numbering of this heading level (if any) --- not yet % implemented/may vanish}{} % \TemplateKey{level}{integer} % {Sets the internal heading-level rather than deducing % it from \key{parent-name}. Can be used to specify the % top-level heading if not \texttt{0}, or all headings % in legacy implementations, e.g., through \cs{@startsection}}{} % % \TemplateKey{force-unnumbered}{boolean} % {This heading is never numbered, even if explicitly requested % in the optional argument to the heading. If % \texttt{false} then numbering is determined in the normal % way (through \texttt{secnumdepth}, \texttt{*}, or a % setting in the optional argument to the heading).}{false} % % \TemplateKey{placement}{choice} % {Set the heading placement, i.e., the behavior of the % heading with respect to page breaks. Allowed values % are \texttt{page} (heading forms a page if its own), % \texttt{top} (heading starts a new page), % \texttt{normal} (heading can appear anywhere on the % page), \texttt{ragged} (like \texttt{normal} but if a % page break is taken before the heading then the % previous page is set ragged and not stretched). % Further possibilities might be \texttt{rectopage} and % \texttt{rectotop} if we implement that. % \\ % This key sets the values for the % \key{placement-start-code} and % \key{placement-end-code} keys. However, if % \key{placement-start-code} or \key{placement-end-code} % has been set to non-empty value it will overwrite % whatever the \key{placement} key would put in. Thu % providing unsuitable values for one or both of them % may produce strange effects. % \\ % If a special placement is wanted that is not covered % by the choice values for the \key{placement} key it is % normally best to set both \key{placement-start-code} and % \key{placement-end-code} to a non-empty value, so that % the \key{placement} key is fully ignored. % } % {\texttt{normal}} % % \TemplateKey{placement-start-code}{tokenlist} % {The value for this key is normally set by the % \key{placement} key. Executed before the heading % starts, so can issue, for example, a \cs{clearpage}. % However, it is \emph{not} a user key that can be % arbitrarily set! It is only provided as to allow % extending the functionality of the \key{placement} key % and if used requires that everything necessary for % placement is then handled by the code.} % {\meta{empty}} % % \TemplateKey{placement-end-code}{tokenlist} % {The value for this key is normally set by the % \key{placement} key. Executed after the heading is % typeset. Can be used to set up code for putting the % heading on a page by its own, or arrange for paragraph % handling of a following paragraph, etc. This is % \emph{not} a user key to place arbitrary code after % the heading because this will then replace what was % put there by the \key{placement} key to handle % paragraph indentation, etc.\ after the heading. It is % only provided as a means to extend the % functionality of the \key{placement} key and if used % requires that everything necessary is handled by the % code.} % {\meta{empty}} % % % \TemplateKey{column-spanning}{boolean} % {If true produces a heading that spans columns in % \texttt{twocolumn} typesetting. Requires % \key{placement} \texttt{=} \keyvalue{top} (and adjusts % if necessary) and only has an effect % if the columns are produced via the \texttt{twocolumn} % class option and not when using \env{multicol}. This % might get extended when we refactor the OR handling.}{false} % % \TemplateKey{prefix}{tokenlist} % {A fixed string, such as \enquote{Chapter}, that can be % used together with the number (if any) to form a % \enquote{heading label}. Usage and placement is up to % the template, so it could be placed after the number % by the template (in which case it isn't really a % prefix).} {\cs{NoValue}} % % \TemplateKey{punct}{tokenlist} % {A fixed string to be added to the end of the heading % number, i.e., it shows up in on the heading but not % in cross-references.} {\meta{empty}} % % \TemplateKey{mark-cmd}{function(1)} % {Function that receives the \meta{running} argument and % creates a suitable mark insertion} % {\texttt\textbackslash\meta{name}\texttt{mark}} % % \TemplateSemantics % % The above keys should be implemented by all heading templates. % % At the moment I have retained the \LaTeXe{} interfaces for marks, % e.g., one has to set up \cs{chaptermark}, \cs{sectionmark}, % etc.\ but I'm not sure this should stay (even though it is % certainly simpler from a compatibility perspective). % % All templates should set up \cs{theheading} to correspond to % \cs{the\meta{name}}. % \end{TemplateDescription} % % % The keys \key{placement-start-code} and \key{placement-end-code} % have formerly been called \cs{start-code} and \cs{final-code} but % as they are directly tied to the \key{placement} key and can't be % arbitrarily set, they got renamed. The old names will eventually % be removed! % % % \begin{TemplateDescription}{heading}{display} % % \TemplateKey{para-indent}{boolean} % {Should the paragraph after the heading be indented?} % {false} % % \TemplateKey{before-vspace}{skip} % {Vertical space before the heading if there is no page % or column break. If there is one it vanishes. % In particular this means it will not be used in the % heading \texttt{placement}s \texttt{page} or \texttt{top}} % {0pt} % % \TemplateKey{penalty}{integer} % {Penalty to break before the heading. The default % (\TeX's largest integer) indicates that no penalty was set in % which case \cs{@secpenalty} is used (to support the \LaTeXe{} interface).} % {\number\maxdimen} % % \TemplateKey{after-penalty-vspace}{skip} % {Vertical space before the heading but after the % penalty for the heading. If the penalty results in a page or % column break, this space remains at the top of the page} % {0pt} % % \TemplateKey{after-vspace}{skip} % {Vertical space after the heading} % {0pt} % % \TemplateKey{heading-decls}{tokenlist} % {Declarations (such as font or color settings) applied % to all heading elements, i.e., number, title, % subtitle, and quotation, if present. % Note that color commands (unless \pkg{luacolor} is used) % introduce a break point before the heading and therefore one should either % surround color commands with \cs{SaveLastSkip} and \cs{RestoreLastSkip} or % to use the keys \texttt{prefix-decls}, \texttt{number-decls}, % \texttt{title-decls}, etc.\ instead.} % {\cs{normalfont}} % % \TemplateKey{prefix-decls}{tokenlist} % {Declarations (such as font or color settings) applied % to the heading prefix, overwriting the setting % of \key{heading-decls}.} % {\meta{empty}} % % \TemplateKey{number-decls}{tokenlist} % {Declarations (such as font or color settings) applied % to the heading number, overwriting the setting % of \key{heading-decls}.} % {\meta{empty}} % % \TemplateKey{title-decls}{tokenlist} % {Declarations (such as font or color settings) applied % to the heading title, overwriting the setting % of \key{heading-decls}.} % {\meta{empty}} % % \TemplateKey{subtitle-decls}{tokenlist} % {Declarations (such as font or color settings) applied % to the heading subtitle, overwriting the setting % of \key{heading-decls}.} % {\meta{empty}} % % \TemplateKey{quote-decls}{tokenlist} % {Declarations (such as font or color settings) applied % to the heading quote, overwriting the setting % of \key{heading-decls}.} % {\meta{empty}} % % \TemplateKey{heading-format}{function(1)} % {Code that receives the whole heading as an argument.} % {\#1} % \TemplateKey{prefix-format}{function(1)} % {Code that receives the prefix string as an argument.} % {\#1} % \TemplateKey{number-format}{function(1)} % {Code that receives the number string as an argument.} % {\#1} % \TemplateKey{punct-format}{function(1)} % {Code that receives the punctuation string as an argument.} % {\#1} % \TemplateKey{title-format}{function(1)} % {Code that receives the title string as an argument. % By default it ends in \cs{par} so that \cs{baselineskip} % changes in the settings (e.g., in \key{title-decls}) % are applied} % {\#1 \cs{par}} % \TemplateKey{subtitle-format}{function(1)} % {Code that receives the subtitle string as an argument.} % {\#1} % \TemplateKey{quote-format}{function(1)} % {Code that receives the quote string as an argument.} % {\#1} % \TemplateKey{number-tag}{tokenlist} % {Name of the tag to put around the number (empty means % no tag).} % {heading-number} % % \TemplateKey{headformat-instance}{instance} % {Template instances of type \insttype{headformat}} % {std} % \TemplateKey{contents-extra}{tokenlist} % {Code containing \cs{addcontents} calls to write to % files like \texttt{.lot} or \texttt{.lot}} % {\meta{empty}} % % % \TemplateSemantics % % The key \key{heading-decls} determines the overall % \cs{baselineskip} used in the heading. Font changes in individual % declarations do not have that effect, because they are all % executed in groups and if if all elements are within the same % paragraph then the final \cs{par} after the heading comes too % late. If you want that the font setting for a specific key, e.g., % \key{title}, depends on the font setting in \key{title-decls} you % have to ensure that a \cs{par} happens within \key{title-format} % key, e.g., \key{title-format}\texttt{=\#1\cs{par}} (which is what % happens in the default value of \key{title-format}). % \end{TemplateDescription} % % % % % \begin{TemplateDescription}{heading}{runin} % % \TemplateKey{before-vspace}{skip} % {Vertical space before the heading if there is no page % or column break. If there is one it vanishes.} % {0pt} % % \TemplateKey{penalty}{integer} % {Penalty to break before the heading. The default % (\TeX's largest integer) indicates that no penalty was set in % which case \cs{@secpenalty} is used.} % {\number\maxdimen} % % \TemplateKey{after-penalty-vspace}{skip} % {Vertical space before the heading but after the % penalty for the heading. If the penalty results in a page or % column break, this space remains at the top of the page. It is % also applied if the heading is a \texttt{page} or \texttt{top} heading.} % {0pt} % % \TemplateKey{after-hspace}{skip} % {Horizontal space after the heading.}{0pt} % % \TemplateKey{heading-decls}{tokenlist} % {Declarations (such as font or color settings) applied % to all heading elements, i.e., number, title, % subtitle, and quotation, if present. % Note that color commands (unless \pkg{luacolor} is used) % introduce a break point before the heading and therefore one should either % surround color commands with \cs{SaveLastSkip} and \cs{RestoreLastSkip} or % to use the keys \texttt{prefix-decls}, \texttt{number-decls}, % \texttt{title-decls}, etc.\ instead.} % {\cs{normalfont}} % % \TemplateKey{prefix-decls}{tokenlist} % {Declarations (such as font or color settings) applied % to the heading prefix, overwriting the setting % of \key{heading-decls}.} % {\meta{empty}} % % \TemplateKey{number-decls}{tokenlist} % {Declarations (such as font or color settings) applied % to the heading number, overwriting the setting % of \key{heading-decls}.} % {\meta{empty}} % % \TemplateKey{title-decls}{tokenlist} % {Declarations (such as font or color settings) applied % to the heading title, overwriting the setting % of \key{heading-decls}.} % {\meta{empty}} % % \TemplateKey{subtitle-decls}{tokenlist} % {Declarations (such as font or color settings) applied % to the heading subtitle, overwriting the setting % of \key{heading-decls}.} % {\meta{empty}} % % \TemplateKey{quote-decls}{tokenlist} % {Declarations (such as font or color settings) applied % to the heading quote, overwriting the setting % of \key{heading-decls}.} % {\meta{empty}} % % \TemplateKey{heading-format}{function(1)} % {Code that receives the whole heading as an argument.} % {\#1} % \TemplateKey{prefix-format}{function(1)} % {Code that receives the prefix string as an argument.} % {\#1} % \TemplateKey{number-format}{function(1)} % {Code that receives the number string as an argument.} % {\#1} % \TemplateKey{punct-format}{function(1)} % {Code that receives the punctuation string as an argument.} % {\#1} % \TemplateKey{title-format}{function(1)} % {Code that receives the title string as an argument. % Since this is a runin heading we should remain in % horizontal mode so the default does not include a % \cs{par}.} % {\#1} % \TemplateKey{subtitle-format}{function(1)} % {Code that receives the subtitle string as an argument.} % {\#1} % \TemplateKey{quote-format}{function(1)} % {Code that receives the quote string as an argument.} % {\#1} % \TemplateKey{number-tag}{tokenlist} % {Name of the tag to put around the number (empty means % no tag).} % {heading-number} % % \TemplateKey{headformat-instance}{instance} % {Template instances of type \insttype{headformat}} % {std} % \TemplateKey{contents-extra}{tokenlist} % {Code containing \cs{addcontents} calls to write to % files like \texttt{.lot} or \texttt{.lot}} % {\meta{empty}} % % \TemplateSemantics % % The runin template is fairly similar to the display one. Main % differences are due to the fact that we stay in horizontal mode % at the end of the heading so that we have \key{after-hspace} % instead of \key{after-vspace} and we do not have a \cs{par} % inside \key{title-format}. % % \end{TemplateDescription} % % % % % \subsubsection{Templates of type \insttype{headformat}} % % The \insttype{headformat} templates deal with positioning prefix, % number, punct, title, subtitle and quote. If the heading is unnumbered % then prefix, number and punct are expected to be \cs{NoValue} (this % is arranged in the calling \insttype{heading} template). % % Tagging is taken care of in the calling template as well, all the % the \insttype{headformat} templates do is opening and closing % MCs as necessary. % % The templates make use of the generic order key mechanism. % % % \begin{TemplateDescription}{headformat}{display} % % \TemplateKey{heading-indent}{dimen} % {Horizontal space before the heading (shifting it to % the right in a LR typesetting context)} % {0pt} % % \TemplateKey{order}{commalist} % {Order of elements that form the heading. Some of the % separators are dropped if the previous element is % \cs{NoValue}, e.g., \key{separator-a} and % \key{separator-b}. % The key{quote} key is currently ignored.} % {prefix, separator-a, ?, number, punct, separator-b, ?, % title, separator-c, subtitle} % % \TemplateKey{separator-a}{tokenlist} % {By default the separation between \key{prefix} and % \key{number}.} % {\cs{nobreakspace}} % % \TemplateKey{separator-b}{tokenlist} % {By default the separation between \key{punct} and % \key{title}. The default starts a new line with extra % vertical space.} % {\cs{par} \cs{nobreak} \cs{vspace}\{20pt\}} % % \TemplateKey{separator-c}{tokenlist} % {By default the separation between \key{title} and % \key{subtitle}.} % {\cs{ }} % % \TemplateKey{separator-d}{tokenlist} % {By default not used.} % {\meta{empty}} % % % \TemplateSemantics % % Implements a heading layout where number and title are vertically % separated. It is what \LaTeX{} always used by default for % \cs{chapter} and \cs{part}. % % This template can be called from the \insttype{heading} % \instname{display} template. It is not suitable for use with the % \instname{runin} template. % % \end{TemplateDescription} % % % \begin{TemplateDescription}{headformat}{hang} % % \TemplateKey{heading-indent}{dimen} % {Horizontal space before the heading (shifting it to % the right in a LR typesetting context)} % {0pt} % % \TemplateKey{order-hang}{commalist} % {Order of elements of the heading that are used to form % the hanging indentation (and are placed in that % space).} % {prefix, separator-a, ?, number, punct, separator-b, ?} % % \TemplateKey{order}{commalist} % {Order of remaining headings elements. % The key{quote} key is currently not used.} % {title, separator-c, subtitle} % % \TemplateKey{separator-a}{tokenlist} % {By default the separation between \key{prefix} and % \key{number}.} % {\cs{nobreakspace}} % % \TemplateKey{separator-b}{tokenlist} % {By default the horizontal separation after \key{punct} still % being counted in the hanging indentation} % {\cs{hspace}\{1em\}} % % \TemplateKey{separator-c}{tokenlist} % {By default the separation between \key{title} and % \key{subtitle}.} % {\cs{ }} % % \TemplateKey{separator-d}{tokenlist} % {By default not used.} % {\meta{empty}} % % % \TemplateSemantics % % Implements a heading layout where number and title start on the % same line and the title is hanging off from that if the title % has more than one line. It is what \LaTeX{} always used by % default for \cs{section}, \cs{subsection}, and % \cs{subsubsection}. % % This template can be called from the \insttype{heading} % \instname{display} template. It is not suitable for use with the % \instname{runin} template. % % \end{TemplateDescription} % % % \begin{TemplateDescription}{headformat}{runin} % % \TemplateKey{heading-indent}{dimen} % {Horizontal space before the heading (shifting it to % the right in a LR typesetting context)} % {0pt} % % \TemplateKey{order}{commalist} % {Order of elements that form the heading. Some of the % separators are dropped if the previous element is % \cs{NoValue}, e.g., \key{separator-a} and % \key{separator-b}. % The key{quote} key is currently ignored.} % {prefix, separator-a, ?, number, punct, separator-b, ?, % title, separator-c, subtitle} % % \TemplateKey{separator-a}{tokenlist} % {By default the separation between \key{prefix} and % \key{number}.} % {\cs{nobreakspace}} % % \TemplateKey{separator-b}{tokenlist} % {By default the separation between \key{punct} and % \key{title}. The default starts a new line with extra % vertical space.} % {\cs{hspace}\{1em\}} % % \TemplateKey{separator-c}{tokenlist} % {By default the separation between \key{title} and % \key{subtitle}.} % {\cs{ }} % % \TemplateKey{separator-d}{tokenlist} % {By default not used.} % {\meta{empty}} % % % \TemplateSemantics % % Implements a heading layout where text after the heading % continues on the same line as the heading. It is what \LaTeX{} % always used by default for % \cs{paragraph} and \cs{subparagraph}. % % This template is intended to be used with the \insttype{heading} % \instname{runin} template (but could perhaps also be used with % \instname{display}). % \end{TemplateDescription} % % % % \subsection{Default instances} % % These instances are set up to mimic the design of the standard % \LaTeX{} classes. For other classes they could be adjusted by % changing the instance values and/or by basing them on a different % template. % % \subsubsection{Instances of \insttype{heading} templates} % % \begin{description} % % \item[\texttt{part} (instance of \texttt{display} template)] Used for the % \cs{part} command. % % \item[\texttt{chapter} (instance of \texttt{display} template)] Used for the % \cs{chapter} command. % % \item[\texttt{section} (instance of \texttt{display} template)] Used for the % \cs{section} command. % % \item[\texttt{subsection} (instance of \texttt{display} template)] Used for % the \cs{subsection} command. % % \item[\texttt{subsubsection} (instance of \texttt{display} template)] Used for % the \cs{subsubsection} command. % % \item[\texttt{paragraph} (instance of \texttt{runin} template)] Used for the % \cs{paragraph} command. % % \item[\texttt{subparagraph} (instance of \texttt{runin} template)] Used for % the \cs{subparagraph} command. % % \end{description} % % % \subsubsection{Instances of \insttype{headformat} templates} % % \begin{description} % % \item[\texttt{part} (instance of \texttt{display} template)] Used in % \insttype{heading} instance for \cs{part}. % % \item[\texttt{chapter} (instance of \texttt{display} template)] Used in % \insttype{heading} instance for \cs{chapter}. By default identical % to the one used for \cs{part}. % % \item[\texttt{std} (instance of \insttype{hang} template)] Used as % default in the heading template if nothing else is specified. % % \item[\texttt{section} (instance of \insttype{hang} template)] Used in % \insttype{heading} instance for \cs{section}. By default identical % to the \texttt{std} instance. % % \item[\texttt{subsection} (instance of \insttype{hang} template)] Used in % \insttype{heading} instance for \cs{subsection}. By default identical % to the \texttt{std} instance. % % \item[\texttt{subsubsection} (instance of \insttype{hang} template)] Used in % \insttype{heading} instance for \cs{subsubsection}. By default identical % to the \texttt{std} instance. % % \item[\texttt{paragraph} (instance of \texttt{runin} template)] Used in % \insttype{heading} instance for \cs{paragraph}. % % \item[\texttt{subparagraph} (instance of \texttt{runin} template)] Used in % \insttype{heading} instance for \cs{subparagraph}. % % \end{description} % % % % % \subsection{Use of templates from the 2026-06-01 release of \LaTeX{}} % % In the first heading implementation using templates we provided a % number of somewhat restricted templates that covered the default % layouts of standard \LaTeX{} classes but not much beyond this. % % For the 2026-11-01 release we have replaced them with more % general templates described above that use the general order key % mechanism. As a side effect the new templates have some % additional keys and some keys used in the templates of first % implementation got dropped. % % For the next couple of releases we will keep the original % templates available (only renamed to % \meta{name}\texttt{-v1}). Eventually, we want to drop the % \texttt{-v1} versions but for the next couple of releases we % provide them alongside the new more flexible templates, so that % there is no need to transitioning to the new % templates in a rush. % % If you have defined instances using the first template version % then you can (for now) continue to use them simply by changing % the template names in your instance declarations from \meta{name} % to \meta{name}\texttt{-v1}, e.g., you have to change from %\begin{verbatim} % \DeclareInstance{heading} {chapter}{display} { ... } % \DeclareInstance{headformat}{chapter}{display} { ... } % % \DeclareInstance{heading} {section}{hang} { ... } % \DeclareInstance{headformat}{section}{hang} { ... } % ... %\end{verbatim} % to %\begin{verbatim} % \DeclareInstance{heading} {chapter}{display-v1} { ... } % \DeclareInstance{headformat}{chapter}{display-v1} { ... } % % \DeclareInstance{heading} {section}{hang-v1} { ... } % \DeclareInstance{headformat}{section}{hang-v1} { ... } % ... %\end{verbatim} % No other changes are necessary for now. However, going forward % you should switch to the extended templates because the % \texttt{-v1} templates will eventually be dropped. % % Switching mainly means stopping the use of the keys % \key{prefix-number-sep} and \key{number-title-sep} because they % have been replaced with the more general \key{separator-a}, % \key{separator-b}, \ldots\ keys.\footnote{There are some more % changes in the keys, but it is less likely that they have been % used. If necessary, compare the documentation for the old and the % new templates to find out what else needs adjusting.} % % The template documentation for these deprecated templates is % available as part of Appendix~\ref{appendix:a} in case you need % to consult it in order to update your template instance % declarations to use the new templates. Please do not use these % templates for new work, as they will eventually get removed. % % % % % \section{Support for legacy classes and packages} % % \subsection{Support classes based on legacy \LaTeXe{} interfaces} % % \DescribeMacro\@startsection % The \cs{@startsection} command has the following syntax: % \begin{verbatim} % \@startsection{name}{level}{indent}{beforeskip}{afterskip}{style} % \end{verbatim} % with a special logic that the sign of \meta{beforeskip} determines % display or runin heading and that of \meta{afterskip} whether or % not the next paragraph is indented. % % All arguments can be cleanly mapped to \insttype{heading} and % \insttype{headformat} templates. Thus, if encountered suitable % instances are automatically declared unless they already exist. % % % % \DescribeMacro\secdef % In contrast, \cs{secdef} only does the parsing and delegates the % layout to two commands which can contain arbitrary code (usually % hardwired) so that there is no realistic chance to take this % apart and figure out what values should be used for what template % parameters. % % We therefore do not attempt to model this, but instead just use % the standard layout from \LaTeX's standard classes for % \cs{chapter} and \cs{part} if we can identify that the \cs{secdef} % is used to define one of these. % % Maybe we can try at some stage to get some static analysis tool % going. % % % \subsection[label=sec:titlesec]{Support for the \pkg{titlesec} package interfaces} % TODO % % \noindent % \DescribeMacro\titleformat % The \cs{titleformat} declaration has the following syntax: % \begin{verbatim} % \titleformat{cmd}[shape]{format}{label}{sep}{before-code}[after-code] % \end{verbatim} % % \noindent % \DescribeMacro\titlespacing % The \cs{titlespacing} declaration has the following syntax: % \begin{verbatim} % \titlespacing*{cmd}{left-sep}{before-vspace}{after-vspace}[right-sep] % \end{verbatim} % % \section{Support for links} % % All heading commands (numbered and unnumbered) should contain support for % active links directly. hyperref does not patch the new heading commands! % As \cs{refstepcounter} is not used there is no automatic target. % % This means that all definitions should contain at an appropriate place % a \cs{MakeLinkTarget} command. Typically numbered heading commands will use % \verb+\MakeLinkTarget{+\meta{counter}\verb+}+ while unnumbered heading commands % use \verb+\MakeLinkTarget[+\meta{counter}\verb+]{}+. % % The definition must take care that the target is not separated from the heading % by a page break. It also should not affect spacing. % The target should be placed so that a jump to the target gives % a satisfying user experience, in most cases the best places is at the left margin % a bit above the heading. % % The targets create also structure destinations that % are used by the tagging code. It is therefore important that the targets % are in the right place in relation to the tagging commands. % % \section{Bookmarks} % % Bookmarks are created by the \cs{addcontentsline} command, more precisely in the % new latex-lab toc code a hook with arguments is inserted at the begin of the command % which contains the command that creates the bookmark. The arguments of \cs{addcontentsline} % and so also of the hook are the type of the toc, % the level name and the (short-)title of the heading. To pass a differing % bookmark text the code uses \cs{texorpdfstring} in the \cs{addcontentsline}. % \ufi{check if this is still the implementation ...} % \section{Tagging support} % % Tagging of heading commands has to do two tasks: % \begin{itemize} % \item surround the whole section (heading and text) with a \texttt{Sect} structure % \item tag the heading with an Hn structure. % \end{itemize} % This is done with tagging sockets which are documented in the code and in \texttt{latex-lab-sec}. % % \section{Debugging support} % % \begin{function}{\DebugHeadingsOn,\DebugHeadingsOff, \head_debug_on:, \head_debug_off:} % % These commands enable/disable debugging messages. % % \end{function} % % % % \end{documentation} % % \StopEventually{\setlength\IndexMin{200pt} \PrintIndex } % % % % \begin{implementation} % % \section{The Implementation} % % % \begin{macrocode} %<*package> %<@@=head> % \end{macrocode} % % % \begin{macrocode} \ProvidesExplPackage {latex-lab-testphase-sec-template} {\ltlabsecIIdate} {\ltlabsecIIversion} {heading implementation} % \end{macrocode} % % \changes{v0.9c}{2026-03-12}{Switch from \texttt{x}- to \texttt{e}-type % expansion in expl3 code} % % \bigskip % % \subsection{Debugging} % % \begin{variable}{\g_@@_debug_bool} % % \begin{macrocode} \bool_new:N \g_@@_debug_bool % \end{macrocode} % \end{variable} % % % \begin{macro}{\@@_debug:n,\@@_debug_typeout:n} % % \begin{macrocode} \cs_new_eq:NN \@@_debug:n \use_none:n \cs_new_eq:NN \@@_debug_typeout:n \use_none:n % \end{macrocode} % \end{macro} % % \begin{macro}{\head_debug_on:,\head_debug_off:, % \@@_debug_gset:} % \begin{macrocode} \cs_new_protected:Npn \head_debug_on: { \bool_gset_true:N \g_@@_debug_bool \@@_debug_gset: } % \end{macrocode} % % \begin{macrocode} \cs_new_protected:Npn \head_debug_off: { \bool_gset_false:N \g_@@_debug_bool \@@_debug_gset: } % \end{macrocode} % % \begin{macrocode} \cs_new_protected:Npn \@@_debug_gset: { \cs_gset_protected:Npe \@@_debug:n ##1 { \bool_if:NT \g_@@_debug_bool {##1} } \cs_gset_protected:Npe \@@_debug_typeout:n ##1 { \bool_if:NT \g_@@_debug_bool { \typeout{[head]~ ##1} } } } % \end{macrocode} % \end{macro} % % % \begin{macro}{\DebugHeadingsOn,\DebugHeadingsOff} % % \begin{macrocode} \cs_new_protected:Npn \DebugHeadingsOn { \head_debug_on: } \cs_new_protected:Npn \DebugHeadingsOff { \head_debug_off: } % \end{macrocode} % % \begin{macrocode} \DebugHeadingsOff % \end{macrocode} % \end{macro} % % \begin{macro}{\@@_show_arguments:nnnnnn} % Some debug data \ldots % \begin{macrocode} \cs_new:Npn \@@_show_arguments:nnnnnn #1#2#3#4#5#6 { \@@_debug_typeout:n{---------~ Headformat~ instance~ arguments:} \@@_debug_typeout:n{1:~ keys~ =~ \exp_not:n{#1}} \@@_debug_typeout:n{2:~ prefix~ =~ \exp_not:n{#2}} \@@_debug_typeout:n{3:~ number~ =~ \exp_not:n{#3}} \@@_debug_typeout:n{4:~ title~ =~ \exp_not:n{#4}} \@@_debug_typeout:n{5:~ subtitle~ =~ \exp_not:n{#5}} \@@_debug_typeout:n{6:~ quotation~ =~ \exp_not:n{#6}} } % \end{macrocode} % \end{macro} % \begin{macro}{\@@_show_arguments:nnnnnnnnn} % Some debug data \ldots % \begin{macrocode} \cs_new:Npn \@@_show_arguments:nnnnnnnnn #1#2#3#4#5#6#7#8#9 { \@@_debug_typeout:n{---------~ Heading~ instance~ arguments:} \@@_debug_typeout:n{1:~ keys~ =~ \exp_not:n{#1}} \@@_debug_typeout:n{2:~ unnumbered~ =~ \exp_not:n{#2}} \@@_debug_typeout:n{3:~ title~ =~ \exp_not:n{#3}} \@@_debug_typeout:n{4:~ toc~ =~ \exp_not:n{#4}} \@@_debug_typeout:n{5:~ running~ =~ \exp_not:n{#5}} \@@_debug_typeout:n{6:~ bookmark~ =~ \exp_not:n{#6}} \@@_debug_typeout:n{7:~ nameref~ =~ \exp_not:n{#7}} \@@_debug_typeout:n{8:~ label~ =~ \exp_not:n{#8}} \@@_debug_typeout:n{9:~ subtitle | quote ~ =~ \exp_not:n{#9}} } % \end{macrocode} % \end{macro} % % \subsection{Temp variable(s)} % \begin{variable}{\l_@@_tmpa_tl} % \begin{macrocode} \tl_new:N\l_@@_tmpa_tl % \end{macrocode} % \end{variable} % % \subsection{variable(s)} % These token lists are automatically declared by the key machinery. % \begin{macrocode} %\tl_new:N \l_@@_bookmark_tl %\tl_new:N \l_@@_running_tl %\tl_new:N \l_@@_toc_tl %\tl_new:N \l_@@_nameref_tl %\tl_new:N \l_@@_subtitle_tl %\tl_new:N \l_@@_quote_tl %\bool_new:N l_@@_unnumbered_bool % \end{macrocode} % % \begin{variable}{ % \l_@@_label_tl, % \l_@@_instance_keys_tl, % \l_@@_number_tl, % \l_@@_saved_secnumdepth_tl, % \l_@@_title_tl, % \l_@@_placement_tl} % % But these token lists needs a declaration: % % \begin{macrocode} \tl_new:N \l_@@_label_tl \tl_new:N \l_@@_instance_keys_tl \tl_new:N \l_@@_number_tl \tl_new:N \l_@@_saved_secnumdepth_tl \tl_new:N \l_@@_title_tl \tl_new:N \l_@@_placement_tl % \end{macrocode} % \end{variable} % % \subsection{New user commands} % % \begin{macro}{\theheading} % This command expands to \cs{thechapter}, \cs{thesection} etc % in every heading command. % \begin{macrocode} \tl_new:N\theheading % \end{macrocode} % \end{macro} % % \begin{macro}{\partmark} % This replaces the fix \verb+\markboth{}{}+ or similar in the standard \cs{part} % definition. As the command is already defined in some classes (like \pkg{memoir}), % we provide it through a hook. % \begin{macrocode} \AddToHook{class/after}{\providecommand*\partmark[1]{\markboth{}{}}} % \end{macrocode} % \end{macro} % % \subsection{The \LaTeXe{} parsing interface} % % \LaTeXe{} provides heading commands with a starred form % (unnumbered) and one optional argument (to specify an alternative % toc/running head). We augment this slightly by supporting a key % value interface in the optional argument. This is handled by % defining the commands through \cs{ParseLaTeXeHeading}. This % should happen in the document class. % % % \begin{macro}{\ParseLaTeXeHeading} %^^A % \cs{ParseLaTeXeHeading} arguments: % \begin{itemize} % \item[1:] instance name to use (of type \enquote{heading}) % \item[2:] numbered? boolean % \item[3:] shorttitle or key/val for layout adjustments and individual % title settings % \item[4:] main title % \end{itemize} % % This command handles the document input that may be hidden in the % optional argument, i.e., special data for toc, running (header), % bookmark, label, and for more specialized headings subtitle and % quote. It also handles the legacy case that the optional argument is just % an alternate title to be used for toc, running, and bookmark % (through the key shorttitle to which it gets converted). % % \begin{macrocode} \cs_new_protected:Npn \ParseLaTeXeHeading #1 #2 #3 #4 { % \end{macrocode} % % Before we start parsing a heading we have to make sure that there % isn't a runin heading still waiting to be typeset. Otherwise, the % new heading might overwrite some variables that are still needed % to typeset the dangling heading. % \changes{0.9p}{2026-09-18}{Output a dangling runin heading before parsing % the next heading} % \begin{macrocode} \if@noskipsec \leavevmode \fi \par % \end{macrocode} % % \begin{macrocode} \@@_debug_typeout:n{==================================} \@@_debug_typeout:n{#1:~ \IfBooleanT{#2}{*} \IfValueT{#3}{[\exp_not:n{#3}]} {\exp_not:n{#4}}} % \end{macrocode} % We first check if the title argument contains a \cs{label} % command. If yes, we remove it and add it to \cs{l_@@_label_tl} % (and if more than one all of them) and save the rest of the main title in % \cs{l_@@_title_tl} for later use. If there was no \cs{label} % command then \cs{l_@@_title_tl} is set to \texttt{\#4}. % \begin{macrocode} \@@_find_label:w #4 \label\q_no_value \@@_find_label:w % \end{macrocode} % By default toc, running, and bookmark use the data provided by % the main title. However, if the second argument is true (i.e., a % star was given) they are all suppressed (because this is the \LaTeXe{} logic). % \begin{macrocode} \IfBooleanTF{#2} { \tl_clear:N \l_@@_toc_tl \tl_clear:N \l_@@_running_tl \tl_clear:N \l_@@_bookmark_tl \bool_set_true:N \l_@@_unnumbered_bool } { \tl_set_eq:NN \l_@@_toc_tl \l_@@_title_tl \tl_set_eq:NN \l_@@_running_tl \l_@@_title_tl \tl_set_eq:NN \l_@@_bookmark_tl \l_@@_title_tl \bool_set_false:N \l_@@_unnumbered_bool } % \end{macrocode} % The nameref always starts out matching the main title. % \begin{macrocode} \tl_set_eq:NN \l_@@_nameref_tl \l_@@_title_tl % \end{macrocode} % Normally we don't have a subtitle or quote unless they are % explicitly given through keys, so we start with \cs{c_novalue_tl} % \begin{macrocode} \tl_set_eq:NN \l_@@_subtitle_tl \c_novalue_tl \tl_set_eq:NN \l_@@_quote_tl \c_novalue_tl % \end{macrocode} % If the optional argument is empty we set the % \cs{l_@@_instance_keys_tl} to empty, otherwise process the % key/val list setting the keys defined in the module \enquote{head}. This % overwrites the defaults specified above if keys like \enquote{toc} are in % the list. All the key/vals unknown by that module are put into % \cs{l_@@_instance_keys_tl} which may or may not make this token % list non-empty. % % If the user has a \key{label} key and also a \cs{label} in the % main title argument then the latter is ignored (no error checking % for that). We could alternatively set all labels we find, % perhaps that is the better approach? % \begin{macrocode} \IfNoValueTF { #3 } { \tl_clear:N \l_@@_instance_keys_tl } { \keys_set_known:nnN {heading} { #3 } \l_@@_instance_keys_tl } % \end{macrocode} % % Finally we call the heading instance using the collected % mandatory arguments. To simplify downstream processing we pass % the values not the container tokenlists resulting from the above % processing.\footnote {I think this is a common requirement and % perhaps \cs{UseInstance} should really o-expand all arguments it % receives.} % \begin{macrocode} \@@_debug_typeout:n{use~ 'heading'~ instance:~ #1 } \use:e { \exp_not:N \UseInstance{heading}{#1} { \exp_not:o \l_@@_instance_keys_tl } { \bool_if:NTF \l_@@_unnumbered_bool \BooleanTrue \BooleanFalse } { \exp_not:o \l_@@_title_tl} { \exp_not:o \l_@@_toc_tl } { \exp_not:o \l_@@_running_tl } { \exp_not:o \l_@@_bookmark_tl } { \exp_not:o \l_@@_nameref_tl } { \exp_not:o \l_@@_label_tl } { { \exp_not:o \l_@@_subtitle_tl } { \exp_not:o \l_@@_quote_tl } } } } % \end{macrocode} % % Syntax keys that can appear in the optional argument of headings % parsed by \cs{ParseLaTeXeHeading}. All other keys used there are % passed to the heading instance to overwrite instance setting. % % \begin{macrocode} \keys_define:nn {heading} { , shorttitle .meta:n = {toc = {#1} , running = {#1} , bookmark = {#1} , nameref= {#1} } , bookmark .tl_set:N = \l_@@_bookmark_tl , running .tl_set:N = \l_@@_running_tl , toc .tl_set:N = \l_@@_toc_tl , nameref .tl_set:N = \l_@@_nameref_tl % , numbered .bool_set_inverse:N = \l_@@_unnumbered_bool , numbered .default:n = true , unnumbered .bool_set:N = \l_@@_unnumbered_bool , unnumbered .default:n = true % , subtitle .tl_set:N = \l_@@_subtitle_tl , quote .tl_set:N = \l_@@_quote_tl % \end{macrocode} % We collect labels together with their \cs{label} command in case % there is more than one. This way we can later simply execute % \cs{l_@@_label_tl} without any status checks. % \begin{macrocode} , label .code:n = \tl_put_right:Nn \l_@@_label_tl { \label{#1} } } % \end{macrocode} % % % \end{macro} % % \begin{macro}{\@@_find_label:w} % A simple-minded check for an existing \cs{label} command in the % main title (could be done better I guess). We add % \cs{label}\cs{q_no_value} at the end of the argument, so that we % can be sure to find something. % % As currently implemented the spaces on both sides of a \cs{label} % command survive if it is removed. This may be an issue in which % case this may need some adjustments. % \begin{macrocode} \cs_new:Npn \@@_find_label:w #1 \label #2#3 \@@_find_label:w { % \end{macrocode} % If the token after \cs{label} is \cs{q_no_value} then the title % had no label and we set the two variables accordingly. % \begin{macrocode} \quark_if_no_value:nTF {#2} { \@@_debug_typeout:n{---~no~label } \tl_clear:N \l_@@_label_tl \tl_set:Nn\l_@@_title_tl {#1#3} } % \end{macrocode} % Otherwise, there was a real \cs{label} command and \texttt{\#2} % holds the label string. % \begin{macrocode} { \@@_debug_typeout:n{---~label~found~#2} \tl_set:Nn\l_@@_label_tl { \label{#2} } % \end{macrocode} % To construct the value for \cs{l_@@_title_tl} we have to get rid % of the two tokens we added at its end, this is done with % \cs{@@_find_label_aux:w}. % \begin{macrocode} \tl_set:Nn\l_@@_title_tl {#1} \@@_find_label_aux:w #3\@@_find_label_aux:w } \@@_debug_typeout:n{---~return:~ '\exp_not:o \l_@@_title_tl' } } % \end{macrocode} % There is at least one more \cs{label} now and the token after it % should be our \cs{q_no_value}. If not then the title argument had % at least 2 labels and we collect the newly found one and recurse. % \begin{macrocode} \cs_new:Npn \@@_find_label_aux:w #1 \label #2#3 \@@_find_label_aux:w { % \end{macrocode} % The \texttt{\#1} may not be everything we have to pick up but we % know for sure that it belongs to the title. % \begin{macrocode} \tl_put_right:Nn \l_@@_title_tl { #1 } % \end{macrocode} % Now let's see if we are at the end of the argument. If not pick % up the newly found \cs{label} and recurse on the remaining material. % \begin{macrocode} \quark_if_no_value:nF {#2} { \@@_debug_typeout:n{---~ extra~ label~ '#2'~ found } \tl_put_right:Nn \l_@@_label_tl { \label{#2} } \@@_find_label_aux:w #3\@@_find_label_aux:w } } % \end{macrocode} % \end{macro} % % % % \subsection{General helper commands} % % % % \begin{macro}{\WordSpaceAmount} % Produce the amount of a (fractional) word space in the current % font in a way that it can be used in a skip or dimen register % assignment. The argument specifies the fraction, e.g., \texttt{2} % gives two word spaces and \texttt{.5} would produce half a word % space. % % This general helper doesn't really belong here, so should be % eventually moved. % \begin{macrocode} \newcommand\WordSpaceAmount[1]{ \glueexpr #1\fontdimen2\the\font plus #1\fontdimen3\the\font minus #1\fontdimen4\the\font \relax } % \end{macrocode} % \end{macro} % % % Some designs automatically add a punctuation symbol (typically a % period) at the end of a title, but that should only happen if the % title doesn't already end in a punctuation symbol. % % This is achieved by setting the \cs{spacefactor} for punctuation % symbols (slightly) higher than \texttt{1000} and then checking the current % \cs{spacefactor} before trying to add the punctuation. If it is % \texttt{1000} or less then the title didn't end in a punctuation % and we can safely add one, otherwise we don't. % % If \cs{nonfrenchspacing} is in force this is automatically the % case, but in \LaTeXe{} \cs{frenchspacing} did set the % \cs{spacefactor} of all punctuation symbols to \texttt{1000} % making them indistinguishable from other characters. % % Thus, to make this work, we have to change \cs{frenchspacing} % which is a breaking change as it can lead to pagination % differences given that the word space after a punctuation gets % (very minimally) larger this way. However, this approach % was successfully used in the AMS classes for ages and it offers % nice design possibilities so we enable it in documents using % \cs{DocumentMetadata} automatically. % % % \begin{macro}{\frenchspacing,\nonfrenchspacing} % We give all punctuation symbols a unique \cs{sfcode} value so % that it is even possible to determine which punctuation was just % typeset. % \begin{macrocode} \def\frenchspacing{\sfcode`\.1006\sfcode`\?1005\sfcode`\!1004% \sfcode`\:1003\sfcode`\;1002\sfcode`\,1001 } % \end{macrocode} % The same should be done for \texttt{.?!} if we want to be able to % distinguish those when \cs{nonfrenchspacing} is in force. % \begin{macrocode} \def\nonfrenchspacing{\sfcode`\.3002\sfcode`\?3001\sfcode`\!3000% \sfcode`\:2000\sfcode`\;1500\sfcode`\,1250 } % \end{macrocode} % \end{macro} % % % \begin{macro}{\nopunct} % The AMS classes also offered \cs{nopunct} that could be added at % the end of a title and would prevent the addition of a punctuation % symbol. % % It should have a value for \cs{spacefactor} that is not % used for \cs{frenchspacing} so that this special case can be % detected. % \begin{macrocode} \cs_new_protected:Npn \nopunct {\spacefactor 1007 } % \end{macrocode} % \end{macro} % % % % \begin{macro}{\AddPunct} % In the AMS classes this was called \cs{@addpunct}. % % Note: this mechanism will fail if the text ending in a % punctuation has an uppercase character just before the % punctuation, e.g., if somebody writes \texttt{SOLUTION:}. In that % case \texttt{SOLUTION\cs{@}.} would be necessary! % \changes{0.9j}{2026-057-12}{Support adding punctuation if not % already present (tagging/1477)} % \begin{macrocode} \cs_new_protected:Npn \AddPunct #1 { \relax\ifhmode \ifnum\spacefactor>\@m \@@_debug_typeout:n {--->~ punctuation~ '#1'~ not~ added} \else \@@_debug_typeout:n {--->~ punctuation~ '#1'~ added} #1 \fi \fi } % \end{macrocode} % \end{macro} % % % % % \subsection{Templates} % % \subsubsection{Template types} % % \begin{templatetype}{heading} % Templates of type \insttype{heading} are used to produce headings. The % positional arguments are: % \begin{verbatim} % 1: key/val list % 2: unnumbered? % 3: title % 4: toc % 5: running % 6: bookmark % 7: nameref % 8: label(s) % 9: { subtitle } { quote } % \end{verbatim} % \begin{macrocode} \NewTemplateType{heading}{9} % \end{macrocode} % \end{templatetype} % % \begin{templatetype}{headformat} % Templates of type \insttype{headformat} are used to produce heading % layout from the formatted heading number (if any), the heading title, % subtitle and quotation. The % positional arguments are: % \begin{verbatim} % 1: key/val list for document-level customizations % 2: prefix string % 3: formatted heading number % 4: title % 5: subtitle % 6: quote % \end{verbatim} % \begin{macrocode} \NewTemplateType{headformat}{6} % \end{macrocode} % \end{templatetype} % % % % % % % \subsubsection{heading templates interfaces} % % We have two heading templates: display and runin. % % \begin{template}{heading display} % The \texttt{display} template produces a display heading, i.e., % one that has vertical space before and after it. % \changes{0.9h}{2026-06-21}{Support \texttt{column-spanning} key (tagging/1436)} % % \begin{macrocode} \DeclareTemplateInterface{heading}{display}{9} { , name : tokenlist , parent-name : tokenlist , reset-counter : tokenlist , level : integer = 0 % \end{macrocode} % By default headings can be numbered and the numbering is % determined for each heading in the document individually. % \changes{0.9h}{2026-07-04}{Support a \texttt{force-unnumbered} key % (tagging/1473)} % \begin{macrocode} , force-unnumbered : boolean = false % \end{macrocode} % The \key{placement} key sets default values for the keys % \key{placement-start-code} and \key{placement-end-code}. % \begin{macrocode} , placement : choice {page , top , normal, ragged } = normal , column-spanning : boolean = false , mark-cmd : function(1) = % \end{macrocode} % % \begin{macrocode} , para-indent : choice { true , false } = false % \end{macrocode} % We use \cs{c_max_int} as a fake penalty to indicate that no % penalty was given in a key. % \begin{macrocode} , before-vspace : skip = 0pt , penalty : integer = \c_max_int , after-penalty-vspace : skip = 0pt , after-vspace : skip = 0pt % \end{macrocode} % The next two keys are only there to overwrite the \key{placement} % settings with explicit code. Thus, their default value is set up % by the \key{placement} key (which has a default). % \begin{macrocode} , placement-start-code : tokenlist = % no default values! , placement-end-code : tokenlist = % no default values! % \end{macrocode} % Old names for the above to be dropped soon: % \begin{macrocode} , start-code : tokenlist = % no default values! , final-code : tokenlist = % no default values! % \end{macrocode} % A prefix string such as \cs{@chappap} could be specified in the % next variable. By default it has no value. % \begin{macrocode} , prefix : tokenlist = \NoValue % \end{macrocode} % A postfix string to be added to the heading number (if the % heading is numbered). By default empty (not \cs{NoValue})! % \begin{macrocode} , punct : tokenlist = % \end{macrocode} % % \begin{macrocode} , heading-decls : tokenlist = \normalfont , prefix-decls : tokenlist = , number-decls : tokenlist = , title-decls : tokenlist = , subtitle-decls : tokenlist = , quote-decls : tokenlist = % \end{macrocode} % % \begin{macrocode} , heading-format : function{1} = #1 , prefix-format : function{1} = #1 , number-format : function{1} = #1 , punct-format : function{1} = #1 , title-format : function{1} = #1 \par , subtitle-format : function{1} = #1 , quote-format : function{1} = #1 % \end{macrocode} % Not clear where this key should live and if it really makes sense % to offer variations on the individual heading levels as in % \verb=\UseStructureName{sec/???/number}=. % \begin{macrocode} , number-tag : tokenlist = heading-number % \UseStructureName{sec/???} % \end{macrocode} % % \begin{macrocode} , headformat-instance : tokenlist = std , contents-extra : tokenlist = } % \end{macrocode} % \end{template} % % % \begin{template}{heading runin} % This template is similar to the \texttt{display} template but % implements a runin heading, i.e., one where the paragraph text % continues on the same line. % % Unfortunately, we can't really use \cs{DeclareTemplateCopy} to % set it up because with \cs{EditTemplateDefaults} we are not able % to alter the setup for the \key{placement} and \key{para-indent} keys % as that involves changing the implementation code. Thus with % \cs{DeclareTemplateCopy} we can copy the interface setup but the % code setup still needs to be done using \cs{DeclareTemplateCode}. % % \begin{macrocode} \DeclareTemplateInterface{heading}{runin}{9} { , name : tokenlist , parent-name : tokenlist , reset-counter : tokenlist , level : integer = 0 , force-unnumbered : boolean = false , placement : choice {page , top , normal, ragged } = normal , column-spanning : boolean = false , mark-cmd : function(1) = , para-indent : choice { true , false } = false , before-vspace : skip = 0pt , penalty : integer = \c_max_int , after-penalty-vspace : skip = 0pt , after-hspace : skip = 0pt , placement-start-code : tokenlist = % no default values! , placement-end-code : tokenlist = % no default values! % \end{macrocode} % Old names for the above to be dropped soon: % \begin{macrocode} , start-code : tokenlist = % no default values! , final-code : tokenlist = % no default values! % \end{macrocode} % % \begin{macrocode} , prefix : tokenlist = \NoValue , punct : tokenlist = , heading-decls : tokenlist = \normalfont , prefix-decls : tokenlist = , number-decls : tokenlist = , title-decls : tokenlist = , subtitle-decls : tokenlist = , quote-decls : tokenlist = , heading-format : function{1} = #1 , prefix-format : function{1} = #1 , number-format : function{1} = #1 , punct-format : function{1} = #1 % \end{macrocode} % We have one difference in the defaults: in runin headings % everything has to stay in horizontal mode so we do not append % \cs{par} in the default value for \key{title-format}. % \begin{macrocode} , title-format : function{1} = #1 , subtitle-format : function{1} = #1 , quote-format : function{1} = #1 , number-tag : tokenlist = heading-number % \UseStructureName{sec/???} , headformat-instance : tokenlist = std , contents-extra : tokenlist = } % \end{macrocode} % \end{template} % % % % \subsubsection{headformat templates interfaces} % % We have three template: display, hang and runin. % % \begin{template}{headformat display} % The \instname{display} template produces a heading in which the % heading elements form a single block, potentially with separators % that separates some elements vertically from the other. % \begin{macrocode} \DeclareTemplateInterface{headformat}{display}{6} { , heading-indent : length = 0pt % , order : commalist = {prefix,separator-a,?,number,punct, separator-b,?,title, separator-c, subtitle} , separator-a : tokenlist = \nobreakspace , separator-b : tokenlist = \par \nobreak \vspace{20pt} , separator-c : tokenlist = \ , separator-d : tokenlist = } % \end{macrocode} % \end{template} % % \begin{template}{headformat hang} % The \texttt{hang} template produces a heading where some elements % are measured to determine a hanging indentation while the % remaining ones are then typeset in that constrained space. This % is why we have two order keys. % \begin{macrocode} \DeclareTemplateInterface{headformat}{hang}{6} { , heading-indent : length = 0pt , order-hang : commalist = {prefix,separator-a,?,number,punct, separator-b,?} , order : commalist = {title, separator-c, subtitle} , separator-a : tokenlist = \nobreakspace , separator-b : tokenlist = \hspace{1em} , separator-c : tokenlist = \ , separator-d : tokenlist = } % \end{macrocode} % \end{template} % % % \begin{template}{headformat runin} % The \texttt{runin} template produces a heading which is % horizontally oriented and text following the heading will % continue on the same line as the heading. % \begin{macrocode} \DeclareTemplateInterface{headformat}{runin}{6} { , heading-indent : length = 0pt , order : commalist = {prefix,separator-a,?,number,punct, separator-b,?,title, separator-c,subtitle} , separator-a : tokenlist = \nobreakspace , separator-b : tokenlist = \hspace{1em} , separator-c : tokenlist = \ , separator-d : tokenlist = } % \end{macrocode} % \end{template} % % % % % % % % \subsubsection{heading templates code} % % \begin{template}{heading display} % % \begin{macrocode} \DeclareTemplateCode{heading}{display}{9} { , name = \l_@@_name_tl , level = \l_@@_level_int % next two not yet used , parent-name = \l_@@_pname_tl , reset-counter = \l_@@_reset_cnt_tl % \end{macrocode} % % \changes{0.9h}{2026-07-04}{Support a \texttt{force-unnumbered} key % (tagging/1473)} % \begin{macrocode} , force-unnumbered = \l_@@_unnumbered_bool % \end{macrocode} % The \key{placement} key determines whether the heading can appear % anywhere (\texttt{normal}), automatically starts a new page or % column (\texttt{top}), or is on a page of its own (\texttt{page}. % % It is usually implemented by setting \cs{l_@@_placement_start_code_tl} and % \cs{l_@@_placement_end_code_tl} (exceptions are \texttt{normal} and \texttt{ragged}). % These settings can be fine-tuned or % overwritten with the keys \key{placement-start-code} and % \key{placement-end-code}, if necessary. % \begin{macrocode} , placement = { ,page = \@@_debug_typeout:n{ A~ page~ heading } \tl_set:Nn \l_@@_placement_tl { page } ,top = \@@_debug_typeout:n{ A~ top~ heading } \tl_set:Nn \l_@@_placement_tl { top } ,normal = \@@_debug_typeout:n{ A~ normal~ heading } \tl_set:Nn \l_@@_placement_tl { normal } ,ragged = \@@_debug_typeout:n{ A~ normal~ heading~ (ragged~ style) } \tl_set:Nn \l_@@_placement_tl { ragged } } % \end{macrocode} % % \changes{0.9h}{2026-06-21}{Support \texttt{column-spanning} key % (tagging/1436)} % \begin{macrocode} , column-spanning = \l_@@_column_spanning_bool % \end{macrocode} % % \begin{macrocode} , mark-cmd = \@@_mark_cmd:n % \end{macrocode} % For now we make use of the legacy coding for indentation of % the following paragraph. % \begin{macrocode} , para-indent = { ,true = \@afterindenttrue ,false = \@afterindentfalse } , before-vspace = \l_@@_before_skip , penalty = \l_@@_penalty_int , after-penalty-vspace = \l_@@_after_penalty_skip , after-vspace = \l_@@_after_skip , placement-start-code = \l_@@_placement_start_code_tl , placement-end-code = \l_@@_placement_end_code_tl % \end{macrocode} % Old names for the above, to be dropped % \begin{macrocode} , start-code = \l_@@_deprecated_start_code_tl , final-code = \l_@@_deprecated_end_code_tl % \end{macrocode} % % \begin{macrocode} , prefix = \l_@@_prefix_tl , punct = \l_@@_punct_tl % \end{macrocode} % % \begin{macrocode} , headformat-instance = \l_@@_headformat_instance_tl % \end{macrocode} % % \begin{macrocode} , heading-decls = \l_@@_heading_decls_tl , prefix-decls = \l_@@_prefix_decls_tl , number-decls = \l_@@_number_decls_tl , title-decls = \l_@@_title_decls_tl , subtitle-decls = \l_@@_subtitle_decls_tl , quote-decls = \l_@@_quote_decls_tl % \end{macrocode} % % \begin{macrocode} , heading-format = \@@_heading_format:n , prefix-format = \@@_prefix_format:n , number-format = \@@_number_format:n , punct-format = \@@_punct_format:n , title-format = \@@_title_format:n , subtitle-format = \@@_subtitle_format:n , quote-format = \@@_quote_format:n % , number-tag = \l_@@_number_tag_tl % \end{macrocode} % % \begin{macrocode} , contents-extra = \l_@@_contents_extra_tl } % \end{macrocode} % % \begin{macrocode} { % \end{macrocode} % % \begin{macrocode} \tl_set_eq:Nc \theheading { the \l_@@_name_tl } % \end{macrocode} % We store the value of \texttt{secnumdepth} so that we can change it locally. % \begin{macrocode} \tl_set:Ne\l_@@_saved_secnumdepth_tl{\int_use:N\c@secnumdepth} % \end{macrocode} % % \begin{macrocode} \@@_show_arguments:nnnnnnnnn {#1}{#2}{#3}{#4}{#5}{#6}{#7}{#8}{#9} % \end{macrocode} % First evaluate any key setting done by the user in the % optional first argument. % \fmi{It would be nice if there is a variant of % \cs{SetTemplateKey} in which the template type and name are % implicit, e.g., \cs{SetCurrentTemplateKeys} because this is % usually what I need.} % \begin{macrocode} \SetKnownTemplateKeys{heading}{display}{#1} % \end{macrocode} % % If \key{column-spanning} is requested but we are not typesetting % in two columns we turn it off to avoid using \cs{@topnewpage} as % this would be wrong. Otherwise we check the placement value and % adjust it if necessary. % \changes{0.9h}{2026-06-21}{Support \texttt{column-spanning} key % (tagging/1436)} % \begin{macrocode} \bool_if:NT \l_@@_column_spanning_bool { \if@twocolumn \str_if_eq:VnF \l_@@_placement_tl {top} { \@@_debug_typeout:n {column-spanning~ requires~ placement=top.~ Adjusted!} \tl_set:Nn \l_@@_placement_tl {top} } \else \bool_set_false:N \l_@@_column_spanning_bool \fi } % \end{macrocode} % Based on the \key{placement} key we set up \cs{l_@@_placement_start_code_tl} % and \cs{l_@@_placement_end_code_tl}. These two variables can also be set % with the keys \key{placement-start-code} and \key{placement-end-code} in which case % we don't alter their definitions. % % Support the old key names (will vanish the moment we have support % for this in lttemplate). % \begin{macrocode} \tl_if_empty:oF \l_@@_deprecated_start_code_tl { \tl_set_eq:NN \l_@@_placement_start_code_tl \l_@@_deprecated_start_code_tl } \tl_if_empty:oF \l_@@_deprecated_end_code_tl { \tl_set_eq:NN \l_@@_placement_end_code_tl \l_@@_deprecated_end_code_tl } % \end{macrocode} % % \begin{macrocode} \tl_if_empty:oT \l_@@_placement_start_code_tl { \str_case:Vn \l_@@_placement_tl { { page } { \tl_set:Nn \l_@@_placement_start_code_tl % \end{macrocode} % Straight from the placement definition of \cs{part}.\fmi{Clearly not % \cs{@tempswa} and the page styles should be adjustable.} % \begin{macrocode} { \if@openright \cleardoublepage \else \clearpage \fi \thispagestyle{plain}% \if@twocolumn \onecolumn \@tempswatrue \else \@tempswafalse \fi \null\vfil } } { top } { \tl_set:Nn \l_@@_placement_start_code_tl { \if@openright\cleardoublepage\else\clearpage\fi \thispagestyle{plain}% \global\@topnum\z@ } } % \end{macrocode} % Ragged headings (i.e., those that leave a previous page ragged % bottom if they generate a page break) are produced by replacing % \cs{addpenalty} with a version that also adds \cs{vfil} and % \cs{vfilneg}. This is handled by altering % \cs{sec_add_penalty_with_possible_fil:n} which after use resets % itself to \cs{addpenalty}. % \changes{0.9k}{2026-08-04}{Support \texttt{placement=ragged} (github/1844)} % \begin{macrocode} { ragged } { \cs_set_eq:NN \sec_add_penalty_with_possible_fil:n \sec_add_penalty_with_fil:n } } % \end{macrocode} % In all other cases we want \cs{l_@@_placement_start_code_tl} to be % empty, which is already the case so no need to have an explicit F branch. % \begin{macrocode} % { \tl_clear:N \l_@@_placement_start_code_tl } } % \tl_if_empty:oT \l_@@_placement_end_code_tl { \str_case:VnF \l_@@_placement_tl { { page } { \tl_set:Nn \l_@@_placement_end_code_tl { \vfil\newpage \if@twoside \if@openright \null \thispagestyle{empty}% \newpage \fi \fi \if@tempswa \twocolumn \fi } } } % \end{macrocode} % For all other values of \key{placement} we execute \cs{@afterheading}: % \begin{macrocode} { \tl_set:Nn \l_@@_placement_end_code_tl { \@afterheading } } } % \end{macrocode} % Then we set up the penalty to use (might be given as a key value). % \begin{macrocode} \@@_determine_penalty: % \end{macrocode} % % \begin{macrocode} \@@_determine_number_typesetting:N #2 % \end{macrocode} % Next comes the vertical spacing and penalty before the heading. This includes % running \cs{l_@@_placement_start_code_tl} if it contains any code, e.g., to % start a new page. % \begin{macrocode} \@@_vertical_before_spacing: % \end{macrocode} % We are in vmode now and here is the point where we (with tagging) % can close a previous Sect structure and open the new one. % \begin{macrocode} \UseTaggingSocket{sec/end}{\int_use:N\l_@@_level_int} \UseTaggingSocket{sec/begin} {{\int_use:N\l_@@_level_int} {tag=\UseStructureName{sec/\int_use:N\l_@@_level_int}} } % \end{macrocode} % Up to this point everything is identical for both display and % runin headings. But from now on they have their own code. % We have dealt with argument \#1 and \#2 so now we can unbundle % argument \#9 so that it is easier to process downstream % \begin{macrocode} \@@_debug_typeout:n{use~ 'headformat'~instance:~ \l_@@_headformat_instance_tl } \UseTaggingSocket{sec/title/begin}{{\int_use:N\l_@@_level_int}{#3}} % \end{macrocode} % % As long as we don't have a decent interface for the OR we have to % use \cs{@topnewpage} for getting spanning headings (which means % we are really using a top float box. That in turn means we have % to get all the vertical spacing and so inside this box so there % is a lot of back and forth based on the value of % \cs{l_@@_column_spanning_bool} for now. % \changes{0.9h}{2026-06-21}{Support \texttt{column-spanning} key % (tagging/1436)} % \begin{macrocode} \use:e { \bool_if:NT \l_@@_column_spanning_bool { \exp_not:n { \@topnewpage [ \dim_compare:nNnF{\l_@@_after_penalty_skip}={0pt} { \vspace* \l_@@_after_penalty_skip } } } \UseInstance{headformat} { \l_@@_headformat_instance_tl } { \exp_not:o \UnusedTemplateKeys } { \exp_not:o { \l_@@_prefix_tl } } { \exp_not:o { \l_@@_number_tl } } { \exp_not:n { #3 } } { \exp_not:o { \use_i:nn #9 } } { \exp_not:o { \use_ii:nn #9 } } % \end{macrocode} % % \changes{0.9h}{2026-06-21}{Support \texttt{column-spanning} key % (tagging/1436)} % \begin{macrocode} \bool_if:NT \l_@@_column_spanning_bool { \skip_vertical:N \l_@@_after_skip % \end{macrocode} % Add the final vspace after the heading and close the % \cs{@topnewpage} which has an optional argument. % \begin{macrocode} ] } } % \end{macrocode} % % \begin{macrocode} \UseTaggingSocket{sec/title/end} % % --- handle marks, toc-entry, bookmark, nameref, and label % \@@_handle_marks_etc:nnnnn {#4}{#5}{#6}{#7}{#8} % % --- post-heading handling (vertical) % \end{macrocode} % Restore \texttt{secnumdepth} % \begin{macrocode} \int_gset:Nn\c@secnumdepth{\l_@@_saved_secnumdepth_tl} \par % \end{macrocode} % If we have a spanning heading then the vertical skip is handled % inside \cs{@topnewpage} and not here. % \changes{0.9h}{2026-06-21}{Support \texttt{column-spanning} key % (tagging/1436)} % \begin{macrocode} \bool_if:NF \l_@@_column_spanning_bool { \nobreak \skip_vertical:N \l_@@_after_skip } % --- prepare next paragraph (defaults to \cs{@afterheading}): % \l_@@_placement_end_code_tl % \ignorespaces } % \end{macrocode} % \end{template} % % % \begin{macro}{\sec_add_penalty_with_fil:n, % \sec_add_penalty_with_possible_fil:n} % The command \cs{sec_add_penalty_with_fil:n} adds a penalty % surrounded by stretchable glue like the plain \TeX{} % \cs{filbreak} command. After use it sets % \cs{sec_add_penalty_with_possible_fil:n} back to \cs{addpenalty}. % % They have public names so that they can be used in other heading % templates outside of this module. % \changes{0.9k}{2026-08-04}{Support \texttt{placement=ragged} (github/1844)} % \begin{macrocode} \cs_new_protected:Npn \sec_add_penalty_with_fil:n #1 { \@@_debug_typeout:n{apply~ ragged~ bottom~ fil} % \end{macrocode} % Next code is straight from \cs{addpenalty} except that the % generated \cs{penalty} is surrounded by \cs{vfil} and \cs{vfilneg}. % \begin{macrocode} \addpenaltywithfil { #1 } % \end{macrocode} % Then reset so that next time \cs{addpenalty} is used again. % \begin{macrocode} \cs_set_eq:NN \sec_add_penalty_with_possible_fil:n \addpenalty } % \end{macrocode} % By default \cs{sec_add_penalty_with_possible_fil:n} is the same % as \cs{addpenalty}. If you set it to % \cs{sec_add_penalty_with_fil:n} then it uses the "filbreak" % mechanism and afterwards resets itself to its default definition. % \begin{macrocode} \cs_set_eq:NN \sec_add_penalty_with_possible_fil:n \addpenalty % \end{macrocode} % \end{macro} % % % % \begin{macro}{\__kernel_add_penalty:n, % \addpenalty,\addpenaltywithfil} % % Next code is for inclusion in the kernel. It is basically the % code from \LaTeX{}'s \cs{addpenalty} but with a slightly % different argument so that you have to supply % \verb=\penalty #1\relax= to mimic \cs{addpenalty} and % \verb=\vfil \penalty #1\vfilneg= to produce a penalty generating % a filbreak, i.e., a break where the previous page is ragged bottom. % \begin{macrocode} \cs_new_protected:Npn \__kernel_add_penalty:n #1 { % \end{macrocode} % Don't add anything at the start of a minipage or when % \texttt{@nobreak} is set (i.e., directly after a heading). % \begin{macrocode} \mode_if_horizontal:T { \mode_if_inner:TF { \@LRmoderr }{ \par } } \legacy_if:nF { @minipage } { \legacy_if:nF { @nobreak } % \end{macrocode} % Do everything in a group so that the temp variables are restored % afterwards in case they are used at the outside. % \begin{macrocode} { \group_begin: % \end{macrocode} % The rest is also from \cs{addpenalty} except that we have to give % \cs{penalty} explicitly as part of the argument. % % If there wasn't any previous skip or only a zero-sized one simply % set the penalty. % \begin{macrocode} \skip_set_eq:NN \l_tmpa_skip \tex_lastskip:D \dim_compare:nNnTF \l_tmpa_skip = \c_zero_dim { #1 } % \end{macrocode} % Otherwise copy the last skip value also into \cs{l_tmpb_skip} % using \TeX's fast direct assignment. % \begin{macrocode} { \skip_set_eq:NN \l_tmpb_skip \l_tmpa_skip % \end{macrocode} % Then add to it the current \cs{prevdepth} unless it is % \texttt{-1000pt} (which means it should be suppressed) or if it % is unusually large, in which case add \cs{maxdepth} instead. We % remember that value because it will be reused below. % \begin{macrocode} \dim_set:Nn \l_tmpa_dim { \dim_compare:nNnTF \prevdepth > \maxdepth \maxdepth { \dim_compare:nNnTF \prevdepth = { -1000pt } \c_zero_dim \prevdepth } } \skip_add:Nn \l_tmpb_skip \l_tmpa_dim % \end{macrocode} % This is the amount we have to back up in order to position us % vertically where the bottom of the page would be if a natural % break would happen. % \begin{macrocode} \vskip -\l_tmpb_skip % \end{macrocode} % That is then the point where we have to add the explicit penalty % or the filbreak code. % \begin{macrocode} #1 % \end{macrocode} % After the penalty (and after a possible \cs{vfilneg}) we have to % re-add what we back up but that isn't as trivial as one might % think. % % First we have to move forward by whatever amount we backed up due % to \cs{prevdepth} because going forward the \cs{prevdepth} will % be zero. % \begin{macrocode} \dim_compare:nNnF \l_tmpa_dim = \c_zero_dim { \vskip \l_tmpa_dim } % \end{macrocode} % Finally we have to reinsert what we saved as \cs{lastskip}, % because that is what any following \cs{addvspace} should see to % make its calculations from. % \begin{macrocode} \vskip \l_tmpa_skip } \group_end: } } } % \end{macrocode} % Reimplement \cs{addpenalty} using the above macro: % \begin{macrocode} \cs_set_protected:Npn \addpenalty #1 { \__kernel_add_penalty:n { \penalty #1 \scan_stop: } } % \end{macrocode} % And a new command to add a penalty with some \cs{vfil} around % it. They cancel each other if no break is taken. % \begin{macrocode} \cs_new_protected:Npn \addpenaltywithfil #1 { \__kernel_add_penalty:n { \vfil \penalty #1 \vfilneg } } % \end{macrocode} % % \end{macro} % % % % % % \begin{macro}{\if@openright} % % \begin{macrocode} \AddToHook{begindocument}{ \ifcsname if@openright\endcsname \else % \end{macrocode} % Need to hide this a little if it is in fact already defined! % \begin{macrocode} \expandafter\newif\csname if@openright\endcsname \fi } % \end{macrocode} % \end{macro} % % % % % % \begin{template}{heading runin} % The \texttt{runin} template is very similar to the % \texttt{display} one. % \begin{macrocode} \DeclareTemplateCode{heading}{runin}{9} { , name = \l_@@_name_tl , level = \l_@@_level_int % next two not yet used , parent-name = \l_@@_pname_tl , reset-counter = \l_@@_reset_cnt_tl % \end{macrocode} % % \changes{0.9h}{2026-07-04}{Support a \texttt{force-unnumbered} key % (tagging/1473)} % \begin{macrocode} , force-unnumbered = \l_@@_unnumbered_bool % \end{macrocode} % % It wouldn't make sense to have page placement with a runin heading since % there would be nothing to run into. % \begin{macrocode} , placement = { page = \typeout{ ^^JA~ runin~ page~ placement~ heading~makes~no~sense (top~used)} \tl_set:Nn \l_@@_placement_tl { top } ,top = \@@_debug_typeout:n{ A~ top~ heading } \tl_set:Nn \l_@@_placement_tl { top } ,normal = \@@_debug_typeout:n{ A~ normal~ heading } \tl_set:Nn \l_@@_placement_tl { normal } ,ragged = \@@_debug_typeout:n{ A~ normal~ heading~ (ragged~ style) } \tl_set:Nn \l_@@_placement_tl { ragged } } % \end{macrocode} % % \changes{0.9h}{2026-06-21}{Support \texttt{column-spanning} key % (tagging/1436)} % \begin{macrocode} , column-spanning = \l_@@_column_spanning_bool % \end{macrocode} % % \begin{macrocode} , mark-cmd = \@@_mark_cmd:n % \end{macrocode} % In a runin heading you can't set up paragraph indentation of the % following paragraph (since that one is \enquote{run in}. But we % accept the key and just spit out a warning. % \ufi{this types out messages for all run-in headers! Therefore disabled for now} % \begin{macrocode} , para-indent = { ,true = %\typeout{para-indent~ setting~ ignored} ,false = %\typeout{para-indent~ setting~ ignored} } , before-vspace = \l_@@_before_skip , penalty = \l_@@_penalty_int , after-penalty-vspace = \l_@@_after_penalty_skip , after-hspace = \l_@@_after_skip , placement-start-code = \l_@@_placement_start_code_tl , placement-end-code = \l_@@_placement_end_code_tl % \end{macrocode} % Old names for the above, to be dropped % \begin{macrocode} , start-code = \l_@@_deprecated_start_code_tl , final-code = \l_@@_deprecated_end_code_tl % \end{macrocode} % % \begin{macrocode} , prefix = \l_@@_prefix_tl , punct = \l_@@_punct_tl % \end{macrocode} % % \begin{macrocode} , headformat-instance = \l_@@_headformat_instance_tl % \end{macrocode} % % \begin{macrocode} , heading-decls = \l_@@_heading_decls_tl % \end{macrocode} % % \begin{macrocode} , prefix-decls = \l_@@_prefix_decls_tl , number-decls = \l_@@_number_decls_tl , title-decls = \l_@@_title_decls_tl , subtitle-decls = \l_@@_subtitle_decls_tl , quote-decls = \l_@@_quote_decls_tl % \end{macrocode} % % \begin{macrocode} , heading-format = \@@_heading_format:n , prefix-format = \@@_prefix_format:n , number-format = \@@_number_format:n , punct-format = \@@_punct_format:n , title-format = \@@_title_format:n , subtitle-format = \@@_subtitle_format:n , quote-format = \@@_quote_format:n % , number-tag = \l_@@_number_tag_tl % \end{macrocode} % % \begin{macrocode} , contents-extra = \l_@@_contents_extra_tl } { \@@_show_arguments:nnnnnnnnn {#1}{#2}{#3}{#4}{#5}{#6}{#7}{#8}{#9} % \end{macrocode} % store the value of \texttt{secnumdepth} so that we can change it locally. % \begin{macrocode} \tl_set:Ne\l_@@_saved_secnumdepth_tl{\int_use:N\c@secnumdepth} % \end{macrocode} % define \cs{theheading} % \begin{macrocode} \tl_set_eq:Nc \theheading { the \l_@@_name_tl } \SetKnownTemplateKeys{heading}{runin}{#1} % \end{macrocode} % For now we don't support column-spanning in this template. % \changes{0.9h}{2026-06-21}{Support \texttt{column-spanning} key % (tagging/1436)} % \begin{macrocode} \bool_if:NT \l_@@_column_spanning_bool { \@@_debug_typeout:n {column-spanning~ is~ not~ (yet)~ supported~ for~ runin~ headings!} \bool_set_false:N \l_@@_column_spanning_bool } % \end{macrocode} % % Support the old key names (will vanish the moment we have support % for this in lttemplate). % \begin{macrocode} \tl_if_empty:oF \l_@@_deprecated_start_code_tl { \tl_set_eq:NN \l_@@_placement_start_code_tl \l_@@_deprecated_start_code_tl } \tl_if_empty:oF \l_@@_deprecated_end_code_tl { \tl_set_eq:NN \l_@@_placement_end_code_tl \l_@@_deprecated_end_code_tl } % \end{macrocode} % % \begin{macrocode} \tl_if_empty:oT \l_@@_placement_start_code_tl { \str_case:Vn \l_@@_placement_tl { { top } { \tl_set:Nn \l_@@_placement_start_code_tl {\clearpage } } % \end{macrocode} % % \changes{0.9k}{2026-08-04}{Support \texttt{placement=ragged} (github/1844)} % \begin{macrocode} { ragged } { \cs_set_eq:NN \sec_add_penalty_with_possible_fil:n \sec_add_penalty_with_fil:n } } % \end{macrocode} % All other cases want an empty \cs{l_@@_placement_start_code_tl} so nothing % to do. % \begin{macrocode} % { \tl_clear:N \l_@@_placement_start_code_tl } } % % \end{macrocode} % Nothing at all to do (for now) for \cs{l_@@_placement_end_code_tl}, it % should by default be empty in all heading placement. But maybe % we end up supporting further placements, so~\ldots % \begin{macrocode} % \tl_if_empty:oT \l_@@_placement_end_code_tl % { \str_case:VnF \l_@@_placement_tl % { % { top } { \tl_clear:N \l_@@_placement_end_code_tl } % } % { \tl_clear:N \l_@@_placement_end_code_tl } % } % \end{macrocode} % % \begin{macrocode} \@@_determine_penalty: \@@_determine_number_typesetting:N #2 \@@_vertical_before_spacing: % \end{macrocode} % We are in vmode now and here is the point where we (with tagging) % can close a previous Sect structure and open the new one. We also % have to initialize the change in the para tagging here as run-in % titles typeset the heading in \cs{everypar}. % \begin{macrocode} \UseTaggingSocket{sec/end}{\int_use:N\l_@@_level_int} \UseTaggingSocket{sec/begin} {{\int_use:N\l_@@_level_int} {tag=\UseStructureName{sec/\int_use:N\l_@@_level_int}} } \UseTaggingSocket{sec/title/init}{\int_use:N\l_@@_level_int} \def \@svsechd { \@@_debug_typeout:n{use~ 'headformat'~instance:~ \l_@@_headformat_instance_tl } \use:e { \UseInstance{headformat} { \l_@@_headformat_instance_tl } { \exp_not:o \UnusedTemplateKeys } { \exp_not:o { \l_@@_prefix_tl } } { \exp_not:o { \l_@@_number_tl } } { \exp_not:n { #3 } } { \exp_not:o { \use_i:nn #9 } } { \exp_not:o { \use_ii:nn #9 } } } \@@_handle_marks_etc:nnnnn {#4}{#5}{#6}{#7}{#8} % \end{macrocode} % Restore \texttt{secnumdepth} % \begin{macrocode} \int_gset:Nn\c@secnumdepth{\l_@@_saved_secnumdepth_tl} } \@nobreakfalse \global\@noskipsectrue \everypar{% \if@noskipsec \global\@noskipsecfalse {\setbox\z@\lastbox} \clubpenalty\@M \@svsechd \unskip % \end{macrocode} % This tagging socket starts the \enquote{paragraph} after the run-in heading % \begin{macrocode} \UseTaggingSocket{sec/title/split} \skip_horizontal:N \l_@@_after_skip \else \clubpenalty \@clubpenalty \everypar{}% \fi } % \end{macrocode} % --- prepare next paragraph (does nothing by default) % \begin{macrocode} \l_@@_placement_end_code_tl \ignorespaces } % \end{macrocode} % \end{template} % % \subsubsection{headformat templates code} % % \begin{template}{headformat display} % This template creates a headformat where the number is on a line on its own. % \begin{macrocode} \DeclareTemplateCode{headformat}{display}{6} % args: keys,prefix,number,title,subtitle,quotation { , heading-indent = \l_@@_heading_indent_dim , order = \l_@@_order_clist , separator-a = name {l_@@_separator-a_tl} , separator-b = name {l_@@_separator-b_tl} , separator-c = name {l_@@_separator-c_tl} , separator-d = name {l_@@_separator-d_tl} } { \@@_show_arguments:nnnnnn {#1}{#2}{#3}{#4}{#5}{#6} % \end{macrocode} % First evaluate any key setting done by the user (normally % supplied from the main heading instance. % \begin{macrocode} \SetTemplateKeys{headformat}{display}{#1} % \end{macrocode} % % \begin{macrocode} \group_begin: % \end{macrocode} % \cs{parbox}es can be used within the title of a heading (or % withing it layout). \env{minipage} environments currently do not % work if used directly in the title because purify can't handle % that environment. But it can be used as part of the layout % definition which is why we also add a suitable setting for it. % \begin{macrocode} \AssignTaggingSocketPlug{parbox/before}{heading} \AssignTaggingSocketPlug{minipage/before}{heading} \AssignTaggingSocketPlug{para/restore}{heading} \tagpdfparaOff % \tl_set:Nn \l_@@_prefix_tl {#2} \tl_set:Nn \l_@@_number_tl {#3} \tl_set:Nn \l_@@_title_tl {#4} % % \end{macrocode} % \changes{0.9f}{2026-05-23}{Removed \cs{normalcolor} as it adds a % break point and prevents color changes tagging/1215} TODO: % revisit if \cs{normalcolor} is needed, if yes, use as % \cs{SaveLastSkip} \cs{normalcolor} \cs{RestoreLastSkip}. % \begin{macrocode} \normalfont \interlinepenalty \@M \l_@@_heading_decls_tl % \end{macrocode} % If there is a number and so a prefix, the link target should be % before the number. We use for now the same place in the % unnumbered case. TODO: If we put the target here we do not know % the height of the line and the target is perhaps not high % enough. And for unnumbered chapter it is perhaps too high. % Check! % \begin{macrocode} \bool_if:NTF \l_@@_unnumbered_bool { \dim_compare:nNnTF \l_@@_heading_indent_dim < \c_zero_skip { \skip_horizontal:N \l_@@_heading_indent_dim \MakeLinkTarget[\l_@@_name_tl]{} } { \MakeLinkTarget[\l_@@_name_tl]{} \skip_horizontal:N \l_@@_heading_indent_dim } } { \dim_compare:nNnTF \l_@@_heading_indent_dim < \c_zero_skip { \skip_horizontal:N \l_@@_heading_indent_dim \MakeLinkTarget{\l_@@_name_tl} } { \MakeLinkTarget{\l_@@_name_tl} \skip_horizontal:N \l_@@_heading_indent_dim } } % \@@_heading_format:n { \template_process_order_clist:nnn { head }{ order } { number, prefix, punct, separator-a, separator-b, separator-c, separator-d, title, subtitle } } \par \group_end: } % \end{macrocode} % \end{template} % % % % The the plugs we assigned to the tagging sockets in the template % need to be defined. % \begin{macrocode} \NewTaggingSocketPlug{minipage/before}{heading} { \tag_mc_end_push: \bool_if:NT \l__tag_para_bool {\tag_struct_end:} \tag_struct_begin:n{tag=\UseStructureName{sec/minipage}} } % \end{macrocode} % % \begin{macrocode} \NewTaggingSocketPlug{parbox/before}{heading} { \tag_mc_end_push: \bool_if:NT \l__tag_para_bool {\tag_struct_end:} \tag_struct_begin:n{tag=\UseStructureName{sec/parbox}} } % \end{macrocode} % % \begin{macrocode} \NewTaggingSocketPlug{para/restore}{heading} % \end{macrocode} % We have only structure names on heading levels for this, so for % now I use \texttt{heading-fragment} directly. Should probably % become \texttt{sec/fragment}.\fmi{resolve} % \begin{macrocode} { \AssignStructureRole{para/textblock}{heading-fragment} \bool_set_true:N\l__tag_para_flattened_bool \tagpdfparaOn } % \end{macrocode} % % % % % % % \begin{template}{headformat hang} % This template creates a heading with a hanging number. % \begin{macrocode} \DeclareTemplateCode{headformat}{hang}{6} % args: keys,prefix,number,title,subtitle,quotation { , heading-indent = \l_@@_heading_indent_dim , order-hang = name {l_@@_order-hang_clist} , order = \l_@@_order_clist , separator-a = name {l_@@_separator-a_tl} , separator-b = name {l_@@_separator-b_tl} , separator-c = name {l_@@_separator-c_tl} , separator-d = name {l_@@_separator-d_tl} } { \@@_show_arguments:nnnnnn {#1}{#2}{#3}{#4}{#5}{#6} % \end{macrocode} % First evaluate any key setting done by the user (normally % supplied from the main heading instance. % \begin{macrocode} \SetTemplateKeys{headformat}{hang}{#1} % \end{macrocode} % % \begin{macrocode} \group_begin: % \AssignTaggingSocketPlug{parbox/before}{noop} \AssignTaggingSocketPlug{parbox/after}{noop} \AssignTaggingSocketPlug{para/restore}{noop} \tagpdfparaOff % \tl_set:Nn \l_@@_prefix_tl {#2} \tl_set:Nn \l_@@_number_tl {#3} \tl_set:Nn \l_@@_title_tl {#4} % % \end{macrocode} % \changes{0.9f}{2026-05-23}{Removed \cs{normalcolor} as it adds a break point % and prevents color changes tagging/1215} % \begin{macrocode} \normalfont \interlinepenalty \@M \l_@@_heading_decls_tl \noindent \setbox\@tempboxa\hbox{{ % \end{macrocode} % The link target should be at the left text margin, or, if the section % is moved into the margin, at the left of the number. % \begin{macrocode} \bool_if:NTF \l_@@_unnumbered_bool { \dim_compare:nNnTF \l_@@_heading_indent_dim < \c_zero_skip { \skip_horizontal:N \l_@@_heading_indent_dim \MakeLinkTarget[\l_@@_name_tl]{} } { \MakeLinkTarget[\l_@@_name_tl]{} \skip_horizontal:N \l_@@_heading_indent_dim } } { \dim_compare:nNnTF \l_@@_heading_indent_dim < \c_zero_skip { \skip_horizontal:N \l_@@_heading_indent_dim \MakeLinkTarget{\l_@@_name_tl} } { \MakeLinkTarget{\l_@@_name_tl} \skip_horizontal:N \l_@@_heading_indent_dim } \template_process_order_clist:nnn { head }{ order-hang } { number, prefix, punct, separator-a, separator-b, separator-c, separator-d, title, subtitle } } }} \hangindent \wd\@tempboxa \box\@tempboxa \template_process_order_clist:nnn { head }{ order } { number, prefix, punct, separator-a, separator-b, separator-c, separator-d, title, subtitle } % \end{macrocode} % \begin{macrocode} \par \group_end: } % \end{macrocode} % \end{template} % % % % % % % \begin{template}{headformat runin} % This template creates a heading with a run-in title. % Arguments are key-value, number, title, subtitle, quotation. % \begin{macrocode} \DeclareTemplateCode{headformat}{runin}{6} % args: keys,prefix,number,title,subtitle,quotation { , heading-indent = \l_@@_heading_indent_dim , order = \l_@@_order_clist , separator-a = name {l_@@_separator-a_tl} , separator-b = name {l_@@_separator-b_tl} , separator-c = name {l_@@_separator-c_tl} , separator-d = name {l_@@_separator-d_tl} } { \@@_show_arguments:nnnnnn {#1}{#2}{#3}{#4}{#5}{#6} % \end{macrocode} % First evaluate any key setting done by the user (normally % supplied from the main heading instance. % \begin{macrocode} \SetTemplateKeys{headformat}{hang}{#1} % \end{macrocode} % % \begin{macrocode} \group_begin: % \AssignTaggingSocketPlug{parbox/before}{noop} \AssignTaggingSocketPlug{parbox/after}{noop} \AssignTaggingSocketPlug{para/restore}{noop} \tagpdfparaOff % \tl_set:Nn \l_@@_prefix_tl {#2} \tl_set:Nn \l_@@_number_tl {#3} \tl_set:Nn \l_@@_title_tl {#4} % \end{macrocode} % % \changes{0.9f}{2026-05-23}{Removed \cs{normalcolor} as it adds a break point % and prevents color changes tagging/1215} % \begin{macrocode} \normalfont % \end{macrocode} % \fmi{Setting \cs{interlinepenalty} makes little sense I think % (but that's the way it was in \LaTeXe)} % \begin{macrocode} \interlinepenalty \@M \l_@@_heading_decls_tl % \end{macrocode} % We must avoid that the sep between number and title is used in the unnumbered case. % So we test with the boolean. % \begin{macrocode} \bool_if:NTF \l_@@_unnumbered_bool { \dim_compare:nNnTF \l_@@_heading_indent_dim < \c_zero_skip { \skip_horizontal:N \l_@@_heading_indent_dim \MakeLinkTarget[\l_@@_name_tl]{} } { \MakeLinkTarget[\l_@@_name_tl]{} \skip_horizontal:N \l_@@_heading_indent_dim } } { \dim_compare:nNnTF \l_@@_heading_indent_dim < \c_zero_skip { \skip_horizontal:N \l_@@_heading_indent_dim \MakeLinkTarget{\l_@@_name_tl} } { \MakeLinkTarget{\l_@@_name_tl} \skip_horizontal:N \l_@@_heading_indent_dim } } \template_process_order_clist:nnn { head }{ order } { number, prefix, punct, separator-a, separator-b, separator-c, separator-d, title, subtitle } \group_end: } % \end{macrocode} % \end{template} % % \subsubsection{Internal commands used by the template code} % % \begin{macro}{\@@_determine_penalty:} % % \begin{macrocode} \cs_new:Npn \@@_determine_penalty: { % \end{macrocode} % If a penalty was specified use it, otherwise use \cs{@secpenalty}. % \begin{macrocode} \int_compare:nNnT \l_@@_penalty_int = \c_max_int { \int_set:Nn \l_@@_penalty_int \@secpenalty } } % \end{macrocode} % \end{macro} % % % % % \begin{macro}{\@@_determine_number_typesetting:N} % % \begin{macrocode} \cs_new:Npn \@@_determine_number_typesetting:N #1 { % \end{macrocode} % If \key{force-unnumbered} was set to true (i.e., % \cs{l_@@_unnumbered_bool}) we don't do numbering. % If that is not the case then using or suppressing a heading % number depends on the heading level % compared to the document value of \cs{c@secnumdepth}. If that % doesn't suppress the number then an explicit key or a star form % might still have suppressed it. % \changes{0.9h}{2026-07-04}{Support a \texttt{force-unnumbered} key % (tagging/1473)} % \begin{macrocode} \bool_if:NF \l__head_unnumbered_bool { \bool_set:Nn \l_@@_unnumbered_bool { \bool_lazy_or_p:nn { \int_compare_p:nNn \l_@@_level_int > \c@secnumdepth } { \bool_if_p:N #1 } } } % \end{macrocode} % If we aren't producing a heading with a number we set % \cs{c@secnumdepth} to a low number (it is reset at the end of the template % code) % \begin{macrocode} \bool_if:NTF \l_@@_unnumbered_bool { \int_gset:Nn\c@secnumdepth{-99} % \end{macrocode} % and we set \cs{l_@@_number_tl}, \cs{l_@@_prefix_tl}, and % \cs{l_@@_punct_tl} to do nothing. % \begin{macrocode} \tl_set:Nn \l_@@_number_tl { \NoValue } \tl_set:Nn \l_@@_prefix_tl { \NoValue } \tl_set:Nn \l_@@_punct_tl { \NoValue } } % \end{macrocode} % Otherwise the heading counter is incremented and a formatted % version of the number plus any following (or preceding) space is % stored in \cs{l_@@_number_tl}. % We use the kernel version of \cs{refstepcounter} as anchors are % handled elsewhere. % \begin{macrocode} { \@kernel@refstepcounter{ \l_@@_name_tl } \tl_set:Nn \l_@@_number_tl { \theheading } } } % \end{macrocode} % \end{macro} % \begin{macro}{\@@_vertical_before_spacing:} % % \changes{0.9p}{2026-09-18}{Output a dangling runin heading before parsing % the next heading} % \begin{macrocode} \cs_new:Npn \@@_vertical_before_spacing: { \tl_if_blank:VTF \l_@@_placement_start_code_tl { % \if@noskipsec \leavevmode \fi \par % <--- too late \if@nobreak \everypar{} \else % \end{macrocode} % Normally the penalty is added with \cs{addpenalty} but in some % cases (\texttt{placement=ragged}) we want to use a variant of % \cs{filbreak} instead. So we use % \cs{sec_add_penalty_with_possible_fil:n} which is either equal to % \cs{addpenalty} or to \cs{sec_add_penalty_with_fil:n} depending % on the setup. % \changes{0.9k}{2026-08-04}{Support \texttt{placement=ragged} (github/1844)} % \begin{macrocode} \sec_add_penalty_with_possible_fil:n \l_@@_penalty_int \addvspace \l_@@_before_skip % \end{macrocode} % The \cs{vspace*} inserts a rule, we insert therefore the % skip only if it is different to zero. % \begin{macrocode} \dim_compare:nNnF{\l_@@_after_penalty_skip}={0pt} { \vspace* \l_@@_after_penalty_skip } \fi } { \l_@@_placement_start_code_tl % \end{macrocode} % If \cs{l_@@_placement_start_code_tl} holds code, we assume that it handles % pagination, e.g., a \cs{clearpage}, etc. We therefore only add % the skip that would follow the penalty. % % And we do nothing at all when we have a spanning heading. Then % this vspace is done inside \cs{@topnewpage}. % \changes{0.9h}{2026-06-21}{Support \texttt{column-spanning} key % (tagging/1436)} % \begin{macrocode} \bool_if:NF \l_@@_column_spanning_bool { \dim_compare:nNnF{\l_@@_after_penalty_skip}={0pt} { \vspace* \l_@@_after_penalty_skip } } } } % \end{macrocode} % \end{macro} % % \begin{macro}{\addcontentslinebookmark,\addcontentslinebookmarkOff, % \addcontentslinebookmarkOn} % % We want to be able to control bookmarks independently from the % toc entries, and also want to disable a bookmark by setting it to % empty. For this we need some command to handle the bookmark % command. % \begin{macrocode} \newcommand\addcontentslinebookmark[3]{} \newcommand\addcontentslinebookmarkOff{} \newcommand\addcontentslinebookmarkReset{} \providecommand\texorpdfstring[2]{#1} % \end{macrocode} % This can go once hyperref is updated (ufi,2026-04-21): % \begin{macrocode} \AddToHook{package/hyperref/after} { \RenewCommandCopy\addcontentslinebookmark \Hy@addcontentsline@addbookmark \renewcommand\addcontentslinebookmarkOff { \ifHy@bookmarks \let\addcontentslinebookmarkReset \Hy@bookmarkstrue \else \let\addcontentslinebookmarkReset\relax \fi \Hy@bookmarksfalse } } % \end{macrocode} % \end{macro} % % \begin{macro}{\@@_handle_marks_etc:nnnnn} % Arg 1: toc entry, arg 2: mark, arg 3: bookmark, arg 4: nameref, arg 5: label code % \begin{macrocode} \cs_new:Npn \@@_handle_marks_etc:nnnnn #1#2#3#4#5 { % \tl_set:Nn\@currentlabelname{#4} \IfBlankF {#2} { \@@_mark_cmd:n { #2 } } \IfBlankTF {#1} % \end{macrocode} % If the toc entry is empty the bookmarks must be set without % \cs{addcontentsline}. % \begin{macrocode} { \IfBlankF{#3} { \addcontentslinebookmark{toc}{\l__head_name_tl} { \bool_if:NF \l_@@_unnumbered_bool { \protect\numberline{ \use:c{ the \l_@@_name_tl } } } #3 } } } % \end{macrocode} % If the bookmark entry is empty we must disable the bookmarks locally % before the \cs{addcontentsline} and reenable afterwards. We do not % check if they are already disabled, perhaps later. % \begin{macrocode} { \IfBlankT{#3}{\addcontentslinebookmarkOff} \addcontentsline{toc}{ \l_@@_name_tl } { \bool_if:NF \l_@@_unnumbered_bool { \protect\numberline{ \use:c{ the \l_@@_name_tl } } } % \end{macrocode} % We use \cs{texorpdfstring} to separate toc and bookmark text. % \begin{macrocode} \texorpdfstring{#1}{#3} } \addcontentslinebookmarkReset } % \end{macrocode} % Some headings (like \cs{chapter}) also want to write stuff to % other contents files like \texttt{.lot} or % \texttt{.lof}.\fmi{perhaps this should have a hook for packages} % \begin{macrocode} \l_@@_contents_extra_tl % \end{macrocode} % We can always run the label code (it might be empty) % \begin{macrocode} \@@_debug_typeout:n{--->~label(s):~ \exp_not:n{#5}} #5 } % \end{macrocode} % \end{macro} % % % % % \subsection{Support for legacy classes and packages} % % If we define heading commands in the kernel (or in the tagging % code) using % \begin{verbatim} % \DeclareDocumentCommand \section {s ={shorttitle}o m} % { \ParseLaTeXeHeading {section} {#1} {#2} {#3} } % \end{verbatim} % then this gets overwritten by every class right now. % % If we redeclare them after the class was loaded then % we overwrite the layout of legacy classes (done, for example, with % \cs{@startsection}). New classes that use the above interface % would be fine though, as long as we have declared the instances % before the class is loaded. % % So to make legacy classes work with their existing layout, we % should not (ever) overwrite \cs{section} but let the class % definition call % \cs{@startsection} which would then set up suitable instances and % only after that call \cs{ParseLaTeXeHeading}. % % However, that would mean a \enquote{new} class like ltx-article % would need to add the above definition and declare or edit the % corresponding instances, instead of just declaring or adding the % instances. % % A perhaps better alternative could be to delay the definition of % \cs{section} and friends until after the class got loaded and % then take a peak at \cs{section} as defined by the class and if % it contains a \cs{@startsection} call, leave it alone and % otherwise overwrite it. And if the class hasn't defined % \cs{section} (because it is a new class) declare it. Of course % that means \cs{section} would then be available with every class % even if it was never meant to contain headings. % % But while writing this up, I start to think it is best if a new % class defines both the document interface (i.e., the above % command) as well as the layout (declare the instances) and the % kernel does neither. It has been this way before and it is % consistent, so why change. % % The situation with headings defined via \cs{secdef} is worse: we % don't have a nice handle as with \cs{@startsection} so there is % no real way other than through static analysis to set up such % a heading in the new template style. So all that is possible (I % think) is that after the class has been loaded, we look if the % usual candidates (\cs{part} and \cs{chapter}) have been defined % and overwrite them with a standard layout. That would make the % class tagging aware but, of course, would likely change the % layout. % % The current implementation % \begin{itemize} % \item provides instance for the heading commands of the standard classes; % \item declares the heading commands for the standard classes with % the new interfaces through class hooks; % \item other classes call the adapted \cs{@startsection}; other headings % command like \cs{part} or \cs{chapter} are not handled and must be setup % individually. % \end{itemize} % % \subsubsection{Instances (sample/default definitions)} % % % \begin{instance}{heading part} % An instance suitable for \cls{book} and \cls{report}. An instance suitable % for \cls{article} is above in the hook. % \begin{macrocode} \DeclareInstance{heading}{part}{display} { , name = part , level = -1 , placement = page , after-penalty-vspace = 0pt , prefix = \partname , number-format = \thepart , heading-decls = \centering\bfseries\huge , title-decls = \Huge , headformat-instance = part , mark-cmd = \partmark {#1} } % \end{macrocode} % \end{instance} % % \begin{instance}{heading chapter} % % \begin{macrocode} \DeclareInstance{heading}{chapter}{display} { , name = chapter , level = 0 , placement = top , column-spanning = true , after-penalty-vspace = 50pt , after-vspace = 40pt , prefix = \@chapapp , number-format = \thechapter , heading-decls = \raggedright \parindent0pt \bfseries \huge , title-decls = \Huge , headformat-instance = chapter , mark-cmd = \chaptermark {#1} , contents-extra = \addtocontents{lof}{\addvspace{10pt}} \addtocontents{lot}{\addvspace{10pt}} } % \end{macrocode} % \end{instance} % % % \begin{macro}{\@chapapp} % To improve compatibility with classes that use \cs{secdef} but % don't provide a definition for \cs{@chapapp} used in the % \key{prefix} key above, we define this command if % necessary. Otherwise such a class would error if \cs{chapter} is % used. % \changes{0.9h}{2026-07-08}{Define \cs{@chapapp}, if necessary (tagging/1482)} % \begin{macrocode} \AtBeginDocument{ \cs_if_exist:NF \@chapapp { \cs_new:Npn \@chapapp {Chapter} } } % \end{macrocode} % \end{macro} % \begin{instance}{heading section} % % \begin{macrocode} \DeclareInstance{heading}{section}{display} { , name = section , level = 1 , mark-cmd = \sectionmark {#1} , before-vspace = 3.5ex plus 1ex minus .2ex , after-vspace = 2.3ex plus .2ex , heading-decls = \normalfont\Large\bfseries , headformat-instance = section } % \end{macrocode} % \end{instance} % % % \begin{instance}{heading subsection} % % \begin{macrocode} \DeclareInstance{heading}{subsection}{display} { , name = subsection , parent-name = section , level = 2 , mark-cmd = \subsectionmark {#1} , before-vspace = 3.25ex plus 1ex minus .2ex , after-vspace = 1.5ex plus .2ex , heading-decls = \normalfont\large\bfseries , headformat-instance = subsection } % \end{macrocode} % \end{instance} % % % \begin{instance}{heading subsubsection} % % \begin{macrocode} \DeclareInstance{heading}{subsubsection}{display} { , name = subsubsection , parent-name = subsection , level = 3 , before-vspace = 3.25ex plus 1ex minus .2ex , after-vspace = 1.5ex plus .2ex , heading-decls = \normalfont\normalsize\bfseries , headformat-instance = subsubsection } % \end{macrocode} % \end{instance} % % \begin{instance}{heading paragraph} % % \begin{macrocode} \DeclareInstance{heading}{paragraph}{runin} { , name = paragraph , parent-name = subsubsection , level = 4 , before-vspace = 3.25ex \@plus1ex \@minus.2ex , after-hspace = 1em , heading-decls = \normalfont\normalsize\bfseries , mark-cmd = \paragraphmark {#1} , headformat-instance = paragraph } % \end{macrocode} % \end{instance} % % % % \begin{instance}{heading subparagraph} % % \begin{macrocode} \DeclareInstance{heading}{subparagraph}{runin} { , name = subparagraph , parent-name = paragraph , level = 5 , before-vspace = 3.25ex \@plus1ex \@minus .2ex , after-hspace = 1em , heading-decls = \normalfont\normalsize\bfseries , mark-cmd = \subparagraphmark {#1} , headformat-instance = subparagraph } % \end{macrocode} % \end{instance} % % \begin{instance}{headformat part} % \begin{instance}{headformat chapter} % The \insttype{headformat} instances for part and chapter use the % \texttt{display} template with identical settings by default, % so one is made a copy of the other to speed up loading. % \begin{macrocode} \DeclareInstance{headformat}{part}{display} { , heading-indent = 0pt , separator-a = \nobreakspace % between prefix and number , separator-b = \par \nobreak \vspace{20pt} % between label block and title } % \end{macrocode} % % \begin{macrocode} \DeclareInstanceCopy{headformat}{chapter}{part} % \end{macrocode} % \end{instance} % \end{instance} % % % \begin{instance}{headformat std} % \begin{instance}{headformat section} % \begin{instance}{headformat subsection} % \begin{instance}{headformat subsubsection} % Similarly, by default the instances for section, subsection, and % subsubsection are all using the \texttt{hang} template and the % same key values as the \texttt{std} instance, so they % are all made a copy of that one. % \begin{macrocode} \DeclareInstance{headformat}{std}{hang} { , heading-indent = 0pt } % \end{macrocode} % % \begin{macrocode} \DeclareInstanceCopy{headformat}{section}{std} \DeclareInstanceCopy{headformat}{subsection}{std} \DeclareInstanceCopy{headformat}{subsubsection}{std} % \end{macrocode} % \end{instance} % \end{instance} % \end{instance} % \end{instance} % % % \begin{instance}{headformat paragraph} % \begin{instance}{headformat subparagraph} % In contrast, paragraph and subparagraph differ even though both % use the \texttt{runin} template. % \begin{macrocode} \DeclareInstance{headformat}{paragraph}{runin} { , heading-indent = 0pt } % \end{macrocode} % % \begin{macrocode} \DeclareInstance{headformat}{subparagraph}{runin} { , heading-indent = \parindent } % \end{macrocode} % \end{instance} % \end{instance} % % % \begin{instance}{headformat section-special} % % A special headformat instance for testing. It can be used with % \verb+\section[headformat-instance=section-special]{bla}+ % \begin{macrocode} \DeclareInstance{headformat}{section-special}{hang} { , heading-indent = 4em %%%% , title-format = {#1!} % no longer } % \end{macrocode} % \end{instance} % % \subsubsection{New \cs{@startsection}} % % \begin{macro}{\@startsection} % To support legacy classes that implement headings through a call % to \cs{@startsection} we redefine this command to generate a % \text{heading} instance from the arguments of \cs{@startsection} if % it doesn't yet exist and then use this instance to typeset the % heading. % % NOTE: To handle legacy definition like % \verb+{\normalfont\Large\bfseries\MakeUppercase}+ in the last argument % it copies this argument both to % \key{heading-decls} and to \key{title-format}. % % \begin{macrocode} \DeclareDocumentCommand \@startsection {mmmmmm s ={shorttitle}o m}{ % \end{macrocode} % If there already exists an instance \texttt{\#1-@startsection} then arguments % 2--6 are ignored and we simply call that instance. Otherwise we % go through the process to set it up. This allows classes, packages % and authors to create and overwrite definitions with \cs{@startsection}. % But after a call to a command using the \cs{@startsection} % the instances are created. It is therefore not possible to redefine the command % after a first use or to create two commands using the same level, e.g. \cs{section} % and \cs{specialsection}. Such setups should use the new interfaces to declare % heading commands. % \begin{macrocode} \IfInstanceExistsF{heading}{#1-@startsection}{ % \end{macrocode} % % \begin{macrocode} \@@_debug_typeout:n{Info:~ setting~ up~ instances~ for~ legacy~ \string\@startsection} % \end{macrocode} % \cs{@startsection} supported the use of a macro % with one argument as the last token in its \meta{style} % argument, for example, \cs{MakeUppercase} to make the whole title % uppercase. To support that we need to take a look at \texttt{\#6} % and if that ends in such a macro split it off, so that we can put % the first tokens into \key{heading-decls} and this last token % into \key{title-format}. This is what the next call prepares for: % \changes{0.9m}{2026-08-12}{Split style argument of % \cs{@startsection} based off type of last token (tagging/1521)} % \begin{macrocode} \@@_prep_decls_and_format_from_tl:nNN {#6} \l_@@_heading_decls_tl \l_@@_title_format_tl % \end{macrocode} % In the \LaTeXe{} logic a negative \#4 means we do not indent the % following paragraph \ldots % % It is okay to change any \texttt{em} or \texttt{ex} % specifications to real \texttt{pt} values here, because this code % is executed in the same place where \cs{@startsection} is % normally executed and inside the original definitions such % assignments happen as well, before there is any font change % happening for \#4 and \#5. % \begin{macrocode} \@tempskipa #4\relax \@afterindenttrue \ifdim \@tempskipa <\z@ \@tempskipa -\@tempskipa \@afterindentfalse \fi % \end{macrocode} % \ldots\ and a negative \#5 means we should produce a runin heading. % \begin{macrocode} \@tempskipb #5\relax \ifdim \@tempskipb>\z@ \use:e { \DeclareInstance{heading}{#1-@startsection}{display}{ , name = #1 , level = #2 , mark-cmd = \exp_not:c {#1mark} {##1} , before-vspace = \the\@tempskipa , after-vspace = \the\@tempskipb , para-indent = \if@afterindent true \else false\fi % \end{macrocode} % Argument \texttt{\#6} contains the declarations for the whole % heading but it is allowed that the last token is a macro with one % argument that receives the heading text as its argument. % This is why we have split off the last token above, in case it % contained a macro with one argument. That token is then used as % the \key{title-format}. We end this key value in \cs{par} if we % are in a display instance. % \begin{macrocode} , heading-decls = \exp_not:o \l_@@_heading_decls_tl , title-format = \exp_not:o \l_@@_title_format_tl {##1} \exp_not:N \par % \end{macrocode} % \begin{macrocode} , headformat-instance = #1-@startsection }} \DeclareInstance{headformat}{#1-@startsection}{hang}{heading-indent = #3} % \end{macrocode} % We can expect that the instance \texttt{\#1-@startsection} is not % set up by the user if \texttt{\#1-@startsection} isn't, so no check. % \begin{macrocode} \else \@tempskipb=-\@tempskipb \use:e { \DeclareInstance{heading}{#1-@startsection}{runin}{ , name = #1 , level = #2 , mark-cmd = \exp_not:c {#1mark} {##1} , before-vspace = \the\@tempskipa , after-hspace = \the\@tempskipb , heading-decls = \exp_not:o \l_@@_heading_decls_tl % \end{macrocode} % In a runin instance \key{title-format} should not end in \cs{par}! % \begin{macrocode} , title-format = \exp_not:o \l_@@_title_format_tl {##1} , headformat-instance = #1-@startsection }} \DeclareInstance{headformat}{#1-@startsection}{runin}{heading-indent = #3} \fi } \ParseLaTeXeHeading {#1-@startsection}{#7}{#8}{#9} } % \end{macrocode} % \end{macro} % % % % \begin{macro}{\@@_prep_decls_and_format_from_tl:nNN} % Examine \texttt{\#1}. If the last token is a macro with one % argument put it in \texttt{\#3} and the remainder of \texttt{\#1} % into \texttt{\#2}. % Otherwise, put \cs{use:n} in \texttt{\#3} and all of \texttt{\#1} % in \texttt{\#2}. % \changes{0.9m}{2026-08-12}{Provide command to split off last token of % a token list if it is a macro with one argument (tagging/1521)} % \begin{macrocode} \cs_new:Npn \@@_prep_decls_and_format_from_tl:nNN #1#2#3 { \tl_set:Nn #3 { \use:n } \exp_args:Ne \@@_prep_decls_and_auxi:nnNN { \tl_reverse:n {#1} } {#1} #2 #3 } % \end{macrocode} % % \begin{macrocode} \cs_new:Npn \@@_prep_decls_and_auxi:nnNN #1#2#3#4 { \tl_if_head_is_N_type:nTF {#1} { \@@_prep_decls_and_auxii:nNwNN {#2} #1 \q_stop #3 #4 } { \tl_set:Nn #3 {#2} } } % \end{macrocode} % % \begin{macrocode} \cs_new:Npn \@@_prep_decls_and_auxii:nNwNN #1#2#3 \q_stop #4 #5 { \token_if_macro:NTF #2 { \exp_args:Ne \@@_prep_decls_and_auxiii:nnNnNN { \cmd_arg_spec:N #2 } {#1} #2 {#3} #4 #5 } { \tl_set:Nn #4 {#1} } } % \end{macrocode} % % \begin{macrocode} \cs_new:Npn \@@_prep_decls_and_auxiii:nnNnNN #1#2#3#4#5#6 { \bool_lazy_any:nTF { % \end{macrocode} % This tests for macros with one argument and defined by \cs{def} % or similar. % \begin{macrocode} { \str_if_eq_p:ee { \cs_parameter_spec:N #3 } { \c_hash_str 1 } } % \end{macrocode} % For those defined with \cs{NewDocumentCommand} we need to look at % the signature instead. We also accept macros with one optional % argument first followed by a mandatory one. % \begin{macrocode} { \str_if_eq_p:nn {#1} { m } } { \str_if_eq_p:nn {#1} { +m } } { \str_if_eq_p:nn {#1} { O{} m } } { \str_if_eq_p:nn {#1} { O{} +m } } % \end{macrocode} % Another possibility are macros declared with \cs{DeclareRobustCommand}. % \begin{macrocode} { \bool_lazy_and_p:nn { \cs_if_exist_p:c { \cs_to_str:N #3 \c_space_tl } } { \str_if_eq_p:ee { \cs_parameter_spec:c { \cs_to_str:N #3 \c_space_tl } } { \c_hash_str 1 } } } { \bool_lazy_and_p:nn { \cs_if_exist_p:c { \token_to_str:N #3 \c_space_tl } } { \str_if_eq_p:ee { \cs_parameter_spec:c { \token_to_str:N #3 \c_space_tl } } { [ \c_hash_str 1 ] \c_hash_str 2 } } } } { \tl_set:Ne #5 { \tl_reverse:n {#4} } \tl_set:Nn #6 {#3} } { \tl_set:Nn #5 {#2} } } % \end{macrocode} % % \begin{macrocode} \cs_generate_variant:Nn \cs_parameter_spec:N { c } % \end{macrocode} % \end{macro} % % % Some classes redefine \cs{@startsection} and then our redefinition is lost. So we % reinstate it % \begin{macrocode} \NewCommandCopy\kernel@startsection\@startsection \AddToHook{class/after}[head/@startsection] {\RenewCommandCopy\@startsection\kernel@startsection} % \end{macrocode} % % % \subsubsection{New \cs{secdef}} % The command maps standard \cs{secdef} definition of \cs{part} and \cs{chapter} to % the new interface. Unknown heading commands warn (or error if tagging is active) and % then call the old commands. % \begin{macro}{\secdef} % \begin{macrocode} \RenewDocumentCommand\secdef{mmsO{#5}m} { \str_case:enF {\cs_to_str:N#1} { {@part} { \IfNoValueTF{#4} {\ParseLaTeXeHeading{part}{#3} {placement-start-code=\relax} {#5}} { \@@_process_shorttitle:nN{#4}\l_@@_tmpa_tl \ExpandArgs{nne}\ParseLaTeXeHeading{part}{#3} {placement-start-code=\relax,\exp_not:o{\l_@@_tmpa_tl}} {#5} } \@latex@warning{\noexpand\secdef~ detected~ in~ \noexpand\part command.\MessageBreak \noexpand\part~will~be~redefined~to~use~templates.\MessageBreak This~may~change~the~layout!} \DeclareDocumentCommand \part {s ={shorttitle}o m} { \ParseLaTeXeHeading {part} {##1} {##2} {##3} } } {@chapter} { \IfNoValueTF{#4} { \ParseLaTeXeHeading{chapter}{#3} {#4} {#5} } { \@@_process_shorttitle:nN{#4}\l_@@_tmpa_tl \ExpandArgs{nno}\ParseLaTeXeHeading{chapter}{#3} {\l_@@_tmpa_tl} {#5} } \@latex@warning{\noexpand\secdef~ detected~ in~ \noexpand\chapter command.\MessageBreak \noexpand\chapter~ will~ be~ redefined~ to~ use~ templates.\MessageBreak This~may~change~the~layout!} \DeclareDocumentCommand \chapter {s ={shorttitle}o m} { \ParseLaTeXeHeading {chapter} {##1} {##2} {##3} } } } { \tag_if_active:TF\@latex@error\@latex@warning {\noexpand\secdef~ with~ unknown~ argument~ \noexpand#1 found.\MessageBreak The~command~can~not~be~adapted~on~the~fly~to~support~tagging.\MessageBreak The~heading~command~using~this~\noexpand\secdef~should~be~ reimplemented\MessageBreak with~templates}{} \IfBooleanTF{#3}{#2{#5}}{#1[#4]{#5}} } } % \end{macrocode} % A helper command to reprocess the argument as key-val % \begin{macrocode} \NewDocumentCommand\@@_process_shorttitle:nN{={shorttitle}mm} { \tl_set:Nn#2{#1} } % \end{macrocode} % \end{macro} % % % % \subsubsection{Core \LaTeX{}} % % We define here as an example the heading commands with the new % interfaces for the three standard classes. % % \begin{macrocode} \AddToHook{class/article/after}[head/example] { \DeclareInstance{heading}{part}{display} { , name = part , level = -1 , before-vspace = 4ex , after-vspace = 3ex , mark-cmd = \partmark {#1} , prefix = \partname , punct = , heading-decls = \raggedright\bfseries\Large , title-decls = \huge , headformat-instance = part } \DeclareInstance{headformat}{part}{display} { , heading-indent = 0pt , separator-a = \nobreakspace , separator-b = \par \nobreak \vspace{20pt} } \DeclareDocumentCommand \part {s ={shorttitle}o m} { \ParseLaTeXeHeading {part} {#1} {#2} {#3} } \@@_setup_default_heading: } % \end{macrocode} % \changes{0.9b}{2026-01-26}{Correct chapter definition, tagging/1984} % \begin{macrocode} \AddToHook{class/report/after}[head/example] { \DeclareDocumentCommand \part {s ={shorttitle}o m} { \ParseLaTeXeHeading {part} {#1} {#2} {#3} } \DeclareDocumentCommand \chapter {s ={shorttitle}o m} { \ParseLaTeXeHeading {chapter}{#1} {#2} {#3} } \@@_setup_default_heading: } \AddToHook{class/book/after}[head/example] { \DeclareDocumentCommand \part {s ={shorttitle}o m} { \ParseLaTeXeHeading {part} {#1} {#2} {#3} } \DeclareDocumentCommand \chapter {s ={shorttitle}o m} { \if@mainmatter \ParseLaTeXeHeading {chapter}{#1} {#2} {#3} \else \IfBooleanTF{#1} {% starred: \ParseLaTeXeHeading {chapter}{\BooleanTrue} {#2} {#3} } {\IfNoValueTF{#2} {\ParseLaTeXeHeading {chapter}{\BooleanTrue} {shorttitle={#3}} {#3}} {\ParseLaTeXeHeading {chapter}{\BooleanTrue} {#2} {#3}} } \fi } \@@_setup_default_heading: } % \end{macrocode} % % % % \begin{macrocode} \cs_new_protected:Npn \@@_setup_default_heading: { % \end{macrocode} % \begin{macro}{\section} % % \begin{macrocode} \DeclareDocumentCommand \section {s ={shorttitle}o m} { \ParseLaTeXeHeading {section} {##1} {##2} {##3} } % \end{macrocode} % \end{macro} % % \begin{macro}{\subsection} % % \begin{macrocode} \DeclareDocumentCommand \subsection {s ={shorttitle}o m} { \ParseLaTeXeHeading {subsection} {##1} {##2} {##3} } % \end{macrocode} % \end{macro} % % \begin{macro}{\subsubsection} % % \begin{macrocode} \DeclareDocumentCommand \subsubsection {s ={shorttitle}o m} { \ParseLaTeXeHeading {subsubsection} {##1} {##2} {##3} } % \end{macrocode} % \end{macro} % % \begin{macro}{\paragraph} % % \begin{macrocode} \DeclareDocumentCommand \paragraph {s ={shorttitle}o m} { \ParseLaTeXeHeading {paragraph} {##1} {##2} {##3} } % \end{macrocode} % \end{macro} % % \begin{macro}{\subparagraph} % % \begin{macrocode} \DeclareDocumentCommand \subparagraph {s ={shorttitle}o m} { \ParseLaTeXeHeading {subparagraph} {##1} {##2} {##3} } } %end of command % \end{macrocode} % \end{macro} % \begin{macrocode} % % \end{macrocode} % % % % % % % \section{Issues and problems noticed along the way} % % \subsection{Local \cs{DeclareInstance}} % % \cs{DeclareInstance} does its declaration locally. That might be the % right decision (or not) but we need to make sure that it in that % case all of the declaration is local. % % One potential problem with that is that it might allocate \TeX{} % registers. % % The problem showed up in the redefinition of \cs{@startsection}, % because in the current document some headings are local to an % environment, so things get redeclared over and over again. % % \subsection{\cs{SetCurrentTemplateKeys}} % % Furthermore, I think I would like to have some % \cs{SetCurrentTemplateKeys} where one does not have to specify % the type nor the template name, because that is how it is always % used (by me). % % \subsection{O-expansion of \cs{UseInstance} arguments} % % Whenever a template code calls a sub-instance and that % sub-instance takes arguments, then the values for these arguments % are typically inside tokenlists or registers so that for % efficiency (and sometimes as a total must) one has to o-expand % all arguments. While that can be coded somehow, e.g., % \begin{verbatim} % \use:e { % \UseInstance{headformat} { \l_@@_headformat_instance_tl } % { \exp_not:o \UnusedTemplateKeys } % { \exp_not:o { \l_@@_number_tl } } % { \exp_not:n { #3 } } % { \exp_not:o { \use_i:nn #9 } } % { \exp_not:o { \use_ii:nn #9 } } % } % \end{verbatim} % % it would be better to have a version of \cs{UseInstance} that % does this automatically. No suggestion for a name. % % % % \subsection{Switch \cs{openright} is not defined by core} % % I think this should change and it might be enough to just define % it in the kernel (I think \cs{newif} no longer complains if it % acts on an existing \cs{if...} % % \subsection{Indexheading at least for l3doc is wrong now} % % Needs checking. % % % % % % % % \appendix % % \section{Templates from the 2026-06-01 release of \LaTeX{}} \label{appendix:a} % % In this appendix we provide the template implementations that I came % up with in my first attempt (only renamed to % \meta{name}\texttt{-v1}). Eventually, we want to drop the % \texttt{-v1} versions but for the next couple of releases we % provide them alongside the new more flexible templates, so that % there is no need to transitioning to the new % templates in a rush. % % \subsection{Template documentation for \texttt{-v1} templates} % % % \subsubsection{Templates of type \texttt{heading}} % % % There are a number of keys that are expected to be recognized by % all heading templates (though they may choose not to make use of % them). These are listed below instead of being repeated on the % actual templates. % % All other keys are either attached to the \texttt{heading} or to % the \texttt{headformat} templates. Those that typically vary % from heading instance to the next (e.g., \key{heading-decls}) are % all declared in the % \texttt{heading} templates even if they are actually only used % within \texttt{headformat} templates. In other words, % \texttt{headformat} templates have a number of implicit % variables that they expect to be set.\footnote{Maybe questionable} % % % \begin{TemplateDescription}{heading}{\meta{all}} % % \TemplateKey{name}{tokenlist} % {Referenceable name of the heading instance. String that % is acceptable in csnames for use in building counter % names, etc.}{} % \TemplateKey{parent-name}{tokenlist} % {Name of the next higher heading instance. If not % given, then the internal heading level of the heading % instance is set to \texttt{0}}{} % \TemplateKey{reset-counter}{tokenlist} % {Name of the heading instance that should reset the % numbering of this heading level (if any)}{} % \TemplateKey{level}{integer} % {Sets the internal heading-level rather than deducing % it from \key{parent-name}. Can be used to specify the % top-level heading if not \texttt{0}, or all headings % in legacy implementations, e.g., through \cs{@startsection}}{} % % \TemplateKey{placement}{choice} % {Set the heading placement, i.e., the behavior of the % heading with respect to page breaks. Allowed values % are % \texttt{page} (heading forms a page if its own), % \texttt{top} (heading starts a new page), % \texttt{normal} (heading can appear anywhere on the % page). Further possibilities might be % \texttt{rectopage} and \texttt{rectotop} if we % implement that.}{\texttt{normal}} % % \TemplateKey{start-code}{tokenlist} % {Default value is set by the \key{placement} % key. Executed before the heading starts, so can issue, for % example, a \cs{clearpage}} % {} % % \TemplateKey{final-code}{tokenlist} % {Default value is set by the \key{placement} % key. Executed after the heading is typeset. Can set up code for % putting the heading on a page by its own, or arrange for % paragraph handling of a following paragraph, etc.} % {} % % \TemplateKey{prefix}{tokenlist} % {A fixed string, such as \enquote{Chapter}, that can be % used together with the number (if any) to form a % \enquote{heading label}. Usage and placement is up to % the template, so it could be placed after the number % by the template (in which case it isn't really a % prefix).} {\cs{NoValue}} % % \TemplateKey{mark-cmd}{function(1)} % {Function that receives the \meta{running} argument and % creates a suitable mark insertion} % {\texttt\textbackslash\meta{name}\texttt{mark}} % % \TemplateSemantics % % The above keys should be implemented by all heading templates. % % At the moment I have retained the \LaTeXe{} interfaces for marks, % e.g., one has to set up \cs{chaptermark}, \cs{sectionmark}, % etc.\ but I'm not sure this should stay (even though it is % certainly simpler from a compatibility perspective). % % All templates should set up \cs{theheading} to correspond to % \cs{the\meta{name}}. % \end{TemplateDescription} % % % \begin{TemplateDescription}{heading}{display-v1} % % \TemplateKey{para-indent}{boolean} % {Should the paragraph after the heading be indented?} % {false} % % \TemplateKey{before-vspace}{skip} % {Vertical space before the heading if there is no page % or column break. If there is one it vanishes. % In particular this means it will not be used in the % heading \texttt{placement}s \texttt{page} or \texttt{top}} % {0pt} % % \TemplateKey{penalty}{integer} % {Penalty to break before the heading. The default % (\TeX's largest integer) indicates that no penalty was set in % which case \cs{@secpenalty} is used.} % {\number\maxdimen} % % \TemplateKey{after-penalty-vspace}{skip} % {Vertical space before the heading but after the % penalty for the heading. If the penalty results in a page or % column break, this space remains at the top of the page} % {0pt} % % \TemplateKey{after-vspace}{skip} % {Vertical space after the heading} % {0pt} % % \TemplateKey{heading-decls}{tokenlist} % {Declarations (such as font or color settings) applied % to all heading elements, i.e., number, title, % subtitle, and quotation, if present. % Note that color commands (unless \pkg{luacolor} is used) % introduce a break point before the heading and therefore one should either % surround color commands with \cs{SaveLastSkip} and \cs{RestoreLastSkip} or % to use the keys \texttt{prefix-decls}, \texttt{number-decls}, % \texttt{title-decls}, etc.\ instead.} % {\cs{normalfont}} % % \TemplateKey{prefix-decls}{tokenlist} % {Declarations (such as font or color settings) applied % to the heading prefix, overwriting the setting % of \key{heading-decls}.} % {\meta{empty}} % % \TemplateKey{number-decls}{tokenlist} % {Declarations (such as font or color settings) applied % to the heading number, overwriting the setting % of \key{heading-decls}.} % {\meta{empty}} % % \TemplateKey{title-decls}{tokenlist} % {Declarations (such as font or color settings) applied % to the heading title, overwriting the setting % of \key{heading-decls}.} % {\meta{empty}} % % \TemplateKey{subtitle-decls}{tokenlist} % {Declarations (such as font or color settings) applied % to the heading subtitle, overwriting the setting % of \key{heading-decls}.} % {\meta{empty}} % % \TemplateKey{quote-decls}{tokenlist} % {Declarations (such as font or color settings) applied % to the heading quote, overwriting the setting % of \key{heading-decls}.} % {\meta{empty}} % % \TemplateKey{number-format}{function(1)} % {Code that produces a formatted version of the % heading number % including any ornamentations. Its argument is the % counter name for the head. However, instead of using % a specific counter representations, e.g., % \cs{thesection}, it % can refer to the counter representation for the % current heading counter via \cs{theheading} and ignore % the argument.} % {\cs{theheading}} % % \TemplateKey{contents-extra}{tokenlist} % {Code containing \cs{addcontents} calls to write to % files like \texttt{.lot} or \texttt{.lot}} % {\meta{empty}} % % % \TemplateKey{headformat-instance}{instance} % {Template instances of type \texttt{headformat}} % {std} % % \TemplateSemantics % % Several of the key names are simply bad and need revision! % \end{TemplateDescription} % % % % % \begin{TemplateDescription}{heading}{runin-v1} % % \TemplateKey{before-vspace}{skip} % {Vertical space before the heading if there is no page % or column break. If there is one it vanishes.} % {0pt} % % \TemplateKey{penalty}{integer} % {Penalty to break before the heading. The default % (\TeX's largest integer) indicates that no penalty was set in % which case \cs{@secpenalty} is used.} % {\number\maxdimen} % % \TemplateKey{after-penalty-vspace}{skip} % {Vertical space before the heading but after the % penalty for the heading. If the penalty results in a page or % column break, this space remains at the top of the page. It is % also applied if the heading is a \texttt{page} or \texttt{top} heading.} % {0pt} % % \TemplateKey{after-vspace}{skip} % {Vertical space after the heading -- ignored in runin % headings so should be dropped!} % {0pt} % % \TemplateKey{heading-decls}{tokenlist} % {Declarations (such as font or color settings) applied % to all heading elements, i.e., number, title, % subtitle, and quotation, if present.} % {\cs{normalfont}} % % \TemplateKey{prefix-decls}{tokenlist} % {Declarations (such as font or color settings) applied % to the heading prefix, overwriting the setting % of \key{heading-decls}.} % {\meta{empty}} % % \TemplateKey{number-decls}{tokenlist} % {Declarations (such as font or color settings) applied % to the heading number, overwriting the setting % of \key{heading-decls}.} % {\meta{empty}} % % \TemplateKey{title-decls}{tokenlist} % {Declarations (such as font or color settings) applied % to the heading title, overwriting the setting % of \key{heading-decls}.} % {\meta{empty}} % % \TemplateKey{subtitle-decls}{tokenlist} % {Declarations (such as font or color settings) applied % to the heading subtitle, overwriting the setting % of \key{heading-decls}.} % {\meta{empty}} % % \TemplateKey{quote-decls}{tokenlist} % {Declarations (such as font or color settings) applied % to the heading quote, overwriting the setting % of \key{heading-decls}.} % {\meta{empty}} % % \TemplateKey{number-format}{function(1)} % {Code that produces a formatted version of the % heading number % including any ornamentations. Its argument is the % counter name for the head. However, instead of using % a specific counter representations, e.g., % \cs{thesection}, it % can refer to the counter representation for the % current heading counter via \cs{theheading} and ignore % the argument.} % {\cs{theheading}} % % \TemplateKey{headformat-instance}{instance} % {Template instances of type \texttt{headformat}} % {std} % % \TemplateSemantics % % Several of the key names are simply bad and need revision! % \end{TemplateDescription} % % % % % \subsubsection{Templates of type \texttt{headformat}} % % At the moment all templates of this type assume that placement of % prefix, number, title is done in exactly this order. Anything % else would require defining further templates. % % TODO: consider a more versatile mechanism (like in theorem-like % templates) to allow for more flexible placements without the need % to define additional templates. % % \begin{TemplateDescription}{headformat}{hang-v1} % % \TemplateKey{heading-indent}{dimen} % {Horizontal space before the heading (shifting it to % the right in a LR typesetting context)} % {0pt} % % \TemplateKey{title-format}{tokenlist} % {Code executed directly before the heading is typeset % and after the vertical spacing is done. The code takes an argument, % e.g., \cs{MakeUppercase}} % {\meta{\#1}} % % \TemplateKey{prefix-number-sep}{tokenlist} % {Token list that can be used in the assignment to a % \TeX{} dimension (can contain \texttt{em} or % \texttt{ex} values) specifying the separation between % title prefix (if used) and title number. Evaluated after fonts for % title text have been set up, i.e., the \texttt{em} will be based % on the then current font.} % {.5em} % % \TemplateKey{number-title-sep}{tokenlist} % {Token list that can be used in the assignment to a % \TeX{} dimension (can contain \texttt{em} or % \texttt{ex} values) specifying the separation between % title number and title text. Evaluated after fonts for % title text have been set up, i.e., the \texttt{em} will be based % on the then current font.} % {1em} % % \TemplateSemantics % % Implements a heading layout where number and title start on the % same line and the title is hanging off from that if the title % has more than one line. It is what \LaTeX{} always used by % default for \cs{section}, \cs{subsection}, and % \cs{subsubsection}. % % Several of the key names are simply bad and need a revision! % \end{TemplateDescription} % % % \begin{TemplateDescription}{headformat}{runin-v1} % % \TemplateKey{heading-indent}{dimen} % {Horizontal space before the heading (shifting it to % the right in a LR typesetting context)} % {0pt} % % \TemplateKey{title-format}{tokenlist} % {Code executed directly before the heading is typeset % and after the vertical spacing is done. The code can take an argument, % e.g., \cs{MakeUppercase}} % {\meta{empty}} % % \TemplateKey{prefix-number-sep}{tokenlist} % {Token list that can be used in the assignment to a % \TeX{} dimension (can contain \texttt{em} or % \texttt{ex} values) specifying the separation between % title prefix (if used) and title number. Evaluated after fonts for % title text have been set up, i.e., the \texttt{em} will be based % on the then current font.} % {.5em} % % \TemplateKey{number-title-sep}{tokenlist} % {Token list that can be used in the assignment to a % \TeX{} dimension (can contain \texttt{em} or % \texttt{ex} values) specifying the separation between % title number and title text. Evaluated after fonts for % title text have been set up, i.e., the \texttt{em} will be based % on the then current font. % % In the hang template it is a horizontal dimension!} % {1em} % % \TemplateSemantics % % Implements a heading layout where number and title start on the % same line but then continue with the following paragraph on the % same line (or the next if the title overflows, i.e., the title is % run-in. It is what \LaTeX{} always used by default for % \cs{paragraph} and \cs{subparagraph}. % % Several of the key names are simply bad and need a revision! % \end{TemplateDescription} % % % \begin{TemplateDescription}{headformat}{display-v1} % % \TemplateKey{heading-indent}{dimen} % {Horizontal space before the heading (shifting it to % the right in a LR typesetting context)} % {0pt} % \TemplateKey{title-format}{tokenlist} % {Code executed directly before the heading is typeset % and after the vertical spacing is done. The code can take an argument, % e.g., \cs{MakeUppercase}} % {\meta{\#1}} % % \TemplateKey{prefix-number-sep}{tokenlist} % {Token list that can be used in the assignment to a % \TeX{} dimension (can contain \texttt{em} or % \texttt{ex} values) specifying the separation between % title prefix (if used) and title number. Evaluated after fonts for % title text have been set up, i.e., the \texttt{em} will be based % on the then current font.} % {.5em} % % \TemplateKey{number-title-sep}{tokenlist} % {Token list that can be used in the assignment to a % \TeX{} dimension (can contain \texttt{em} or % \texttt{ex} values) specifying the separation between % title number and title text. Evaluated after fonts for % title text have been set up, i.e., the \texttt{em} will be based % on the then current font. % \endgraf % In the display template it is a vertical dimension!} % {20 pt} % % \TemplateSemantics % % Implements a heading layout where number and title are vertically % separated. It is what \LaTeX{} always used by default for % \cs{chapter} and \cs{part}. % % Several of the key names are simply bad and need a revision! % \end{TemplateDescription} % % % \subsection{Implementation of \texttt{-v1} templates} % % \begin{template}{heading display-v1} % \begin{macrocode} \DeclareTemplateInterface{heading}{display-v1}{9} { , name : tokenlist , parent-name : tokenlist , reset-counter : tokenlist , level : integer = 0 , placement : choice {page , top , normal } = normal , mark-cmd : function(1) = , para-indent : choice { true , false } = false , before-vspace : skip = 0pt , penalty : integer = \c_max_int , after-penalty-vspace : skip = 0pt , after-vspace : skip = 0pt , start-code : tokenlist = % no default values! , final-code : tokenlist = % no default values! , prefix : tokenlist = \NoValue , heading-decls : tokenlist = \normalfont , prefix-decls : tokenlist = , number-decls : tokenlist = , title-decls : tokenlist = , subtitle-decls : tokenlist = , quote-decls : tokenlist = , headformat-instance : tokenlist = std , number-format : function(1) = \theheading , contents-extra : tokenlist = } \DeclareTemplateCode{heading}{display-v1}{9} { , name = \l__head_name_tl , level = \l__head_level_int , parent-name = \l__head_pname_tl , reset-counter = \l__head_reset_cnt_tl , placement = { ,page = \__head_debug_typeout:n{ A~ page~ heading } \tl_set:Nn \l__head_placement_tl { page } ,top = \__head_debug_typeout:n{ A~ top~ heading } \tl_set:Nn \l__head_placement_tl { top } ,normal = \__head_debug_typeout:n{ A~ normal~ heading } \tl_set:Nn \l__head_placement_tl { normal } } , mark-cmd = \__head_mark_cmd:n , para-indent = { ,true = \@afterindenttrue ,false = \@afterindentfalse } , before-vspace = \l__head_before_skip , penalty = \l__head_penalty_int , after-penalty-vspace = \l__head_after_penalty_skip , after-vspace = \l__head_after_skip , start-code = \l__head_start_code_tl , final-code = \l__head_final_code_tl , prefix = \l__head_typeset_prefix_tl , headformat-instance = \l__head_headformat_instance_tl , heading-decls = \l__head_heading_decls_tl , prefix-decls = \l__head_prefix_decls_tl , number-decls = \l__head_number_decls_tl , title-decls = \l__head_title_decls_tl , subtitle-decls = \l__head_subtitle_decls_tl , quote-decls = \l__head_quote_decls_tl , number-format = \__head_number_format:n , contents-extra = \l__head_contents_extra_tl } { \tl_set_eq:Nc \theheading { the \l__head_name_tl } \tl_set:Ne\l__head_saved_secnumdepth_tl{\int_use:N\c@secnumdepth} \__head_show_arguments:nnnnnnnnn {#1}{#2}{#3}{#4}{#5}{#6}{#7}{#8}{#9} \tl_if_empty:oF {#1} { \SetTemplateKeys{heading}{display}{#1} } \tl_if_empty:oT \l__head_start_code_tl { \str_case:VnF \l__head_placement_tl { { page } { \tl_set:Nn \l__head_start_code_tl { \if@openright \cleardoublepage \else \clearpage \fi \thispagestyle{plain} \if@twocolumn \onecolumn \@tempswatrue \else \@tempswafalse \fi \null\vfil } } { top } { \tl_set:Nn \l__head_start_code_tl { \if@openright\cleardoublepage\else\clearpage\fi \thispagestyle{plain} \global\@topnum\z@ } } } { \tl_clear:N \l__head_start_code_tl } } \tl_if_empty:oT \l__head_final_code_tl { \str_case:VnF \l__head_placement_tl { { page } { \tl_set:Nn \l__head_final_code_tl { \vfil\newpage \if@twoside \if@openright \null \thispagestyle{empty} \newpage \fi \fi \if@tempswa \twocolumn \fi } } } { \tl_set:Nn \l__head_final_code_tl { \@afterheading } } } \__head_determine_penalty: % \end{macrocode} % Next lines are an inline version of % \cs{__head_determine_number_typesetting:N}\verb= #2= % This macro has changed so here we inline the original code; % \begin{macrocode} \bool_set:Nn \l__head_unnumbered_bool { \bool_lazy_or_p:nn { \int_compare_p:nNn \l__head_level_int > \c@secnumdepth } { \bool_if_p:N #2 } } \bool_if:NTF \l__head_unnumbered_bool { \int_gset:Nn\c@secnumdepth{-99} \tl_clear:N \l__head_typeset_number_tl } { \@kernel@refstepcounter{ \l__head_name_tl } \protected@edef \l__head_typeset_number_tl { \__head_number_format:n { \l__head_name_tl } } } % \end{macrocode} % End of inlined code. % \begin{macrocode} \__head_vertical_before_spacing: \UseTaggingSocket{sec/end}{\int_use:N\l__head_level_int} \UseTaggingSocket{sec/begin} {{\int_use:N\l__head_level_int} {tag=\UseStructureName{sec/\int_use:N\l__head_level_int}}} \__head_debug_typeout:n{use~ 'headformat'~instance:~ \l__head_headformat_instance_tl } \use:e { \UseInstance{headformat} { \l__head_headformat_instance_tl } { \exp_not:o \UnusedTemplateKeys } { \exp_not:o { \l__head_typeset_prefix_tl } } { \exp_not:o { \l__head_typeset_number_tl } } { \exp_not:n { #3 } } { \exp_not:o { \use_i:nn #9 } } { \exp_not:o { \use_ii:nn #9 } } } \__head_handle_marks_etc:nnnnn {#4}{#5}{#6}{#7}{#8} \int_gset:Nn\c@secnumdepth{\l__head_saved_secnumdepth_tl} \par \nobreak \skip_vertical:N \l__head_after_skip \l__head_final_code_tl \ignorespaces } % \end{macrocode} % \end{template} % % % % % % \begin{template}{heading runin-v1} % \begin{macrocode} \DeclareTemplateCopy{heading}{runin-v1}{display-v1} \DeclareTemplateCode{heading}{runin-v1}{9} { , name = \l__head_name_tl , level = \l__head_level_int , parent-name = \l__head_pname_tl , reset-counter = \l__head_reset_cnt_tl , placement = { page = \typeout{ ^^JA~ runin~ page~ placement~ heading~makes~no~sense (top~used)} \tl_set:Nn \l__head_placement_tl { top } ,top = \__head_debug_typeout:n{ A~ top~ heading } \tl_set:Nn \l__head_placement_tl { top } ,normal = \__head_debug_typeout:n{ A~ normal~ heading } \tl_set:Nn \l__head_placement_tl { normal } } , mark-cmd = \__head_mark_cmd:n , para-indent = { ,true = %\typeout{para-indent~ setting~ ignored} ,false = %\typeout{para-indent~ setting~ ignored} } , before-vspace = \l__head_before_skip , penalty = \l__head_penalty_int , after-penalty-vspace = \l__head_after_penalty_skip , after-vspace = \l__head_after_skip , start-code = \l__head_start_code_tl , final-code = \l__head_final_code_tl , prefix = \l__head_typeset_prefix_tl , headformat-instance = \l__head_headformat_instance_tl , heading-decls = \l__head_heading_decls_tl , prefix-decls = \l__head_prefix_decls_tl , number-decls = \l__head_number_decls_tl , title-decls = \l__head_title_decls_tl , subtitle-decls = \l__head_subtitle_decls_tl , quote-decls = \l__head_quote_decls_tl , number-format = \__head_number_format:n , contents-extra = \l__head_contents_extra_tl } { \__head_show_arguments:nnnnnnnnn {#1}{#2}{#3}{#4}{#5}{#6}{#7}{#8}{#9} \tl_set:Ne\l__head_saved_secnumdepth_tl{\int_use:N\c@secnumdepth} \tl_set_eq:Nc \theheading { the \l__head_name_tl } \tl_if_empty:oF {#1} { \SetTemplateKeys{heading}{runin}{#1} } \tl_if_empty:oT \l__head_start_code_tl { \str_case:Vn \l__head_placement_tl { { top } { \tl_set:Nn \l__head_start_code_tl {\clearpage } } } } \__head_determine_penalty: % \end{macrocode} % Next lines are an inline version of % \cs{__head_determine_number_typesetting:N}\verb= #2= % This macro has changed so here we inline the original code; % \begin{macrocode} \bool_set:Nn \l__head_unnumbered_bool { \bool_lazy_or_p:nn { \int_compare_p:nNn \l__head_level_int > \c@secnumdepth } { \bool_if_p:N #2 } } \bool_if:NTF \l__head_unnumbered_bool { \int_gset:Nn\c@secnumdepth{-99} \tl_clear:N \l__head_typeset_number_tl } { \@kernel@refstepcounter{ \l__head_name_tl } \protected@edef \l__head_typeset_number_tl { \__head_number_format:n { \l__head_name_tl } } } % \end{macrocode} % End of inlined code. % \begin{macrocode} \__head_vertical_before_spacing: \UseTaggingSocket{sec/end}{\int_use:N\l__head_level_int} \UseTaggingSocket{sec/begin} {{\int_use:N\l__head_level_int}{tag=\UseStructureName{sec/\int_use:N\l__head_level_int}}} \UseTaggingSocket{sec/title/init}{\int_use:N\l__head_level_int} \def \@svsechd { \__head_debug_typeout:n{use~ 'headformat'~instance:~ \l__head_headformat_instance_tl } \use:e { \UseInstance{headformat} { \l__head_headformat_instance_tl } { \exp_not:o \UnusedTemplateKeys } { \exp_not:o { \l__head_typeset_prefix_tl } } { \exp_not:o { \l__head_typeset_number_tl } } { \exp_not:n { #3 } } { \exp_not:o { \use_i:nn #9 } } { \exp_not:o { \use_ii:nn #9 } } } \__head_handle_marks_etc:nnnnn {#4}{#5}{#6}{#7}{#8} \int_gset:Nn\c@secnumdepth{\l__head_saved_secnumdepth_tl} } \@nobreakfalse \global\@noskipsectrue \everypar{% \if@noskipsec \global\@noskipsecfalse {\setbox\z@\lastbox} \clubpenalty\@M \@svsechd \unskip \UseTaggingSocket{sec/title/split} \skip_horizontal:N \l__head_after_skip \else \clubpenalty \@clubpenalty \everypar{}% \fi } \l__head_final_code_tl \ignorespaces } % \end{macrocode} % \end{template} % % % % % % % % \begin{template}{headformat display-v1} % The \texttt{display} template produces % \begin{macrocode} \DeclareTemplateInterface{headformat}{display-v1}{6} { , heading-indent : length = 0pt , title-format : function(1) = #1 , prefix-number-sep : tokenlist = \WordSpaceAmount{1} , number-title-sep : tokenlist = 20pt } % \end{macrocode} % \end{template} % % \begin{template}{headformat hang-v1} % \begin{macrocode} \DeclareTemplateInterface{headformat}{hang-v1}{6} { , heading-indent : length = 0pt , title-format : function(1) = #1 , prefix-number-sep : tokenlist = \WordSpaceAmount{1} , number-title-sep : tokenlist = 1em } % \end{macrocode} % \end{template} % % % \begin{template}{headformat runin-v1} % \begin{macrocode} \DeclareTemplateInterface{headformat}{runin-v1}{6} { , heading-indent : length = 0pt , title-format : function(1) = #1 , prefix-number-sep : tokenlist = \WordSpaceAmount{1} , number-title-sep : tokenlist = 1em } % \end{macrocode} % \end{template} % % % % \begin{template}{headformat display-v1} % \begin{macrocode} \DeclareTemplateCode{headformat}{display-v1}{6} % args: keys,prefix,number,title,subtitle,quotation { , heading-indent = \l_@@_heading_indent_dim , title-format = \@@_title_format:n , prefix-number-sep = \l_@@_prefix_number_sep_tl , number-title-sep = \l_@@_number_title_sep_tl } { \@@_show_arguments:nnnnnn {#1}{#2}{#3}{#4}{#5}{#6} \tl_if_empty:oF {#1} { \SetTemplateKeys{headformat}{display-v1}{#1} } \group_begin: \UseTaggingSocket{sec/title/begin}{{\int_use:N\l_@@_level_int}{#4}} \normalfont \interlinepenalty \@M \l_@@_heading_decls_tl{} \bool_if:NTF \l_@@_unnumbered_bool { \dim_compare:nNnTF \l_@@_heading_indent_dim < \c_zero_skip { \skip_horizontal:N \l_@@_heading_indent_dim \MakeLinkTarget[\l_@@_name_tl]{} } { \MakeLinkTarget[\l_@@_name_tl]{} \skip_horizontal:N \l_@@_heading_indent_dim } } { \dim_compare:nNnTF \l_@@_heading_indent_dim < \c_zero_skip { \skip_horizontal:N \l_@@_heading_indent_dim \MakeLinkTarget{\l_@@_name_tl} } { \MakeLinkTarget{\l_@@_name_tl} \skip_horizontal:N \l_@@_heading_indent_dim } \IfNoValueF{#2} { { \l_@@_prefix_decls_tl #2 } \nobreak \skip_horizontal:n { \l_@@_prefix_number_sep_tl } } \l_@@_number_decls_tl #3 \par\nobreak \skip_vertical:n { \l_@@_number_title_sep_tl } } \l_@@_title_decls_tl \@@_title_format:n {#4} \par \UseTaggingSocket{sec/title/end} \group_end: } % \end{macrocode} % \end{template} % % % % % % % \begin{template}{headformat hang-v1} % \begin{macrocode} \DeclareTemplateCode{headformat}{hang-v1}{6} { , heading-indent = \l_@@_heading_indent_dim , title-format = \@@_title_format:n , prefix-number-sep = \l_@@_prefix_number_sep_tl , number-title-sep = \l_@@_number_title_sep_tl } { \@@_show_arguments:nnnnnn {#1}{#2}{#3}{#4}{#5}{#6} \tl_if_empty:oF {#1} { \SetTemplateKeys{headformat}{hang-v1}{#1} } \group_begin: \UseTaggingSocket{sec/title/init}{\int_use:N\l_@@_level_int} \normalfont \interlinepenalty \@M \l_@@_heading_decls_tl{} \bool_if:NTF \l_@@_unnumbered_bool { \tl_set:Nn\l_@@_tmpa_tl { \dim_compare:nNnTF \l_@@_heading_indent_dim < \c_zero_skip { \skip_horizontal:N \l_@@_heading_indent_dim \MakeLinkTarget[\l_@@_name_tl]{} } { \MakeLinkTarget[\l_@@_name_tl]{} \skip_horizontal:N \l_@@_heading_indent_dim } } } { \tl_set:Nn \l_@@_tmpa_tl { \dim_compare:nNnTF \l_@@_heading_indent_dim < \c_zero_skip { \skip_horizontal:N \l_@@_heading_indent_dim \MakeLinkTarget{\l_@@_name_tl} } { \MakeLinkTarget{\l_@@_name_tl} \skip_horizontal:N \l_@@_heading_indent_dim } \IfNoValueF{#2} { { \l_@@_prefix_decls_tl #2 } \nobreak \skip_horizontal:n { \l_@@_prefix_number_sep_tl } } { \l_@@_number_decls_tl #3 } \skip_horizontal:n { \l_@@_number_title_sep_tl } } } \UseTaggingSocket{sec/title/hang} {{\int_use:N\l_@@_level_int}\l_@@_unnumbered_bool{\l_@@_tmpa_tl}{#4}} { \@hangfrom { \l_@@_tmpa_tl } } \l_@@_title_decls_tl \@@_title_format:n {#4} \par \group_end: } % \end{macrocode} % \end{template} % % % \begin{template}{headformat runin-v1} % \begin{macrocode} \DeclareTemplateCode{headformat}{runin-v1}{6} { , heading-indent = \l_@@_heading_indent_dim , title-format = \@@_title_format:n , prefix-number-sep = \l_@@_prefix_number_sep_tl , number-title-sep = \l_@@_number_title_sep_tl } { \@@_show_arguments:nnnnnn {#1}{#2}{#3}{#4}{#5}{#6} \tl_if_empty:oF {#1} { \SetTemplateKeys{headformat}{runin-v1}{#1} } \group_begin: \normalfont \interlinepenalty \@M \l_@@_heading_decls_tl{} \bool_if:NTF \l_@@_unnumbered_bool { \dim_compare:nNnTF \l_@@_heading_indent_dim < \c_zero_skip { \skip_horizontal:N \l_@@_heading_indent_dim \MakeLinkTarget[\l_@@_name_tl]{} } { \MakeLinkTarget[\l_@@_name_tl]{} \skip_horizontal:N \l_@@_heading_indent_dim } } { \dim_compare:nNnTF \l_@@_heading_indent_dim < \c_zero_skip { \skip_horizontal:N \l_@@_heading_indent_dim \MakeLinkTarget{\l_@@_name_tl} } { \MakeLinkTarget{\l_@@_name_tl} \skip_horizontal:N \l_@@_heading_indent_dim } { \UseTaggingSocket{sec/title/number}{\int_use:N\l_@@_level_int} { \IfNoValueF{#2} { { \l_@@_prefix_decls_tl #2 } \nobreak \skip_horizontal:n { \l_@@_prefix_number_sep_tl } } \l_@@_number_decls_tl #3 } } \skip_horizontal:n { \l_@@_number_title_sep_tl } } \l_@@_title_decls_tl \@@_title_format:n {#4} \group_end: } % \end{macrocode} % \end{template} % % % % % % % % % \end{implementation} % % % % \Finale % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% \endinput