% \iffalse meta-comment % %% File: latex-lab-block.dtx (C) Copyright 2021-2026 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 % \def\ltlabblockdate{2026-01-26} \def\ltlabblockversion{0.9m} %<*driver> \DocumentMetadata{tagging=on,pdfstandard=ua-2,lang=en} \documentclass[kernel]{l3in2edoc} \usepackage{amstext} \EnableCrossrefs \CodelineIndex \setcounter{secnumdepth}{4} \setcounter{tocdepth}{4} \usepackage{todonotes} \begin{document} \DocInput{latex-lab-block.dtx} \end{document} % % % \fi % % % % \title{Prototype reimplementation of \LaTeXe{}'s block environments using templates} % \author{\LaTeX{} Project\thanks{Initial reimplementation of lists done by Bruno % Le Floch, generalized second version with tagging support by Frank Mittelbach.}} % \date{v\ltlabblockversion\ \ltlabblockdate} % % \maketitle % % % \newcommand{\xt}[1]{\textsl{\textsf{#1}}} % \newcommand{\TODO}[1]{\textbf{[TODO:} #1\textbf{]}} % \newcommand{\docclass}{document class \marginpar{\raggedright document class % customizations}} % % \providecommand\struct[1]{\texttt{<#1>}} % % \newcommand\key[1]{\texttt{#1}} % \newcommand\keyvalue[1]{\texttt{#1}} % \newcommand\valuefrom[1]{\textrm{value from }\key{#1}} % % \newcommand\instname[1]{\texttt{#1}} % \newcommand\insttype[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}}} % % % \begin{abstract} % \end{abstract} % % % \tableofcontents % \medskip % % % \begin{documentation} % % % % \section{Introduction} % % The list implementation in \LaTeXe{} serves a dual purpose: it % implements real lists such as \env{itemize} or \env{enumerate}, % but it is also used as the basis for vertical blocks, i.e., to specify % the vertical spacing and paragraph handling after such block, e.g., % in environments like \env{center}, \env{quote}, \env{verbatim}, or in % the theorem environments. They are all implemented as % \enquote{trivial} lists with a single (hidden) item. % % While this was convenient to get a consistent layout using a single % implementation it is not adequate if it comes to interpreting the % structure of a document, because environments based on \env{trivlist} % should not advertise themselves as being a \enquote{list} --- after all, % from a semantic point of view they aren't lists. % % The approach taking here is therefore to offer separate template % types: \insttype{block} (horizontally or vertically oriented data that % needs some handling at the start and the end), \insttype{para} (that deals % with different paragraph layouts), \insttype{list} (that handles list % related parameters, and \insttype{item} (for item layouts and handling). % % To address the independent aspects we have the template type % \insttype{blockenv} that ties them together as necessary when we % build document level environments. % % For example, a \env{quote} environment would make use of a (display) % \insttype{block} and some \insttype{para} instance while a standard % \env{enumerate} would make use of a display \insttype{block}, a \insttype{list}, % and an \insttype{item} and a \insttype{para} instance. % An inline list (like \env{enumerate*} from the \pkg{enumitem} % package) would be using the same \insttype{list} instance but a different % (horizontally oriented) \insttype{block} instance build from a % different template. % % Instead of a \insttype{list} instance to handle the inner structure % of the environment one can use an instance of the type % \insttype{captionedtext} to produce a display environment with an % associated heading/caption, such as a theorem-like environment or a % proof environment. Further possibilities (not yet implemented) are % templates for producing boxed text or formal quotes like those % produced by the \pkg{csquotes} package. % % % \section{Template types and templates for blocks and lists} % % \subsection{Template types} % % % \begin{TemplateInterfaceDescription}{blockenv} % \TemplateArgument{1}{key/value list to alter the default parameters of the template % instances used by the particular blockenv environment} % \TemplateArgument{2}{Boolean to suppress a number in case this % environment normally produces a numbered caption} % \TemplateArgument{3}{Caption/heading text in case this environment % supports a caption (most don't), otherwise \cs{NoValue}} % \TemplateArgument{4}{Sub-caption/heading text in case this environment % supports a caption (most don't), otherwise \cs{NoValue}} % \TemplateSemantics % This template type is used to implement document-level % environments. It defines a \insttype{block} instance to handle the % layout at the \enquote{edge} of the environment data, possibly % some paragraph setup through a \insttype{para} instance, potentially an % \enquote{inner} instance for more complicated environments (such % as lists), and possibly some additional setup code for certain % environments. % % Arguments 2--4 are passed to the instance handling the inner % structure, e.g., \insttype{list} or \insttype{captionedtext} % which may or may not make use of it. % % It also defines how the \insttype{blockenv} behaves with respect to % nesting, e.g., does it change when nested and if so how many % levels of nesting are supported, etc. % % Finally, the template type defines how it appears in a tagged PDF % document, what tag names are used, how they are role-mapped and % whether it adds additional attributes, etc. % \end{TemplateInterfaceDescription} % % \begin{TemplateInterfaceDescription}{block} % \TemplateArgument{1}{key/value list to alter the default block parameters} % \TemplateSemantics % Handle the layout aspects of a block of data. In case of a % \enquote{display} block (i.e., vertically oriented) the spacing % and page breaking as well as the handling if the block starts a % paragraph or ends one, that is, if text is immediately following % the block without being separated by an empty line, then this % text is considered to be in the same paragraph as the block. % % In case of a horizontally oriented block it covers any special % handling at the start and end of the block, e.g, extra spacing, % prohibiting or encouraging line breaks, and so forth. % \end{TemplateInterfaceDescription} % % \begin{TemplateInterfaceDescription}{para} % \TemplateArgument{1}{key/value list to alter the default item parameters} % \TemplateSemantics % Sets up paragraph-specific parameters for H\&J, e.g., to % implement justification variations, the behavior of \verb=\\= % etc. The instances are used in higher-level templates, e.g., in a % \insttype{block}. % \end{TemplateInterfaceDescription} % % \begin{TemplateInterfaceDescription}{list} % \TemplateArgument{1}{key/value list to alter the default item parameters} % \TemplateArgument{2}{Boolean to suppress a number in case this % list environment also produces a numbered heading/caption} % \TemplateArgument{3}{Caption/heading text in case this environment % supports a caption (lists normally don't), otherwise \cs{NoValue}} % \TemplateArgument{4}{Sub-caption/heading text in case this environment % supports a caption, otherwise \cs{NoValue}} % \TemplateSemantics % Handle the aspects related to list design, e.g., the use and % formatting of counters, etc. % % Standard \LaTeXe{} lists have no heading/caption, so arguments % 2--4 are ignored in the standard \insttype{list} template. But % special lists, such as a list of ingredients for a cookbook, % might so there might be other templates that make use of them in % the future. % % Note that this template type does not cover block-related % aspects, i.e., a list instance could be used both for a display % list or for an inline list. % \end{TemplateInterfaceDescription} % % \begin{TemplateInterfaceDescription}{captionedtext} % \TemplateArgument{1}{key/value list to alter the default item parameters} % \TemplateArgument{2}{Boolean to suppress a number in case this % environment also produces a numbered heading/caption} % \TemplateArgument{3}{Caption/heading text for this text block; if % not given then \cs{NoValue}} % \TemplateArgument{4}{Sub-caption/heading text in case this environment % supports a caption, otherwise \cs{NoValue}} % \TemplateSemantics % Produces a text block with an associated caption/heading, e.g., a % theorem-like environment. There may not be a user-supplied % caption text---the caption may consist of a fixed text only like ``Lemma''. % % Handles the aspects related to the caption design and typically % supports keys for adjusting the layout of the body text, % e.g., its font, etc. % % Note that this template type does not cover block-related % aspects, e.g., the dimensions of the display block are handled there. % \end{TemplateInterfaceDescription} % % \begin{TemplateInterfaceDescription}{item} % \TemplateArgument{1}{key/value list to alter the default item parameters} % \TemplateSemantics % A sub-type used as part of \insttype{list} to easily cover alternative layout % for list items. % \end{TemplateInterfaceDescription} % % \begin{TemplateInterfaceDescription}{thmstyle} % \TemplateArgument{1}{key/value list to alter the default item parameters} % \TemplateArgument{2}{Boolean to suppress a number in case this % environment also produces a numbered heading/caption} % \TemplateArgument{3}{Caption/heading text for this text block; if % not given then \cs{NoValue}} % \TemplateArgument{4}{Sub-caption/heading text in case this environment % supports a caption, otherwise \cs{NoValue}} % \TemplateSemantics % A sub-type used as part of \insttype{captionedtext} when % producing theorem-like environments. It does the bulk of the work % and sets up most of the formatting. It has been separated out % because many theorem-like environments use the same theorem % layout and only differ in the fixed caption text they % generate. % % Not all templates of type \insttype{captionedtext} use % \insttype{thmstyle} as an inner instance, e.g., proofs are % implemented with a template that does everything necessary % directly. % \end{TemplateInterfaceDescription} % % % % \subsection{Templates} % % % \begin{TemplateDescription}{blockenv}{std} % % \TemplateKey{name}{tokenlist} % {Name of the environment used in tracing and error messages.}{} % \TemplateKey{tag-name}{tokenlist} % {Name of the tag used for the block inside the PDF. If not explicitly given % the name is defined by the \key{tagging-recipe}. Note that in case of % \key{tagging-recipe}\texttt{=basic} no tag for the block is produced, so any % key settings are ignored.}{\meta{empty}} % \TemplateKey{tag-attr-class}{tokenlist} % {An explicit tag class attribute.}{\meta{empty}} % \TemplateKey{tagging-recipe}{tokenlist} % {Defines the way tagging is done. Currently the values % \keyvalue{basic}, \keyvalue{standard}, and \keyvalue{list} % are supported.}{standard} % \TemplateKey{transparent-level}{boolean}{Is this \insttype{blockenv} % transparent for any blocks nested inside?}{false} % \TemplateKey{legacy-code}{tokenlist} % {Legacy setup code. This is % executed after legacy defaults (from \tn{@listi}, % \tn{@listii}, etc.) are used but before the block instance % is called.}{\meta{empty}} % \TemplateKey{block-instance}{tokenlist}{Part of the name of the % \insttype{block} instance that is called. The full name has % a \texttt{-}\meta{level} appended.}{std-display} % \TemplateKey{para-instance}{tokenlist}{Paragraph settings to use % within the environment. If \meta{empty} then the % current (outer) values are % retained. However, the \key{inner-instance} template % might reset/overwrite some of the \instname{para} % values, e.g., \instname{list} makes used of % \cs{listparindent} to explicitly set the paragraph indentation % for compatibility.}{\meta{empty}} % \TemplateKey{inner-level-counter}{tokenlist}{Name of an existing (!) counter % that is incremented and used to determine final name % of the \key{inner-instance} or empty if always the % same inner instance should be used.}{} % \TemplateKey{max-inner-levels}{tokenlist}{Maximum number of nested % environments of this kind. Only relevant if there is a % \key{inner-level-counter} specified.}{4} % \TemplateKey{inner-instance-type}{tokenlist}{Template type of the % inner instance. Currently supported types are % \insttype{list} and \insttype{captionedtext}.}{\meta{empty}} % \TemplateKey{inner-instance}{tokenlist}{Name of the inner instance % (if any). If there is an \key{inner-level-counter} then % the instance name gets \texttt{-}\meta{counter value} % appended.}{\meta{empty}} % \TemplateKey{tagging-suppress-paras}{boolean}{\emph{describe}}{false} % \TemplateKey{final-code}{tokenlist}{Final setup code}{\tn{ignorespaces}} % % \TemplateSemantics % % The \insttype{blockenv} type handles the overall setup for the % document-level environments. % % This \insttype{blockenv} template supports the legacy list setting that % are found in many document classes in the macros \tn{@listi}, % \tn{@listii}, up to \tn{@listvi}. It also uses the counter % \tn{@listdepth} to track nesting of block, again mainly to % support legacy setups (internally it gives it a more appropriate % name but it remains accessible through the \LaTeXe{} name). % % % The internal block nesting level is stored (for historical % reasons) in the \tn{@listdepth} counter and incremented by each % block by one. The starting value at top-level (outside any block) % is zero. A block environment with % \key{transparent-level}\texttt{=true} also increments the level % before it evaluates and sets its parameters but then decrements % it again, just before it starts processing its body. % % The template first checks that the block is not too deeply nested. % % After the level was increased then corresponding \tn{@list...} macro % to update the legacy defaults is called. % % It then sets up the % tagging via the \key{tagging-recipe} setting and executes any % code in \key{legacy-code}. % % Afterwards it calls the appropriate \insttype{block} instance % based on \key{block-instance} and current level, e.g., % \texttt{std-display-1}. % % Then it sets up paragraph parameters if % a \key{para-instance} was specified (otherwise they stay as % they are). % % If a \key{inner-instance} was specified this is called next, % or more precisely: if no \key{inner-level-counter} was % specified the instance \key{inner-instance} is % called. % % Otherwise, the \key{inner-level-counter} is incremented and the % instance with the name % \key{inner-instance}\texttt{-}\key{inner-level-counter} is % called. % % Finally, the \key{final-code} is executed (by default % \tn{ignorespaces}). % % % \end{TemplateDescription} % % The maximum number of \insttype{blockenv}s that can be nested into each % other is restricted by the \LaTeX{} counter % \texttt{maxblocklevels} with a default value of \keyvalue{6}. If this % value is increased then it is necessary to provide additional % instances, e.g., \texttt{std-display-7}, etc. Decreasing is, of % course, always possible, then some of the instances defined are not % used and instead the user gets an error that there is too much % nesting going on. % % If the key \key{transparent-level} is set to \keyvalue{true} then % such an environment alters the nesting level only temporarily (while % processing the \insttype{blockenv} template) and you can therefore % nest those environments as often as you like (a typical example % would be \env{flushleft} anywhere in the nesting hierarchy) as long % as the level isn't already at \texttt{maxblocklevels}). % % % \changes{v0.9l}{2025/11/04}{Fix default for para-vspace, should be the outer \cs{parskip}} % % \begin{TemplateDescription}{block}{std} % % \TemplateKey{begin-vspace}{skip}{Vertical space before the block.}{\tn{topsep}} % \TemplateKey{begin-extra-vspace}{skip}{Extra vertical space before % the block if the block forms its own paragraph.}{\tn{partopsep}} % \TemplateKey{begin-unchained-vspace}{skip}{Vertical space before % the block to use if this is a nested block, both blocks have % items or captions, and these should not be chained; see % description below.}{.5\tn{topsep}} % \TemplateKey{para-vspace}{skip}{The default for ordinary blocks is to use the % \cs{parskip} from the outer galley. In lists and some other special blocks % this is then changed.}{\tn{parskip}} % \TemplateKey{end-vspace}{skip}{Vertical space after the % block.}{\valuefrom{begin-vspace}} % \TemplateKey{end-extra-vspace}{skip}{Extra vertical space after the % block if the block forms its own paragraph.}{\valuefrom{begin-extra-vspace}} % \TemplateKey{item-vspace}{skip}{The space in front of an item if the % block is a list; if not, the setting has no effect.}{\tn{itemsep}} % \TemplateKey{begin-penalty}{integer}{Penalty for breaking before % the block.}{\tn{@beginparpenalty}} % \TemplateKey{end-penalty}{integer}{Penalty for breaking after % the block.}{\tn{@endparpenalty}} % \TemplateKey{item-penalty}{integer}{Penalty for breaking before % an item in the list (except the first).}{\tn{@itempenalty}} % \TemplateKey{left-margin}{length}{Space on the left of the block.}{\tn{leftmargin}} % \TemplateKey{right-margin}{length}{Space on the right of the block.}{\tn{rightmargin}} % \TemplateKey{para-indent}{length}{Paragraph indention for % paragraphs within the block.}{0pt} % % \TemplateSemantics % Sets up the main block parameters, e.g. its spacing before and % after and the indentation on either side. % % It also sets up some parameter defaults for the inner level, % e.g., \texttt{item-penalty}, \texttt{item-vspace} and % \texttt{para-indent}, which may get overwritten by inner % instances that are called. % % The vertical spacing before the block covers four different use % cases: If there is a caption or an item waiting to be placed, and % this item allows for \enquote{chaining}, and the new block also wants % to place an item then no space is added (spacing was already % added by the outer block). Instead, the items are chained and % placed that the start of the block, i.e., producing a layout like the % two nested \env{itemize} environments here: % \begin{itemize} % \item \begin{itemize} % \item A second-level item % \item Another \ldots % \end{itemize} % More text for the first-level item % \item Another first-level item % \end{itemize} % In that case there is also no vertical space after the block. % If the items should not be chained (as specified by the setup of % the outer block), then one gets a result like this one (using % \env{itemize} environments inside \env{description} with % different treatment of individual description \cs{item}s): % \begin{description} % \item[label={An normal label},label-placement=chained] % \begin{itemize} % \item A second-level item % \item Another \ldots % \end{itemize} % More text for the first-level item % \item[label={An unchained label},label-placement=unchained] % \begin{itemize} % \item A second-level item % \item Another \ldots % \end{itemize} % More text for the first-level item % \item[A normal label] Another first-level item % \end{description} % If \enquote{unchaining} happens, as in the second item, then % vertical spacing with the value of \key{begin-unchained-vspace} % is used and at the end you get \key{end-vertical-space}. % % Otherwise, if there is no item or caption waiting to be placed % you get a vertical space of \key{begin-vspace} before the block % and if the block is its own paragraph you additionally get % \key{begin-extra-vspace} added to this. % % Note that \LaTeXe{} always chained the list items, so the ability % to prohibit this is new functionality. % \end{TemplateDescription} % % % \begin{TemplateDescription}{para}{std} % % \TemplateKey{para-indent}{length}{}{\tn{parindent}} % \TemplateKey{begin-hspace}{skip}{Horizontal skip added just in % front of the indentation box if non-zero}{0pt} % \TemplateKey{left-hspace}{skip}{}{0pt} % \TemplateKey{right-hspace}{skip}{}{0pt} % \TemplateKey{end-hspace}{skip}{}{\tn{@flushglue}} % \TemplateKey{fixed-word-spaces}{boolean}{}{false} % \TemplateKey{final-hyphen-demerits}{integer}{}{5000} % \TemplateKey{newline-cmd}{function(0)}{This defines the meaning of % \cs{\bslash}}{\tn{@normalcr}} % \TemplateKey{para-attr-class}{tokenlist}{}{justify} % % \TemplateSemantics % % The \key{begin-hspace} (normally \texttt{0pt}) is the counterpart % of \key{end-hspace} (which is normally \texttt{0pt plus 1fil}). % It can be useful in special paragraph shapes. The skip is only % inserted into the paragraph if it is non-zero. If it is made % non-zero then paragraphs are always at least one line including a % construct like \verb=\noindent\par=! % % TODO: to be further documented % % \end{TemplateDescription} % % % % \begin{TemplateDescription}{list}{std} % % \TemplateKey{counter}{tokenlist} % {Counter name to be used in a numbered list or empty, % if the list is unnumbered.}{\meta{empty}} % \TemplateKey{item-label}{tokenlist} % {Label \enquote{string} for a fixed label or as % generated from the current \key{counter} value.}{\meta{empty}} % \TemplateKey{start}{integer} % {Start value for the counter if the list is numbered, % otherwise irrelevant.}{1} % \TemplateKey{resume}{boolean} % {Should a numbered list be resumed from the last instance?.}{false} % \TemplateKey{item-instance}{instance} % {Instance of type \texttt{item} to be used to format % the label string.}{basic} % \TemplateKey{item-vspace}{skip}{The space in front of an item in the % list. If not specified the value specified in the % block template instance is used.}{} % \TemplateKey{item-penalty}{integer} % {Penalty for breaking before an item (except the % first). If not specified the value specified in the % block template instance is used.}{} % \TemplateKey{item-indent}{length}{Horizontal displacement of the item.}{0pt} % \TemplateKey{label-width}{length} % {Width reserved for the formatted item label.}{\tn{labelwidth}} % \TemplateKey{label-sep}{length} % {Horizontal separation between label and following text.}{\tn{labelsep}} % \TemplateKey{legacy-support}{boolean} % {Is formatting the label via \tn{makelabel} supported?}{false} % % \TemplateSemantics % Sets up handling of list material, e.g., numbering (if any), % layout of items and list elements, and tagging, if requested. % % \end{TemplateDescription} % % % \begin{TemplateDescription}{item}{std} % % \TemplateKey{counter-label}{function{1}}{\emph{unused}.}{\tn{arabic}\{\#1\}} % \TemplateKey{counter-ref}{function{1}}{\emph{unused}.}{\valuefrom{counter-label}} % \TemplateKey{label-ref}{function{1}}{\emph{unused}.}{\#1} % \TemplateKey{label-autoref}{function{1}}{\emph{unused}.}{item~\#1} % \TemplateKey{label-format}{function{1}} % {Formatting of the label, questionable the way it is used.}{\#1} % \TemplateKey{label-strut}{boolean}{Add a \tn{strut} to the label?}{false} % \TemplateKey{label-align}{choice} % {Supported values \keyvalue{left},\keyvalue{center}, % \keyvalue{right}, and \keyvalue{parleft}. \emph{Only partly implemented}.}{right} % \TemplateKey{label-placement}{choice} % {Placement of the label in relation to a directly following label % (of a following inner list). Supported values % are \keyvalue{chained}, \keyvalue{unchained}, and % \keyvalue{standalone}.}{chained} % \TemplateKey{label-boxed}{boolean} % {Should the label be boxed?}{true} % \TemplateKey{next-line}{boolean}{}{false} % \TemplateKey{text-font}{tokenlist}{\emph{unused}.}{} % \TemplateKey{compatibility}{boolean}{}{true} % % \TemplateSemantics % % This template is only rudimentary implemented at the moment. It % probably needs other keys and the existing ones need a proper % implementation!\fmi{fix} % % \end{TemplateDescription} % % % % \begin{TemplateDescription}{captionedtext}{thmlike} % % \TemplateKey{counter}{tokenlist} % {Counter name to be used if the caption is numbered, % otherwise empty.}{\meta{empty}} % \TemplateKey{title}{tokenlist} % {Fixed part of the caption, e.g., a theorem-like % environment may want to specify \enquote{Lemma} here.}{\meta{empty}} % \TemplateKey{style}{instance} % {Instance of type \insttype{thmstyle} that actually % implements the theorem-like environment.}{plain} % \TemplateSemantics % % The template combines the fixed \key{title} and a number (if % present) with the caption text as specified on the document % element, if one is given, e.g., \enquote{Theorem 1. (Fermat)}. % See also the \texttt{proof} template, which handles this % differently. % % The bulk of the work is then outsourced to an instance of % type \insttype{thmstyle}. As many such theorem-like % environments share the same layout and only differ in the first % caption string they use, there is this split for convenience. % \end{TemplateDescription} % % % \begin{TemplateDescription}{captionedtext}{proof} % % \TemplateKey{title}{tokenlist} % {Heading for the environment unless overwritten on % document level.}{Proof} % \TemplateKey{punct}{tokenlist} % {Punctuation following the heading.}{.} % \TemplateKey{caption-placement}{choice} % {Supported values % \keyvalue{chained},\keyvalue{unchained}, and % \keyvalue{standalone}}{unchained} % \TemplateKey{before-hspace}{skip} % {Horizontal displacement of the heading.}{0pt} % \TemplateKey{after-hspace}{skip} % {Space following the heading, only relevant if text % follows on the same line.}{5pt} % \TemplateKey{caption-decls}{tokenlist} % {Declarations that are applied to the whole caption, % e.g., some font settings.}{\meta{empty}} % \TemplateKey{title-format}{function{1}} % {Formatting applied to the \key{title} value.}{\#1} % \TemplateKey{punct-format}{function{1}} % {Formatting applied to the \key{punct} value.}{\#1} % \TemplateKey{body-decls}{tokenlist} % {Declarations that are applied to body of the % environment, e.g., font settings.}{\meta{empty}} % \TemplateSemantics % % The \enquote{unnumbered?} argument (\texttt{\#2}) is ignored, as % proofs aren't numbered. The template makes use of the caption % argument (\texttt{\#3}) but in contrast to theorem-like % environments this template replaces the \key{title} key value with the content of % this argument (if not \cs{NoValue}). % % Typically there is only one layout for proofs so that there is no % need to split the formatting over two templates as done for % theorem-like environment. That's the reason why the template has % several layout customization parameters. % \end{TemplateDescription} % % % \begin{TemplateDescription}{thmstyle}{std} % % \TemplateKey{numbered}{boolean} % {Is this kind of environment numbered?}{true} % \TemplateKey{space}{tokenlist} % {Space to be applied between elements of the % heading}{\textbackslash\textvisiblespace } % \TemplateKey{punct}{tokenlist} % {Punctuation following the heading.}{.} % \TemplateKey{caption-placement}{choice} % {Supported values % \keyvalue{chained},\keyvalue{unchained}, and % \keyvalue{standalone}}{unchained} % \TemplateKey{before-hspace}{skip} % {Horizontal displacement of the heading.}{0pt} % \TemplateKey{after-hspace}{skip} % {Space following the heading, only relevant if text % follows on the same line.}{5pt} % \TemplateKey{order}{commalist} % {Order of elements in the environment % caption/heading. Supported values are \texttt{title}, % \texttt{number}, \texttt{punct}, \texttt{space}, and \texttt{note}.}{title,space,number,space,note} % \TemplateKey{caption-decls}{tokenlist} % {Declarations that are applied to the whole caption, % e.g., some font settings.}{\meta{empty}} % \TemplateKey{title-format}{function{1}} % {Formatting applied to the \key{title} value.}{\#1} % \TemplateKey{number-format}{function{1}} % {Formatting applied to the \key{number} value.}{\#1} % \TemplateKey{punct-format}{tokenlist} % {Formatting applied to the \key{punct} value.}{\#1} % \TemplateKey{note-format}{function{1}} % {Formatting applied to the \key{note} value.}{(\#1)} % \TemplateKey{body-decls}{tokenlist} % {Declarations that are applied to body of the % environment, e.g., font settings.}{\meta{empty}} % % \TemplateSemantics % % Numbering of the environment is suppressed unconditionally if the % \key{numbered} is set to \keyvalue{false}. Otherwise the % environment is numbered except when \texttt{\#2} is % \cs{BooleanTrue}, i.e., if the star form of the environment was % used. % % The caption of the environment can consist of a title, a number, % a punctuation, some spaces and a note. Their order is defined by % the key \key{order}. If a component is specified but has no % value, e.g., no note or the numbering suppressed on an individual % environment, then the component and any preceding spaces are % ignored. % % Spaces between elements are uniform (as one can only specify % \texttt{space} in the \key{order} key, but it is possible to % use this several times in a row and adjust the \key{space} key % accordingly. % % Alternatively, one can omit using \texttt{space} in the % \key{order} key and instead put all necessary spacing into the % individual \key{...-format} keys. This approach is used, for example, if a % theorem style is set up with \cs{newtheoremstyle} and its ninth % argument contains a declaration such as %\begin{verbatim} % \thmname{#1}\thmnumber{ #2}\thmnote{ (#3)} %\end{verbatim} % This is then translated to %\begin{verbatim} % order = {title,number,punct,note} , % title-format = {#1} , % number-format = { #2} , % note-format = { (#3)} , %\end{verbatim} % when \cs{newtheoremstyle} sets up a new instance. % The downside of this approach is that \cs{swapnumbers} would not % work with such styles (because it would be necessary to transfer the % space inside value for the \key{number-format} key to the value % of \key{title-format}). % % If you look closely you also see that in the \key{order} key a % \texttt{punct} was added in the list even though it was not present % originally. This is they way \cs{newtheoremstyle} worked and so % we mimic that. % \end{TemplateDescription} % % % % % \section{Declaring standard display block environments and their instances} % % Historically the \LaTeX{} kernel has defined a number of block % environments directly, e.g., \env{center} or lists like % \env{itemize}, but left others to be set up by document % classes. For now we declare all of them here, but in the future, % some (or even all) might get moved to new class files. % % \noindent % \DescribeMacro\SimpleBlockEnv % Most of the standard block environments have no need for a caption, % so to simplify the setup we have added the command % \cs{SimpleBlockEnv} that hides the arguments 2--4 required by a % \insttype{blockenv} instance and gives them suitable values, i.e., % \cs{BooleanFalse}\cs{NoValue}\cs{Novalue}. This way, a document % level definition for the \env{center} environment will look like this: % \begin{verbatim} % \NewDocumentEnvironment{center} { !O{} } % { \SimpleBlockEnv{center}{#1} } { \BlockEnvEnd } % \end{verbatim} % instead of the more verbose % \begin{verbatim} % \NewDocumentEnvironment{center} { !O{} } % { \UseInstance{blockenv}{center}{#1} \BooleanFalse \NoValue \NoValue } % { \BlockEnvEnd } % \end{verbatim} % % We use \verb=!O{}= for the optional argument so that it is only % recognized if it immediately follows \verb=\begin{center}= without % any spaces to avoid that a \texttt{[} at the start of the body text % is misinterpreted as the opening bracket of the optional % argument. This is only done for environments where this could be a % problem. % % This will then call the \texttt{center} instance of type % \insttype{blockenv} that handles the rest. % % \noindent % \DescribeMacro\BlockEnv % \DescribeMacro\BlockEnvEnd % For the environments that make use of the other arguments, we % offer \cs{BlockEnv} as syntactic sugar so that most environment % declarations look similar. And we use \cs{BlockEnvEnd} in both % cases to finish off. % \begin{macrocode} %<*class-code> % \end{macrocode} % % In the following sections we provide for all block environments % the top-level definition and all instances that are used by % it. Instances of type \insttype{block} are often reused across % the environments, in which case we just provide % cross-references. Note that this is a design decision, different % classes my want to have adjusted settings for individual % environments, in which case they would provide special % \insttype{block} instances instead of reusing, say, the % \instname{std-display-\meta{level}} instances. % % % \subsection{The \env{display} and \env{displayflattened} environments} % % % \begin{environment}{displayblock,displayblockflattened} % There are two basic block environments (\env{displayblock} and % \env{displayblockflattened}) which are similar to \LaTeXe{}'s % \env{trivlist} except that they aren't degenerated lists and % thus have no hidden \tn{item} inside. % % \begin{macrocode} \NewDocumentEnvironment{displayblock}{ !O{} } { \SimpleBlockEnv{displayblock} {#1} } { \BlockEnvEnd } % \end{macrocode} % % \begin{macrocode} \NewDocumentEnvironment{displayblockflattened}{ !O{} } { \SimpleBlockEnv{displayblockflattened} {#1} } { \BlockEnvEnd } % \end{macrocode} % \end{environment} % % \subsubsection{Their \insttype{blockenv} instances} % % \begin{instance}{blockenv displayblock} % This is like \LaTeXe{}'s \env{trivlist}, i.e., it produces a % vertical block with default setting, but doesn't put a list % inside but uses a \struct{Div} structure. % % We list all keys, those with default values, commented out. % \begin{macrocode} \DeclareInstance{blockenv}{displayblock}{std} { name = displayblock % ,tagging-recipe = standard % ,tag-name = % ,tag-attr-class = ,transparent-level = true % ,legacy-code = % ,block-instance = std-display % ,para-instance = % ,tagging-suppress-paras = false % ,inner-instance = % ,inner-instance-type = % not relevant as there is no inner instance % ,inner-level-counter = % not relevant as there is no inner instance % ,max-inner-levels = 4 % not relevant as there is no inner instance % ,final-code = \ignorespaces } % \end{macrocode} % % The \insttype{block} uses the instance \instname{std-display} % which is shown below. % \end{instance} % % % % \begin{instance}{blockenv displayblockflattened} % This flattens inner paragraphs without any surrounding tag % structure by using the \keyvalue{basic} tagging recipe. % \begin{macrocode} \DeclareInstance{blockenv}{displayblockflattened}{std} { name = displayblockflattened ,tagging-recipe = basic ,tagging-suppress-paras = true ,transparent-level = true } % \end{macrocode} % \end{instance} % % % % \subsubsection{Their \insttype{block} instances} % \label{sec:std-display} % % We provide 6 nesting levels (as in \LaTeXe{}). If you want to % provide more you need to change the \texttt{maxblocklevels} % counter, offer further \texttt{std-display-\meta{level}} instances but % also define further (legacy) \tn{list\meta{romannumeral}} commands % for the defaults. If not, then the settings from the previous % level are reused automatically---which may or may not be good enough). % \begin{macrocode} \setcounter{maxblocklevels}{6} % \end{macrocode} % % \begin{instance}{block std-display-1, % block std-display-2, % block std-display-3, % block std-display-4, % block std-display-5, % block std-display-6 } % ^^A % We show all keys here for reference, with those using their % default values commented out: % \begin{macrocode} \DeclareInstance{block}{std-display-1}{std} { % ,begin-vspace = \topsep % ,begin-extra-vspace = \partopsep % ,para-vspace = \parskip % ,end-vspace = \KeyValue{begin-vspace} % ,end-extra-vspace = \KeyValue{begin-extra-vspace} % ,item-vspace = \itemsep % ,begin-penalty = \UseName{@beginparpenalty} % ,end-penalty = \UseName{@endparpenalty} ,left-margin = 0pt % ,right-margin = \rightmargin % ,para-indent = 0pt } % \end{macrocode} % % \begin{macrocode} \DeclareInstanceCopy{block}{std-display-2}{std-display-1} \DeclareInstanceCopy{block}{std-display-3}{std-display-1} \DeclareInstanceCopy{block}{std-display-4}{std-display-1} \DeclareInstanceCopy{block}{std-display-5}{std-display-1} \DeclareInstanceCopy{block}{std-display-6}{std-display-1} % \end{macrocode} % \end{instance} % % % % % % % % \subsection{The \env{center}, \env{flushleft}, and \env{flushright} environments} % % All three environments use the \instname{std-display} instance as % block instance. They only differ in the choice of para instance. % % \begin{environment}{center,flushleft,flushright} % For now we redeclare various document environments as late as % possible in order to make tagging work, even if classes have changed % the definitions. Of course, this means that such changes get lost. % \begin{macrocode} \AddToHook{begindocument/before}[./legacy-core]{ % \end{macrocode} % % \begin{macrocode} \RenewDocumentEnvironment{center} { !O{} } { \SimpleBlockEnv{center}{#1} } { \BlockEnvEnd } % \end{macrocode} % % \begin{macrocode} \RenewDocumentEnvironment{flushright} { !O{} } { \SimpleBlockEnv{flushright}{#1} } { \BlockEnvEnd } % \end{macrocode} % % \begin{macrocode} \RenewDocumentEnvironment{flushleft} { !O{} } { \SimpleBlockEnv{flushleft}{#1} } { \BlockEnvEnd } } % \end{macrocode} % \end{environment} % % % \subsubsection{Their \insttype{blockenv} instances} % % \begin{instance}{blockenv center} % The \env{center} environment is defined through the % \insttype{blockenv} instance \instname{center} which makes use of % the \insttype{block} instance \instname{std-display-\meta{level}} % and the \insttype{para} instance \instname{center}. The block % nesting level is not incremented. With respect to tagging, text % separated by \cs{par} commands (or empty lines) inside the % environment is not tagged as separate paragraphs, i.e., the whole % environment is considered to be part of an outer paragraph. % \begin{macrocode} \DeclareInstance{blockenv}{center}{std} { name = center ,tag-name = ,tag-attr-class = ,tagging-recipe = basic ,tagging-suppress-paras = true ,inner-level-counter = ,transparent-level = true ,legacy-code = ,block-instance = std-display ,para-instance = center ,inner-instance = } % \end{macrocode} % \end{instance} % % \begin{instance}{blockenv flushleft} % Same as \instname{center} except that we use the % \insttype{para} instance \instname{raggedright}. % \begin{macrocode} %\DeclareInstance{blockenv}{flushleft}{std} %{ % name = flushleft % ,tag-name = % ,tag-attr-class = % ,tagging-recipe = basic % ,tagging-suppress-paras = true % ,inner-level-counter = % ,transparent-level = true % ,legacy-code = % ,block-instance = std-display % ,para-instance = raggedright % ,inner-instance = %} % \end{macrocode} % Or more concise in the source and perhaps even faster in % processing if only few keys are changed: % \begin{macrocode} \DeclareInstanceCopy{blockenv}{flushleft}{center} \EditInstance{blockenv}{flushleft}{ name = flushleft ,para-instance = raggedright } % \end{macrocode} % \end{instance} % % % \begin{instance}{blockenv flushright} % Same game for \instname{flushright}. % \begin{macrocode} \DeclareInstanceCopy{blockenv}{flushright}{center} \EditInstance{blockenv}{flushright}{ name = flushright ,para-instance = raggedleft } % \end{macrocode} % \end{instance} % % % % \subsubsection{Their \insttype{block} instances} % % They all use the \insttype{block} instances \instname{std} % which have already been set up in section~\ref{sec:std-display}. % % % \subsubsection{Their \insttype{para} instances} % % Formatting of paragraphs is handled through the % \key{para-instance} key which either refers to a % instance of type \insttype{para} or is empty, in which case the % handling of paragraphs is inherited. The predefined instances are % discussed in section~\ref{sec:para-instances}. % % % % \subsection{The \env{quote} and \env{quotation} environments} % % \LaTeXe{} has two environments for quoting: \env{quote} and % \env{quotation}. By default they differ only in indentation of % inner paragraphs. This is handled by using separate block % instances. The paragraph setup is inherited. The block nesting % level is incremented. % % The tag names are both role-mapped to \struct{BlockQuote}. % % \begin{environment}{quote,quotation} % We can't use \cs{RenewDocumentEnvironment} for \env{quote} and % other environments that are class defined, because some classes % aren't implementing them at all. So we use % \cs{DeclareDocumentEnvironment} instead. This problem will vanish % if all such definitions move in new versions of the classes instead. % % \changes{v0.9l}{2025/11/20}{Use \cs{DeclareDocumentEnvironment} for \env{quote}, % \env{quotation}, \env{description}, and \env{verse} (tagging/284)} % \begin{macrocode} \AddToHook{begindocument/before}[./legacy-quotes]{ \DeclareDocumentEnvironment{quote}{ !O{} } { \SimpleBlockEnv{quote} {#1} } { \BlockEnvEnd } % \end{macrocode} % % \begin{macrocode} \DeclareDocumentEnvironment{quotation}{ !O{} } { \SimpleBlockEnv{quotation} {#1} } { \BlockEnvEnd } } % \end{macrocode} % \end{environment} % % \subsubsection{Their \insttype{blockenv} instances} % % \begin{instance}{blockenv quotation} % For the \env{quotation} environment: % \begin{macrocode} \DeclareInstance{blockenv}{quotation}{std} { name = quotation ,tag-name = \UseStructureName{block/quotation} ,tag-attr-class = ,tagging-recipe = standard ,inner-level-counter = ,transparent-level = false ,legacy-code = ,block-instance = quotation ,inner-instance = } % \end{macrocode} % \end{instance} % % \begin{instance}{blockenv quote} % For the \env{quote} environment: % \begin{macrocode} \DeclareInstance{blockenv}{quote}{std} { name = quote ,tag-name = \UseStructureName{block/quote} ,tag-attr-class = ,tagging-recipe = standard ,inner-level-counter = ,transparent-level = false ,legacy-code = ,block-instance = quote ,inner-instance = } % \end{macrocode} % \end{instance} % % % % % \subsubsection{Their \insttype{block} instances} % % \begin{instance}{block quote-1, % block quote-2, % block quote-3, % block quote-4, % block quote-5, % block quote-6 } % Default layout is to indent equally from both sides. % \begin{macrocode} \DeclareInstance{block}{quote-1}{std} { right-margin = \KeyValue{left-margin} } % \end{macrocode} % % \begin{macrocode} \DeclareInstanceCopy{block}{quote-2}{quote-1} \DeclareInstanceCopy{block}{quote-3}{quote-1} \DeclareInstanceCopy{block}{quote-4}{quote-1} \DeclareInstanceCopy{block}{quote-5}{quote-1} \DeclareInstanceCopy{block}{quote-6}{quote-1} % \end{macrocode} % \end{instance} % % % % \begin{instance}{block quotation-1, % block quotation-2, % block quotation-3, % block quotation-4, % block quotation-5, % block quotation-6 } % Quotation additionally changes the \key{para-indent}. % \begin{macrocode} \DeclareInstance{block}{quotation-1}{std} { para-indent = 1.5em , right-margin = \KeyValue{left-margin} } % \end{macrocode} % % \begin{macrocode} \DeclareInstanceCopy{block}{quotation-2}{quotation-1} \DeclareInstanceCopy{block}{quotation-3}{quotation-1} \DeclareInstanceCopy{block}{quotation-4}{quotation-1} \DeclareInstanceCopy{block}{quotation-5}{quotation-1} \DeclareInstanceCopy{block}{quotation-6}{quotation-1} % \end{macrocode} % \end{instance} % % % % % % % \subsection{The \env{verse} environment} % % The \env{verse} environment of \LaTeX{} is intended for % poetry. Not sure what that should mean with respect to % tagging. % % \begin{environment}{verse} % Implementation is like \env{quote} etc. % \begin{macrocode} \AddToHook{begindocument/before}[./legacy]{ \DeclareDocumentEnvironment{verse}{!O{}} { \SimpleBlockEnv{verse} {#1} } { \BlockEnvEnd } } % \end{macrocode} % \end{environment} % % \subsubsection{Their \insttype{blockenv} instances} % % \begin{instance}{blockenv verse} % \begin{macrocode} \DeclareInstance{blockenv}{verse}{std} { name = verse ,tag-name = \UseStructureName{block/verse} ,tag-attr-class = ,tagging-recipe = standard ,inner-level-counter = ,transparent-level = false ,legacy-code = ,block-instance = quote % reuse? ,para-instance = verse ,inner-instance = } % \end{macrocode} % The special indentation on continuation lines (the way \LaTeX{} % handled poetry is done in the \insttype{para} instance % \instname{verse}, defined later on. % \end{instance} % % % % % % \subsection{The \env{verbatim}, \env{verbatim*} and \env{alltt} environments} % % % \begin{environment}{verbatim,verbatim*} % Here are the definitions for the verbatim environments They look % somewhat different than others (but this isn't the final % definition). % At the moment we use 2 optional arguments, the second is only % there so that there is yet another scan even if one optional % argument got detected. That then scans away the newline so that % afterwards we can reinsert one via \cs{obeyedline}. A better % solution will be to use a \texttt{c} specifier for grabbing the % body, but that is for another day not Christmas Eve.\fmi{fix} % \changes{v0.9f}{2025/06/21}{Move special verbatim code into a key} % \begin{macrocode} \AddToHook{begindocument/before}[./legacy-verbatims]{ \RenewDocumentEnvironment{verbatim}{ ={legacy-code} !o !o } { \SimpleBlockEnv{verbatim} {#1} \obeyedline } { \BlockEnvEnd } % \end{macrocode} % % \changes{v0.9f}{2025/06/21}{Use separate blockenv instances for % verbatim and verbatim*} % \begin{macrocode} \RenewDocumentEnvironment{verbatim*}{ ={legacy-code} !o !o } { \SimpleBlockEnv{verbatim*} {#1} \obeyedline } { \BlockEnvEnd } % \end{macrocode} % \end{environment} % % % \begin{environment}{alltt,alltt*} % The \pkg{alltt} package implements a variation on verbatim % handling where backslash and braces retain their normal % meanings. We also reimplement it using the template approach % The \env{alltt*} variant didn't exist in the package, % but it is trivial to set it up as well.\fmi{The parsing here % should be adjusted as well, eventually.} % \begin{macrocode} \NewDocumentEnvironment{alltt}{ ={legacy-code} !o } { \SimpleBlockEnv{alltt} {#1} } { \BlockEnvEnd } \NewDocumentEnvironment{alltt*}{ ={legacy-code} !o } { \SimpleBlockEnv{alltt*} {#1} } { \BlockEnvEnd } } % \end{macrocode} % \end{environment} % % \subsubsection{Their \insttype{blockenv} instances} % % \begin{instance}{blockenv verbatim} % The \env{verbatim} environment is defined through % \insttype{blockenv} instance \instname{verbatim} that makes use % of the \insttype{block} instance % \instname{verbatim-\meta{level}} and the \insttype{para} % instance \instname{justify}. The block nesting level is not % incremented. Verbatim processing requires various catcode % changes, etc.\ and as a consequence a special parsing routine % that grabs the whole environment while these catcodes are in % force. This setup is done in the \key{final-code} key and its % last action is to initiate the special parsing. % % \changes{v0.8x}{2025/01/12}{Use flattened para inside verbatim} % \begin{macrocode} \DeclareInstance{blockenv}{verbatim}{std} { name = verbatim ,tag-name = \UseStructureName{block/verbatim} ,tag-attr-class = ,tagging-recipe = standard ,tagging-suppress-paras = true ,inner-level-counter = ,transparent-level = true ,legacy-code = ,block-instance = verbatim ,inner-instance = ,para-instance = justify % \end{macrocode} % Here is where \env{verbatim} and \env{verbatim*} technically % differ: in the former we set up spaces to become nonbreakable % spaces (if necessary followed by a \tn{pdffakespace} in the % pdf\TeX{} engine) % and in \env{verbatim*} we set it up to generate visible space chars. % \changes{v0.9f}{2025/06/21}{Move special verbatim code into a key} % \begin{macrocode} ,final-code = \legacyverbatimsetup{invisible} % \end{macrocode} % Then we start the special scanning process to look for % \verb=\end{verbatim}= with special catcodes and grab everything % in between. For \env{verbatim*} we use \cs{@sxverbatim} to look % for \verb=\end{verbatim*}= instead.\footnote{Perhaps there should % be some other command names for this?} % \begin{macrocode} \@xverbatim } % \end{macrocode} % \end{instance} % % The role-mapping is \struct{verbatim} to \struct{Code} and % \struct{codeline} to \struct{Sub} (which is role mapped % to \struct{Span} in pdf 1.7). Sub inside Code is allowed according % the errata of ISO 32005. The paragraphs inside verbatim are flattened. % Line numbers should be inside the \struct{codeline} structure % and be tagged either as \struct{Lbl} or \struct{Artifact}\struct{Lbl}. % % % \begin{instance}{blockenv verbatim*} % The implementation of \env{verbatim*} is similar using the % \insttype{blockenv} instance \instname{verbatim*}. Its % \key{final-code} sets up visible spaces and a slightly different % parsing that grabs everything up to % \verb=\end{verbatim*}=. Otherwise the setup is identical. % % \changes{v0.9f}{2025/06/21}{Use separate blockenv instances for % verbatim and verbatim*} % \begin{macrocode} \DeclareInstance{blockenv}{verbatim*}{std} { name = verbatim ,tag-name = \UseStructureName{block/verbatim} ,tag-attr-class = ,tagging-recipe = standard ,tagging-suppress-paras = true ,inner-level-counter = ,transparent-level = true ,legacy-code = ,block-instance = verbatim ,inner-instance = ,para-instance = justify ,final-code = \legacyverbatimsetup{visible} \@sxverbatim } % \end{macrocode} % \end{instance} % % \begin{instance}{blockenv alltt} % The implementation of the \env{alltt} environment from the % \pkg{alltt} is more or less identical as well. % We just need a slightly different final code to keep backslash % and braces functional. % % \begin{macrocode} \DeclareInstance{blockenv}{alltt}{std} { name = alltt ,tag-name = \UseStructureName{block/verbatim} % private tag instead? ,tag-attr-class = ,tagging-recipe = standard ,tagging-suppress-paras = true ,inner-level-counter = ,transparent-level = true ,legacy-code = ,block-instance = verbatim ,inner-instance = ,para-instance = justify % \end{macrocode} % Now set up the special environment settings with most characters % verbatim. % We don't even have to scan ahead for the \verb=\end{alltt}= % because backslash and braces still have their normal meaning. % \begin{macrocode} ,final-code = \legacyallttsetup {invisible} } % \end{macrocode} % \end{instance} % % % \begin{instance}{blockenv alltt*} % The \env{alltt*} variant didn't exist in the \pkg{alltt} package, % but it is trivial to set it up as well. % \begin{macrocode} \DeclareInstance{blockenv}{alltt*}{std} { name = alltt* ,tag-name = \UseStructureName{block/verbatim} % private tag instead? ,tag-attr-class = ,tagging-recipe = standard ,tagging-suppress-paras = true ,inner-level-counter = ,transparent-level = true ,legacy-code = ,block-instance = verbatim ,inner-instance = ,para-instance = justify ,final-code = \legacyallttsetup {visible} } % \end{macrocode} % \end{instance} % % % % \subsubsection{Their \insttype{block} instances} % % % \begin{instance}{block verbatim-1, % block verbatim-2, % block verbatim-3, % block verbatim-4, % block verbatim-5, % block verbatim-6 } % Verbatim instances have there own levels so that one can specify % specific indentations or vertical separations between lines. % \begin{macrocode} \DeclareInstance{block}{verbatim-1}{std} { ,left-margin = 0pt ,para-vspace = 0pt } % \end{macrocode} % % \begin{macrocode} \DeclareInstanceCopy{block}{verbatim-2}{verbatim-1} \DeclareInstanceCopy{block}{verbatim-3}{verbatim-1} \DeclareInstanceCopy{block}{verbatim-4}{verbatim-1} \DeclareInstanceCopy{block}{verbatim-5}{verbatim-1} \DeclareInstanceCopy{block}{verbatim-6}{verbatim-1} % \end{macrocode} % \end{instance} % % % % % \subsection{The \env{trivlist} environment} % % In \LaTeXe{} \env{trivlist} was used to define various display % environments that aren't really lists at all. To support such % legacy definitions (even though they should be updated to achieve % proper tagging) we continue to support and implement it as a % \env{list} environment with a few hardwired settings mimicking % the original behavior.\fmi{maybe we should simply implement it as % a \instname{displayblock} instance (at least when doing tagging) % - decide} % % % % % \subsection{The standard lists: \env{itemize}, \env{enumerate}, and \env{description}} % % % \begin{environment}{itemize,enumerate,description} % For the standard lists everything is managed by the blockenv % instances. % \begin{macrocode} \AddToHook{begindocument/before}[./legacy-lists]{ \RenewDocumentEnvironment{itemize}{!O{}} { \SimpleBlockEnv{itemize} {#1} } { \BlockEnvEnd } % \end{macrocode} % % \begin{macrocode} \RenewDocumentEnvironment{enumerate}{!O{}} { \SimpleBlockEnv{enumerate} {#1} } { \BlockEnvEnd } % \end{macrocode} % % \begin{macrocode} \DeclareDocumentEnvironment{description}{!O{}} { \SimpleBlockEnv{description} {#1} } { \BlockEnvEnd } } % \end{macrocode} % \end{environment} % % \subsubsection{Their \insttype{blockenv} instances} % % \begin{instance}{blockenv itemize} % The \env{itemize} environment is defined through the % \insttype{blockenv} instance \instname{itemize} which makes use % of the \insttype{block} instance \instname{list-\meta{level}}, % and an inner instance \instname{itemize-\meta{inner-level}} of % type \insttype{list}. The paragraph setup is % inherited.\footnote{In the \LaTeXe{} implementation justified % paragraphs where forced, even if the whole document was set in % ragged text. If this slightly strange behavior is desired then one has to set % the \key{para-instance} key to \keyvalue{justify}.} The % \meta{inner-level} is determined through \cs{@itemdepth}. The % block nesting level and the inner list nesting level are % incremented. % \begin{macrocode} \DeclareInstance{blockenv}{itemize}{std} { name = itemize ,tag-name = \UseStructureName{block/itemize} ,tag-attr-class = itemize ,tagging-recipe = list ,inner-level-counter = \@itemdepth ,transparent-level = false ,max-inner-levels = 4 ,legacy-code = ,block-instance = std-list ,inner-instance-type = list ,inner-instance = itemize ,para-instance = } % \end{macrocode} % \end{instance} % % \begin{instance}{blockenv enumerate} %^^A % The \env{enumerate} environment is similar to \env{itemize} but % uses the \insttype{blockenv} instance \instname{enumerate}, the % \insttype{block} instance \instname{list-\meta{level}}, and the % inner instance \instname{enumerate-\meta{inner-level}}. The % \meta{inner-level} is determined through \cs{@enumdepth}. % % \begin{macrocode} \DeclareInstance{blockenv}{enumerate}{std} { name = enumerate ,tag-name = \UseStructureName{block/enumerate} ,tag-attr-class = enumerate ,tagging-recipe = list ,transparent-level = false ,max-inner-levels = 4 ,legacy-code = ,block-instance = std-list ,inner-level-counter = \@enumdepth ,inner-instance-type = list ,inner-instance = enumerate } % \end{macrocode} % \end{instance} % % % \begin{instance}{blockenv description} %^^A % The \env{description} environment uses the \insttype{blockenv} % instance \instname{description}, the \insttype{block} instance % \instname{list-\meta{level}}, and the inner instance % \instname{description} (no dependency on the nesting level), % i.e., the environment has the same appearance on all nesting % levels. % % \begin{macrocode} \DeclareInstance{blockenv}{description}{std} { name = description ,tag-name = \UseStructureName{block/description} ,tag-attr-class = description ,tagging-recipe = list ,inner-level-counter = ,transparent-level = false ,legacy-code = ,block-instance = std-list ,inner-instance-type = list ,inner-instance = description } % \end{macrocode} % \end{instance} % % % % % \subsubsection{Their \insttype{block} instances} % % \begin{instance}{block std-list-1, % block std-list-2, % block std-list-3, % block std-list-4, % block std-list-5, % block std-list-6 } % The block instances for the various list environments use the % same underlying instance (well, by default) and nothing % needs to be set up specifically (because that is already done in % the legacy \tn{list\meta{romannumeral}} unless a % different layout is wanted. % % \begin{macrocode} \DeclareInstance{block}{std-list-1}{std}{ % begin-vspace = \topsep % ,begin-extra-vspace = \partopsep % \end{macrocode} % This is the only one we have to explicitly set for lists if the default % setup is wanted. % \changes{v0.9l}{2025/11/04}{Fix default for para-vspace, should be % \cs{parsep} (only) inside lists} % \begin{macrocode} ,para-vspace = \parsep % ,end-vspace = \KeyValue{begin-vspace} % ,end-extra-vspace = \KeyValue{begin-extra-vspace} % ,item-vspace = \itemsep % ,begin-penalty = \UseName{@beginparpenalty} % ,end-penalty = \UseName{@endparpenalty} % ,left-margin = \leftmargin % ,right-margin = \rightmargin % ,para-indent = 0pt } % \end{macrocode} % % \begin{macrocode} \DeclareInstanceCopy{block}{std-list-2}{std-list-1} \DeclareInstanceCopy{block}{std-list-3}{std-list-2} \DeclareInstanceCopy{block}{std-list-4}{std-list-3} \DeclareInstanceCopy{block}{std-list-5}{std-list-4} \DeclareInstanceCopy{block}{std-list-6}{std-list-5} % \end{macrocode} % % If the legacy \tn{list\meta{romannumeral}} is not used in a % modern class then, of course, these instances all need to set up % the different parameters explicitly. The new implementation of % the standard classes (will) show that approach. % \end{instance} % % % % \subsubsection{Their \insttype{list} instances} % % For all list instances we have to say what kind of label we want % (\key{item-label}) and how it should be formatted. % % \begin{instance}{list itemize-1, % list itemize-2, % list itemize-3, % list itemize-4} % For \env{itemize} environments this is all we need to do and we % refer back to the external definitions rather than defining the % \key{item-label} code in the instance to ensure that old % documents still work. % % \begin{macrocode} \DeclareInstance{list}{itemize-1}{std}{ item-label = \labelitemi } \DeclareInstance{list}{itemize-2}{std}{ item-label = \labelitemii } \DeclareInstance{list}{itemize-3}{std}{ item-label = \labelitemiii } \DeclareInstance{list}{itemize-4}{std}{ item-label = \labelitemiv } % \end{macrocode} % \end{instance} % % % % \begin{instance}{list enumerate-1, % list enumerate-2, % list enumerate-3, % list enumerate-4} % \env{enumerate} environments are similar, except that we also % have to say which counter to use on each level. % \begin{macrocode} \DeclareInstance{list}{enumerate-1}{std} { item-label = \labelenumi , counter = enumi } \DeclareInstance{list}{enumerate-2}{std} { item-label = \labelenumii , counter = enumii } \DeclareInstance{list}{enumerate-3}{std} { item-label = \labelenumiii , counter = enumiii } \DeclareInstance{list}{enumerate-4}{std} { item-label = \labelenumiv , counter = enumiv } % \end{macrocode} % \end{instance} % % % \begin{instance}{list description} % The \env{description} lists also use only a single list instance % with only one key not using the default: % \begin{macrocode} \DeclareInstance{list}{description}{std} { item-instance = description } % \end{macrocode} % Of course, if handling of description lists should differ in % nested lists all one has to do is to provide an % \key{inner-level-counter} and then define % \instname{description-1}, \instname{description-2}, etc. % \end{instance} % % % \subsubsection{Their \insttype{item} instances} % % \begin{instance}{item basic, item description} % There are two item instances to set up: \keyvalue{description} for use % with the \env{description} environment and \keyvalue{basic} for use % with all other lists (up to now). % \begin{macrocode} \DeclareInstance{item}{basic}{std} { label-align = right } % \end{macrocode} % % \begin{macrocode} \DeclareInstance{item}{description}{std} { ,label-format = \normalfont\bfseries #1 ,label-align = left } % \end{macrocode} % \end{instance} % % % % % \subsection{The legacy \env{list} and \env{trivlist} environments} % % \begin{environment}{list} % The legacy 2e list environment is more complicated as we have to get the % extra arguments accounted for. % \begin{macrocode} \AddToHook{begindocument/before}[./legacy]{ \RenewDocumentEnvironment{list}{O{} m m } { % \end{macrocode} % We do this by storing them away and then call the list % instance. Inside this instance the \key{legacy-code} key % contains \tn{legacylistsetup} which makes use of the stored values. % \changes{v0.8v}{2024/10/11}{Update \tn{@itemlabel} in % \cs{l_@@_legacy_env_params_tl} and not at the start of the % environment (tagging/730)} % \begin{macrocode} \tl_set:Nn \l_@@_legacy_env_params_tl { \tl_set:Nn \@itemlabel {#2} #3 } % \end{macrocode} % The \LaTeXe{} lists don't support captions so we use \cs{SimpleBlockEnv}. % \begin{macrocode} \SimpleBlockEnv{list} {#1} } { \BlockEnvEnd } } % \end{macrocode} % \end{environment} % % % % \begin{environment}{trivlist} % \LaTeXe{} defined \env{trivlist} as an implementation of % \env{list} (or rather the other way around).\fmi{Replace with % code not using \tn{list}} % \begin{macrocode} \AddToHook{begindocument/before}[./legacy]{ \RenewDocumentEnvironment{trivlist}{ !O{} } { \list[#1]{} { \dim_zero:N \leftmargin \dim_zero:N \labelwidth \cs_set_eq:NN \makelabel \use:n } } { \BlockEnvEnd } } % \end{macrocode} % \end{environment} % % % % \subsubsection{Its \insttype{blockenv} instance} % % \begin{instance}{blockenv list} % The generic \env{list} environment of \LaTeXe{} is modeled with a % \insttype{blockenv} instance named \instname{list}, a % \insttype{block} instance named \instname{std-list-\meta{level}}, and % an inner instance named \instname{legacy} (with no dependency on % the nesting level). This environment has two arguments and % customization of the layout is expected to be directly set in the % second argument. For this reason this \instname{legacy} instance % is something that shouldn't be changed (all that is attempted to % provide a way to support legacy setups). % % To set up the default settings (as they were used in \LaTeXe{}) % the \key{legacy-code} key gets \cs{legacylistsetup} assigned % that contains the necessary code to set up these defaults. % Changing the \insttype{blockenv} is therefore not recommended for % the legacy \env{list} environment. % \begin{macrocode} \DeclareInstance{blockenv}{list}{std} { name = list ,tag-name = \UseStructureName{block/list} ,tag-attr-class = ,tagging-recipe = list ,transparent-level = false ,legacy-code = \legacylistsetup ,block-instance = std-list ,inner-level-counter = ,inner-instance-type = list ,inner-instance = legacy } % \end{macrocode} % \end{instance} % % % \subsubsection{Its \insttype{list} instance} % % \begin{instance}{list legacy} % For the legacy \env{list} environment there is only one instance % which is reused on all levels. This is done this way because % the legacy \env{list} environment sets all its % parameters through its arguments. So this instances shouldn't % really be touched. It sets the \key{legacy-support} key to % true, which means that the list code uses \tn{makelabel} for % formatting the label. % \begin{macrocode} \DeclareInstance{list}{legacy}{std} { ,item-instance = basic ,legacy-support = true } % \end{macrocode} % \end{instance} % % % % \subsection{Theorem-like environments declared through \cs{newtheorem}} % % In standard \LaTeX{} theorem-like environments are not defined % directly, but with the help of a \cs{newtheorem} % declaration. That allows specifying the typeset environment title, % e.g., \enquote{Lemma}, and the counter to use to number the % environments, e.g., they could be all numbered individually or one % could number them using the same counter as some other theorem-like % environment. % % This was first augmented by the \pkg{theorem} package which % implemented the idea of a \cs{theoremstyle}; this is now considered % obsolete. Michael Downes from the AMS improved on these early ideas % and wrote the \pkg{amsthm} package, which offered more functionality % including a \cs{newtheoremstyle} declaration and for the document % level a \cs{swapnumbers} and an \env{proof} environment. It also % provided star-forms for \cs{newtheorem} (to define an unnumbered % environment) and allowed to use star-forms of the theorem-like % environments to suppress numbering on an individual instance in the % document. % % This new implementation based on templates, is supposed to cover the % functionality of \pkg{amsthm} including it declarations so that % documents that use \pkg{amsthm} explicitly or implicitly via their % class should continue to work seamlessly. % % For other packages that provide theorem-like environments we have to % see if they could be easily remodeled using the new implementation % or if there is a need for extended templates. % % Assuming declarations such as %\begin{verbatim} % % \swapnumbers % <- commented out % \theoremstyle{definition} % \newtheorem{axiom}[def]{Axiom} %\end{verbatim} % in a document, then the following instances of type \insttype{blockenv} and % \insttype{captionedtext} are declared by \cs{newtheorem}. % % % \subsubsection{The \insttype{blockenv} instances they use} % % Given the above input \cs{newtheorem} defines the following % \insttype{blockenv} instance: %\begin{verbatim} % \DeclareInstance{blockenv}{axiom}{std} % { % name = theorem-like % ,tag-name = \UseStructureName{block/theorem-like} % ,tagging-recipe = standalone % ,transparent-level = true % ,block-instance:e = thm- % \IfInstanceExistsTF{block} % { thm-definition-1 } % { definition } { plain } % ,inner-instance-type = captionedtext % ,inner-instance = axiom % ,para-instance = justify % } %\end{verbatim} % % The setting for \key{block-instance} means that it checks if a % \insttype{block} instance with the name % \instname{thm-definition-1} exists. If so then the value % \keyvalue{thm-definition} is used, otherwise % \keyvalue{thm-plain} is used which is always defined, i.e., % if the theoremstyle does not specify any special vertical spacing % the \insttype{block} instance from the \texttt{plain} style is % reused. % % What varies from \insttype{blockenv} instance to instance are the % values for \key{block-instance} and \key{inner-instance}. % % We use \struct{\UseStructureName{block/theorem-like}} as the % structure name and role-map it to a \struct{Sect} because that % can hold a \struct{Caption}. % % % % \subsubsection{The \insttype{captionedtext} instances they use} % % The instance of type \insttype{captionedtext} is also defined by % \cs{newtheorem} and in this case it looks like this: %\begin{verbatim} % \DeclareInstance{captionedtext}{axiom}{thmlike} % { % ,counter = def % ,title = Axiom % <-- that the title provided to \newtheorem % ,style = definition % <-- that's the used \theoremstyle % } %\end{verbatim} % % If we uncomment the \cs{swapnumbers} line in the example above then we get %\begin{verbatim} % ,style = definition-swap %\end{verbatim} % in the \insttype{captionedtext} instance instead. % % % % % % % \subsubsection{The \insttype{thmstyle} instances they use} % % New theorem styles can be declared with \cs{newtheoremstyle} % which then generates an instance of type % \insttype{thmstyle}. Alternatively, it is, of course, possible to % declare the instances directly (which gives you a bit more % flexibility). A few such styles are predeclared, matching what % is offered by \pkg{amsthm}. These are shown below. % % \begin{instance}{thmstyle plain} % The main style used for many theorem-like environments, i.e., the % one you get if no special \cs{theoremstyle} has been specified. % % \begin{macrocode} \DeclareInstance{thmstyle}{plain}{std} { ,caption-placement = unchained ,numbered = true ,space = \ ,punct = . ,before-hspace = 0pt ,after-hspace = 5pt plus 1pt minus 1pt ,order = {title, space, number, punct, space, note} ,caption-decls = \bfseries ,title-format = #1 ,number-format = #1 ,punct-format = #1 ,note-format = (#1) ,body-decls = \itshape } % \end{macrocode} % % \end{instance} % % \begin{instance}{thmstyle remark} % The \instname{remark} is like \instname{plain} with two changes: % \begin{macrocode} \DeclareInstanceCopy{thmstyle}{remark}{plain} \EditInstance{thmstyle}{remark} { ,caption-decls = \itshape ,body-decls = \normalfont } % \end{macrocode} % \end{instance} % % % % \begin{instance}{thmstyle definition} % The \instname{definition} is like \instname{plain} with only a % difference in the font used for the body: % \begin{macrocode} \DeclareInstanceCopy{thmstyle}{definition}{plain} \EditInstance{thmstyle}{definition} { ,body-decls = \normalfont } % \end{macrocode} % % \end{instance} % % % \begin{instance}{thmstyle legacy2e} % Vanilla \LaTeXe{} (without \pkg{amsthm} loaded) had a slightly % different default. We provide this under the name % \instname{legacy2e}. It doesn't use a punctuation after the % number and it has slightly different vertical spacing (defined by % \instname{thm-legacy2e-1} below). % % Thus, to reprocess an old document for tagging that uses % \cs{newtheorem} without loading \pkg{amsthm} one has to set % \verb=\theoremstyle{legacy2e}= to avoid layout changes. How % such a compatibility setting is automated is not yet decided. % \begin{macrocode} \DeclareInstanceCopy{thmstyle}{legacy2e}{plain} \EditInstance{thmstyle}{legacy2e}{ punct = } % \end{macrocode} % % \end{instance} % % % % % \subsubsection{The \insttype{block} instances they use} % % % \begin{instance}{block thm-plain-1, % block thm-plain-2} % Theorems do not support nesting, so in theory we have only one to set up. % There are, however, documents that put theorem-like environments % inside of lists or other block environments. % While that is in most case somewhat dubious, it % can make sense, for example, in \env{description} lists. So we % support it by providing \instname{thm-plain} instances for % levels 1 and 2. If somebody really nests them further down, then % more such instances need to be declared. % % The \LaTeX{} default reused the general value of \tn{parindent} % and \tn{parskip} and, of course, they start at the outer margin. % \changes{v0.8w}{2024/11/23}{Add block instance for theorems} % \begin{macrocode} \DeclareInstance{block}{thm-plain-1}{std} { ,begin-extra-vspace = 0pt ,left-margin = 0pt ,para-indent = \parindent ,para-vspace = \parskip } % \end{macrocode} % \begin{macrocode} \DeclareInstanceCopy{block}{thm-plain-2}{thm-plain-1} % \end{macrocode} % \end{instance} % % \begin{instance}{block thm-remark-1, % block thm-remark-2} % The \cs{thmstyle} for \enquote{remarks} is defined by % \pkg{amsthm} to use less vertical spacing. It therefore needs its % own \insttype{block} instance. % \begin{macrocode} \DeclareInstance{block}{thm-remark-1}{std} { ,begin-vspace = 0.5\topsep ,begin-extra-vspace = 0pt ,left-margin = 0pt ,para-indent = \parindent ,para-vspace = \parskip } % \end{macrocode} % % \begin{macrocode} \DeclareInstanceCopy{block}{thm-remark-2}{thm-remark-1} % \end{macrocode} % \end{instance} % % % \begin{instance}{block thm-legacy2e-1, % block thm-legacy2e-2} % These are like the \texttt{plain} ones but without resetting % \key{begin-extra-vspace} to zero. % \begin{macrocode} \DeclareInstance{block}{thm-legacy2e-1}{std} { ,left-margin = 0pt ,para-indent = \parindent ,para-vspace = \parskip } % \end{macrocode} % \begin{macrocode} \DeclareInstanceCopy{block}{thm-legacy2e-2}{thm-legacy2e-1} % \end{macrocode} % \end{instance} % % % % \subsection{The \env{proof} environment (from \pkg{amsthm})} % % % % \begin{environment}{proof} % The \env{proof} environment expects one optional argument holding % an alternative title for the proof. We parse this optional % argument as an implicit key/value argument, so that it is % possible to interpret it either as the value for the key % \key{note} or as a key/value list that holds special key settings % for this particular environment instance. The result is analyzed % by \cs{ParseLaTeXeTheoremlike} which then calls a \insttype{blockenv} % instance with the name \instname{proof}. % % In addition we have to set up handling of QED symbols using % \cs{pushQED} and \cs{popQED} using the logic already defined in % \pkg{amsthm}. Details on all this is given in the code section of % this module but normally this top-level declaration doesn't % require any changes. % \begin{macrocode} \NewDocumentEnvironment{proof}{ ={note}o } { \pushQED{\qed}% \ParseLaTeXeTheoremlike {proof} \BooleanTrue {#1} } { \popQED \BlockEnvEnd } % \end{macrocode} % \end{environment} % % \begin{instance}{blockenv proof} % A proof uses its own \instname{proofblock} instance of type % \insttype{block} for vertical spacing. As the proof has a heading % we use a \insttype{captionedtext} instance with name % \instname{proof} as the inner instance and the paragraphs of the % proof are justified. % \begin{macrocode} \DeclareInstance{blockenv}{proof}{std} { name = proof ,tag-name = \UseStructureName{block/proof} ,tag-attr-class = ,tagging-recipe = standalone ,inner-level-counter = ,transparent-level = true ,legacy-code = ,block-instance = proof ,inner-instance-type = captionedtext ,inner-instance = proof ,para-instance = justify } % \end{macrocode} % \end{instance} % % % % \begin{instance}{captionedtext proof} % We use a special \insttype{captionedtext} template to set up the % proof because proofs are not numbered and the argument to a proof % environment has a somewhat different semantic meaning than that % of theorem-like environments. % \begin{macrocode} \DeclareInstance{captionedtext}{proof}{proof} { ,title = Proof ,punct = . ,before-hspace = 0pt ,after-hspace = 5pt plus 1pt minus 1pt ,caption-decls = \itshape ,title-format = #1 ,punct-format = #1 ,body-decls = \normalfont } % \end{macrocode} % \end{instance} % % % \subsubsection{Block instances for the proofs} % % % \begin{instance}{block proof-1, % block proof-2} % Blocks for proofs are pretty normal (the values are taken from % the \pkg{amsthm} implementation): % \begin{macrocode} \DeclareInstance{block}{proof-1}{std} { ,begin-vspace = 6pt plus 6pt ,left-margin = 0pt ,para-indent = \parindent ,para-vspace = \parskip } \DeclareInstanceCopy{block}{proof-2}{proof-1} % \end{macrocode} % % \end{instance} % % % \section{Declaring \insttype{para} instances}\label{sec:para-instances} % % Display block environments often require special paragraph % settings and therefore have a \key{para-instance} key to specify % and appropriate instance. Here are the standard instances that % are predefined for this purpose. % % \begin{instance}{para justify} % Justifying is exactly what the default values do, so the instance % hasn't any special setup. % \begin{macrocode} \DeclareInstance{para}{justify}{std} { % ,para-attr-class = justify % ,para-indent = \parindent % ,begin-hspace = 0pt % ,left-hspace = \z@skip % ,right-hspace = \z@skip % ,end-hspace = \@flushglue % ,final-hyphen-demerits = 5000 % ,newline-cmd = \@normalcr } % \end{macrocode} % \end{instance} % % % \begin{instance}{para center} % Centering a paragraph means putting stretchable glue on both sides. % \begin{macrocode} \DeclareInstance{para}{center}{std} { ,para-attr-class = center ,para-indent = 0pt % ,begin-hspace = 0pt ,left-hspace = \@flushglue ,right-hspace = \@flushglue ,end-hspace = \z@skip ,final-hyphen-demerits = 0 ,newline-cmd = \@centercr } % \end{macrocode} % \end{instance} % % \begin{instance}{para raggedright} % This is the plain \TeX{} version of ragged right, which basically % means no hyphenation unless a word is truly longer than a % line. This implements \env{flushleft}. % \begin{macrocode} \DeclareInstance{para}{raggedright}{std} { ,para-attr-class = raggedright ,para-indent = 0pt % ,begin-hspace = 0pt ,left-hspace = \z@skip ,right-hspace = \@flushglue ,end-hspace = \z@skip ,final-hyphen-demerits = 0 ,newline-cmd = \@centercr } % \end{macrocode} % \end{instance} % % \begin{instance}{para raggedleft} % This here is for \env{flushright}. % \begin{macrocode} \DeclareInstance{para}{raggedleft}{std} { ,para-attr-class = raggedleft ,para-indent = 0pt % ,begin-hspace = 0pt ,left-hspace = \@flushglue ,right-hspace = \z@skip ,end-hspace = \z@skip ,final-hyphen-demerits = 0 ,newline-cmd = \@centercr } % \end{macrocode} % \end{instance} % % Here are the attribute definitions used in the % \key{para-attr-class} in the above instances:\fmi{this should be % moved elsewhere} % \begin{macrocode} \tagpdfsetup { ,role/new-attribute = {justify} {/O /Layout /TextAlign/Justify} ,role/new-attribute = {center} {/O /Layout /TextAlign/Center} ,role/new-attribute = {raggedright}{/O /Layout /TextAlign/Start} ,role/new-attribute = {raggedleft} {/O /Layout /TextAlign/End} } % \end{macrocode} % % \begin{macro}[no-user-doc]{\centering,\raggedleft,\raggedright,\justifying} % These instances are also used to implement declarations for % direct use in documents or in user definitions. % \begin{macrocode} \DeclareRobustCommand\centering {\UseInstance{para}{center}{}} \DeclareRobustCommand\raggedleft {\UseInstance{para}{raggedleft}{}} \DeclareRobustCommand\raggedright{\UseInstance{para}{raggedright}{}} \DeclareRobustCommand\justifying {\UseInstance{para}{justify}{}} % \end{macrocode} % \LaTeX{}'s default is to typeset paragraphs justified. % \begin{macrocode} \justifying % \end{macrocode} % \end{macro} % % \begin{instance}{para verse} % For the \env{verse} environment we use a special para % instance. If the right hand side should be ragged then a % different \key{right-hspace} is needed. % \begin{macrocode} \DeclareInstance{para}{verse}{std} { para-attr-class = justify , para-indent = 0pt , begin-hspace = -1.5em , left-hspace = 1.5em , right-hspace = 0pt , end-hspace = \@flushglue , final-hyphen-demerits = 0 , newline-cmd = \@centercr , } % \end{macrocode} % \end{instance} % % % \begin{macrocode} % % \end{macrocode} % % % % % \section{Advice on adjusting the layout of standard block environments} % % \emph{to document} % % % % % \section{Tagging support} % % \subsection{Paragraph tags} % % Paragraphs in \LaTeX{} can be nested, e.g., you can have a paragraph % containing a display quote, which in turn consists of more than one % (sub)paragraph, followed by some more text which all belongs to the % same outer paragraph. % % In the PDF model and in the HTML model that is not supported --- a % limitation that conflicts with real life, given that such % constructs are quite normal in spoken and written language. % % The approach we take to resolve this is to model such \enquote{big} % paragraphs with a structure named % \struct{\UseStructureName{para/semantic}} % and use \struct{\UseStructureName{para/textblock}} (role-mapped to \struct{P}) % only for (portions of) the actual paragraph text in a way that the % \struct{\UseStructureName{para/textblock}}s are not nested. As a % result we have for a simple paragraph the structures % \begin{tabbing} % \hspace*{1em}\=\hspace*{1em}\=\hspace*{1em}\=\hspace*{1em}\= \kill % \> \struct{\UseStructureName{para/textblock}}\\ % \>\> \struct{\UseStructureName{para/textblock}}\\ % \>\>\> The paragraph text \ldots\\ % \>\> \struct{/\UseStructureName{para/textblock}}\\ % \> \struct{/\UseStructureName{para/textblock}} % \end{tabbing} % The \struct{\UseStructureName{para/semantic}} structure is % role-mapped to \struct{Part} or possibly to \struct{Div} so we get a % valid PDF, but processors who care can identify the complete % paragraphs by looking for \struct{\UseStructureName{para/semantic}} % tags. % % In the case of an element, such as a display quote or a display list % inside the paragraph, we then have % \begin{tabbing} % \hspace*{1em}\=\hspace*{1em}\=\hspace*{1em}\=\hspace*{1em}\= \kill % \> \struct{\UseStructureName{para/semantic}}\\ % \>\> \struct{\UseStructureName{para/textblock}}\\ % \>\>\> The paragraph text before the display element \ldots\\ % \>\> \struct{/\UseStructureName{para/textblock}}\\ % \>\> \struct{display element structure}\\ % \>\>\> Content of the display structure possibly involving inner % \struct{\UseStructureName{para/semantic}} tags\\ % \>\> \struct{/display element structure}\\ % \>\> \struct{\UseStructureName{para/textblock}}\\ % \>\>\> \ldots{} continuing the outer paragraph text\\ % \>\> \struct{/\UseStructureName{para/textblock}}\\ % \> \struct{/\UseStructureName{para/semantic}} % \end{tabbing} % In other words such a display block is always embedded in a % \struct{\UseStructureName{para/semantic}} structure, possibly % preceded by a \struct{\UseStructureName{para/textblock}}\ldots % \struct{/\UseStructureName{para/textblock}} block and possibly % followed by one, though both such blocks are optional. % % Thus an \env{itemize} environment that has some introductory text % but no text immediately following the list would be tagged as % follows: % \begin{tabbing} % \hspace*{1em}\=\hspace*{1em}\=\hspace*{1em}\=\hspace*{1em}\=\hspace*{1em}\= \kill % \> \struct{\UseStructureName{para/semantic}}\\ % \>\> \struct{\UseStructureName{para/textblock}}\\ % \>\>\> The intro text for the \env{itemize} environment \ldots\\ % \>\> \struct{/\UseStructureName{para/textblock}}\\ % \>\> \struct{itemize}\\ % \>\>\> \struct{LI}\\ % \>\>\>\> \struct{\UseStructureName{block/list/label}} label \struct{/\UseStructureName{block/list/label}}\\ % \>\>\>\> \struct{\UseStructureName{block/list/body}}\\ % \>\>\>\>\> The text of the first item involving % \struct{\UseStructureName{para/semantic}} as necessary \ldots\\ % \>\>\>\> \struct{/\UseStructureName{block/list/body}}\\ % \>\>\> \struct{/LI}\\ % \>\>\> \struct{LI}\\ % \>\>\>\> The second item \ldots{}\\ % \>\>\> \struct{/LI}\\ % \>\>\> \ldots{} further items \ldots\\ % \>\> \struct{/itemize}\\ % \> \struct{/\UseStructureName{para/semantic}} % \end{tabbing} % The \struct{itemize} is roll-mapped to \struct{L}. % % For some display blocks, such as centered text, we use a simpler % strategy. Such blocks still ensure that they are inside a % \struct{\UseStructureName{para/semantic}} structure but their body % uses simple \struct{\UseStructureName{para/textblock}} blocks and % not % \struct{\UseStructureName{para/semantic}}\struct{\UseStructureName{para/textblock}} % inside, e.g., the input % \begin{verbatim} % This is a paragraph with some % \begin{center} % centered lines % % with a paragraph break between them % \end{center} % followed by some more text. % \end{verbatim} % will be tagged as follows: % \begin{tabbing} % \hspace*{1em}\=\hspace*{1em}\=\hspace*{1em}\=\hspace*{1em}\=\hspace*{1em}\= \kill % \> \struct{\UseStructureName{para/semantic}}\\ % \>\> \struct{\UseStructureName{para/textblock}}\\ % \>\>\> This is a paragraph with some\\ % \>\> \struct{/\UseStructureName{para/textblock}}\\ % \>\> \struct{text /O /Layout /TextAlign/Center}\\ % \>\>\> centered lines\\ % \>\> \struct{/\UseStructureName{para/textblock}}\\ % \>\> \struct{text /O /Layout /TextAlign/Center}\\ % \>\>\> with a paragraph break between them\\ % \>\> \struct{/\UseStructureName{para/textblock}}\\ % \>\> \struct{\UseStructureName{para/textblock}}\\ % \>\>\> followed by some more text.\\ % \> \struct{/\UseStructureName{para/semantic}} % \end{tabbing} % % The text-unit structures are added by using the tagging sockets % \texttt{para/semantic/begin} and \texttt{para/semantic/end} declared in % \texttt{lttagging.dtx}. They can be disabled by assigning these sockets the % plug \texttt{noop}. % % % \subsubsection{Tagging recipes} % % There are a number of different tagging recipes that implement % different tagging approaches. They are selected through the % \key{tagging-recipe} of the \insttype{blockenv} template. Currently % the following values are implemented: % \begin{description} % \raggedright % \item[\keyvalue{standalone}] % This recipe does the following: % \begin{itemize} % \item Ensure that the \insttype{blockenv} is not inside a % \struct{\UseStructureName{para/semantic}} structure. If % necessary, close the open one (and any open % \struct{\UseStructureName{para/textblock}} structure). % % \item Text inside the body of the environment start with % \struct{\UseStructureName{para/semantic}}\struct{\UseStructureName{para/textblock}} % unless the key \key{tagging-suppress-paras} is set to % \keyvalue{true} (which is most likely the wrong thing to do % because we then get just % \struct{\UseStructureName{para/textblock}} as the structure). % % \item At the end of the environment close % \struct{/\UseStructureName{para/textblock}} and possibly an % inner \struct{/\UseStructureName{para/semantic}} if open. % % \item % Finally, ensure that after the environment a new % \struct{\UseStructureName{para/semantic}} is started, if % appropriate, e.g., if text is following. % \end{itemize} % % \item[\keyvalue{basic}] % This recipe does the following: % \begin{itemize} % \item % Ensure that the \insttype{blockenv} is inside a % \struct{\UseStructureName{para/semantic}} % structure, if necessary, start one. % % \item If inside a % \struct{\UseStructureName{para/semantic}}\struct{\UseStructureName{para/textblock}}, % then close the \struct{/\UseStructureName{para/textblock}} but % leave the \struct{\UseStructureName{para/semantic}} open. % % \item Text inside the body of the environment start with % \struct{\UseStructureName{para/semantic}}\struct{\UseStructureName{para/textblock}} % if \key{tagging-suppress-paras} is set to \keyvalue{false}, % otherwise just with \struct{\UseStructureName{para/textblock}}. % % \item At the end of the environment close % \struct{/\UseStructureName{para/textblock}} and possibly an % inner \struct{/\UseStructureName{para/semantic}} if open. % % \item Then look if the environment is followed by an empty line % (\tn{par}). If so, close the outer % \struct{/\UseStructureName{para/semantic}} and start any % following text with % \struct{\UseStructureName{para/semantic}}\struct{\UseStructureName{para/textblock}}. % Otherwise, don't and following text restarts with a just a % \struct{\UseStructureName{para/textblock}} (and no paragraph % indentation) % \end{itemize} % % \item[\keyvalue{standard}] % This recipe is like the \keyvalue{basic} % one as far as handling \struct{\UseStructureName{para/semantic}} % and \struct{\UseStructureName{para/textblock}} is concerned. In % addition % \begin{itemize} % \item % it starts an inner tagging structure (i.e., which is therefore a % child of the outer \struct{\UseStructureName{para/semantic}}). % \item % By default this structure is a \struct{Div} unless overwritten % by the key \key{tag-name}. If that key is used, a suitable % role-map needs to be provided for the name given. % \item % At the end of the environment that inner structure is closed % again so that we are back on the % \struct{\UseStructureName{para/semantic}} level from the % outside. % \item % Then the lookahead for an empty line is done as described previously. % \end{itemize} % % % % \item[\keyvalue{list}] % This recipe is like the \keyvalue{standard} one except that % \begin{itemize} % \item % the inner structure is a list (\struct{L}). % \item % Furthermore everything is set up so that we have list items % (\struct{LI}) with suitable substructures (\struct{\UseStructureName{block/list/label}} for the item % labels and \struct{\UseStructureName{block/list/body}} for the item bodies). % \item % If the key \key{tag-name} is specified, this is used as the tag % name for the whole list instead of \struct{L}. Of course, it % should then have a suitable rolemap. % \item % If the key \key{tag-attr-class} is specified then this is used as the % class attribute. Again, this requires a suitable setup on the outside. % \item % At the end of the environment the \struct{/\UseStructureName{block/list/body}}, \struct{/LI}, and % \struct{/L} (or the tag name used) are closed. % \item % Then the lookahead for an empty line is done as described previously. % \end{itemize} % \end{description} % % % % % % \section{Tracing and debugging} % % \begin{function}{\DebugBlocksOn,\DebugBlocksOff, \block_debug_on:, \block_debug_off:} % These commands enable/disable debugging messages for blocks. They also % enable/disable debugging of templates (e.g., call % \cs{DebugTemplatesOn} or \cs{DebugTemplatesOff}). % \end{function} % % The data that is produced is rather verbose and largely guided % (so far) by what seemed helpful while developing the code. This % needs some cleanup at a later stage.\fmi{cleanup} At the moment, % if you have the following simple document % %\begin{footnotesize} %\begin{verbatim}[xleftmargin=-20pt,numbers=left,numbersep=6pt,numberblanklines] % \DocumentMetadata{tagging=on, lang=en} % % \documentclass{article} % % \DebugBlocksOn % % \begin{document} % \begin{itemize}[item-vspace=3pt] % \item A normal item % \item[\textbf{+}] A special item % \end{itemize} % \end{document} %\end{verbatim} %\end{footnotesize} % % \smallskip % \noindent % then you will get the following information on the screen and in % the \texttt{.log} file: % \smallskip % %\begin{footnotesize} %\begin{verbatim}[xleftmargin=-40pt] % [Template] ==> Use 'blockenv' instance: itemize on input line 8 % [Template] ==> template: 'std'; arguments: |item-vspace=3pt|\BooleanFalse |\NoValue |\NoValue | % [Template] ==> Use 'block' instance: std-list-1 on input line 8 % [Template] ==> template: 'std'; argument: |item-vspace={3pt}| % [Blocks] ==> @endpe=false on input line 8 % [Template] ==> Use 'list' instance: itemize-1 on input line 8 % [Template] ==> template: 'std'; arguments: ||\BooleanFalse |\NoValue |\NoValue | % [Blocks] ==> Set first block everypar on input line 8 % [Blocks] ==> template:list:std end % % [Template] ==> Use 'item' instance: basic on input line 9 % [Template] ==> template: 'std'; argument: || % [Blocks] ==> Set item block everypar on input line 9 % [Blocks] ==> ... in item block everypar on input line 9 % [Blocks] ==> increment P on input line 9 % [Blocks] ==> Set noop block everypar on input line 9 % % [Template] ==> Use 'item' instance: basic on input line 10 % [Template] ==> template: 'std'; argument: |label={\textbf {+}}| % [Blocks] ==> item with optional % [Blocks] ==> Set item block everypar on input line 10 % [Blocks] ==> ... in item block everypar on input line 10 % [Blocks] ==> increment P on input line 10 % [Blocks] ==> Set noop block everypar on input line 10 % % [Blocks] ==> blockenv common ending on input line 11 % % [Blocks] ==> flattened=false on input line 12 % [Blocks] ==> Structure-end text-unit after displayblock on input line 12 %\end{verbatim} %\end{footnotesize} % % % % \section{New and redefined kernel command} % % \begin{function}{\SimpleBlockEnv,\BlockEnv,\BlockEnvEnd,\g_block_nesting_depth_int} % \emph{to be documented} % \end{function} % % \begin{function}{\legacyverbatimsetup,\legacyallttsetup,\legacylistsetup} % \emph{to be documented} % \end{function} % % \begin{function}{\@setupverbinvisiblespace} % A counterpart definition to the kernel command \tn{@setupverbinvisiblespace}, % needed as we need to handle real space chars in verbatim. % \end{function} % % \begin{function}{\newtheorem,\newtheoremstyle} % Reimplemented to fit the template approach. \cs{newtheoremstyle} % was defined by \pkg{amsthm}. % \end{function} % % \begin{function}{\@nthm,\@xnthm,\@ynthm, % \@thm,\@xthm,\@ythm,\@othm, % \@begintheorem,\@opargbegintheorem,\@endtheorem} % These are no longer used (to be removed). % \end{function} % % \begin{function}{\item,\@itemlabel} % The \tn{item} is redefined. % \end{function} % % \begin{function}{\c@maxblocklevels} % A counter to increase or decrease the number of supported % level. If increased, one needs to supply additional level instances. % \end{function} % % \begin{function}{\begin} % The \tn{begin} is slightly redefined to handle \tn{@doendpe} better. % TODO: move to kernel % \end{function} % % \begin{function}{\@doendpe} % The original \LaTeXe{} command is augmented to allow for tagging. % \end{function} % % \begin{function}{\para_end:} % TODO: consider name, document % \end{function} % % \begin{function}{para/begin} % The para/begin hook is enhanced to support list ends % \end{function} % % \end{documentation} % % \StopEventually{\setlength\IndexMin{200pt} \PrintIndex } % % % % \begin{implementation} % % \section{The Implementation} % % % \begin{macrocode} %<*package-start> % \end{macrocode} % % \begin{macrocode} %<@@=block> % \end{macrocode} % % % \begin{macrocode} \ProvidesPackage {latex-lab-testphase-block} [\ltlabblockdate\space v\ltlabblockversion\space blockenv implementation] % \end{macrocode} % % \subsection{Candidates for kernel changes} % % General kernel changes, also loaded by the sec and toc code. % \begin{macrocode} \RequirePackage{latex-lab-kernel-changes} % \end{macrocode} % % For testing we temporarily load it here (it has to come before % the definition of \cs{DebugBlocksOff} below: % \begin{macrocode} \RequirePackage{latex-lab-testphase-context} % \end{macrocode} % % \begin{macrocode} \ExplSyntaxOn % \end{macrocode} % % % \subsubsection{Augmented \cs{SetKnownTemplateKeys}} % % % \begin{macro}{\SetKnownTemplateKeys} % A key/val list passed to \cs{SetKnownTemplateKeys} can either be % empty (in which we do not want to start up the parsing machinery) % or it could be \cs{NoValue} in which we do not want to do that % either. The latter can happen, for example, with \env{verbatim} % where we define the optional argument with % \verb/={legacy-code} !o/ so that people can write % \verb/\begin{verbatim}[\small]/ a syntax promoted by the TUGboat % class. % \begin{macrocode} \cs_set_protected:Npn \SetKnownTemplateKeys #1#2#3 { % \end{macrocode} % An \enquote{empty} argument (or rather one that is empty after one % expansion) is most likely the case that happens most often % so we test for this first. % \begin{macrocode} \tl_if_empty:oTF {#3} { \tl_set_eq:NN \UnusedTemplateKeys \c_empty_tl } { \tl_if_novalue:nTF {#3} { \tl_set_eq:NN \UnusedTemplateKeys \c_empty_tl } { \keys_set_known:noN { template / #1 / #2 } {#3} \UnusedTemplateKeys } } } % \end{macrocode} % \end{macro} % % % % \begin{macro}{\SetTemplateKeys} % Same kind of extension for \cs{SetTemplateKeys}: % \begin{macrocode} \cs_set_protected:Npn \SetTemplateKeys #1#2#3 { \tl_if_empty:oF {#3} { \tl_if_novalue:nF {#3} { \keys_set:no { template / #1 / #2 } {#3} } } } % \end{macrocode} % \end{macro} % % \subsubsection{Tracing templates and instances} % % \begin{macro}{\template_debug_typeout:n} % I guess that tracing macro is needed in several modules, so % should become public (or at least kernel). % \begin{macrocode} \cs_new_protected:Npn \template_debug_typeout:n { \__template_debug_typeout:n } % \end{macrocode} % \end{macro} % % % % \subsubsection{Handling \tn{par} after the end of the list} % % An empty line (or a \tn{par}) after a list has semantic meaning as % it defines whether then following text is logically within the same % paragraph as the list (no empty line) or whether it starts a new % paragraph and the paragraph containing the list ends at the end of % the list (empty line after the list). % This is handled by \LaTeX{} using a legacy flag called % \texttt{@endpe} and set of commands inside the % generic \tn{end} (calling \tn{@doendpe}) and as part of the list % environments identifying themselves as \enquote{paragraph ending % environments} (by setting this flag). % % For the reimplementation of the list environments including support % of tagging we need to augment that mechanism slightly and add some % kernel hook(s) to add the tagging code if needed. % % % % % \begin{macro}{\@doendpe} % The original \LaTeXe{} command is augmented to allow for tagging. % TODO: use sockets for this and move to the kernel eventually. % \begin{macrocode} \def\@doendpe{\@endpetrue \def\par { % \end{macrocode} % If we are processing a \verb=$$= math display and we encounter a real % \cs{par} after it, we need to add a \cs{parskip} when tagging is % done, because the one % added by \TeX{} is always canceled by the processing in % \cs{__math_tag_dollardollar_display_end:} in that case. This is % signaled by the global legacy switch \texttt{@domathendpe} which is set % to true in that case. Once the skip is applied we set it to % false. If there is no \cs{par} at all, it will be reset in % \cs{everypar} when the next paragraph starts. % \changes{v0.8x}{2024/12/01}{Handle \cs{parskip} after \texttt{\$\$} % display (tagging/762)} % \begin{macrocode} \if@domathendpe \skip_vertical:n { \tex_parskip:D } \@domathendpefalse \fi \@restorepar \clubpenalty\@clubpenalty % \end{macrocode} % At this point we add the tagging code that closes an open % \struct{\UseStructureName{para/semantic}}, % \struct{\UseStructureName{para/textblock}} tag combination, if % necessary: % \begin{macrocode} \tag_socket_use:n {@doendpe} % \end{macrocode} % % The standard \tn{par} command (\cs{par_end:}) acts on % \texttt{@endpe} and attempts to close a still open % \struct{\UseStructureName{para/semantic}}s and this would be % wrong if it was already closed above. So we have to reset the % switch to false first. % \begin{macrocode} \@endpefalse \everypar{} \par } \everypar{{\setbox\z@\lastbox} \everypar{} \@endpefalse % \end{macrocode} % Not sure what is faster: testing for the status of the switch or % setting it unconditionally to false (globally), probably roughly % the same, so we set it always: % \begin{macrocode} % \if@domathendpe \@domathendpefalse % \fi } } % \end{macrocode} % \end{macro} % % \begin{socketdecl}{tagsupport/@doendpe} % The socket used in the \cs{@doendpe} % TODO: if this goes into the kernel, the name should probably be different. % \begin{macrocode} \socket_if_exist:nF{ tagsupport/@doendpe } { \NewTaggingSocket {@doendpe}{0} } % \end{macrocode} % \end{socketdecl} % % \begin{plugdecl}{default} % If a display block ends and is followed by a blank line we have to end the % enclosing paragraph tagging structure. % \begin{macrocode} \NewTaggingSocketPlug {@doendpe}{default} { \bool_if:NT \l__tag_para_bool { % \end{macrocode} % Given that restoring \tn{par} through the legacy \LaTeXe{} method % can take a few iterations (for example, in case of nested lists, % e.g., \verb=...\end{itemize} \item ...\par= it can happen that % the socket code is called while % \texttt{@endpe} is already handled and then we should not attempt % to close a \struct{\UseStructureName{para/semantic}} structure). % So we need to check for this. % \begin{macrocode} \legacy_if:nT { @endpe } { % \end{macrocode} % If the display block currently ending was \enquote{flattened} % (i.e., uses simplified paragraphs that are not tagged by a % combination of \struct{\UseStructureName{para/semantic}} followed by \struct{\UseStructureName{para/textblock}}, but simply % with a \struct{\UseStructureName{para/textblock}}), % then we don't have to do anything, because the \struct{\UseStructureName{para/textblock}} is already closed. % \begin{macrocode} \@@_debug_typeout:n { flattened= \bool_if:NTF \l__tag_para_flattened_bool {true}{false} \on@line } \bool_if:NF \l__tag_para_flattened_bool { % \end{macrocode} % \changes{v0.9k}{2025/07/21}{Use tagging socket for text-unit structure} % \begin{macrocode} \UseTaggingSocket{para/semantic/end} { \@@_debug_typeout:n{Structure-end~ \l__tag_para_main_tag_tl\space after~ displayblock \on@line } } } } } } % \end{macrocode} % % \begin{macrocode} \AssignTaggingSocketPlug{@doendpe}{default} % \end{macrocode} % \end{plugdecl} % % % % \begin{macro}[int]{\if@domathendpe,\@domathendpefalse,\@domathendpetrue} % Signal that special paragraph handling after a math display is required. % \changes{v0.8x}{2024/12/01}{Handle \cs{parskip} after \texttt{\$\$} % display (tagging/762)} % \begin{macrocode} \newif\if@domathendpe \def\@domathendpefalse{\global\let\if@domathendpe\iffalse} \def\@domathendpetrue {\global\let\if@domathendpe\iftrue} % \end{macrocode} % \end{macro} % % % There is a general bug in the para handling: when the output routine is % triggered the current setting of \texttt{@endpe} affects what % happens in the OR. but it shouldn't so we need to reset its value % (which is global) and set it back after the OR has finished. This % is what the following code does (final implementation should % probably not involve a normal hook):\fmi{fix in kernel} % \begin{macrocode} \AddToHook{build/column/before}{% \if@endpe \@endpefalse \aftergroup\@endpetrue \fi } % \end{macrocode} % % % % % % \subsubsection{Other useful \pkg{expl3} commands} % % This section collects \pkg{expl3} commands that will be useful in % the code here and possibly generally. % % \begin{macro}{\@@_skip_set_to_last:N,\@@_skip_remove_last:} % Set a skip register to the value of an immediately preceding % skip or zero if there was none. % \begin{macrocode} \cs_new_protected:Npn \@@_skip_set_to_last:N #1 { \skip_set:Nn #1 { \tex_lastskip:D } } % \end{macrocode} % Remove a skip previous skip if it is directly in front (not % allowed in unrestricted vertical mode). % \begin{macrocode} \cs_new_eq:NN \@@_skip_remove_last: \tex_unskip:D % \end{macrocode} % \end{macro} % % \begin{macro}{\tl_if_novalue:oTF} % Not sure this is still necessary (or even correct) after the move % to \cs{NoValue}. % \fmi{check} % \begin{macrocode} \cs_generate_variant:Nn \tl_if_novalue:nTF { o } % \end{macrocode} % \end{macro} % % % \subsection{Tracing and debugging interfaces} % % This follows the same convention as in other modules, but % eventually that should be given a better % implementation.\fmi{refactor at some stage} % % \begin{variable}{\g_@@_debug_bool} % Boolean to indicate if we want to get debugging info from % commands and templates handling block displays. % \begin{macrocode} \bool_new:N \g_@@_debug_bool % \end{macrocode} % \end{variable} % % % \begin{macro}{\@@_debug:n,\@@_debug_typeout:n} % Put debugging info in the code, displayed or not displayed % depending on the value in \cs{g_@@_debug_bool}. % \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}{\block_debug_on:,\block_debug_off:, % \@@_debug_gset:} % Changing the debugging status. % \begin{macrocode} \cs_new_protected:Npn \block_debug_on: { \bool_gset_true:N \g_@@_debug_bool \@@_debug_gset: } % \end{macrocode} % % \begin{macrocode} \cs_new_protected:Npn \block_debug_off: { \bool_gset_false:N \g_@@_debug_bool \@@_debug_gset: } % \end{macrocode} % % \begin{macrocode} \cs_new_protected:Npn \@@_debug_gset: { \cs_gset_protected:Npx \@@_debug:n ##1 { \bool_if:NT \g_@@_debug_bool {##1} } \cs_gset_protected:Npx \@@_debug_typeout:n ##1 { \bool_if:NT \g_@@_debug_bool { \iow_term:x { ^^J [Blocks]~ ==>~ ##1} } } } % \end{macrocode} % \end{macro} % % % \begin{macro}{\DebugBlocksOn,\DebugBlocksOff} % If we are debugging blocks we also want to know about template % instances, so we turn the debugging for templates as well (for now). % \begin{macrocode} \cs_new_protected:Npn \DebugBlocksOn { \block_debug_on: \template_debug_on: } \cs_new_protected:Npn \DebugBlocksOff { \block_debug_off: \template_debug_off: } % \end{macrocode} % % \begin{macrocode} \DebugBlocksOff % \end{macrocode} % \end{macro} % % % % \begin{macro}{\DebugSwitchesOn,\DebugSwitchesOff} % This debugs the use of legacy switches (so perhaps better % called \cs{DebugLegacySwitchesOn}) but so far it was just a quick % debugging aid while I was trying to understand. It needs some % further thoughts and is probably not necessary at all in the end. % \begin{macrocode} \cs_new_protected:Npn \DebugSwitchesOn { \AddToHookWithArguments{cmd/legacy_if_gset_false:n/before}[debug] {\typeout{[Switch]~==>~ ##1~=~false~(global)}} \AddToHookWithArguments{cmd/legacy_if_gset_true:n/before}[debug] {\typeout{[Switch]~==>~ ##1~=~true~(global)}} \AddToHookWithArguments{cmd/legacy_if_set_false:n/before}[debug] {\typeout{[Switch]~==>~ ##1~=~false}} \AddToHookWithArguments{cmd/legacy_if_set_true:n/before}[debug] {\typeout{[Switch]~==>~ ##1~=~true}} } % \end{macrocode} % % \begin{macrocode} \cs_new_protected:Npn \DebugSwitchesOff { \RemoveFromHook{cmd/legacy_if_gset_false:n/before}[debug] \RemoveFromHook{cmd/legacy_if_gset_true:n/before}[debug] \RemoveFromHook{cmd/legacy_if_set_false:n/before}[debug] \RemoveFromHook{cmd/legacy_if_set_true:n/before}[debug] } % \end{macrocode} % % \begin{macrocode} %\DebugSwitchesOn %\DebugSwitchesOff % \end{macrocode} % \end{macro} % % % % % \subsection{Template types and template interfaces} % % This section is devoted to the template interfaces, and the template % code is covered later. % % \begin{templatetype}{blockenv,list,captionedtext,thmstyle,block,item,para} % ^^A % All template types expect a first key--value argument used to % tweak template parameters at a specific point in the document for % a single environment or command. The template types % \insttype{blockenv}, \insttype{list}, \insttype{captionedtext}, % and \insttype{thmstyle} take three more arguments which are a % boolean for suppressing numbering, a possible caption, and a % possible sub-caption. % % \begin{macrocode} \NewTemplateType{blockenv}{4} \NewTemplateType{list}{4} \NewTemplateType{captionedtext}{4} \NewTemplateType{thmstyle}{4} % \end{macrocode} % % \begin{macrocode} \NewTemplateType{block}{1} \NewTemplateType{item}{1} \NewTemplateType{para}{1} % \end{macrocode} % \end{templatetype} % % % % \begin{template}{blockenv std} % ^^A % \begin{macrocode} \DeclareTemplateInterface{blockenv}{std}{4} { name : tokenlist , % \end{macrocode} % If not explicitly set then \key{tag-name} and % \key{tag-attr-class} are set by the % \key{tagging-recipe}. However, we have to default both to % \meta{empty} so that nested blocks do not inherit from the outer % level. % \changes{v0.9g}{2025/06/30}{ (tagging/925)} % \begin{macrocode} ,tag-name : tokenlist = ,tag-attr-class : tokenlist = ,tagging-recipe : tokenlist = standard ,transparent-level : boolean = false ,legacy-code : tokenlist = ,block-instance : tokenlist = std-display % \end{macrocode} % Paragraph instance is normally inherited so no default. % \begin{macrocode} ,para-instance : tokenlist ,inner-level-counter : tokenlist ,max-inner-levels : tokenlist = 4 ,inner-instance-type : tokenlist = ,inner-instance : tokenlist = ,tagging-suppress-paras : boolean = false ,final-code : tokenlist = \ignorespaces } % \end{macrocode} % \end{template} % % % \begin{template}{block std} % ^^A % \changes{v0.8s}{2024/10/03}{Offer item-vspace key also on block templates} % \changes{v0.9l}{2025/11/04}{Fix default for para-vspace, should be the outer \cs{parskip}} % \begin{macrocode} \DeclareTemplateInterface{block}{std}{1} { ,begin-vspace : skip = \topsep ,begin-extra-vspace : skip = \partopsep ,begin-unchained-vspace : skip = .5\topsep ,para-vspace : skip = \parskip ,end-vspace : skip = \KeyValue{begin-vspace} ,end-extra-vspace : skip = \KeyValue{begin-extra-vspace} ,item-vspace : skip = \itemsep ,begin-penalty : integer = \UseName{@beginparpenalty} ,end-penalty : integer = \UseName{@endparpenalty} ,item-penalty : integer = \UseName{@itempenalty} ,left-margin : length = \leftmargin ,right-margin : length = \rightmargin ,para-indent : length = 0pt } % \end{macrocode} % \end{template} % % % \begin{template}{para std} % ^^A % \begin{macrocode} \DeclareTemplateInterface{para}{std}{1} { ,para-attr-class : tokenlist = justify ,para-indent : length = \parindent ,begin-hspace : skip = 0pt ,left-hspace : skip = 0pt ,right-hspace : skip = 0pt ,end-hspace : skip = \@flushglue ,fixed-word-spaces : boolean = false ,final-hyphen-demerits : integer = 5000 ,newline-cmd : function(0) = \@normalcr } % \end{macrocode} % \end{template} % % % \begin{template}{list std} % ^^A % \begin{macrocode} \DeclareTemplateInterface{list}{std}{4} { ,counter : tokenlist = ,item-label : tokenlist = ,start : integer = 1 ,resume : boolean = false ,item-instance : instance{item} = basic ,item-vspace : skip = \itemsep ,item-penalty : integer = \UseName{@itempenalty} ,item-indent : length = \itemindent ,label-width : length = \labelwidth ,label-sep : length = \labelsep ,legacy-support : boolean = false } % \end{macrocode} % \end{template} % % % \begin{template}{item std} % ^^A % \begin{macrocode} \DeclareTemplateInterface{item}{std}{1} { ,counter-label : function{1} = \arabic{#1} ,counter-ref : function{1} = \KeyValue{counter-label} ,label-ref : function{1} = #1 ,label-autoref : function{1} = item~#1 ,label-format : function{1} = #1 ,label-strut : boolean = false ,label-align : choice {left,center,right,parleft} = right ,label-boxed : boolean = true ,next-line : boolean = false % <- review viz standalone below ,text-font : tokenlist ,compatibility : boolean = true ,label-placement : choice {chained,unchained,standalone} = chained , } % \end{macrocode} % \end{template} % % % \begin{template}{captionedtext thmlike} % ^^A % The \insttype{captionedtext} \instname{thmlike} template for % theorem-like environments has only three keys because it % delegates most of the work to the \insttype{thmstyle} % template specified in the key \key{style}. % \begin{macrocode} \DeclareTemplateInterface{captionedtext}{thmlike}{4} { ,counter : tokenlist = ,title : tokenlist = % <- bad name? ,style : instance{thmstyle} = plain } % \end{macrocode} % \end{template} % % % \begin{template}{captionedtext proof} % ^^A % In contrast, the \insttype{captionedtext} \instname{proof} % template implements all of the \env{proof} environment without % any delegation and therefore shows several keys for customizing % the layout (similar to those seen with \insttype{thmstyle} % \instname{std}). % \begin{macrocode} \DeclareTemplateInterface{captionedtext}{proof}{4} { ,title : tokenlist = Proof ,punct : tokenlist = . ,caption-placement : choice {chained,unchained,standalone} = unchained ,before-hspace : skip = 0pt ,after-hspace : skip = 5pt ,caption-decls : tokenlist = ,title-format : function{1} = #1 ,punct-format : function{1} = #1 ,body-decls : tokenlist = } % \end{macrocode} % \end{template} % % % % % % \begin{template}{thmstyle std} % % \begin{macrocode} \DeclareTemplateInterface{thmstyle}{std}{4} { ,numbered : boolean = true ,space : tokenlist = \ % <- bad name? ,punct : tokenlist = . ,caption-placement : choice {chained,unchained,standalone} = unchained ,before-hspace : skip = 0pt ,after-hspace : skip = 5pt ,order : commalist = { title, space, number, punct, space, note } ,caption-decls : tokenlist = ,title-format : function{1} = #1 ,number-format : function{1} = #1 ,punct-format : function{1} = #1 ,note-format : function{1} = (#1) ,body-decls : tokenlist = } % \end{macrocode} % \end{template} % % % % % % \subsection{Implementation of templates} % % % \subsubsection{Some notes on the \LaTeXe{} legacy switches} % % \LaTeXe{} used a number of switches to manage its list environments % and everything that was based on them. % % For the reimplementation I made some notes about the original % usage and how this got changed (while keeping the names for now). % % Some of these switches really need to keep their names, e.g., % \texttt{@nobreak} or \texttt{minipage}, because they are used all % over the place. Others can probably replaced with L3 booleans % which makes things faster and cleaner, but for now I kept them % too. % % \paragraph{Original usage:} % \begin{macrocode} % % @newlist (global): signal that we are at the start of a list % % -> true at the start of a list before the first item when control % is returned to document % -> false in everypar setting the first item % -> false at end of list if still true (after generating an error) % % -> tests: at list start setting @noparitemtrue and @noparlisttrue % % % @inlabel (global): signaling that some item label waits to be typeset % % -> true in \@item % -> false at list end % -> false in everypar after label has been typeset % -> false in \newpage after \leavemode to typeset item label % (probably not needed) % % -> tests: at list start setting @noparitemtrue and @noparlisttrue % -> tests: at list end to ensure that dangling label is typeset % -> tests: in \@item to output a dangling item label by switching to hmode % -> tests: in \everypar to output a dangling item label % -> tests: in \newpage to output a dangling item label before the page is ended % -> tests: in tagging hook {para/begin}{kernel} % % % @noparlist (local): % % -> true at start of list if already @inlabel=true % -> false at start of list otherwise % % -> tests: in \endtrivlist. If true suppress vertical spacing after the list % % % @noparitem (local): % % -> true at start of list if already @inlabel=true % -> false % % \end{macrocode} % \paragraph{Repurpose:} % \begin{macrocode} % % Interpret legacy switches as follows (keeping the names for now) % % @newlist -> signals that we are at the start of a new block with a caption or % at the start of a list block expecting an item next % % In other words this is now really start of a block % with inner structure. % % @noparlist -> signals that we are on a new block with @inlabel already true, i.e., % and this placement should happen horizontally % % @inlabel -> Signals that we have at least one item or caption waiting to be typeset % inside the label box % % @noparitem -> dropped (handled directly) % % \end{macrocode} % % % % % % \subsubsection{Implementation of \insttype{blockenv} templates} % % So far there is only one, but who knows \ldots\ --- however, the % majority will be vertically oriented blocks, so we make this the \texttt{std}. % % \begin{template}{blockenv std} % ^^A % \begin{macrocode} \DeclareTemplateCode{blockenv}{std}{4} { name = \l_@@_env_name_tl ,tag-name = \l_@@_tag_name_tl ,tag-attr-class = \l_@@_tag_class_tl ,tagging-recipe = \l_@@_tagging_recipe_tl ,transparent-level = \l_@@_transparent_level_bool ,legacy-code = \l_@@_legacy_code_tl ,block-instance = \l_@@_block_instance_tl ,para-instance = \l_@@_para_instance_tl ,tagging-suppress-paras = \l__tag_para_flattened_bool ,inner-level-counter = \l_@@_inner_level_counter_tl ,max-inner-levels = \l_@@_max_inner_levels_tl ,inner-instance-type = \l_@@_inner_instance_type_tl ,inner-instance = \l_@@_inner_instance_tl ,final-code = \l_@@_final_code_tl } { \template_debug_typeout:n{~\space template:~ 'std;~ arguments:~ \exp_not:n{|#1|#2|#3|#4|}} % \end{macrocode} % % \begin{macrocode} \UseHook{blockenv} % \end{macrocode} % We first evaluate the key list passed from the document (if % any). All known keys are used, the remainder is stored in % \cs{UnusedTemplateKeys} to be passed to any inner instances below. % \begin{macrocode} \SetKnownTemplateKeys{blockenv}{std}{#1} % \end{macrocode} % We need to know later if we have nested \insttype{blockenv}s inside % a flattened environment. Whenever we start a new blockenv we % increment \cs{l__tag_block_flattened_level_int} if it is already % different from zero. If it is zero we increment it if flattening % is requested. % Thus a value of \texttt{0} means no flattening requested so far % and \texttt{1} means this is the first blockenv requesting % flattening. In either case we have to make sure that the blockenv % is surrounded by a \struct{\UseStructureName{para/semantic}} tag, while for any value above % \texttt{1} we have to omit the \struct{\UseStructureName{para/semantic}}. % \begin{macrocode} \int_compare:nNnTF \l__tag_block_flattened_level_int > 0 { \int_incr:N \l__tag_block_flattened_level_int } { \bool_if:NT \l__tag_para_flattened_bool { \int_incr:N \l__tag_block_flattened_level_int } } % \end{macrocode} % % \begin{macrocode} \tl_if_empty:NF \l_@@_inner_level_counter_tl { \int_compare:nNnTF \l_@@_inner_level_counter_tl > { \l_@@_max_inner_levels_tl - 1 } { \@toodeep } { \int_incr:N \l_@@_inner_level_counter_tl } % not clean "o"? } % \end{macrocode} % Legacy defaults are only roped in if the list level changes. For % display blocks that remain on the same level the current values % are kept. % \begin{macrocode} \int_compare:nNnTF \g_block_nesting_depth_int > { \c@maxblocklevels - 1 } { \@toodeep } { \int_gincr:N \g_block_nesting_depth_int % \end{macrocode} % If there are no legacy defaults for that level then the next line % does nothing, i.e., the current values (from the last level) % become the defaults for the next. % % If have a transparent level (e.g., something like a \env{center} % environment) we omit setting the legacy defaults, because that is % the way \LaTeXe{} lists worked as well. % \changes{v0.9m}{2026-01-05}{Don't use legacy \texttt{@listi..} values for % transparent blocks (tagging/1163)} % \begin{macrocode} \bool_if:NF \l__block_transparent_level_bool { \use:c { @list \int_to_roman:n { \g_block_nesting_depth_int } } } } % \end{macrocode} % If we are doing tagging we load one of the available recipes for % tagging, which alters various kernel hooks to add appropriate % tagging structures. % \changes{v0.9g}{2025-07-01}{use tagging socket for recipe} % \begin{macrocode} \UseTaggingSocket{block/recipe}{\l_@@_tagging_recipe_tl} % \end{macrocode} % The default for \env{list} environments is that they have an % empty label and are not numbered (something that is then % overwritten by the setup of a specific list). We ensure % this here even for non-lists, because we need a defined state % that then can be overwritten by the legacy setup code for % the \env{list} environment in \cs{l_@@_legacy_code_tl}. % This is needed in case lists are nested as they otherwise would % inherit outer values (and suddenly an \env{itemize} would start % incrementing an outer \env{enumerate} counter, etc. % \changes{v0.8v}{2024/10/11}{Set the defaults for \tn{@itemlabel}, % \tn{@listctr} and \texttt{@nmbrlist} early in the block code % before the setup code gets executed (tagging/730)} % \begin{macrocode} \tl_clear:N \@itemlabel \tl_clear:N \@listctr \legacy_if_set_false:n { @nmbrlist } % \end{macrocode} % Then run the legacy setup code if any is given in the instance. % \begin{macrocode} \l_@@_legacy_code_tl % \end{macrocode} % Next call a block instance at the appropriate level passing it % any remaining key/value from the optional document-level argument % (i.e., those now stored in \cs{UnusedTemplateKeys}). % % % \changes{v0.9j}{2025/07/11}{Expand arguments before passing them to % \cs{UseInstance} to get better error messages} % \begin{macrocode} \exp_args:Nee \UseInstance{block} { \l_@@_block_instance_tl - \int_use:N \g_block_nesting_depth_int } \UnusedTemplateKeys % \end{macrocode} % After this instance has been processed, any remaining unused keys % are stored in \cs{UnusedTemplateKeys} and we can make use of this % data later as long as we do not call another instance that also % does unused key processing and overwrites it. But this is what % happens below, so we better save its current value for now. % \begin{macrocode} \tl_set_eq:NN \l_@@_unused_blockenv_keys_tl \UnusedTemplateKeys % \end{macrocode} % After the block instance call the para and then inner (list) % instance if either or both are % specified (which may not be the case). % \begin{macrocode} \tl_if_empty:NF \l_@@_para_instance_tl { % \end{macrocode} % For now we don't offer to alter instance parameters here so we % pass an empty argument.^^A % \changes{v0.9j}{2025/07/11}{Expand arguments before passing them to % \cs{UseInstance} to get better error messages} % \begin{macrocode} \exp_args:Ne \UseInstance{para}{ \l_@@_para_instance_tl } {} } % \end{macrocode} % The inner instance may have its own levels or none depending % on which the instance name differs. Again we pass it the optional % key/value list. % \begin{macrocode} \tl_if_empty:NF \l_@@_inner_instance_tl { % \end{macrocode} % We expand the first two arguments so that we get proper names for % template type and instance, because \cs{UseInstance} is not doing % that for us in the right way. % \changes{v0.9j}{2025/07/11}{Expand arguments before passing them to % \cs{UseInstance} to get better error messages} % \begin{macrocode} \exp_args:Nee \UseInstance{ \l_@@_inner_instance_type_tl } { \l_@@_inner_instance_tl \tl_if_empty:NF \l_@@_inner_level_counter_tl % not clean use "o"? { - \int_use:N \l_@@_inner_level_counter_tl } } \l_@@_unused_blockenv_keys_tl #2 % <-- \BooleanTrue or False { #3 } % <-- \NoValue or content { #4 } % <-- \NoValue or content % \end{macrocode} % Again the instance may have processed a few keys from the so far % unused keys, so we update \cs{l_@@_unused_blockenv_keys_tl} to % match the new reality. % \begin{macrocode} \tl_set_eq:NN \l_@@_unused_blockenv_keys_tl \UnusedTemplateKeys } % \end{macrocode} % At this point, the \cs{l_@@_unused_blockenv_keys_tl} token list % should either be empty or it should contain only keys that are suitable for % the item template, but right now there is no code to test that % can test % the latter; it would help probably if we have an interface for % this. % % For now we handle that when the first item is encountered, but % that isn't really clean.\fmi{fix} % \begin{macrocode} % \tl_if_empty:NF \l_@@_unused_blockenv_keys_tl % { % % check if only item template keys remain % } % \end{macrocode} % If this is supposed to be a transparent block environment then we % have to decrement the nesting level again so that nested % environments think nothing is there. % \begin{macrocode} \bool_if:NT \l_@@_transparent_level_bool { \int_gdecr:N \g_block_nesting_depth_int } % \end{macrocode} % We finish off with \cs{l_@@_final_code_tl} which defaults to % \tn{ignorespaces} so that spaces between \verb=\begin{...}= and % the start of the text are ignored. % \begin{macrocode} \l_@@_final_code_tl } % \end{macrocode} % \end{template} % % % \begin{hookdecl}{blockenv} % Might want a hook or a socket for legacy support.\fmi{decide} % \begin{macrocode} \NewHook{blockenv} % \end{macrocode} % \end{hookdecl} % % \begin{macro}{\BlockEnv,\SimpleBlockEnv} % To simplify the environment declarations later we provide two % simple commands that invoke a \insttype{blockenv} instance. The % matching counterpart to these commands is \cs{BockEnvEnd} % (defined below) that carries out all necessary action when a % block environment ends. % \begin{macrocode} \cs_new_protected:Npn \BlockEnv % #1#2#3#4 implicit { \UseInstance{blockenv} } % \end{macrocode} % % This here is the most common one that hides arguments 2--4 when % they aren't needed, e.g., in a \env{center} environment. % \begin{macrocode} \cs_new_protected:Npn \SimpleBlockEnv #1#2 { \UseInstance{blockenv}{#1}{#2} \BooleanFalse \NoValue \NoValue } % \end{macrocode} % \end{macro} % % % % \begin{macro}{\g_block_nesting_depth_int} % \LaTeXe{} already has a counter to record the nesting depth of % blocks, but we want our own name because it isn't really tied to % \enquote{lists} any more. However, \tn{@listdepth} is really part % of the legacy interface (for example \env{minipage} alters it to % point to a different counter) so that we are stuck with using at % least indirectly for now and the following line makes this look % like an L3 integer variable but internally expands to \tn{@listdepth}: % \begin{macrocode} \cs_new_protected:Npn \g_block_nesting_depth_int { \@listdepth } % a fake int % for now % \end{macrocode} % \end{macro} % % % % \begin{macro}{\l_@@_unused_blockenv_keys_tl} % The token list that holds key values we haven't yet used while % we are processing the instances in a block environment. % \begin{macrocode} \tl_new:N \l_@@_unused_blockenv_keys_tl % \end{macrocode} % \end{macro} % % % \begin{macro}{\l__tag_block_flattened_level_int} % Count the levels of nested \insttype{blockenv}s starting with the first that % is \enquote{flattened}. The counter is defined in lttagging.dtx, % but until the next release 11/24 we set it up here too % \begin{macrocode} \int_if_exist:NF \l__tag_block_flattened_level_int { \int_new:N \l__tag_block_flattened_level_int } % \end{macrocode} % \end{macro} % % \begin{macro}{\c@maxblocklevels} % A counter to increase or decrease the number of supported % level. If increased, one needs to supply additional level instances. % \begin{macrocode} \newcounter{maxblocklevels} \setcounter{maxblocklevels}{6} % \end{macrocode} % \end{macro} % % \begin{macro}{\BlockEnvEnd} % The code executed when a \insttype{blockenv} ends is 99\% the % same for all \insttype{blockenv}s (at least up to now). Small % differences exist, though. They are accounted for first in the % conditionals. % % \changes{v0.9l}{2025/11/21}{Renamed \cs{endblockenv} to \cs{BlockEnvEnd}} % % We make this a public command so that new block environments can % be set up without the need to resort to L3 layer % programming. % \begin{macrocode} \cs_new_protected:Npn \BlockEnvEnd { \@@_debug_typeout:n{blockenv~ common~ ending \on@line} % \end{macrocode} % If this block is not a transparent one we have to decrement the level % now again, otherwise that had happened earlier: % \begin{macrocode} \bool_if:NF \l_@@_transparent_level_bool { \int_gdecr:N \g_block_nesting_depth_int } % \end{macrocode} % If the \texttt{@inlabel} switch is true, i.e., if there is a % caption or an item waiting to be placed we move to horizontal % mode to get them typeset. % \begin{macrocode} \legacy_if:nT { @inlabel } { \mode_leave_vertical: \legacy_if_gset_false:n { @inlabel } } % \end{macrocode} % If we are ending a list environment and we have not seen any % \tn{item}, i.e., \texttt{@newlist} is still true, we raise an % error. In basic a ``displayblock'' scenario \texttt{@newlist} will % always be false, but if such an environment appears inside an outer % list then \tn{noitemerr} could still be triggered and that is undesirable % (as the missing item will be detected at the wrong point and again later, % during the outer list processing). We % therefore run it only if the current environment is a list. % \changes{v0.8q}{2024/09/03}{Raise a \tn{@noitemerr} if appropriate} % \begin{macrocode} \@@_if_list:T { \legacy_if:nT { @newlist } { \@noitemerr } } % \end{macrocode} % \begin{macrocode} \mode_if_horizontal:TF { \@@_skip_remove_last: \@@_skip_remove_last: \par } { \@inmatherr{\end{\@currenvir}} } % \end{macrocode} % Once we are back in vertical mode we can add the appropriate % closing tagging structure(s), if we are doing tagging. % \begin{macrocode} \__kernel_displayblock_end: % \end{macrocode} % Resetting the \texttt{@newlist} switch is also only done if the % current environment is a list. % \changes{v0.8q}{2024/09/03}{Setting \texttt{@newlist} to false % moved after tagging code if in a list} % \begin{macrocode} \@@_if_list:T { \legacy_if_gset_false:n { @newlist } } % \end{macrocode} % % There is a possibility that the \texttt{@nobreak} switch is still % true so we set it back just in case. % \begin{macrocode} \legacy_if_gset_false:n { @nobreak } % \end{macrocode} % What to do in terms of vertical spacing in different situations % is still somewhat open to debate, right now this is more or less % implementing what \LaTeXe{} list environments have been % doing.\fmi{some redesign/extensions here?} % \begin{macrocode} % \@@_debug_typeout:n{@noparlist = % \legacy_if:nTF { @noparlist }{true}{false}} \legacy_if:nF { @noparlist } { \@@_skip_set_to_last:N \l_tmpa_skip \dim_compare:nNnT \l_tmpa_skip > \c_zero_dim { \skip_vertical:n { - \l_tmpa_skip } \skip_vertical:n { \l_tmpa_skip + \parskip - \@outerparskip } } \addpenalty \@endparpenalty \addvspace \l_@@_topsepadd_skip % \end{macrocode} % \LaTeXe{} triggered the paragraph handling after a list at this % point here, i.e., only if the list didn't start a paragraph. One % can make a case for that, but it can be somewhat surprising to % the user and there is a good argument that even such a list could % be followed explanatory text that is part of the same paragraph % and doesn't start a new one.\fmi{decide which logic we want to % use! If the old logic is used we need to close the text-unit % ourselves in the true branch} % \begin{macrocode} % \legacy_if_gset_true:n { @endpe } } % \end{macrocode} % So this is for now always done. Probably \cs{l_@@_topsepadd_skip} above % should be added only if the paragraph ends here and not if it % continues, so this need some further cleanup.\fmi{decide} % % Finally, we have a socket that handles the \tn{par} handling % after the block. Normally, we use it with the \plug{on} plug (check for a % following \tn{par}) but in the case of standalone environments we % assign it the \plug{off} plug. % % \begin{macrocode} \socket_use:n {block/endpe} } % \end{macrocode} % % \end{macro} % % % \begin{macro}{\@@_if_list:T} % The following code may need some redesigning, as there is no good test for \enquote{is % this environment a \enquote{list} that has \tn{item}s}. For now % this here does the trick well enough.\fmi{revisit and correct} % \changes{v0.8q}{2024/09/03}{Provide a test for: Am I in a list?} % \begin{macrocode} \cs_new:Npn \@@_if_list:T { \tl_if_eq:NnT \l_@@_block_instance_tl {std-list} } % \end{macrocode} % \end{macro} % % % % \begin{macro}{\__kernel_displayblock_end:} % The kernel hook for tagging at the end of the block. % \begin{macrocode} \cs_new_protected:Npn \__kernel_displayblock_end: { \@@_debug_typeout:n{\detokenize{__kernel_displayblock_end:}} } % \end{macrocode} % \end{macro} % % % % \begin{socketdecl}{block/endpe} % This socket is responsible for the end environment \tn{par} % handling. We define two plugs for it (\plug{on} and \plug{off}). % \begin{macrocode} \socket_new:nn {block/endpe} {0} % \end{macrocode} % \end{socketdecl} % % \begin{plugdecl}{on,off} % The plugs set the legacy \texttt{@endpe} switch. This must always % happen because block environments with different settings can be nested % and should not inherit the setting from the outer environment. % % We can't use \cs{legacy_if_gset_true:n} because this is now doing % more than setting the legacy switch: % \begin{macrocode} \socket_new_plug:nnn{block/endpe} {on} { \@endpetrue } \socket_new_plug:nnn{block/endpe} {off} { \@endpefalse } % \end{macrocode} % % \begin{macrocode} \socket_assign_plug:nn{block/endpe}{on} % \end{macrocode} % \end{plugdecl} % % % % % % \subsubsection{Implementation of \insttype{para} templates} % % \begin{template}{para std} % % \begin{macrocode} \DeclareTemplateCode{para}{std}{1} { ,para-indent = \parindent % \end{macrocode} % The next parameter needs integrating in the basic paragraph % handling (not done yet) and it should therefore probably a public % name like the rest.\fmi{integrate/fix} % \begin{macrocode} ,begin-hspace = \l_para_begin_skip ,left-hspace = \leftskip ,right-hspace = \rightskip ,end-hspace = \parfillskip % \end{macrocode} % Next isn't yet implemented (and the variable name is wrong).\fmi{fix} % \begin{macrocode} ,fixed-word-spaces = \l__par_fixed_word_spaces_bool % name?? ,final-hyphen-demerits = \finalhyphendemerits ,newline-cmd = \\ ,para-attr-class = \l__tag_para_attr_class_tl } { \template_debug_typeout:n{~\space template:~ 'std';~ argument:~ \exp_not:n{|#1|}} \SetTemplateKeys{para}{std}{#1} % \end{macrocode} % % \begin{macrocode} \skip_set:Nn \@rightskip \rightskip } % \end{macrocode} % \end{template} % % % \begin{macro}{\__para_handle_indent:} % We insert \cs{l_para_begin_skip} directly in front of the % indentation box. This way it is hidden from any special setting % of \cs{everypar} (whether that is used to remove the % indentation box or whether it attempts to do something with the % first token(s) of the paragraph). However, we only insert it if % it differs from 0.0pt to avoid adding % \verb=\penalty 10000 \glue 0.0= all over the place. % \begin{macrocode} \tl_const:Ne \c__zero_skip_tl { \skip_use:N \z@skip } \tl_new:N \l__para_begin_skip_tl % \end{macrocode} % % \begin{macrocode} \cs_set:Npn \__para_handle_indent: { \tl_set:Ne \l__para_begin_skip_tl { \skip_use:N \l_para_begin_skip } \if_meaning:w \l__para_begin_skip_tl \c__zero_skip_tl \else: \nobreak \tex_hskip:D \l_para_begin_skip \fi: \box_use_drop:N \g_para_indent_box } % \end{macrocode} % \end{macro} % % % \begin{macro}{\para_raw_noindent:} % \cs{para_raw_noindent:} doesn't call \cs{__para_handle_indent:} % so we have to manually do the \cs {l_para_begin_skip} handling. % \begin{macrocode} \cs_set:Npn \para_raw_noindent: { \mode_if_vertical:TF { \tex_everypar:D { \tex_everypar:D { \g__para_standard_everypar_tl } \tl_set:Ne \l__para_begin_skip_tl { \skip_use:N \l_para_begin_skip } \if_meaning:w \l__para_begin_skip_tl \c__zero_skip_tl \else: \nobreak \tex_hskip:D \l_para_begin_skip \fi: \the\everypar } } { \msg_error:nn { latex2e }{ raw-para } } \tex_noindent:D } % \end{macrocode} % \end{macro} % % % \subsubsection{Implementation of \insttype{block} templates} % % % \begin{template}{block std} % ^^A % In contrast to the \LaTeXe{} implementation we do not directly % use \tn{listparindent} here but a private register of the % template. The reason is that block template instances are also % used outside of lists. % \changes{v0.8s}{2024/10/03}{Offer item-vspace key also on block templates} % \changes{v0.8w}{2024/11/23}{Use private storage bin for parindent % and not \tn{listparindent} to decouple block and list templates (tagging/767)} % \begin{macrocode} \DeclareTemplateCode{block}{std}{1} { ,begin-vspace = \topsep ,begin-extra-vspace = \partopsep ,begin-unchained-vspace = \l_@@_unchained_skip ,para-vspace = \parsep % \end{macrocode} % The bottom skips aren't used yet, even if set instead as before % \cs{topsep} is applied there.\fmi{fix} % \begin{macrocode} ,end-vspace = \l_@@_botsep_skip ,end-extra-vspace = \l_@@_parbotsep_skip ,item-vspace = \itemsep ,begin-penalty = \@beginparpenalty ,end-penalty = \@endparpenalty ,item-penalty = \@itempenalty ,right-margin = \rightmargin ,left-margin = \leftmargin ,para-indent = \l_@@_parindent_dim } { \template_debug_typeout:n{~\space template:~ 'std';~ argument:~ \exp_not:o{\exp_after:wN |#1|}} \SetKnownTemplateKeys{block}{std}{#1} % \end{macrocode} % The code largely follows the logic of \LaTeXe{}'s \env{trivlist} % implementation as far as it applicable for the \enquote{display % block} but coded using the L3 programming layer. However, we keep % most of the legacy variables (e.g., \texttt{@noskipsec}) if there is % some chance that they are set/used in classes or packages. % \begin{macrocode} \legacy_if:nTF { @noskipsec } % \end{macrocode} % A \texttt{@noskipsec} heading is a heading that is placed in the % same line as the following text (using \cs{everypar}) but not if % that text starts with a display block, so we ensure that the % heading gets typeset now.\fmi{This is similar to the standalone case % for block captions so perhaps that can be combined, check} % \begin{macrocode} { \mode_leave_vertical: } % \end{macrocode} % If no such heading is waiting we might have a block caption % waiting to be typeset and this might be requested to be set % \enquote{unchained}. In that case we also have to ensure % that this gets typeset now. % % The situation is slightly different though, because we want to % end in vertical mode in that case also add % some special vertical space and have to properly deal with % avoiding page breaks. % \begin{macrocode} { \bool_if:NT \g_@@_label_unchained_bool { \@@_debug_typeout:n{Set~ captioned~ block~ everypar \on@line } \cs_set_eq:NN \@@_everypar: \@@_captioned_everypar_std: \legacy_if:nT { @inlabel } { \hbox_unpack_drop:N \g_@@_labels_box \legacy_if_gset_false:n { @inlabel } \par \nobreak \skip_vertical:n { \l_@@_unchained_skip } \legacy_if_gset_true:n { @nobreak } } } } \skip_set:Nn \l_@@_topsepadd_skip { \topsep } \mode_if_vertical:TF { \skip_add:Nn \l_@@_topsepadd_skip { \partopsep } % \end{macrocode} % At this point it is safe to add tagging structure(s) so we have % a kernel-owned hook here for tagging. This is used to possibly % start a paragraph structure (to surround the block, for example, % in case of lists) and possibly do some other preparation for % tagging the block. % \begin{macrocode} \__kernel_displayblock_beginpar_vmode: } { % \end{macrocode} % If we are in horizontal mode then the displayblock has to return % to vertical mode now (after removing any immediately preceding % skip or kern. But before we actually issue the \tn{par} we execute % a kernel hook in which we can add tagging code. This hook is % \enquote{weird} because by default it does nothing, but if % tagging is wanted it takes an argument and grabs the following % \tn{par} in order to put tagging code before and after the \tn{par}. % \begin{macrocode} \@@_skip_remove_last: \@@_skip_remove_last: \__kernel_displayblock_beginpar_hmode:w \par } % \end{macrocode} % Next lines set some paragraph defaults, any of them may get overwritten % if there is a \key{para-instance} specified on the \insttype{blockenv} % instance. % \begin{macrocode} \skip_zero:N \leftskip \skip_set_eq:NN \rightskip \@rightskip \skip_set_eq:NN \parfillskip \@flushglue % \end{macrocode} % The next lines establish a parshape which is retained across % paragraphs be executing \cs{para_end:} within a group and thus % reestablishing the parshape for the next paragraph again. In case % a list got started \tn{par} is ignored until we have seen an % \tn{item} (or we have executed \tn{par} one thousand times. % \begin{macrocode} \int_zero:N \par@deathcycles \@setpar { \legacy_if:nTF { @newlist } { \int_incr:N \par@deathcycles \int_compare:nNnTF \par@deathcycles > { 1000 } { \@noitemerr { \para_end: } } } { { \para_end: } } } % \end{macrocode} % \changes{v0.8w}{2024/11/23}{Use private storage bin for parindent % and not \tn{listparindent} to decouple block and list templates (tagging/767)} % \begin{macrocode} \dim_set_eq:NN \parindent \l_@@_parindent_dim \dim_add:Nn \linewidth { - \rightmargin - \leftmargin } \dim_add:Nn \@totalleftmargin { \leftmargin } \tex_parshape:D 1 ~ \@totalleftmargin \linewidth % \end{macrocode} % This is the point where we are ready to add the tagging structure % for the block, e.g., an \verb==, a \verb=
= or some % other structure. % \begin{macrocode} \__kernel_displayblock_begin: % \end{macrocode} % Finally, we have to output the vertical separation and penalty at % the start of the block and make corrections for a change in % \tn{parskip} and some other housekeeping, unless this block is inside a list and the list % \tn{item} has not yet placed. In that case the vertical % space and penalty is suppressed. This % is controlled through the legacy switches \texttt{@inlabel}, % \texttt{minipage}, and \texttt{@nobreak}. % % Now we are back to legacy list implementation \ldots % \begin{macrocode} \skip_set_eq:NN \@outerparskip \parskip \skip_set_eq:NN \parskip \parsep % \legacy_if:nTF { @inlabel } { \legacy_if_set_true:n { @noparlist } \hbox_gset:Nn \g_@@_labels_box { \skip_horizontal:n { - \leftmargin } \hbox_unpack_drop:N \g_@@_labels_box \skip_horizontal:n { \leftmargin } } % \end{macrocode} % \fmi{document 2e logic used here} % \begin{macrocode} \legacy_if:nF { @minipage } % Why this chunk of code? { \@@_skip_set_to_last:N \l_@@_tmpa_skip \skip_vertical:n { - \l_@@_tmpa_skip } \skip_vertical:n { \l_@@_tmpa_skip + \@outerparskip - \parsep } } } { \legacy_if_set_false:n { @noparlist } \legacy_if:nT { @newlist } { \@noitemerr } \legacy_if:nTF { @nobreak } { % \end{macrocode} % We are not resetting \texttt{@nobreak} here as it should also % apply to the upcoming item. % \begin{macrocode} \addpenalty{ 10000 } \addvspace{ \skip_eval:n{\@outerparskip-\parsep} } } { \addpenalty \@beginparpenalty \addvspace { \skip_eval:n { \l_@@_topsepadd_skip + \@outerparskip } } \addvspace { - \parsep } } } } % \end{macrocode} % \end{template} % % % % % % \begin{macro}{\@@_captioned_everypar_std:} % The captioned text is typeset at the start of a paragraph using % code triggered in \cs{everypar} (by setting \cs{@@_everypar} to % this code here). % \begin{macrocode} \cs_new_protected:Npn \@@_captioned_everypar_std: { \@@_debug_typeout:n{...~ in~ captioned~ block~ everypar \on@line } % \end{macrocode} % First set some control switches to false: % \begin{macrocode} \legacy_if_set_false:n { @minipage } \legacy_if_gset_false:n { @newlist } % \end{macrocode} % The \texttt{@inlabel} is normally true at this point, but if we % also have \texttt{@nobreak} then the same routine is called again % at the next paragraph to reset \cs{clubpenalty} and at that point % the \cs{g_@@_labels_box} has been typeset and \texttt{@inlabel} % is false. % \begin{macrocode} \legacy_if:nT { @inlabel } { % \end{macrocode} % Typeset the saved label (aka captioned text): % \begin{macrocode} \legacy_if_gset_false:n { @inlabel } \para_omit_indent: \box_use_drop:N \g_@@_labels_box \__kernel_list_label_after:n { \PARALABEL } % <- change % this name \penalty \c_zero_int } % \end{macrocode} % If \texttt{@nobreak} is true we prevent a break after the first % line by setting \cs{clubpenalty}. % \begin{macrocode} \legacy_if:nTF { @nobreak } { \legacy_if_gset_false:n { @nobreak } \int_set:Nn \clubpenalty { 10000 } } { % \end{macrocode} % Otherwise we reset \cs{clubpenalty} and disable \cs{@@_everypar}. % \begin{macrocode} \int_set_eq:NN \clubpenalty \@clubpenalty \@@_debug_typeout:n{Set~ noop~ block~ everypar \on@line } \cs_set_eq:NN \@@_everypar: \prg_do_nothing: } } % \end{macrocode} % \end{macro} % % % % % % % % \begin{macro}{\__kernel_displayblock_begin:, % \__kernel_displayblock_beginpar_hmode:w, % \__kernel_displayblock_beginpar_vmode:} % The internal kernel hooks for tagging. % \begin{macrocode} \cs_new_protected:Npn \__kernel_displayblock_begin: { \@@_debug_typeout:n {\detokenize{__kernel_displayblock_begin:}} } % \end{macrocode} % % \begin{macrocode} \cs_new_protected:Npn \__kernel_displayblock_beginpar_hmode:w { \@@_debug_typeout:n {\detokenize{__kernel_displayblock_beginpar_hmode:w}} } % \end{macrocode} % % \begin{macrocode} \cs_new_protected:Npn \__kernel_displayblock_beginpar_vmode: { \@@_debug_typeout:n {\detokenize{__kernel_displayblock_beginpar_vmode:}} } % \end{macrocode} % \end{macro} % % % % % \subsubsection{Implementation of \insttype{list} templates} % % This \insttype{list} is one of the template types that can be used % as an \key{inner-type} in a \insttype{blockenv}; the other one % currently implemented is \insttype{captionedtext}. % % \begin{macro}{\@itemlabel,\@listctr} % Both \tn{@itemlabel} and \tn{@listctr} from the \LaTeXe{} list % implementation are used (or set) by various packages. We % therefore use them too, so that these packages have a fighting % chance to work with the new tagging-aware implementation for % \env{list}. % \begin{macrocode} \tl_new:N \@itemlabel % should have a top-level definition \tl_new:N \@listctr % should have a top-level definition % \end{macrocode} % \end{macro} % % % % \begin{macro}{\@@_evaluate_saved_user_keys:nn} % Keys set on individual list environments may be intended to alter % the behavior of the template instance that defines the \tn{item} % command. If meant to alter only a single \tn{item} command one % would specify them in the optional argument of the \tn{item}, but % if they should alter all items the right place would be the list % environment. For this reason we need to store the values and then % set them inside the \tn{item} template code using % \tn{SetKnownTemplateKeys} in the appropriate context (template type % and template name). This is done in % \cs{@@_evaluate_saved_user_keys:nn}. The context is provided in % the two arguments (because different list environments may use % different \tn{item} instances based on different templates. By % default the command does % nothing because most environments do not have user key settings. % \changes{v0.8s}{2024/10/03}{Pass user keys on list to \tn{item} for % evaluation} % \begin{macrocode} \cs_new_eq:NN \@@_evaluate_saved_user_keys:nn \use_none:nn % \end{macrocode} % Maybe something like this should become a public function, but % for now this is a one-off for the \tn{item} command and therefore % coded inline and internal to the block code. % \begin{macrocode} %\cs_new:Npn \@@_save_user_keys:n #1 { % \tl_if_empty:nTF {#1} % { \cs_set_eq:NN \@@_evaluate_saved_user_keys:nn \use_none:nn } % { \cs_set:Npe \@@_evaluate_saved_user_keys:nn ##1##2 % { \SetKnownTemplateKeys{##1}{##2}{ \exp_not:n{#1} } } %} % \end{macrocode} % \end{macro} % % % \begin{template}{list std} % % This template implements numbered and unnumbered lists and can % be combined with display blocks or with inline blocks. % \begin{macrocode} \DeclareTemplateCode{list}{std}{4} { ,counter = \l_@@_counter_tl ,item-label = \l_@@_item_label_tl ,start = \l_@@_counter_start_int ,resume = \l_@@_resume_bool ,item-instance = \@@_item_instance:n ,item-vspace = \itemsep % ,item-para-vspace = \parsep ,item-penalty = \@itempenalty ,item-indent = \itemindent ,label-width = \labelwidth ,label-sep = \labelsep ,legacy-support = \l_@@_legacy_support_bool % FMi questionable } { \template_debug_typeout:n{~\space template:~ 'std';~ arguments:~ \exp_not:o{\exp_after:wN |#1|#2|#3|#4|}} % \end{macrocode} % We start by looking at the user supplied keys in \texttt{\#1}. If % there aren't any we reset \cs{@@_evaluate_saved_user_keys:nn} to % do nothing. Otherwise we evaluate and set the keys in the context % of the current list template. In addition we prepare % \cs{@@_evaluate_saved_user_keys:nn} for execution in the % template for \tn{item}. % \changes{v0.8s}{2024/10/03}{Prepare \cs{@@_evaluate_saved_user_keys:nn} % for use in \tn{item}} % \begin{macrocode} \tl_if_empty:oTF {#1} { \cs_set_eq:NN \@@_evaluate_saved_user_keys:nn \use_none:nn } { \SetKnownTemplateKeys{list}{std}{#1} % \end{macrocode} % The setup for \cs{@@_evaluate_saved_user_keys:nn} is a bit tricky % and has to be done with \cs{cs_set:Npe} even though we don't want % to expand anything and therefore use \cs{exp_not:n} inside. All % this does is that any \texttt{\#} passed in via \texttt{\#1} is % doubled (e.g., from \verb/label-format=\fbox{#1}/ which is % represented as \verb/...\fbox{##1}/). Otherwise, we would end up % with a replacement text like %\begin{verbatim} % \SetTemplateKeys {#1}{#2}{label-format=\fbox {#1}} %\end{verbatim} % instead of %\begin{verbatim} % \SetTemplateKeys {#1}{#2}{label-format=\fbox {##1}} %\end{verbatim} % resulting in very odd and puzzling behavior later on. % % The definition of \cs{@@_evaluate_saved_user_keys:nn} made here % is later used when an \cs{item} is processed and passes % remaining keys to the item instance. After that nothing should % remain, so we test that and issue an error if not. % \begin{macrocode} \cs_set:Npe \@@_evaluate_saved_user_keys:nn ##1##2 { \SetKnownTemplateKeys{##1}{##2}{ \exp_not:o { \UnusedTemplateKeys } } \exp_not:n { \tl_if_empty:NF \UnusedTemplateKeys { \msg_error:nnee { block } { unknown-keys } { \l_@@_env_name_tl \space environment} \UnusedTemplateKeys } } } } % \end{macrocode} % Has this list a counter name defined in the instance? % \begin{macrocode} \tl_if_empty:NTF \l_@@_counter_tl { % \end{macrocode} % If no counter name has been specified as part of the instance % setup the list might still be numbered if it is a legacy list % that uses \cs{usecounter} in the second argument of the legacy % \env{list} environment. However, in that case we don't have to do % much because \cs{usecounter} sets up \cs{@listctr} and sets % it to zero so that the first item is numbered 1. % % So all we do is to check if there was a \key{start} value given % that differs from 1 and if so we change the counter value to % match that. This makes it possible to define a legacy \env{list} % in which the counter doesn't start with 1 by explicitly setting % the counter value in the second argument of the \env{list} % environment but also overwriting that through a \key{start} key % setting on invocation. % \changes{v0.8v}{2024/10/11}{Correct logic for setting up the list % counter (tagging/730)} % \changes{v0.9f}{2025/06/21}{Correct logic for setting up the list % counter further (tagging/893)} % \begin{macrocode} \int_compare:nNnF \l_@@_counter_start_int = 1 { \int_gset:cn{ c@ \@listctr } { \l_@@_counter_start_int - 1 } } } % \end{macrocode} % In that case we only check if we should resume a previous list % (\tn{@listctr} should be set in that case through the legacy % method as well so we should be able to use it). % \begin{macrocode} % \end{macrocode} % If a counter is set in the list instance we use that % one. This should be the name of a \LaTeX{} counter that is % already allocated externally---no runtime check is made for this: % if it is not declared one will get \enquote{no such counter} % error when the list is used. % \begin{macrocode} { \@nmbrlisttrue \tl_set_eq:NN \@listctr \l_@@_counter_tl \bool_if:NF \l_@@_resume_bool { \int_gset:cn{ c@ \@listctr } { \l_@@_counter_start_int - 1 } } } % \end{macrocode} % Does the current instance have an item label representation? This % would be possible whether or not we have a numbered list. If yes, % then we use this for \tn{@itemlabel}, otherwise we expect that % \tn{@itemlabel} is provided from the outside, e.g., as part of % the \env{list} environment argument. % \begin{macrocode} \tl_if_empty:NF \l_@@_item_label_tl { \tl_set_eq:NN \@itemlabel \l_@@_item_label_tl } % \end{macrocode} % Finally, we signal that we are at the start of a new list (which % affects how the first \tn{item} is handled and how \tn{par} % commands are interpreted. % \begin{macrocode} \legacy_if_gset_true:n { @newlist } % \end{macrocode} % If we encounter horizontal material before the first \tn{item} we % do want a \tn{@noitemerr} straight away, because afterwards we % end up with tagging structure faults whose cause is the % missing \tn{item}. So we set up \cs{@@_everypar:} to test % for this; when the first \tn{item} is encountered this will get % reset. This is only relevant for vertical lists, when dealing with % inline lists one would need to test for something else to % identify that there is horizontal material between the start of the list and % the first \tn{item} (maybe some \tn{spacefactor} trick could be % used then, or the material is boxed first and the width is % inspected as suggested by Joseph).\fmi{Think about a better % implementation at some point.} % \changes{v0.8q}{2024/09/02}{} % \begin{macrocode} \@@_debug_typeout:n{Set~ first~ block~ everypar \on@line } \cs_set_eq:NN \@@_everypar: \@@_item_everypar_first: % \end{macrocode} % % \begin{macrocode} \@@_debug_typeout:n{template:list:std~end} } % \end{macrocode} % % The message that is used above when we are left with keys that % are unknown: % \begin{macrocode} \msg_new:nnnn { block } { unknown-keys } { Some~ keys~ specified~ on~ the~ #1~ are~ unknown. } { The~ following~ keys~ are~ unknown~ and~ their~ values~ are~ ignored:\\ \space\space #2\\ Perhaps~ a~ misspelling~ or~ the~ current~ template~ instance~ uses~ special~ keys. } % \end{macrocode} % \end{template} % % % \subsubsection{Implementation of \insttype{item} templates} % % % % \begin{template}{item std} % The item template has one hidden key \key{label} which is not available on the % template for setting because it is only used to receive any % optional data passed to the \tn{item} command. We therefore % declare it with \cs{keys_define:nn} and ensure that the optional % argument data to \tn{item} (if it is not a key/value list % already) is passed to this \key{label} key. % \begin{macrocode} \keys_define:nn { template/item/std } { label .tl_set:N = \l_@@_label_given_tl } % \end{macrocode} % % \fmi{alignment is mostly wrong (test short medium and multiline % labels)} % \begin{macrocode} \DeclareTemplateCode{item}{std}{1} { ,counter-label = \@@_counter_label:n ,counter-ref = \@@_counter_ref:n % \end{macrocode} % \fmi{next set of key not yet used} % \begin{macrocode} ,label-ref = \@@_label_ref:n ,label-autoref = \@@_label_autoref:n ,label-format = \@@_label_format:n ,label-strut = \l_@@_label_strut_bool ,label-boxed = \l_@@_label_boxed_bool ,next-line = \l_@@_next_line_bool ,text-font = \l_@@_text_font_tl ,compatibility = \l_@@_item_compatibility_bool % \end{macrocode} % This probably needs a different implementation (and needs completing)\fmi{complete} % \begin{macrocode} ,label-align = { left = \tl_set:Nn \l_@@_item_align_tl { \relax \hss } , center = \tl_set:Nn \l_@@_item_align_tl { \hss \hss } , right = \tl_set:Nn \l_@@_item_align_tl { \hss \relax } , parleft = \NOT_IMPLEMENTED , } % \end{macrocode} % The key{label-placement} is implemented using two booleans (at % the moment). % \begin{macrocode} ,label-placement = { chained = \bool_gset_false:N \g_@@_label_standalone_bool \bool_gset_false:N \g_@@_label_unchained_bool , unchained = \bool_gset_false:N \g_@@_label_standalone_bool \bool_gset_true:N \g_@@_label_unchained_bool , standalone = \bool_gset_true:N \g_@@_label_standalone_bool \bool_gset_false:N \g_@@_label_unchained_bool , } } % \end{macrocode} % Then typeset the label at its natural width by applying % \cs{@@_make_label_box:n} to the label given or to a label constructed % from the counter. If it is boxed and reasonably short, add padding to % make it at least of size \tn{labelwidth}, then add another layer of % box. This way, when we unpack it in \cs{g_@@_labels_box} it correctly % remains boxed in those cases. Afterwards, in the \keyvalue{nextline} % case add \tn{newline} if the label did not fit in the allotted space. % \begin{macrocode} { \template_debug_typeout:n{~\space template:~ 'std';~ argument:~ \exp_not:n{|#1|}} % \end{macrocode} % % First deal with the key--value input, which in particular may % provide a value for the label (the usual optional argument of % \tn{item}). For this we set \cs{l_@@_label_given_tl} to % \cs{c_novalue_tl} so that we can identify if an optional argument % was given. % \begin{macrocode} \tl_set_eq:NN \l_@@_label_given_tl \c_novalue_tl % \end{macrocode} % First we evaluate and set any keys specified on the list % environment by calling % \cs{@@_evaluate_saved_user_keys:nn}. Then we do the same % with all keys specified on this \tn{item} command (which may % overwrite one or the other setting just made). % \changes{v0.8s}{2024/10/03}{Use \cs{@@_evaluate_saved_user_keys:nn} % to make use of user keys on the list level} % \begin{macrocode} \@@_evaluate_saved_user_keys:nn {item}{std} % \end{macrocode} % We don't care whether all of the user keys from the list level have % been applied, but those explicitly set on the \cs{item} command % should be applicable, so we generate an error if that isn't the case: % \begin{macrocode} \SetKnownTemplateKeys{item}{std}{#1} \tl_if_empty:NF \UnusedTemplateKeys { \msg_error:nnee { block } { unknown-keys } { \noexpand\item command } \UnusedTemplateKeys } % \end{macrocode} % % If no optional argument was given then \cs{l_@@_label_given_tl} % is still equal to \cs{c_novalue_tl} and so we can distinguish % that from \verb=\item[]=. % % \fmi{next line needs checking after novalue implementation was changed} % \begin{macrocode} \tl_if_novalue:oTF \l_@@_label_given_tl { % \end{macrocode} % The rest of the code for this template needs work and is both % incomplete and partly wrong.\fmi{fix} % \begin{macrocode} \tl_if_blank:oF \@listctr { \@kernel@refstepcounter \@listctr } \bool_if:NTF \l_@@_item_compatibility_bool % not sure that % conditional % makes sense { \@@_make_label_box:n { \MakeLinkTarget[\@listctr]{}% \@itemlabel } } % TODO ? { \@@_make_label_box:n { \MakeLinkTarget[\@listctr]{}% \@@_counter_label:n { \@listctr } } } } % \end{macrocode} % \changes{v0.9c}{2025/05/17}{Add \cs{MakeLinkTarget}, tagging issue \#840} % \begin{macrocode} { \@@_debug_typeout:n{item~ with~ optional} \@@_make_label_box:n { \MakeLinkTarget [\l_@@_env_name_tl]{} \l_@@_label_given_tl } } \bool_if:nT { \l_@@_label_boxed_bool % TODO: is \linewidth correct? && \dim_compare_p:n { \box_wd:N \l_@@_one_label_box <= \linewidth } } { \dim_compare:nNnT { \box_wd:N \l_@@_one_label_box } < \labelwidth { \hbox_set_to_wd:Nnn \l_@@_one_label_box { \labelwidth } { \exp_after:wN \use_i:nn \l_@@_item_align_tl % \end{macrocode} % FMi: \LaTeXe{} keeps the label boxed inside (not unboxed). This % means that the content stays rigid and does not vary based on % glue setting in the line with the label. % There are cases where we do want the unboxed version (I think % enumitem offers that in some cases too) but it should probably % not the default. % \begin{macrocode} % TODO: customize? % \hbox_unpack_drop:N \l_@@_one_label_box \box_use_drop:N \l_@@_one_label_box % \end{macrocode} % % \begin{macrocode} \exp_after:wN \use_ii:nn \l_@@_item_align_tl } } % \end{macrocode} % Add another box level to the label box: % \begin{macrocode} \hbox_set:Nn \l_@@_one_label_box { \box_use_drop:N \l_@@_one_label_box } } \dim_compare:nNnTF { \box_wd:N \l_@@_one_label_box } > \labelwidth { \bool_set_true:N \l_@@_long_label_bool } { \bool_set_false:N \l_@@_long_label_bool } \hbox_gset:Nn \g_@@_labels_box { \hbox_unpack_drop:N \g_@@_labels_box \skip_horizontal:n { \itemindent - \labelsep - \labelwidth } \hbox_unpack_drop:N \l_@@_one_label_box \skip_horizontal:n { \labelsep } \bool_if:NT \l_@@_next_line_bool { \bool_if:NT \l_@@_long_label_bool { \nobreak \hfil \break } } % version of \newline inside an hbox that will be unpacked } % TODO??? FMi what's that? % \skip_set_eq:NN \parsep \l_@@_item_parsep_skip % \end{macrocode} % The next setting is for compatibility: The list template sets % \tn{listparindent} to zero and otherwise doesn't use it any % more. However, in the second argument of a legacy \env{list} % environment the user may have set it explicitly to some other % value and whatever value it had was then used for \tn{parindent} % within the list. Now we use its value only if it differs from % zero but otherwise use whatever the template instances % specify. This gives 99.9\% compatibility for legacy % documents. 100\% for definitions using the \env{list} environment % and a setting inside, but if the user used \tn{listparindent} % within the document, e.g., inside a \env{verse} environment there % there is one case in which the setting is ignored, i.e., when it % was set back to zero. That's a rather unlikely scenario, but it % is not impossible. However, I couldn't think of an approach that % circumvents such boundary cases. % % \changes{v0.8w}{2024/11/24}{Only use \tn{listparindent} if it was changed % from its default (tagging/767)} % \begin{macrocode} \dim_compare:nNnF \listparindent = {0pt} { \dim_set_eq:NN \parindent \listparindent } % \end{macrocode} % Placing the list label(s) is done when the paragraph for the % \tn{item} is started, which executes \cs{@@_everypar:} % inside \hook{para/begin}. By default this command does nothing, now we % change it to attach the pending label or labels. % \begin{macrocode} \@@_debug_typeout:n{Set~ item~ block~ everypar \on@line } \cs_set_eq:NN \@@_everypar: \@@_item_everypar_std: } % \end{macrocode} % \end{template} % % % \begin{macro}{g_@@_label_standalone_bool,g_@@_label_unchained_bool} % The two booleans for implementing \key{label-placement} and below % \key{caption-placement}. % \begin{macrocode} \bool_new:N \g_@@_label_standalone_bool % tmp until replaced \bool_new:N \g_@@_label_unchained_bool % tmp until replaced % \end{macrocode} % \end{macro} % % \begin{macro}{\l_@@_item_align_tl} % % \begin{macrocode} \tl_new:N \l_@@_item_align_tl % \end{macrocode} % \end{macro} % % \begin{variable}{\l_@@_one_label_box, \g_@@_labels_box} % Each label is typeset in \cs{l_@@_one_label_box} to be measured. % Once this is ready, it is put (boxed or unboxed) in % \cs{g_@@_labels_box}, together with any pending labels (for the case % where a list begins just after \tn{item}). This is an analogue of % \LaTeXe{}'s \tn{@labels}, but it is always unboxed before use, to % support both boxed and unboxed labels. % \begin{macrocode} \box_new:N \l_@@_one_label_box \box_new:N \g_@@_labels_box % \end{macrocode} % \end{variable} % % \begin{variable}{\l_@@_long_label_bool} % Track whether the \cs{l_@@_one_label_box} is larger than % \tn{labelwidth}. % \begin{macrocode} \bool_new:N \l_@@_long_label_bool % \end{macrocode} % \end{variable} % % \begin{macro}{\@@_make_label_box:n, \@@_label_format:e} % Make one label, wrapped in \cs{@@_label_format:n}, with an % appropriate \tn{strut} and possibly \tn{makelabel} in compatibility % mode (used for the \env{list} environment). % \begin{macrocode} \cs_new_protected:Npn \@@_make_label_box:n #1 { \hbox_set:Nn \l_@@_one_label_box { % \end{macrocode} % If we do tagging then the contents of this box may need to be % wrapped into a structure, e.g., \verb==. % \begin{macrocode} \tag_socket_use:nnn {block/list/label}{} { % \end{macrocode} % % \begin{macrocode} \@@_label_format:n { \bool_if:NT \l_@@_label_strut_bool { \strut } \bool_if:NTF \l_@@_legacy_support_bool \makelabel \use:n {#1} } % \end{macrocode} % And what gets opened also needs closing: % \begin{macrocode} } } } % \end{macrocode} % \end{macro} % % \begin{socketdecl}{block/list/label} % A tagging socket to tag the label. It takes two arguments so that it can transparently % pass the label content.\fmi{I think this socket should just be % called list/label} % \changes{v0.9k}{2025/07/21}{Removed fallback declaration here} % Declaration is in \file{lttagging.dtx}. % \end{socketdecl} % \begin{plugdecl}{default} % \begin{macrocode} \NewTaggingSocketPlug{block/list/label}{default} { % % FMi: this needs a different logic to decide when to make the label % an artifact (after cleaning up the \item code ), therefore % disabled for now % \tl_if_empty:oTF \@itemlabel % { % \tag_mc_begin:n {artifact} % } % { \tagstructbegin{tag=\UseStructureName{block/list/label}} \tagmcbegin{tag=\UseStructureName{block/list/label}} % } #2 \tagmcend % end mc-\UseStructureName{block/list/label} or artifact % FMi: unconditionally for now % \tl_if_empty:oF \@itemlabel \tagstructend % end label \tagstructbegin{tag=\UseStructureName{block/list/body}} } \AssignTaggingSocketPlug{block/list/label}{default} % \end{macrocode} % \end{plugdecl} % % % % \begin{macro}{\@@_everypar:, \@@_item_everypar_std:, \@@_item_everypar_first:} % The \cs{@@_everypar:} command is executed as part of \hook{para/begin} % but most of the time does nothing, i.e., it has the following % default definition outside of lists (and most of the time within lists). % \begin{macrocode} \cs_new_eq:NN \@@_everypar: \prg_do_nothing: % \end{macrocode} % % \begin{macrocode} \AddToHook{para/begin}[items]{\@@_everypar:} % \end{macrocode} % % Note that we have to make sure that the above code is executed % after the hook chunk from \pkg{tagpdf} because the latter uses % \texttt{@inlabel} to make a decision. % % By the end of the day both should probably move into the kernel % hook instead. % \begin{macrocode} \DeclareHookRule{para/begin}{items}{after}{tagpdf} % \end{macrocode} % % % What follows is the version that resets various legacy booleans and puts % the label box in the right place and finally resets itself to do % nothing next time. \cs{@@_everypar:} is set to this by the % item template so that the next paragraph start runs the code below. % \begin{macrocode} \cs_new_protected:Npn \@@_item_everypar_std: { \@@_debug_typeout:n{...~ in~ item~ block~ everypar \on@line } \legacy_if_set_false:n { @minipage } \legacy_if_gset_false:n { @newlist } \legacy_if:nT { @inlabel } { \legacy_if_gset_false:n { @inlabel } % \end{macrocode} % % \begin{macrocode} \box_if_empty:NT \g_para_indent_box { \kern - \itemindent } \para_omit_indent: % \end{macrocode} % % \begin{macrocode} \box_use_drop:N \g_@@_labels_box % \end{macrocode} % After the labels are placed we start a paragraph structure (if % appropriate). This is handled in the following kernel hook: % \begin{macrocode} \__kernel_list_label_after:n {LI-} % \end{macrocode} % % \begin{macrocode} \penalty \c_zero_int } \legacy_if:nTF { @nobreak } { \legacy_if_gset_false:n { @nobreak } \int_set:Nn \clubpenalty { 10000 } } { \int_set_eq:NN \clubpenalty \@clubpenalty % \end{macrocode} % Once the label(s) are typeset and we are past any special % \texttt{@nobreak} handling we reset \cs{@@_everypar:} to do % nothing. % \begin{macrocode} \@@_debug_typeout:n{Set~ noop~ block~ everypar \on@line } \cs_set_eq:NN \@@_everypar: \prg_do_nothing: } } % \end{macrocode} % % This is the definition of \cs{@@_everypar:} before the first % \tn{item} is encountered. % \changes{v0.8q}{2024/09/02}{Call \tn{@noitemerr} if hmode is % started before the first item} % \begin{macrocode} \cs_new_protected:Npn \@@_item_everypar_first: { \@@_debug_typeout:n{...~ in~ first~ block~ everypar \on@line } \legacy_if:nT { @newlist } { \@noitemerr } } % \end{macrocode} % % \end{macro} % % % \begin{variable}{\l_@@_tmpa_skip} % \begin{macrocode} \skip_new:N \l_@@_tmpa_skip % \end{macrocode} % \end{variable} % % % % % % % % \begin{variable}{\l_@@_topsepadd_skip, \l_@@_effective_top_skip} % Variables equivalent to \LaTeXe{}'s \tn{@topsepadd} and \tn{@topsep}. % Roughly equal to a mixture of \texttt{topsep}, \texttt{partopsep}, % and various \texttt{parskip} at different nesting levels in lists. % The code is really elaborate when \texttt{@inlabel} is true. % \begin{macrocode} \skip_new:N \l_@@_topsepadd_skip \skip_new:N \l_@@_effective_top_skip % \end{macrocode} % \end{variable} % % % % % % % % % % % % \begin{macro}{\item} % Here we already have all the building blocks. Complain in math % mode. Distinguish between first item (do necessary tagging) and % later items \cs{@@_inter_item:} to % cleanly close what's before, then call \cs{@@_item_instance:n} (which % calls \tn{UseInstance}\{item\}\marg{instance}) to prepare the % upcoming item: it will be actually inserted only once some later % material triggers \tn{everypar}. % \begin{macrocode} \AddToHook{begindocument/before}[./legacy-lists]{ \RenewDocumentCommand{\item}{ ={label}o } { \@inmatherr \item % \end{macrocode} % TODO: Check if test for being outside of a list is sensible % \begin{macrocode} \cs_if_free:NTF \@@_item_instance:n { \@latex@error{Lonely~\string\item--perhaps~a~missing~ list~environment}\@ehc } { \legacy_if:nTF { @newlist } { \__kernel_list_item_begin: % \end{macrocode} % The first item of a list also has to change the \texttt{@newlist} switch. % \changes{v0.8q}{2024/09/02}{Set \texttt{@newlist} to false after % the first \tn{item}} % \begin{macrocode} \legacy_if_gset_false:n { @newlist } } { \@@_inter_item: } % \end{macrocode} % To avoid unnecessary key/val processing we make a quick check if % there was an optional argument. % \begin{macrocode} \tl_if_novalue:nTF {#1} % avoids reparsing label={} { \@@_item_instance:n { } } { \@@_item_instance:n {#1} } % \end{macrocode} % The \insttype{item} instance puts the item label into % \cs{g_@@_label_standalone_bool} ready to be placed later. To make % that happen we need to signal that by setting the legacy switch % \texttt{@inlabel} to true. However, if this is a label that % should be always placed \enquote{standalone} we instead typeset it % immediately and ensure that there is no page break after % it.\fmi{support extra vertical space as well?} % \begin{macrocode} \bool_if:NTF \g_@@_label_standalone_bool { \bool_gset_false:N \g_@@_label_standalone_bool \leavevmode \box_use_drop:N \g_@@_labels_box \par \legacy_if_gset_true:n { @nobreak } % do not break after % a standalone item } { \legacy_if_gset_true:n { @inlabel } } \ignorespaces } } } % \end{macrocode} % \end{macro} % % % \begin{macro}{\@@_inter_item:} % Between items. If the previous item had no content then we need to % trigger \tn{everypar}. Otherwise we simply close the previous item % with \tn{par} after removing some horizontal space. Between items, % there is a penalty and some space. % \begin{macrocode} \cs_new_protected:Npn \@@_inter_item: { \legacy_if:nT { @inlabel } { \indent \par } % case of \item\item % \end{macrocode} % \tn{par} may have a strange definition and may not get us back to % vertical mode in one go, so we better not treat the next line % as an else case to the above conditional (for now). % \begin{macrocode} \mode_if_horizontal:T { \@@_skip_remove_last: \@@_skip_remove_last: \par } % \end{macrocode} % End any LI-tag, then start the next LI-tag (if doing tagging): % \begin{macrocode} \__kernel_list_item_end: \__kernel_list_item_begin: % \end{macrocode} % % \begin{macrocode} \addpenalty \@itempenalty \addvspace \itemsep } % \end{macrocode} % \end{macro} % % % \begin{macro}{\__kernel_list_item_begin:, % \__kernel_list_item_end:} % % \begin{macrocode} \cs_new_eq:NN \__kernel_list_item_begin: \prg_do_nothing: \cs_new_eq:NN \__kernel_list_item_end: \prg_do_nothing: % \end{macrocode} % \end{macro} % % % % % \subsubsection{Implementation of \insttype{captionedtext} % and \insttype{thmstyle} templates} % % % \begin{template}{captionedtext thmlike} % The template for typical theorem-like environments is rather % trivial, just setting keys and then passing used keys and the % arguments to a \insttype{thmstyle} instance to do the real work. % \begin{macrocode} \DeclareTemplateCode{captionedtext}{thmlike}{4} { ,counter = \l_@@_counter_tl ,title = \l_@@_title_tl ,style = \l_@@_style:nnnn } { % \end{macrocode} % Some debugging info as usual (showing the arguments that are passed): % \begin{macrocode} \template_debug_typeout:n{~\space template:~ 'thmlike';~ arguments:~ \exp_not:o{\exp_after:wN |#1|#2|#3|#4|}} % \end{macrocode} % Then we check if there are any keys passed to the instance from % the outside. % \begin{macrocode} \SetKnownTemplateKeys{captionedtext}{thmlike}{#1} % \end{macrocode} % Finally, we apply the style which is just an instance of % type \insttype{thmstyle}: % \begin{macrocode} \l_@@_style:nnnn \UnusedTemplateKeys {#2} {#3} {#4} } % \end{macrocode} % \end{template} % % % % \begin{macro}{\theoremstyle,\l_@@_thmstyle_tl} % ^^A % All that the \cs{theoremstyle} declaration does is saving its % argument so that it can be used in \cs{newtheorem}. % \begin{macrocode} \cs_new_protected:Npn \theoremstyle #1{ \tl_set:Nn \l_@@_thmstyle_tl {#1} } % \end{macrocode} % \begin{macrocode} \tl_new:N \l_@@_thmstyle_tl % \end{macrocode} % And the default is \texttt{plain}: % \begin{macrocode} \theoremstyle{plain} % \end{macrocode} % \end{macro} % % % % \begin{template}{thmstyle std} % The \insttype{thmstyle} implements the theorem-like % environment and assumes that the fixed part of the caption is % already stored in \cs{l_@@_title_tl}. The reason for this % separation into two templates is that typically the same design % is used for different theorem-like environments only differing in % this fixed string. % \begin{macrocode} \DeclareTemplateCode{thmstyle}{std}{4} { ,numbered = \l_@@_numbered_bool ,space = \l_@@_space_tl ,punct = \l_@@_punct_tl ,caption-placement = { chained = \bool_gset_false:N \g_@@_label_standalone_bool \bool_gset_false:N \g_@@_label_unchained_bool ,unchained = \bool_gset_false:N \g_@@_label_standalone_bool \bool_gset_true:N \g_@@_label_unchained_bool ,standalone = \bool_gset_true:N \g_@@_label_standalone_bool \bool_gset_false:N \g_@@_label_unchained_bool } ,before-hspace = \l_@@_caption_before_skip ,after-hspace = \l_@@_caption_after_skip ,order = \l_@@_order_clist ,caption-decls = \l_@@_caption_decls_tl ,title-format = \@@_title_format:n ,number-format = \@@_number_format:n ,punct-format = \@@_punct_format:n ,note-format = \@@_note_format:n ,body-decls = \l_@@_body_decls_tl } { % \end{macrocode} % Some tracing: % \begin{macrocode} \template_debug_typeout:n{~\space template:~ 'std';~ arguments:~ \exp_not:o{\exp_after:wN |#1|#2|#3|#4|}} % \end{macrocode} % Applying any user keys: % \begin{macrocode} \SetKnownTemplateKeys{thmstyle}{std}{#1} % \end{macrocode} % Since this is the last template that gets applied for % theorem-like environments, all keys should make sense, so if % something is left over we better generate an error: % \begin{macrocode} \tl_if_empty:NF \UnusedTemplateKeys { \msg_error:nnee { block } { unknown-keys } { \l_@@_env_name_tl \space environment} \UnusedTemplateKeys } % \end{macrocode} % % In case there is a dangling \cs{item} we can either join that % with the caption or we can output the item first. For now we % provide no customization for this, but it could be made % customizable. % \begin{macrocode} % \legacy_if:nT { @inlabel } { \indent \par } % \end{macrocode} % Determine if we do numbering: % \begin{macrocode} \bool_lazy_or:nnT { \tl_if_empty_p:N \l_@@_counter_tl } { #2 } { \bool_set_false:N \l_@@_numbered_bool } % \end{macrocode} % Save any note for later use (\verb=\#3= might contain \cs{NoValue}): % \begin{macrocode} \tl_set:Nn \l_@@_note_tl {#3} % \end{macrocode} % If we use numbering then we need a link target and increment the counter. % \begin{macrocode} \bool_if:NTF \l_@@_numbered_bool { \@kernel@refstepcounter{ \l_@@_counter_tl } \MakeLinkTarget{ \l_@@_counter_tl } } { \MakeLinkTarget[theorem-like]{} } % \end{macrocode} % Add the caption into \cs{g_@@_labels_box}: % \begin{macrocode} \hbox_gset:Nn \g_@@_labels_box { \box_use_drop:N \g__block_labels_box % <- does nothing if % there is no dangling label % \end{macrocode} % Now apply the declarations that are for the whole caption. % \begin{macrocode} \l_@@_caption_decls_tl % \end{macrocode} % Then we apply the tagging socket for the caption to the complete content: % \begin{macrocode} \tag_socket_use:nnn {captionedtext/caption} {} { \skip_horizontal:n { \l_@@_caption_before_skip } % \end{macrocode} % For flexibility, the inner structure is given as a clist stored % in \cs{l_@@_order_clist}. We loop through it and call a % processing function for each item in this clist. Everything % happens in a group % \begin{macrocode} \clist_map_inline:Nn \l_@@_order_clist { \group_begin: \use:c { @@_do_##1: } \group_end: } \skip_horizontal:n { \l_@@_caption_after_skip } } } % \end{macrocode} % If the title should be standalone we immediately push it out: % \begin{macrocode} \bool_if:NTF \g_@@_label_standalone_bool { \bool_gset_true:N \g_@@_label_standalone_bool \para_omit_indent: \box_use_drop:N \g_@@_labels_box \par % \end{macrocode} % \fmi{Do we need a \cs{nobreak} here or is this covered? check} % \begin{macrocode} } % \end{macrocode} % Otherwise we signal that we are at the start and have a label % dangling. The name \texttt{@newlist} is a bit unfortunate, but % for now we keep this name.\fmi{check if @newlist could be % replaced by something more general} % \begin{macrocode} { \legacy_if_gset_true:n { @newlist } \legacy_if_gset_true:n { @inlabel } } % \end{macrocode} % Do not break after the first line: % \begin{macrocode} \legacy_if_gset_true:n { @nobreak } % \end{macrocode} % Then set up a special everypar to handle the dangling caption: % \begin{macrocode} \@@_debug_typeout:n{Set~ captioned~ block~ everypar \on@line } \cs_set_eq:NN \@@_everypar: \@@_captioned_everypar_std: % \end{macrocode} % Finally, set up any declarations for the body of the environment: % \begin{macrocode} \l_@@_body_decls_tl % \end{macrocode} % % \begin{macrocode} \@@_debug_typeout:n{template:thmstyle:std~end} } % \end{macrocode} % \end{template} % % % % % \begin{socketdecl}{tagsupport/captionedtext/caption} % \begin{macrocode} \NewTaggingSocket{captionedtext/caption}{2} % \end{macrocode} % \end{socketdecl} % % \begin{plugdecl}{kernel} % \fmi{why kernel?} % \begin{macrocode} \NewTaggingSocketPlug{captionedtext/caption}{kernel} { \tag_struct_begin:n{tag=\UseStructureName{block/theorem-like/caption}} #2 \tag_struct_end: } \AssignTaggingSocketPlug{captionedtext/caption}{kernel} % \end{macrocode} % \end{plugdecl} % % % % Here are the functions that are called when the corresponding name appears % in the caption clist. % % % \begin{macro}{\@@_do_title:} % Handle the title: % \begin{macrocode} \cs_new_protected:Npn \@@_do_title: { % \end{macrocode} % Check if there is a title. % \begin{macrocode} \tl_if_empty:NTF \l_@@_title_tl % \end{macrocode} % If the title is empty we drop accumulated but not yet typeset spaces: % \begin{macrocode} { \@@_drop_spaces: } % \end{macrocode} % Otherwise we typeset the title, first inserting space (or spaces) % that have been waiting. % \begin{macrocode} { \tag_socket_use:nnn {mc} {}{ \@@_insert_spaces: % \end{macrocode} % The title may have its own formatting: % \begin{macrocode} \@@_title_format:n \l_@@_title_tl } } } % \end{macrocode} % \end{macro} % % % \begin{macro}{\@@_do_note:} % Formatting of a note (if present) uses the same structure. % \begin{macrocode} \cs_new_protected:Npn \@@_do_note: { \tl_if_novalue:oTF \l_@@_note_tl { \@@_drop_spaces: } { \tag_socket_use:nnn {mc} {} { \@@_insert_spaces: \@@_note_format:n \l_@@_note_tl } } } % \end{macrocode} % \end{macro} % % % \begin{macro}{\@@_do_number:} % The number (if present) has a similar formatting but it uses an % \UseStructureName{block/theorem-like/label} structure: % \begin{macrocode} \cs_new_protected:Npn \@@_do_number: { \bool_if:NTF \l_@@_numbered_bool { \tag_socket_use:nnn {struct-mc} {tag=\UseStructureName{block/theorem-like/label}} { \@@_insert_spaces: \@@_number_format:n { \use:c{ the \l_@@_counter_tl } } } } { \@@_drop_spaces: } } % \end{macrocode} % \end{macro} % % % \begin{macro}{\@@_do_punct:} % The punctuation is handled slightly differently. It unconditionally drops any % dangling spaces whether or not it is empty: % \begin{macrocode} \cs_new_protected:Npn \@@_do_punct: { \@@_drop_spaces: \tl_if_empty:NF \l_@@_punct_tl { \tag_socket_use:nnn {mc} {}{ \@@_punct_format:n \l_@@_punct_tl } } } % \end{macrocode} % \end{macro} % % % \begin{macro}{\@@_do_space:,\@@_insert_spaces:,\@@_drop_spaces:, % \g_@@_collected_spaces_tl} % ^^A % What's still missing is what \texttt{space} should do. It simply % adds a \cs{l_@@_space_tl} to the \cs{g_@@_collected_spaces_tl} % tokenlist. This way the clist can contain % \texttt{space,space,...} to indicate multiple spaces. The storage % tokenlist is global as the functions are executed inside their % own group, but the collected space is used outside of that group. % \begin{macrocode} \cs_new_protected:Npn \@@_do_space: { \tl_gput_right:Nn \g_@@_collected_spaces_tl \l_@@_space_tl } % \end{macrocode} % So \cs{@@_insert_spaces:} is trivial, all we have to do is to % insert the collected space and then clear the tokenlist % \begin{macrocode} \cs_new_protected:Npn \@@_insert_spaces: { \g_@@_collected_spaces_tl \tl_gclear:N \g_@@_collected_spaces_tl } % \end{macrocode} % Even simpler is \cs{@@_drop_spaces:} % \begin{macrocode} \cs_new_protected:Npn \@@_drop_spaces: { \tl_gclear:N \g_@@_collected_spaces_tl } % \end{macrocode} % What remains is to declare the tokenlist. % \begin{macrocode} \tl_new:N \g_@@_collected_spaces_tl % \end{macrocode} % \end{macro} % % % % % % % % % % % % % % \begin{template}{captionedtext proof} % In case of the templates for proofs we do everything in a single template. % \begin{macrocode} \DeclareTemplateCode{captionedtext}{proof}{4} { ,title = \l_@@_title_tl ,punct = \l_@@_punct_tl ,caption-placement = { chained = \bool_gset_false:N \g_@@_label_standalone_bool \bool_gset_false:N \g_@@_label_unchained_bool ,unchained = \bool_gset_false:N \g_@@_label_standalone_bool \bool_gset_true:N \g_@@_label_unchained_bool ,standalone = \bool_gset_true:N \g_@@_label_standalone_bool \bool_gset_false:N \g_@@_label_unchained_bool } ,before-hspace = \l_@@_caption_before_skip ,after-hspace = \l_@@_caption_after_skip ,caption-decls = \l_@@_caption_decls_tl ,title-format = \@@_title_format:n ,punct-format = \@@_punct_format:n ,body-decls = \l_@@_body_decls_tl } { % \end{macrocode} % Display the template's arguments when tracing: % \begin{macrocode} \template_debug_typeout:n{~\space template:~ 'proof';~ arguments:~ \exp_not:o{\exp_after:wN |#1|#2|#3|#4|}} % \end{macrocode} % Evaluate document-level key settings. As all given keys should be % handled we use \cs{SetTemplateKeys} to raise an error if one or % more are not recognized: % \begin{macrocode} \SetTemplateKeys{captionedtext}{proof}{#1} % \end{macrocode} % By default the title is defined by the proof instance, but if the % user provides an optional argument that optional argument % overwrites the title (in contrast to theorem-like environments % that use the optional argument to provide an additional note): % \begin{macrocode} \IfNoValueF {#3} { \tl_set:Nn \l_@@_title_tl {#3} } % \end{macrocode} % Now we prepare typesetting the title by placing it in the \cs{g_@@_labels_box}: % \begin{macrocode} \hbox_gset:Nn \g_@@_labels_box { \box_use_drop:N \g_@@_labels_box % <- does nothing if there % is no dangling label \l_@@_caption_decls_tl \tag_socket_use:nnn {captionedtext/caption} {} { \skip_horizontal:n { \l_@@_caption_before_skip } % \end{macrocode} % \cs{@@_do_title:} and \cs{@@_do_punct:} unnecessarily call % \cs{@@_drop_spaces:} but otherwise they do well, so \ldots % \begin{macrocode} \group_begin: \@@_do_title: \group_end: \group_begin: \@@_do_punct: \group_end: \skip_horizontal:n { \l_@@_caption_after_skip } } } % \end{macrocode} % The remaining code is identical to the one in \insttype{thmstyle} % \instname{std}; for documentation see there: % \begin{macrocode} \bool_if:NTF \g_@@_label_standalone_bool { \bool_gset_true:N \g_@@_label_standalone_bool \para_omit_indent: \box_use_drop:N \g_@@_labels_box \par } { \legacy_if_gset_true:n { @newlist } \legacy_if_gset_true:n { @inlabel } } \legacy_if_gset_true:n { @nobreak } \@@_debug_typeout:n{Set~ captioned~ block~ everypar \on@line } \cs_set_eq:NN \@@_everypar: \@@_captioned_everypar_std: \l_@@_body_decls_tl % \end{macrocode} % % \begin{macrocode} \@@_debug_typeout:n{template:captionedtext:proof~end} } % \end{macrocode} % \end{template} % % % % % % % % % % % % % \subsection{Tagging support commands} % % In this section we provide code to the various kernel hooks to support % the tagging of different displayblock environments. % % \changes{v0.9g}{2025-07-01}{Define the following command % even if tagging is not active, to allow to activate tagging later} % \changes{v0.9g}{2025-07-01}{Removed para-hook code, it is in tagpdf currently} % % % \begin{macro}{\@@_beginpar_vmode:} % When a block starts out in vertical mode, i.e., is not yet part % of a paragraph, we have to start a paragraph structure. However, % this is not the case if we are already flattening paragraphs, % thus in this case we do nothing. We also do nothing if % \texttt{@endpe} is currently true, because that means we are % right now just after the end of a \texttt{blockenv} and in the % process of looking if we have to end the current % \struct{\UseStructureName{para/semantic}}, i.e., it is already % open. % % The command is mapped to % \cs{__kernel_displayblock_beginpar_vmode:} in various tagging % recipes. It is also used in the math code! % \begin{macrocode} \cs_set:Npn \@@_beginpar_vmode: { \@@_debug_typeout:n { @endpe = \legacy_if:nTF { @endpe }{true}{false} \on@line } \legacy_if:nTF { @endpe } { \legacy_if_gset_false:n { @endpe } } % \end{macrocode} % We test for \texttt{<2} because the first flattened environment % has to surround itself with a % \struct{\UseStructureName{para/semantic}}. Only any inner ones % then have to avoid adding another % \struct{\UseStructureName{para/semantic}}. % \begin{macrocode} { \int_compare:nNnT \l__tag_block_flattened_level_int < 2 { % \end{macrocode} % \changes{v0.9k}{2025/07/21}{Use tagging socket for text-unit structure} % \begin{macrocode} \UseTaggingSocket{para/semantic/begin} { \__tag_para_main_store_struct: } } } } % \end{macrocode} % \end{macro} % % \begin{macro}{\@@_beginpar_hmode:N} % If the block is already part of a part of a paragraph, i.e., when % it has some text directly in front, then the first thing to do is % to return to vertical mode. However, that should be done without % inserting a paragraph end tag, so before calling \tn{par} to do % its normal work, we disable paragraph tagging and restarting % afterwards again. The argument to this config point simply % gobbles the \tn{par} following it in the code above (which is % used when there is no tagging going on. % % The command is mapped to % \cs{__kernel_displayblock_beginpar_hmode:w} in various tagging % recipes. % \begin{macrocode} \cs_set:Npn \@@_beginpar_hmode:N #1 { \tag_mc_end: \__tag_gincr_para_end_int: \@@_debug_typeout:n{increment~ /P \on@line } \bool_if:NT \l__tag_para_show_bool { \tag_mc_begin:n{artifact} \rlap{\color_select:n{red}\tiny\ \int_use:N\g__tag_para_end_int} \tag_mc_end: } \tag_struct_end: \tagpdfparaOff \par \tagpdfparaOn } % \end{macrocode} % \end{macro} % % Paragraph tagging is mainly done using the paragraph hooks. % The code is in \file{lttagging.dtx}. % % \begin{socketdecl}{tagsupport/block/startpara/direct} % A tagging socket to start a paragraph structure. It takes an % argument (which is only used in debugging) that should be gobbled % if tagging is not active. Not yet in lttagging (name and % function should be reviewed). % % This is a similar code to the one used in the para/begin hook but % without testing \texttt{@endpe}. This is not needed in the % standalone case and wrong inside lists. % % This code is used in various places and should be a dummy if % tagging is not active. % \begin{macrocode} \socket_if_exist:nF {tagsupport/block/startpara/direct} { \NewTaggingSocket {block/startpara/direct}{1} } % \end{macrocode} % \end{socketdecl} % % \begin{plugdecl}{default} % \begin{macrocode} \NewTaggingSocketPlug{block/startpara/direct}{default} { \bool_if:NF \l__tag_para_flattened_bool { % \end{macrocode} % \changes{v0.9k}{2025/07/21}{Use tagging socket for text-unit structure} % \begin{macrocode} \UseTaggingSocket{para/semantic/begin} { \__tag_para_main_store_struct: } } \__tag_gincr_para_begin_int: \@@_debug_typeout:n{increment~ P \on@line } \tag_struct_begin:n { tag=\l__tag_para_tag_tl ,attribute-class=\l__tag_para_attr_class_tl } \__tag_check_para_begin_show:nn {green}{#1} \tag_mc_begin:n {} } \AssignTaggingSocketPlug{block/startpara/direct}{default} % \end{macrocode} % \end{plugdecl} % % The para/end hook code is in lttagging. % Currently we still need to remove the tagpdf chunk % to avoid that the socket is added twice. % We add empty chunks to avoid warning messages from code parts trying to remove % the chunks. % \begin{macrocode} \AddToHook{para/end}[tagpdf]{} \RemoveFromHook{para/end}[tagpdf] \AddToHook{para/end}{} % \end{macrocode} % % \begin{macrocode} \def\PARALABEL{NP-} % \end{macrocode} % % % \begin{socketdecl}{tagsupport/kernel/endpe/vmode} % A tagging socket which ends a structure. Used in \cs{begin} and % \cs{para_end:}. Not yet in lttagging (name and function should be % reviewed). % \begin{macrocode} \socket_if_exist:nF {tagsupport/kernel/endpe/vmode} { \NewTaggingSocket {kernel/endpe/vmode}{0} } % \end{macrocode} % \end{socketdecl} % \begin{plugdecl}{default} % \begin{macrocode} \NewTaggingSocketPlug{kernel/endpe/vmode}{default} { \if@endpe \ifvmode \bool_if:NT \l__tag_para_bool { \bool_if:NF \l__tag_para_flattened_bool { % \end{macrocode} % \changes{v0.9k}{2025/07/21}{Use tagging socket for text-unit structure} % \begin{macrocode} \UseTaggingSocket{para/semantic/end}{} } % \end{macrocode} % \cs{@endpefalse} is needed by \cs{para_end:}, see test tagging-0097. % \begin{macrocode} \@endpefalse } \fi \fi } \AssignTaggingSocketPlug{kernel/endpe/vmode}{default} % \end{macrocode} % \end{plugdecl} % % \begin{macro}{\para_end:} % If we see a \tn{par} in vmode and a % \struct{\UseStructureName{para/semantic}} is still open we need % to close that. For this we check if a request for \text{@endpe} % was made (but the \tn{par} redefinition got lost due to (bad?) % coding). % \begin{macrocode} \cs_set_protected:Npn \para_end: { \scan_stop: \mode_if_horizontal:TF { \mode_if_inner:F { \tex_unskip:D \hook_use:n{para/end} \@kernel@after@para@end \mode_if_horizontal:TF { \if_int_compare:w 11 = \tex_lastnodetype:D \tex_hskip:D \c_zero_dim \fi: \tex_par:D \hook_use:n{para/after} \@kernel@after@para@after } { \msg_error:nnnn { hooks }{ para-mode }{end}{horizontal} } } } { % \end{macrocode} % TODO 2025-07-01. This is not exactly as before, this doesn't insert an \cs{\@endpefalse} % when tagging is active. Check if this a problem. % \begin{macrocode} \UseTaggingSocket{kernel/endpe/vmode}% \tex_par:D } } % \end{macrocode} % Now reset \LaTeXe{} functions to use the changed \cs{para_end:} % \changes{v0.9j}{2025/07/19}{Use \texttt{@@@@} to obtain 2; found by % explcheck analysis} % \TODO{Need to check if \cs{@@@@par} is ever used in a way that the % vmodetagging hook is needed.} % \begin{macrocode} \cs_set_eq:NN \par \para_end: \cs_set_eq:NN \@@@@par \para_end: \cs_set_eq:NN \endgraf \para_end: % \end{macrocode} % % \end{macro} % % % \begin{macro}{\begin} % We need to do a little more than canceling \texttt{@endpe} now. % \begin{macrocode} \protected\def\begin#1{% \UseHook{env/#1/before}% \@ifundefined{#1}% {\def\reserved@a{\@latex@error{Environment~#1~undefined}\@eha}}% {\def\reserved@a{\def\@currenvir{#1}% \edef\@currenvline{\on@line}% \@execute@begin@hook{#1}% \csname #1\endcsname}}% \@ignorefalse \begingroup \UseTaggingSocket{kernel/endpe/vmode}% \reserved@a} % \end{macrocode} % \end{macro} % % % % \begin{macro}{\__kernel_list_label_after:n} % If starting the text-unit/text tags got delayed because of a pending label we % have to do it after the label got typeset. % TODO: it should do nothing without tagging that's why there is a test, % this should be better hidden in a tagging socket, but it is not quite clear % how to do this.\fmi{internally this is now a tagging socket, why the outer test then?} % \begin{macrocode} \cs_new_protected:Npn \__kernel_list_label_after:n #1 { \bool_lazy_and:nnT { \tag_if_active_p: } {\l__tag_para_bool } { \tag_socket_use:nn {block/startpara/direct} { #1 } } } % \end{macrocode} % \end{macro} % % % \begin{macro}{\@@_inner_begin:} % Start a block that has an inner structure if it isn't also a list. % This command is tagging specific, it is mapped to \cs{__kernel_displayblock_begin:} % in some tagging recipes. % \begin{macrocode} \cs_new_protected:Npn \@@_inner_begin: { \tagstructbegin{tag=\l_@@_tag_inner_tag_tl} } % \end{macrocode} % \end{macro} % % % \begin{macro}{\@@_inner_end:} % End a block (which isn't also a list). % This command is tagging specific, it is mapped to \cs{__kernel_displayblock_end:} % in some tagging recipes. % \begin{macrocode} \cs_new_protected:Npn \@@_inner_end: { \@@_debug_typeout:n{block-end \on@line} \legacy_if:nT { @endpe } { \UseTaggingSocket{para/semantic/end} { \@@_debug_typeout:n{close~ /text-unit \on@line}} } \tagstructend % end inner structure } % \end{macrocode} % \end{macro} % % % % % % % \subsubsection{List tags} % % % \begin{macrocode} \tl_new:N \l__tag_L_tag_tl \tl_set:Nn \l__tag_L_tag_tl {L} \tl_new:N\l__tag_L_attr_class_tl \tl_set:Nn \l__tag_L_attr_class_tl {list} % \end{macrocode} % % \begin{macrocode} \tagpdfsetup { ,role/new-attribute = {itemize} {/O /List /ListNumbering/Unordered} ,role/new-attribute = {enumerate} {/O /List /ListNumbering/Ordered} ,role/new-attribute = {description} {/O /List /ListNumbering/Description} % \end{macrocode} % Initially, we had \texttt{/None} for the basic \env{list} % environment, but that is not allowed in PDF/UA-2 if the list % contains any Lbl tags. So now we default to % \texttt{Unordered}. % \begin{macrocode} ,role/new-attribute = {list}{/O /List /ListNumbering/Unordered} } % \end{macrocode} % % % \begin{macro}{\@@_list_begin:} % Start a list \ldots % This command is tagging specific, it is mapped to \cs{__kernel_displayblock_begin:} % in a tagging recipe. % \begin{macrocode} \cs_set:Npn \@@_list_begin: { \tagstructbegin { tag=\l__tag_L_tag_tl ,attribute-class=\l__tag_L_attr_class_tl } } % \end{macrocode} % \end{macro} % % \begin{macro}{\@@_list_item_begin:} % Start tagging a list item. % This command is tagging specific, it is mapped to \cs{__kernel_list_item_begin:} % in a tagging recipe. % \begin{macrocode} \cs_set:Npn \@@_list_item_begin: { \tagstructbegin{tag=\UseStructureName{block/list/item}} } % \end{macrocode} % \end{macro} % % % % % % \begin{macro}{\@@_list_item_end:} % When a list item ends we have to close \struct{\UseStructureName{block/list/body}} and % \struct{LI} but also a \struct{\UseStructureName{para/textblock}} in the special case that the % item material ends in a list (identifiable via \texttt{@endpe}). % This command is tagging specific. % This command is copied to \cs{__kernel_list_item_end:} in the list recipe. % \begin{macrocode} \cs_set:Npn \@@_list_item_end: { \legacy_if:nT { @endpe } { % \end{macrocode} % \changes{v0.9k}{2025/07/21}{Use tagging socket for text-unit structure} % \begin{macrocode} \UseTaggingSocket{para/semantic/end}{} % \@@_debug_typeout:n{Structure-end~ P~ at~ item-end \on@line } } \tagstructend \tagstructend % end \UseStructureName{block/list/body}, LI } % \end{macrocode} % \end{macro} % % % % \begin{macro}{\@@_list_end:} % Finally, at the list end we have to close the open % \struct{\UseStructureName{block/list/body}}, \struct{LI}, \struct{L}, and possibly a % \struct{\UseStructureName{para/textblock}} if the last item ends with a list. % However, if the user forgot to add an \tn{item} then there will be no % \struct{LI} and \struct{\UseStructureName{block/list/body}} open, so we check for the status % of \texttt{@newlist}. The corresponding no-item error was % generated earlier outside the tagging code. % % One could argue that it doesn't matter if the tagging is wrong % after a \tn{@noitemerr} was issued. However, there is one case % where it isn't an error: In the \env{thebibliography} % environment (which is internally a list) it is often the case % that documents start out with an empty environment, not % containing any \tn{bibitem}s. For that reason \tn{@noitemerr} is % redefined inside that environment to only produce a warning; % hence we have to produce correct tag structures in that case. % \changes{v0.8q}{2024/09/02}{Do not close LI and \UseStructureName{block/list/body} if they % never were opened.} % This command is tagging specific. % This command is copied to \cs{__kernel_displayblock_end:} in the list recipe. % \begin{macrocode} \cs_new_protected:Npn \@@_list_end: { % \end{macrocode} % If \texttt{@newlist} is true (i.e., when we have an error or warning % situation) there is not much to close. % \begin{macrocode} \legacy_if:nF { @newlist } { \legacy_if:nT { @endpe } { % \end{macrocode} % \changes{v0.9k}{2025/07/21}{Use tagging socket for text-unit structure} % \begin{macrocode} \UseTaggingSocket{para/semantic/end} {\@@_debug_typeout:n{Structure-end~ text-unit~ at~ list-end \on@line }} } \tagstructend\tagstructend % end \UseStructureName{block/list/body}, LI } \tagstructend % end L } % \end{macrocode} % \end{macro} % End of tagging related declarations. % % % % \subsubsection{Tagging recipes} % % \changes{v0.9g}{2025-07-01}{a tagging socket for recipe} % \begin{socketdecl}{tagsupport/block/recipe} % A tagging socket to call the tagging recipe. % Declared in lttagging. % \begin{macrocode} \socket_if_exist:nF {tagsupport/block/recipe} { \NewTaggingSocket{block/recipe}{1} } % \end{macrocode} % \end{socketdecl} % \begin{plugdecl}{default} % \begin{macrocode} \NewTaggingSocketPlug{block/recipe}{default} { \use:c { @@_recipe_#1: } } \AssignTaggingSocketPlug{block/recipe}{default} % \end{macrocode} % \end{plugdecl} % % \begin{macro}{\@@_recipe_basic:} % The \keyvalue{basic} recipe simply ensures that the block is inside % a \struct{\UseStructureName{para/semantic}} structure and if necessary starts one. When the % block ends and is followed by a blank line the \struct{\UseStructureName{para/semantic}} % structure is closed too, otherwise it remains open and further % text starts with just a \struct{\UseStructureName{para/textblock}} structure. % % There is otherwise no inner structure so % \cs{__kernel_displayblock_begin:} and % \cs{__kernel_displayblock_end:} do nothing---\insttype{blockenv}s with inner % structure use the \keyvalue{standard} or \keyvalue{list} recipe instead. % \begin{macrocode} \cs_new_protected:Npn \@@_recipe_basic: { \cs_set_eq:NN \__kernel_displayblock_beginpar_hmode:w \@@_beginpar_hmode:N \cs_set_eq:NN \__kernel_displayblock_beginpar_vmode: \@@_beginpar_vmode: \let \__kernel_displayblock_begin: \prg_do_nothing: \let \__kernel_displayblock_end: \prg_do_nothing: % \end{macrocode} % End environment \tn{par} handling: % \begin{macrocode} \socket_assign_plug:nn{block/endpe}{on} } % \end{macrocode} % \end{macro} % % \begin{macro}{\@@_recipe_standalone:} % % The \keyvalue{standalone} recipe produces a block that ensures that % a previous \struct{\UseStructureName{para/semantic}} ends and that after the block a new % \struct{\UseStructureName{para/semantic}} starts. % \begin{macrocode} \cs_new_protected:Npn \@@_recipe_standalone: { \cs_set_eq:NN \__kernel_displayblock_beginpar_hmode:w \prg_do_nothing: \cs_set_eq:NN \__kernel_displayblock_beginpar_vmode: \prg_do_nothing: \cs_set_eq:NN \__kernel_displayblock_begin: \@@_inner_begin: \cs_set_eq:NN \__kernel_displayblock_end: \@@_inner_end: % \end{macrocode} % End environment \tn{par} handling: % \begin{macrocode} \socket_assign_plug:nn{block/endpe}{off} % \end{macrocode} % % \begin{macrocode} \tl_if_empty:NTF \l_@@_tag_name_tl { \tl_set:Nn \l_@@_tag_inner_tag_tl {Sect} } { \tl_set_eq:NN \l_@@_tag_inner_tag_tl \l_@@_tag_name_tl } } % \end{macrocode} % \end{macro} % % \begin{macro}{\@@_recipe_standard:} % The \keyvalue{standard} recipe does the following: % \begin{itemize} % \item surround the block with a % \struct{\UseStructureName{para/semantic}} structure if not % already in a \struct{\UseStructureName{para/semantic}}. In % the latter case end the MC and the % \struct{\UseStructureName{para/textblock}} but leave the % \struct{\UseStructureName{para/semantic}} open. % % If we are producing flattened paragraphs, just close any % \struct{\UseStructureName{para/textblock}} but do not open a % \struct{\UseStructureName{para/semantic}}. % % \item Then open an new (inner) structure (by default \struct{Div} % but typically the one specified on the instance). % % \item At the end of the block close the inner structure % (\struct{Div} or explicit one) but leave the % \struct{\UseStructureName{para/semantic}} open to be either % continued or closed due to a following \tn{par}. % \end{itemize} % \begin{macrocode} \cs_new_protected:Npn \@@_recipe_standard: { \cs_set_eq:NN \__kernel_displayblock_beginpar_hmode:w \@@_beginpar_hmode:N \cs_set_eq:NN \__kernel_displayblock_beginpar_vmode: \@@_beginpar_vmode: \cs_set_eq:NN \__kernel_displayblock_begin: \@@_inner_begin: \cs_set_eq:NN \__kernel_displayblock_end: \@@_inner_end: % \end{macrocode} % End environment \tn{par} handling: % \begin{macrocode} \socket_assign_plug:nn{block/endpe}{on} % \end{macrocode} % % \changes{v0.9g}{2025/06/30}{ (tagging/925)} % \begin{macrocode} \tl_if_empty:NTF \l_@@_tag_name_tl { \tl_set:Nn \l_@@_tag_inner_tag_tl {Div} } { \tl_set_eq:NN \l_@@_tag_inner_tag_tl \l_@@_tag_name_tl } } % \end{macrocode} % \end{macro} % % % % \begin{macro}{\l_@@_tag_inner_tag_tl} % The tag name that is used if the block has an inner structure. % \begin{macrocode} \tl_new:N \l_@@_tag_inner_tag_tl % \end{macrocode} % \end{macro} % % % % \begin{macro}{\@@_recipe_list:} % The \keyvalue{list} recipe does the following. % \begin{itemize} % \item It opens a % \struct{\UseStructureName{para/semantic}}-structure or keeps % the current one open (only closing the MC). % % \item It then starts a new structure role-mapped to L-structure % and arranges for handling list items, e.g., Li, % \UseStructureName{block/list/label} and % \UseStructureName{block/list/body} structures. % % \item At the end it closes open list structures as needed but % keeps the \struct{\UseStructureName{para/semantic}}-structure % open to continue the paragraph after the list, if necessary. % \end{itemize} % \begin{macrocode} \cs_new_protected:Npn \@@_recipe_list: { \cs_set_eq:NN \__kernel_displayblock_beginpar_hmode:w \@@_beginpar_hmode:N \cs_set_eq:NN \__kernel_displayblock_beginpar_vmode: \@@_beginpar_vmode: \cs_set_eq:NN \__kernel_displayblock_begin: \@@_list_begin: \cs_set_eq:NN \__kernel_displayblock_end: \@@_list_end: % \end{macrocode} % The next two lines could be done globally, because they are only % called if we do have \tn{item}s, i.e., if we are in a list. It is % therefore also not necessary to reset them in other recipes % (right now---this may change if we get more templates (like % inline lists)). % \begin{macrocode} \cs_set_eq:NN \__kernel_list_item_begin: \@@_list_item_begin: \cs_set_eq:NN \__kernel_list_item_end: \@@_list_item_end: % \end{macrocode} % End environment \tn{par} handling: % \begin{macrocode} \socket_assign_plug:nn{block/endpe}{on} % \end{macrocode} % % Handle the tag name and attribute classes using the key values % from the current list instance. % \begin{macrocode} \tl_if_empty:NTF \l_@@_tag_name_tl { \tl_set:Nn \l__tag_L_tag_tl {L} } { \tl_set_eq:NN \l__tag_L_tag_tl \l_@@_tag_name_tl } \tl_if_empty:NTF \l_@@_tag_class_tl { \tl_set:Nn \l__tag_L_attr_class_tl {} } { \tl_set_eq:NN \l__tag_L_attr_class_tl \l_@@_tag_class_tl } } % \end{macrocode} % \end{macro} % % % % \begin{macrocode} % % \end{macrocode} % % % \section[Support code for document-level block environments] % {Support code for document-level block\\ environments} % % % % \subsection{Verbatim-like environments} % % \subsubsection{Helper commands for \env{verbatim} and \env{verbatim*}} % % \begin{macro}{\legacyverbatimsetup} % ^^A % This code is called as part of the \key{final-code} of the % \insttype{blockenv} instance and sets up the special conventions % needed for \env{verbatim} environments. We pass one argument to % differentiate between visible and invisible spaces. % % This code resembles the \LaTeXe{} verbatim implementation with a % slight twist: in \LaTeXe{} each code line was a paragraph using % \tn{leftskip}=\tn{@totalleftmargin}. This was possible because % the whole environment was implemented as a trivlist. As this is % no longer the case setting \tn{leftskip} would alter the layout % of a surrounding list. So instead we need to make sure that the % paragraph end is executed in a group so that any parshape setup % is preserved. % \changes{v0.8x}{2025/01/12}{flattened para inside verbatim using codeline tag} % \begin{macrocode} %<*package-finish> % \end{macrocode} % % \begin{macrocode} %<@@=> \def\legacyverbatimsetup #1 {% \language\l@nohyphenation \@tempswafalse \def\par{% \if@tempswa \leavevmode \null {\@@par}\penalty\interlinepenalty \else \@tempswatrue \ifhmode{\@@par}\penalty\interlinepenalty\fi \fi % \end{macrocode} % Do something at the very beginning of each verbatim line:\fmi{might % also need a hook not just a socket} % \begin{macrocode} \UseSocket{verbatim/startline}% }% \let\do\@makeother \dospecials \obeylines \verbatim@font \@noligs \everypar \expandafter{\the\everypar \unpenalty}% % \end{macrocode} % % \begin{macrocode} \frenchspacing % \end{macrocode} % \fmi{Should next line be hidden in a tagging socket?} % \begin{macrocode} \AssignStructureRole {para/textblock}% {\UseStructureName{block/verbatim/codeline}}% % \end{macrocode} % If the argument is neither \texttt{visible} nor % \texttt{invisible} nothing will happen---tough. % \begin{macrocode} \use:c { @setupverb #1 space } \@vobeyspaces } % \end{macrocode} % \end{macro} % % % \begin{socketdecl}{verbatim/startline} % A socket that is executed at the start of each verbatim line, to % be used, for example, to check for % \verb*=% = when the \pkg{doc} package is % active.\fmi{We might also need a hook for line numbers.} % \begin{macrocode} \NewSocket{verbatim/startline}{0} % \end{macrocode} % \end{socketdecl} % % % % % \begin{macro}{\@setupverbinvisiblespace} % ^^A % In the \pdfTeX{} engine we need to use \tn{pdffakespace} chars % for the invisible spaces. In luatex we do not want this as it % would lead to doubling the number of real space chars. In % dvi-mode we do not want that either: with pdftex it would error, % with xetex it does nothing. \changes{v0.9g}{2025-07-01}{changed % logic} % \begin{macrocode} %<@@=block> \newcommand\@setupverbinvisiblespace{} \bool_lazy_or:nnF { \sys_if_engine_luatex_p: } { \sys_if_output_dvi_p: } { \renewcommand\@setupverbinvisiblespace {\def\@xobeysp{\nobreakspace\pdffakespace}} } % \end{macrocode} % \end{macro} % % The command \cs{@setupverbvisiblespace} is already defined in the kernel. % % % % % \subsubsection{Helper commands for \env{alltt} and \env{alltt*}} % % % \begin{macro}{\legacyallttsetup} % The \env{alltt} environment also needs some special setup. We can % reuse \cs{legacyverbatimsetup} but we have to take out \verb=\=, % \verb={=, and \verb=}= from \cs{dospecials} as they should remain % available with their normal catcodes and adjust \verb='= inside % math. This is lifted straight from the original package code. % \begin{macrocode} %<@@=> \ExplSyntaxOff % \end{macrocode} % % \begin{macrocode} \def\legacyallttsetup #1{% \let\org@prime~% \everymath\expandafter{\the\everymath \catcode`\'=12 \let~\org@prime}% \everydisplay\expandafter{\the\everydisplay \catcode`\'=12 \let~\org@prime}% % \end{macrocode} % This alters \cs{dospecials}: % \begin{macrocode} \let\org@dospecials\dospecials \g@remfrom@specials{\\}% \g@remfrom@specials{\{}% \g@remfrom@specials{\}}% % \end{macrocode} % Then call \cs{legacyverbatimsetup}: % \begin{macrocode} \legacyverbatimsetup {#1}% % \end{macrocode} % And afterwards restore \cs{dospecials:} % \begin{macrocode} \let\dospecials\org@dospecials } % \end{macrocode} % % \begin{macro}{\g@remfrom@specials} % Copied from \pkg{alltt}. % \begin{macrocode} \def\g@remfrom@specials#1{% \def\@new@specials{}% \def\@remove##1{% \ifx##1#1\else \g@addto@macro\@new@specials{\do ##1}\fi}% \let\do\@remove\dospecials \let\dospecials\@new@specials } % \end{macrocode} % % \begin{macrocode} \ExplSyntaxOn %<@@=block> % \end{macrocode} % \end{macro} % \end{macro} % % % % \subsubsection{Helper command for legacy \env{list} environment} % % % \begin{macro}{\legacylistsetup} % ^^A % And here is the extra code for use in the list instance setup % in the key \key{legacy-code}: % \begin{macrocode} \cs_new_protected:Npn \legacylistsetup { % \end{macrocode} % Reset values to defaults: % \begin{macrocode} \dim_zero:N \listparindent \dim_zero:N \rightmargin \dim_zero:N \itemindent % \end{macrocode} % % By default a \env{list} environment is not numbered, but this % happens already in the block template. % \changes{v0.8v}{2024/10/11}{Set the defaults for \tn{@itemlabel}, % \tn{@listctr} and \texttt{@nmbrlist} early in the block code % before the setup code gets executed (tagging/730)} % \begin{macrocode} % \tl_set:Nn \@listctr {} % \legacy_if_set_false:n { @nmbrlist } % needed if lists are nested % \end{macrocode} % By default there is a simple definition for \tn{makelabel}. It can be % overwritten in the second mandatory argument to the list % environment (stored in \cs{l_@@_legacy_env_params_tl}) and % is used if the instance sets the compatibility key to true. % \begin{macrocode} \let\makelabel\@mklab % TODO: customize % \end{macrocode} % Now we use the argument with parameter settings to update some or % all of the above defaults (this holds whatever was put into the % second argument to the \env{list} environment): % \begin{macrocode} \l_@@_legacy_env_params_tl % \end{macrocode} % As we don't know much about this list we can only make a guess about % the nature of the list and the setting of the tag name (default % \keyvalue{list} role-mapped to \struct{L}) and any tag attributes % may have to be overwritten in the optional key/value argument. % But we do have some hints to play with. % \begin{macrocode} \legacy_if:nTF { @nmbrlist } { \tl_set:Nn \l__tag_L_attr_class_tl {enumerate} } % numbered list { \tl_if_empty:NTF \@itemlabel { \tl_set:Nn \l__tag_L_attr_class_tl {list} } % no label { \tl_set:Nn \l__tag_L_attr_class_tl {itemize} } % unnumbered, % unordered } } % \end{macrocode} % \end{macro} % % % \begin{macro}{\l_@@_legacy_env_params_tl} % The token list in which the declarations from the second argument % of \env{list} are temporarily stored. This is then used in % \cs{legacylistsetup}. % \begin{macrocode} \tl_new:N\l_@@_legacy_env_params_tl % \end{macrocode} % \end{macro} % % % \subsection{Theorem-like environments} % % Theorem-like environments are defined in \LaTeX{} with the help of % \tn{newtheorem} declarations. Internally they used a list with a % single item. Using lists was convenient back then, but in a tagged % document you end up with a strange structure. We therefore alter the % mechanism. % % % \subsubsection{Declarations for theorem-like environments} % % \begin{macro}[no-user-doc]{\newtheorem} % We reimplement the extended \pkg{amsthm} version of the declaration which % also supports a star form indicating that this theorem-like % environment should not be numbered. % \changes{v0.9m}{2025-12-31}{Use an alias counter} % \begin{macrocode} \RenewDocumentCommand \newtheorem { s m o m o } { % \end{macrocode} % Is the environment definable at all? If not there is not much % point in continuing. % \begin{macrocode} \expandafter\@ifdefinable\csname #2\endcsname { % \end{macrocode} % % If a star was given then there is no need to set up a counter for % this environment. Otherwise we do what \LaTeX2e{} did, except % that we do all the variations in one go, rather than using % \cs{@ynthm}, \cs{@xnthm}, and \cs{@othm}.\fmi{undefined the old % internal commands?} % \begin{macrocode} \IfBooleanF #1 { \IfNoValueTF {#3} % \end{macrocode} % If there was no counter to use (\verb=#2=) then we set up a % counter with the same name as the environment (\verb=#2=). % \begin{macrocode} { \@definecounter {#2} % \end{macrocode} % If there was no \enquote{counter within} the counter % representation is simple, otherwise we build it up from the two % counters: % \begin{macrocode} \IfNoValueTF {#5} { % @ynthm \tl_gset:ce { the #2 } { \@thmcounter{#2} } } { % @xnthm \@newctr{#2}[#5] \tl_gset:ce { the #2 } { \expandafter\noexpand\csname the#5\endcsname \@thmcountersep \@thmcounter{#2} } } } { % @othm % \end{macrocode} % If we should reuse an existing counter (\verb=#3= was given) we % check that this counter actually exists and if so use it: % \begin{macrocode} \@ifundefined{c@#3} { \@nocounterr{#3} } { \newcounteralias{#2}{#3} } } } % \end{macrocode} % With the counter defined we are ready to declare the % environments. There is a slight complication though: the % \enquote{theorem-like} environments have an optional argument % which contains a possible note, but now we also want to use the % first optional argument to hold a key/value list with parameter % settings. We therefore define this argument via \verb/={note}o/ % so that a simple note, if given is assigned to a \key{note} key. % Further processing is then delegated to the command % \cs{ParseLaTeXeTheoremlike} which, after sorting out the argument % situation, eventually calls \cs{BlockEnv}. % \begin{macrocode} \NewDocumentEnvironment{#2}{ ={note}o } { \ParseLaTeXeTheoremlike {#2} \BooleanFalse {##1} } { \BlockEnvEnd } % \end{macrocode} % The starred form of the environment suppresses the number so we % pass it \cs{BooleanTrue}, otherwise it is identical to the % previous definition. % \begin{macrocode} \NewDocumentEnvironment{#2*}{ ={note}o } { \ParseLaTeXeTheoremlike {#2} \BooleanTrue {##1} } { \BlockEnvEnd } % \end{macrocode} % % Now it is about time to provide all necessary template % instances. They depend on the \cs{theoremstyle} specified by the % user and possibly by a \cs{swapnumbers} declaration. We start by % checking the requested \cs{theoremstyle} (if none was given then % \texttt{plain} is the default and if it is an unknown name we % also revert to \texttt{plain} after issuing a warning). % \begin{macrocode} \IfInstanceExistsF{thmstyle}{\l_@@_thmstyle_tl} { \@latex@warning{Unknown~ theoremstyle~ '\l_@@_thmstyle_tl'~ using~ 'plain'} \theoremstyle {plain} } % \end{macrocode} % So now we know that \cs{l_@@_thmstyle_tl} holds a valid % style. What we don't know is whether or not there are special % \insttype{block} instances that go with that style (it might be a style % that reuses the \instname{thm-\meta{style}block-...} instances). % \begin{macrocode} \IfInstanceExistsTF{block} { thm- \l_@@_thmstyle_tl -1 } { \@@_debug_typeout:n{...~ style~ \l_@@_thmstyle_tl\space exists } } { \@@_debug_typeout:n{...~ style~ \l_@@_thmstyle_tl\space does~ not ~exist;~ 'plain'~ used } } % \end{macrocode} % So here is the \insttype{blockenv} instance for the new % \enquote{theorem-like} environment. It uses a % \insttype{captionedtext} instance for the \key{inner-instance} % which we also have to declare. % \begin{macrocode} \DeclareInstance{blockenv}{#2}{std} { name = theorem-like ,tag-name = \UseStructureName{block/theorem-like} ,tag-attr-class = ,tagging-recipe = standalone ,inner-level-counter = ,transparent-level = true ,legacy-code = % \end{macrocode} % What block instance to use is determined by checking if a special % one exists or whether we should use \texttt{plain}: % \begin{macrocode} ,block-instance:e = thm- \IfInstanceExistsTF{block} {thm- \l_@@_thmstyle_tl -1} { \l_@@_thmstyle_tl } { plain } ,inner-instance-type = captionedtext ,inner-instance = #2 % \end{macrocode} % By default the body text is justified, but perhaps we should not % set anything here and use whatever is current.\fmi{decide} % \begin{macrocode} ,para-instance = justify } % \end{macrocode} % The \insttype{captionedtext} instance is simple: the counter, if % present, is either argument \texttt{\#2} or \texttt{\#3}; the \key{title} % receives argument \texttt{\#4}, and the \key{style} to use is % stored in \cs{l_@@_thmstyle_tl}. If \cs{swapnumbers} was % requested we use a style variant with the suffix \texttt{-swap} appended. % \begin{macrocode} \DeclareInstance{captionedtext}{#2}{thmlike} { % \end{macrocode} % The counter to use is either none or \texttt{\#2} % based on the arguments given: % \begin{macrocode} ,counter:e = \IfBooleanF #1 { #2 } ,title = #4 ,style:e = \l_@@_thmstyle_tl \bool_if:NT \l_@@_swap_number_bool {-swap} } % \end{macrocode} % We already know that the style \cs{l_@@_thmstyle_tl} exists, since % we have tested that earlier, but we don't know if that is also % true for the \texttt{-swap} variant. So we have to check that and % declare it if necessary. % \begin{macrocode} \bool_if:NT \l_@@_swap_number_bool { \IfInstanceExistsF{thmstyle}{\l_@@_thmstyle_tl -swap} { % \end{macrocode} % If it doesn't exist we first make a copy of the base instance. % \begin{macrocode} \DeclareInstanceCopy{thmstyle} {\l_@@_thmstyle_tl -swap} {\l_@@_thmstyle_tl} % \end{macrocode} % Then we retrieve the value of the \key{order} key from that % instance which is a clist. % \begin{macrocode} \clist_set:Ne \l_@@_order_clist { \InstanceValue { thmstyle } { \l_@@_thmstyle_tl } { order } } % \end{macrocode} % Then we step through this clist and build a new one in % \cs{l_@@_tmp_clist} with the \texttt{title} and \texttt{number} % swapped. That is done under the assumption that both actually % exist in the clist which would be the case if the instance was % declared with \cs{newtheoremstyle}, i.e., for legacy setups. % \begin{macrocode} \clist_clear:N \l_@@_tmp_clist \clist_map_inline:Nn \l_@@_order_clist { \clist_put_right:Ne \l_@@_tmp_clist { \str_case:nnF {##1} { {title} {number} {number} {title} } {##1} } } % \end{macrocode} % Once that is done we put the new value for \key{order} in the new % instance. % \begin{macrocode} \EditInstance {thmstyle}{\l_@@_thmstyle_tl -swap} { order:e = \l_@@_tmp_clist } } } } } % \end{macrocode} % \end{macro} % % % % % % \begin{macro}{\ParseLaTeXeTheoremlike} % The arguments to \cs{ParseLaTeXeTheoremlike} are as follows: % \begin{itemize} % \item[\#1:] instance name to use (of type \enquote{blockenv}) % \item[\#2:] unnumbered? boolean normally provided by using the star % form of the environment % \item[\#3:] key/val for layout adjustments settings provided in the % optional argument of the \enquote{theorem-like} % environment. If a note to the theorem was given in that argument, % then it has been turned into \verb=note={...}= % \end{itemize} % To be able to pick up the \key{note}, if provided, we make the % following declaration: % \begin{macrocode} \keys_define:nn {blockenv} { , note .tl_set:N = \l_@@_note_tl , note .groups:n = { interface } } % \end{macrocode} % % \begin{macrocode} \cs_new_protected:Npn \ParseLaTeXeTheoremlike #1 #2 #3 { % % \@@_debug_typeout:n{Parse~#1.~ Arguments~ found:~ \IfBooleanT{#2}{*} % \IfValueT{#3}{[\exp_not:n{#3}]}} % % \end{macrocode} % Normally, no note is provided, so that's the starting point: % \begin{macrocode} \tl_set:Nn \l_@@_note_tl { \NoValue } % \end{macrocode} % Then we check \texttt{\#3} if it contains a key/val list % containing \key{note}. This then sets \cs{l_@@_note_tl} and puts % all other key/vals in \cs{l_@@_instance_keys_tl}. Otherwise the % complete key/val list ends up there. % \begin{macrocode} \IfNoValueTF { #3 } { \tl_clear:N \l_@@_instance_keys_tl } { \keys_set_groups:nnnN {blockenv} {interface} { #3 } \l_@@_instance_keys_tl } % \end{macrocode} % We are now ready to invoke the \insttype{blockenv} instance for % \texttt{\#1} but we need to expand some of the values first, % hence the \cs{use:e}. % \begin{macrocode} \use:e { \exp_not:N \BlockEnv { #1 } { \exp_not:o \l_@@_instance_keys_tl } { #2 } { \exp_not:o \l_@@_note_tl } } % \end{macrocode} % We don't have any use for the last argument (the sub-caption) in % the standard setup, so we pass \cs{NoValue}. % \begin{macrocode} \NoValue } % \end{macrocode} % \end{macro} % % % % \begin{macro}{\swapnumbers} % Beside declaring theorem-like environments with \cs{newtheorem} % and \cs{theoremstyle}, the \pkg{amsthm} package also introduced % \cs{swapnumbers} to swap title and number in all environments % (because that is a common requirement). The new implementation % supports this approach as well. % % It is implemented with a boolean \cs{l_@@_swap_number_bool} which % is toggled by \cs{swapnumbers}. % \begin{macrocode} \bool_new:N \l_@@_swap_number_bool \cs_new_protected:Npn \swapnumbers { \bool_set_inverse:N \l_@@_swap_number_bool } % \end{macrocode} % \end{macro} % % % % % % % % % % % \begin{macro}{\newtheoremstyle} % The \cs{newtheoremstyle} declaration was originally provided in % the \pkg{amsthm} package. It has 9 mandatory arguments that sets % some aspects of theorem styles. We map those to the template % mechanism and generate \insttype{thmstyle} instances from them. % % \cs{newtheoremstyle} has a bunch of argument conventions that % haven't been fully implemented yet, e.g., \texttt{\#8} can be a % blank (meaning normal word space or \cs{newline}) or a skip. Those % should eventually also be covered.\fmi{extend} % \begin{macrocode} \cs_set_protected:Npn \newtheoremstyle #1#2#3#4#5#6#7#8#9 { % \end{macrocode} % First we build a \insttype{thmstyle} instance: % \begin{macrocode} \DeclareInstance{thmstyle}{#1}{std}{ ,caption-decls = {#4} ,before-hspace:e = \tl_if_empty:nTF{#5}{0pt}{#5} ,body-decls = {#6} ,punct = {#7} % \end{macrocode} % This setting doesn't cover all syntax possibilities. % \begin{macrocode} ,after-hspace:e = \tl_if_empty:nTF{#8}{0pt} {\tl_if_blank:nTF{#8}{3.3pt}{#8}} } % \end{macrocode} % If \texttt{\#2} or \texttt{\#3} are not empty we also have to set % up a \insttype{block} instance to account for the fact that % special vertical spacing is requested: % \begin{macrocode} \tl_if_empty:nF { #2#3 } { \DeclareInstance{block}{thm-#1-1}{std}{ ,begin-vspace:e = \tl_if_empty:nTF{#2}{0pt}{#2} ,end-vspace:e = \tl_if_empty:nTF{#3}{0pt}{#3} ,left-margin = 0pt ,para-indent = \parindent ,para-vspace = \parskip } % \end{macrocode} % As elsewhere we provide two levels. % \begin{macrocode} \DeclareInstanceCopy{block}{thm-#1-2}{thm-#1-1} } % \end{macrocode} % More complicated is argument \texttt{\#9}. If not empty it can % contain \cs{thmname}, \cs{thmnumber}, and/or \cs{thmnote} to % define the layout of the theorem caption. All the spacing has to % be given inside the arguments of these commands which means that % this doesn't work together with \cs{swapnumbers}, but this is the % way \pkg{amsthm} was defined. If the instances are manually % defined then it is easy to make them work with and without a % \cs{swapnumbers} declaration. So basically, this here is good for % a subset of cases and for backwards compatibility with % \pkg{amsthm}. % % The approach used doesn't cover all circumstances, e.g., if the % argument contains low-level programming on top of the interface % commands that the translation below will fail, but most existing % code should work and the rest would need a replacement using % instances that are directly set up. % \begin{macrocode} \tl_if_empty:oF { \exp_not:n{#9} } { % \end{macrocode} % Give special definitions for the commands and then expand % \texttt{\#9} and use the result to edit the instance we defined % earlier. % \begin{macrocode} \cs_set:Npn \thmname ##1 {title-format={{\exp_not:n{##1}}},} \cs_set:Npn \thmnumber ##1 {number-format={{\exp_not:n{##1}}},} \cs_set:Npn \thmnote ##1 {note-format={{\exp_not:n{##1}}},} \cs_set:Npn \@@_tmp:w ##1##2##3 { \exp_args:Nnne \EditInstance{thmstyle}{#1}{#9}} \@@_tmp:w {##1} {##1} {##1} % \end{macrocode} % We then also use the commands to deduce a suitable \key{order} % and put that into the instance as well. % \begin{macrocode} \cs_set:Npn \thmname ##1 {title,} \cs_set:Npn \thmnumber ##1 {number,} \cs_set:Npn \thmnote ##1 {punct,note} \cs_set:Npn \@@_tmp:w##1##2##3 { \exp_args:Nnne \EditInstance{thmstyle}{#1}{order={#9}}} \@@_tmp:w {##1} {##1} {##1} } % \end{macrocode} % If block tracing is turned on we show the final result: % \begin{macrocode} \@@_debug:n { \ShowInstanceValues{thmstyle}{#1} } } % \end{macrocode} % \end{macro} % % % % % % \subsubsection{Supporting QED in proofs} % % The \pkg{amsthm} package contains some elaborate code to support % placing a QED symbol into the proof (by default at the end, but % alternatively manually placed with \cs{qedhere}). This code is % simply lifted and not adjusted in any way for now (and therefore % also not documented---see the \pkg{amsthm} package for % documentation for now). % % \begin{macrocode} \ExplSyntaxOff \def\math@qedhere{% \@ifundefined{\@currenvir @qed}{% \qed@warning\quad\hbox{\qedsymbol}% }{% \@xp\aftergroup\csname\@currenvir @qed\endcsname }% } \def\displaymath@qed{% \relax \ifmmode \ifinner \aftergroup\linebox@qed \else \eqno \let\eqno\relax \let\leqno\relax \let\veqno\relax \hbox{\qedsymbol}% \fi \else \aftergroup\linebox@qed \fi } \expandafter\let\csname equation*@qed\endcsname\displaymath@qed \def\equation@qed{% \iftagsleft@ \hbox{\phantom{\quad\qedsymbol}}% \gdef\alt@tag{% \rlap{\hbox to\displaywidth{\hfil\qedsymbol}}% \global\let\alt@tag\@empty }% \else \gdef\alt@tag{% \global\let\alt@tag\@empty \vtop{\ialign{\hfil####\cr \tagform@\theequation\cr \qedsymbol\cr}}% \setbox\z@ }% \fi } \def\qed@tag{% \global\tag@true \nonumber &\omit\setboxz@h {\strut@ \qedsymbol}\tagsleft@false \place@tag@gather \kern-\tabskip \ifst@rred \else \global\@eqnswtrue \fi \global\advance\row@\@ne \cr } \def\split@qed{% \def\endsplit{\crcr\egroup \egroup \ctagsplit@false \rendsplit@ \aftergroup\align@qed }% } \def\align@qed{% \ifmeasuring@ \tag*{\qedsymbol}% \else \let\math@cr@@@\qed@tag \fi } \expandafter\let\csname align*@qed\endcsname\align@qed \expandafter\let\csname gather*@qed\endcsname\align@qed % \def\math@qedhere{\quad\hbox{\qedsymbol}}% % \DeclareRobustCommand{\qed}{% \ifmmode \mathqed \else \leavevmode\unskip\penalty9999 \hbox{}\nobreak\hfill \quad\hbox{\qedsymbol}% \fi }% \let\QED@stack\@empty \let\qed@elt\relax \newcommand{\pushQED}[1]{% \toks@{\qed@elt{#1}}\@temptokena\expandafter{\QED@stack}% \xdef\QED@stack{\the\toks@\the\@temptokena}% }% \newcommand{\popQED}{% \begingroup\let\qed@elt\popQED@elt \QED@stack\relax\relax\endgroup }% \def\popQED@elt#1#2\relax{#1\gdef\QED@stack{#2}}% \newcommand{\qedhere}{% \begingroup \let\mathqed\math@qedhere \let\qed@elt\setQED@elt \QED@stack\relax\relax \endgroup }% \def\setQED@elt#1#2\relax{% \ifmeasuring@ \else \iffirstchoice@ \gdef\QED@stack{\qed@elt{}#2}\fi \fi #1% }% \def\qed@warning{% \PackageWarning{amsthm}{The \@nx\qedhere command may not work correctly here}% }% \newcommand{\mathqed}{\quad\hbox{\qedsymbol}}% \DeclareRobustCommand{\qed}{% \ifmmode \mathqed \else \leavevmode\unskip\penalty9999 \hbox{}\nobreak\hfill \quad\hbox{\qedsymbol}% \fi } \newcommand{\openbox}{\leavevmode \hbox to.77778em{% \hfil\vrule \vbox to.675em{\hrule width.6em\vfil\hrule}% \vrule\hfil}} \providecommand{\qedsymbol}{\openbox} \ExplSyntaxOn % \end{macrocode} % % % % % % % \section{Support for other packages and classes} % % \subsection{Replacement for \pkg{alltt}} % % The tools package \pkg{alltt} by Leslie Lamport has been % completely implemented using the template approach and is % therefore no longer necessary. In fact it has also been extended % by providing \env{alltt*}. % \begin{macrocode} \declare@file@substitution{alltt.sty}{null.tex} % \end{macrocode} % % % \subsection{Replacement for \pkg{amsthm}} % % The \pkg{amsthm} package is basically supported out of the box (though there % are currently still a few limitation with \cs{newtheoremstyle} and % perhaps also in other places). So this here is a bit premature, % but for now we disable loading \pkg{amsthm} and wait to see how % far this gets us. % We may have to provide a bit more for better compatibility. % \begin{macrocode} \declare@file@substitution{amsthm.sty}{null.tex} % \end{macrocode} % % % % \subsection{Support for \cls{amsart} and \cls{amsbook} classes} % % Unfortunately, the \cls{amsart} class contains a full % implementation of \pkg{amsthm} inside the class (why ever) % and they use \cs{newcommand}, sigh. % % Thus, to make the new code work with this class we have to hide % some definitions, load the class and only afterwards restore our % own versions. % % So first save some of the problematical definitions under some % other names: % \begin{macrocode} \let \amsnewtheorem \newtheorem \let \amsnewtheoremstyle \newtheoremstyle \let \amstheoremstyle \theoremstyle \let \amsproof \proof \let \amsendproof \endproof % \end{macrocode} % % Then undefine them just before the class gets loaded (quite a handful): % \begin{macrocode} \AddToHook{class/amsart/before}[block]{ \let \newtheoremstyle \relax \let \theoremstyle \relax % \end{macrocode} % % \begin{macrocode} \let \proof \relax \let \endproof \relax % \end{macrocode} % % \begin{macrocode} \let \pushQED \relax \let \popQED \relax \let \qedhere \relax \let \mathqed \relax \let \openbox \relax } % \end{macrocode} % Same for \cls{amsbook} and \cls{amsproc}: % \begin{macrocode} \AddToHook{class/amsbook/before}[block]{ \let \newtheoremstyle \relax \let \theoremstyle \relax \let \proof \relax \let \endproof \relax \let \pushQED \relax \let \popQED \relax \let \qedhere \relax \let \mathqed \relax \let \openbox \relax } % \end{macrocode} % \begin{macrocode} \AddToHook{class/amsproc/before}[block]{ \let \newtheoremstyle \relax \let \theoremstyle \relax \let \proof \relax \let \endproof \relax \let \pushQED \relax \let \popQED \relax \let \qedhere \relax \let \mathqed \relax \let \openbox \relax } % \end{macrocode} % And once the class is loaded restore our versions again. Note % that we don't have to restored all the QED-related commands as % ours are identical to those defined by the AMS. % \begin{macrocode} \AddToHook{class/amsart/after}[block]{ \let \newtheorem \amsnewtheorem \let \newtheoremstyle \amsnewtheoremstyle \let \theoremstyle \amstheoremstyle \let \proof \amsproof \let \endproof \amsendproof } % \end{macrocode} % % \begin{macrocode} \AddToHook{class/amsbook/after}[block]{ \let \newtheorem \amsnewtheorem \let \newtheoremstyle \amsnewtheoremstyle \let \theoremstyle \amstheoremstyle \let \proof \amsproof \let \endproof \amsendproof } % \end{macrocode} % % \begin{macrocode} \AddToHook{class/amsproc/after}[block]{ \let \newtheorem \amsnewtheorem \let \newtheoremstyle \amsnewtheoremstyle \let \theoremstyle \amstheoremstyle \let \proof \amsproof \let \endproof \amsendproof } % \end{macrocode} % % % % % % \subsection{Support for the \pkg{enumitem} interfaces} % % The current implementation incorporates most features of % \pkg{enumitem}. The plan is that the enumitem interfaces are % either natively available or are emulated and mapped to new % interfaces, so that documents using \pkg{enumitem} work % seamlessly. % % % % Most (or even all of the \pkg{enumitem} keys have gotten new % names, so there the task is to map old names to new names. One % question to decide here is which (if any) of the original keys % should remain natively available even if \pkg{enumitem} is not % loaded, and which should only be supported if the document % explicitly loads \pkg{enumitem}, i.e., support them only for % compatibility with old documents. Providing the full set by % default means one ends up with a fairly inconsistent interface, % but not providing some of them may result in people unnecessarily % loading \pkg{enumitem} in new documents just to get at, say, % \key{nosep}.\fmi{decide} % % % The \pkg{enumitem} package also provides declarations to build % out new lists and adjust the layout of existing list using % commands like \cs{newlist} or \cs{setlist}. % With the new implementation this is normally done differently, % e.g., defining instances and simple document level commands via % \cs{SimpleBlockEnv}, etc. However, we probably also want a % declaration such as \cs{newlist} (same name?) to provide a simple % way to make this happen in the document preamble in one go. % % I'm less sure about \cs{setlist} at least as far as its optional % argument is concerned (even though we have to support it in an emulation.\fmi{decide} % % We put the code that emulates \pkg{enumitem} in a separate file % to be loaded instead of the original package, but eventually some % of the code from there has to move back to the kernel to be % always present. % % \begin{macrocode} %\declare@file@substitution{enumitem.sty}{latex-lab-enumitem.sty} % \end{macrocode} % But for now we simply disable \pkg{enumitem} loading and % unconditionally load our replacement into the kernel for ease of % testing. In the end we have to decide which parts of the % interface (if any) we provide out of the box and which parts are % only available if the document requests \pkg{enumitem}. % \begin{macrocode} \declare@file@substitution{enumitem.sty}{null} \RequirePackage{latex-lab-enumitem} % \end{macrocode} % % % \subsection{Support for the \pkg{doc} package} % % When the \pkg{doc} package is loaded it wants to remove a % \texttt{\%} sign from the start of each verbatim line. For this % it uses \cs{check@percent} which we stick into the % \socket{verbatim/startline} socket. % % \begin{plugdecl}{doc} % \begin{macrocode} \NewSocketPlug {verbatim/startline}{doc}{ \check@percent } \AddToHook{package/doc/after}{ \AssignSocketPlug{verbatim/startline}{doc} } % \end{macrocode} % \end{plugdecl} % % \begin{macrocode} % % \end{macrocode} % % % % \begin{macrocode} %<*latex-lab> \ProvidesFile{block-latex-lab-testphase.ltx} [\ltlabblockdate\space v\ltlabblockversion\space blockenv implementation] \RequirePackage{latex-lab-testphase-block} % % \end{macrocode} % % \end{implementation} % % % \Finale % % \endinput ^^A don't typeset the first prototype notes ------- % % % % \appendix % % \section{Documentation from first prototype implementations} % % % \subsection{Open questions} % \begin{itemize} % \item Existing questions --- moved to issues --- % \end{itemize} % % \subsection{Code cleanup} % \begin{itemize} % \raggedright % \item Actually implement what's announced. % % \item Encapsulate most uses of \cs[no-index]{legacy_if\dots} into % commands with \pkg{expl3} syntax: we cannot rename these booleans % for compatibility reasons but we can make the code cleaner % nevertheless. --- made issue --- % % \item The \tn{topsep} and \tn{partopsep} business is tricky to % reproduce exactly (see \tn{@topsepadd} and \tn{@topsep}) because of % how it accumulates when lists are nested immediately. % % \end{itemize} % % % % \subsection{Tasks} % \begin{itemize} % % \item Change author to LaTeX Team once it's nice enough to deserve % that label. % % \item Reproducing exactly the standard layouts and examples in the % \pkg{enumitem} documentation. % % \item Hooks, but do not duplicate those that already exist as % environment hooks. Hence, mostly around items. % % \item Customization and interaction with LDB: % \begin{itemize} % \item Allow arbitrary nesting depth with automatically defined % styles for labels, counters etc. % \item Adapt everything to font size! (e.g. footnotes). % \item How to model the inheritance from trivlist to list to % enumerate? % \end{itemize} % % \item Add key--value settings mimicking \pkg{enumitem}'s ability to % set any four of five horizontal parameters and deduce the fifth by % $\tn{leftmargin} + \tn{itemindent} = \tn{labelindent} + % \tn{labelwidth} + \tn{labelsep}$. % % \item Provide good ways to customize how overlong labels are dealt with. % % \item Use the \texttt{.aux} file. % \begin{itemize} % \item Implement the \tn{ref} styles that \pkg{enumitem} provides. % \item Reverse enumerations, important in publication lists and the % like. Somehow avoid needing 3 compilations for references to % reverse enumerations to settle? % \item Ability to calculate \tn{labelwidth} from the label contents. % Share calculated parameters between multiple environments (cf.\ % \texttt{resume} option). % \end{itemize} % % \item Related to grabbing the whole list environment, and input syntax % variations: % \begin{itemize} % \item Other layouts: tabular (see \pkg{listliketab} vs % \pkg{typed-checklist}), multicolumn and horizontally numbered (see % \pkg{tasks}), inline lists, runin lists in the easy case where % there is no intervening \tn{par}. % \item Formatting the item text in a % box or similar (requires grabbing the whole list). % \item Filtering which items to show: hide certain items according to % criteria (useful together with list reuse), see % \pkg{typed-checklist}. % \item Shorthands \tn{iitem} for automatic nested lists, or \cs{1}, % \cs{2} etc from \pkg{outlines}. % \item Support markdown input like \pkg{asciilist}. % \end{itemize} % % \item Check interaction with \texttt{babel} options such as % \texttt{french} or \texttt{accadian} (see FrenchItemizeSpacing) % % \item RTL and vertical typesetting. % \end{itemize} % % % % % % \section{Plan of attack of first prototype (historical info)} % % Typesetting list environments involves a rather large number of % parameters. They can be affected by the context such as the total % list nesting level, the nesting level of the given type of list, and % the font size. An environment like \texttt{enumerate} has two main % aspects. % \begin{itemize} % \item It has a certain layout in the page, with vertical and % horizontal spacing around it. This type of layout is shared with % environments such as \texttt{quote}, \texttt{flushright}, or % \texttt{tabbing}. This common layout is implemented in \LaTeXe{} % through \tn{trivlist} (or \tn{list}). % \item It defines how each \tn{item} should be typeset: how to % construct the label, in particular the \texttt{counter} name, and % how to format the content of the item. % \end{itemize} % % This suggests defining two template types, \xt{block} and \xt{item} % covering these two aspects.\footnote{Possibly also \xt{endblock} to % deal with decorations at the end?} While the \xt{item} type will % perhaps have a single template, one could typeset a \xt{block} template % in several ways, for instance the standard \LaTeXe{} way or a fancy % colored box. % % The \xt{general} \xt{block} template should receive the following % parameters. The \xt{plain} \xt{block} template is a restricted % template that freezes all item-related parameters to dummy values % (\texttt{counter}, \texttt{start}, \texttt{resume}, % \texttt{label-width}, \texttt{label-sep} and all \texttt{item-*}). % The \xt{list} \xt{block} template is a restricted template\footnote{A % better approach could be to have a notion of inheritance for template % types, so that we end up with two different \emph{template types}. Then % we can implement other template for the list template type: \xt{table} % for lists typeset as rows/columns of a table, \xt{inline} for lists % typeset in horizontal mode within a paragraph, and \xt{runin} for % run-in lists.} that omits the \texttt{heading} parameter and whose % default for \texttt{item-instance} is non-empty. % \begin{itemize} % \raggedright % \item Structural parameters: the \texttt{heading} to place before, % \texttt{counter} name, \texttt{start} value, whether to % \texttt{resume} a previous list, and the \texttt{item-instance} (an % \xt{item} instance) to use when typesetting items. % \item Vertical spacing and penalties: \texttt{begin-penalty}, % \texttt{begin-vspace}, \texttt{begin-extra-vspace}, \texttt{item-penalty}, % \texttt{item-vspace}, \texttt{item-par-skip}, \texttt{end-penalty}, % \texttt{end-vspace}, \texttt{end-extra-vspace}. % \item Horizontal spacing: \texttt{right-margin}, \texttt{left-margin}, % \texttt{para-indent}, \texttt{item-indent}, \texttt{label-width}, % \texttt{label-sep}. % \end{itemize} % A \docclass should edit these templates (or define restricted % templates) to set up default values that depend on \tn{g_block_nesting_depth_int}, % namely how many lists are nested overall.\footnote{Does % \pkg{xtemplate} provide a way to specify default values that are only % evaluated once an instance is used?} The document class should then % set up an instance of these templates for each environment, with % appropriate settings such as a \texttt{heading}, a suitable % \texttt{item-instance}, or making \texttt{margin-right} equal to % \texttt{margin-left} in a \texttt{quote} environment. % % The \xt{inline-list} \xt{block} template receives many fewer % parameters. Note that \texttt{begin-vspace}, \texttt{item-vspace}, % \texttt{end-vspace} are now \emph{horizontal} skips.\fmi{Text wrong % and or concept with vspace and hspace questionable!} % \begin{itemize} % \item Structural parameters: \texttt{counter}, \texttt{start}, % \texttt{resume}, \texttt{item-instance}. % \item Spacing and penalties: \texttt{begin-penalty}, % \texttt{begin-vspace}, \texttt{item-penalty}, \texttt{item-vspace}, % \texttt{end-penalty}, \texttt{end-vspace}.\fmi{revisit!} % \item Horizontal spacing: \texttt{label-width}, \texttt{label-sep}.\fmi{check!} % \end{itemize} % % The \xt{std} \xt{item} template should receive the following % parameters. They depend on the type of list and its nesting level % among lists of such type, but typically not on the total nesting % level. % \begin{itemize} % \item Counter name (\texttt{counter}), shared with the parent % \xt{list} \xt{block} template, but needed for incrementing. % \item Label construction: a function \texttt{counter-label} that % produces the label from the counter name, used if \tn{item} is given % without argument. % \item References: a function \texttt{counter-ref} for how the label % should be referred to when it is constructed from the counter, % \texttt{label-ref} and \texttt{label-autoref} used when \tn{item} % has an optional argument. % \item Label formatting: \texttt{label-format} function, % \texttt{label-strut} boolean. % \item Label alignment (\texttt{label-align}, \texttt{label-boxed}, % \texttt{next-line}). % \item Content parameters: \key{text-font}. % \item A \texttt{compatibility} boolean that controls for instance % whether \tn{makelabel} is used. % \end{itemize} % The \docclass should set up an instance such as \xt{enumiii} for each % environment and nesting level.\footnote{This should be made easily % extendible to deeper levels.} % % A given environment will adjust some nesting levels, then call the % \xt{block} instance appropriate to the environment type, passing it % the \xt{item} instance appropriate to the environment and depth. % Additional context-dependence could be provided by \pkg{l3ldb}, but % the main context-dependence should not rely on it for simplicity % reasons and incidentally because \pkg{l3ldb} is not yet available. % % % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% \endinput