% \iffalse meta-comment % %% File: latex-lab-math.dtx % % Copyright (C) 2022-2023 The LaTeX Project % % It may be distributed and/or modified under the conditions of the % LaTeX Project Public License (LPPL), either version 1.3c of this % license or (at your option) any later version. The latest version % of this license is in the file % % https://www.latex-project.org/lppl.txt % % % The development version of the bundle can be found below % % https://github.com/latex3/latex2e/required/latex-lab % % for those people who are interested or want to report an issue. % % \def\ltlabmathdate{2023-09-11} \def\ltlabmathversion{0.5c} % %<*driver> \documentclass{l3doc} \EnableCrossrefs \CodelineIndex \usepackage{todonotes} \begin{document} \DocInput{latex-lab-math.dtx} \end{document} % % % \fi % % % \title{The \texttt{latex-lab-math} code\thanks{}} % \author{Frank Mittelbach, Joseph Wright, \LaTeX{} Project} % \date{v\ltlabmathversion\ \ltlabmathdate} % % \maketitle % % \newcommand\NEW[1]{\marginpar{\mbox{}\hfill\fbox{New: #1}}} % \providecommand\class[1]{\texttt{#1.cls}} % \providecommand\pkg[1]{\texttt{#1}} % \providecommand\hook[1]{\texttt{#1}} % % ^^A \car {...} for marginal comments % ^^A \car*{...} for longer inline comments % % \NewDocumentCommand\car{sO{}m} % {\IfBooleanTF{#1}{\todo[inline,color=blue!10,#2]{#3}}^^A % {\todo[color=blue!10,#2]{#3}}} % % \NewDocumentCommand\fmi{sO{}m} % {\IfBooleanTF{#1}{\todo[inline,#2]{#3}}^^A % {\todo[#2]{#3}}} % % % % \begin{abstract} % This is an experimental prototype. It captures math material % (basically okay, but the interfaces for packages aren't yet % there) and tags the material (which is not yet anywhere near the % final state). That part is provided for experimentation and to % gather feedback, etc. % \end{abstract} % % \tableofcontents % % \section{Introduction} % \car*{Todo: update all the documentation! Both here and % (what little there is!) in the implementation section.} % % This file implements capture of all math mode material at the outer % level, i.e., a formula is captured in its entirety with inner text % blocks (possibly containing further math) absorbed as part of the % formula. For example, %\begin{verbatim} % \[ a \in A \text{ for all $a<5$} \] %\end{verbatim} % would only result in a single capture of the tokens % ``\verb*/a \in A \text{ for all $a<5$}/''. % % \subsection{Code level interfaces} % % \begin{function}{\math_register_env:n, \math_register_env:nn} % \begin{syntax} % \cs{math_register_env:n} \Arg{env} % \cs{math_register_env:nn} \Arg{env} \Arg{options} % \end{syntax} % Registers the \meta{env} as a math environment which should be captured % and made available. This is necessary for all top-level math mode % environments: low-level errors may result if these are not correct % set up. One or more key--value \meta{options} may also be given: % \begin{itemize} % \item[\texttt{arg-spec}] The argument specification taken by the % beginning of the environment; this is used to remove non-mathematical % material. % \end{itemize} % \end{function} % % \begin{function}{\math_processor:n} % \begin{syntax} % \cs{math_processor:n} \Arg{tokens} % \end{syntax} % Declares that the captured math content should be passed to the % \meta{tokens}, which will receive the environment type as |#1| and % the content as |#2|. % \end{function} % % \subsection{Document level interfaces} % % \begin{function}{\RegisterMathEnvironment} % \begin{syntax} % \cs{RegisterMathEnvironment} \oarg{options} \Arg{env} % \end{syntax} % Registers the \meta{env} as a math environment which should be captured % and made available. This is necessary for all top-level math mode % environments: low-level errors may result if these are not correct % set up. One or more key--value \meta{options} may also be given: % \begin{itemize} % \item[\texttt{arg-spec}] The argument specification taken by the % beginning of the environment; this is used to remove non-mathematical % material. % \end{itemize} % \end{function} % % \section{Known current bugs, etc.} % \car*{New Section, now with subsections.\\ % As indicated, these lists are probably incomplete.\\ % Some of these have been addressed in a more recent branch.} % % \subsection{Capture/grabbing problems} % % \begin{enumerate} % \item Incorrect grabbing of |$|-math when there is also % explicit |$|-math within a \textit{text environment} % that is itself within the math that should all be grabbed. % \item Similar incorrect grabbing with |$$| also. % \item The grabbing, for all the display environments (and |\) \]|), needs % to deal with nesting: \pkg{amsmath} contains code for this. % \item % \end{enumerate} % % \subsection{Other problems} % % \begin{enumerate} % \item % The presence of \cs{m@th} in association with \cs{ensuremath} % does not necessarily indicate fakemath. This is because % wanting mathsurround to be zero is very reasonable and common, % \emph{even when the math is genuine} (and hence needs to be collected). % \item User-defined environments can create problems; but this area, of % new, copied and changed environments, has not yet been developed. % % \car*{Joseph wrote, inter alia:\\ % My thinking [regarding] \cs{RegisterMathEnvironment}\\ % - (New) Math environments should not be created-then-patched, but only % generated by a [(future)] dedicated command (\cs{DeclareMathEnviornment}, % presumably)\\ % - Math environments created with \pkg{ltcmd} [commands] should not be copied, . . .\\ % - Package authors should be able to manually set up math environments with a public boolean.} % \end{enumerate} % % % \subsection{Other ToDos} % % \begin{enumerate} % \item Add (some of) the math display commands that were \enquote{lifted from % plain}, e.g., \cs{displaylines} \cs{eqalign}(??). % \item % \end{enumerate} % % % \car*{\cs{MaybeStop} (temporarily) not executed, as it is unknown on Chris' system.} % \iffalse % \MaybeStop{\setlength\IndexMin{200pt} \PrintIndex } % \else % \StopEventually{\setlength\IndexMin{200pt} \PrintIndex } % \fi % % \section{The Implementation} % % \begin{macrocode} %<@@=math> % \end{macrocode} % % \begin{macrocode} %<*kernel> % \end{macrocode} % % \subsection{File declaration} % % \car{Change description here?} % \begin{macrocode} \ProvidesFile{latex-lab-math.ltx} [\ltlabmathdate\space v\ltlabmathversion\space Grab all the math(s) and tag it (experiments)] % \end{macrocode} % % Temp loading \ldots % \begin{macrocode} \AddToHook{begindocument/before}{\RequirePackage{latex-lab-testphase-block}} % \end{macrocode} % % \begin{macrocode} \ExplSyntaxOn % \end{macrocode} % % \subsection{Setup} % % Loading \pkg{amsmath} is an absolute requirement: this avoids needing to % have conditional definitions and deals with how to define \cs{[}/\cs{]} % neatly. % \begin{macrocode} \tl_gput_right:Nn \@kernel@before@begindocument { \RequirePackage { amsmath } } % \end{macrocode} % % % \subsection{Data structures} % % % % \begin{variable}{\l_@@_collected_bool} % Tracks whether math mode material has been collected, which happens inside % \pkg{amsmath} environments as well as those handled directly here. % % % \begin{macrocode} \bool_new:N \l_@@_collected_bool % \end{macrocode} % \end{variable} % % \car{Change first tl name below: `env' $=>$ `info'?\\ % Or do we need an extra storage tl?} % % \begin{variable}{\g_@@_grabbed_env_tl, \g_@@_grabbed_math_tl} % \cs{g_@@_grabbed_env_tl} contains the name of the math environment (\texttt{math} in the case % of inline math, \cs{g_@@_grabbed_math_tl} the math content. % \begin{macrocode} \tl_new:N \g_@@_grabbed_env_tl \tl_new:N \g_@@_grabbed_math_tl % \end{macrocode} % \end{variable} % % \subsection{Interface commands} % % \begin{macro} % {\@@_process:nn, \@@_process:Vn, \@@_process_auxi:nn, \@@_process_auxii:nn} % A no-op place-holder; the internal wrapper means that it does not need to % be concerned with internals. % \begin{macrocode} \cs_new_protected:Npn \@@_process:nn #1#2 { \legacy_if:nF { measuring@ } { \tl_if_in:nnF {#2} { \m@th } { \tl_trim_spaces_apply:nN {#2} \@@_process_auxi:nn {#1} } } } \cs_generate_variant:Nn \@@_process:nn { V } \cs_new_protected:Npn \@@_process_auxi:nn #1#2 { \tl_gset:Nn \g_@@_grabbed_env_tl {#2} \tl_gset:Nn \g_@@_grabbed_math_tl {#1} \@@_process_auxii:nn {#2} {#1} } \cs_new_protected:Npn \@@_process_auxii:nn #1#2 { } % \end{macrocode} % \end{macro} % % \begin{macro}{\math_processor:n} % A simple installer % \begin{macrocode} \cs_new_protected:Npn \math_processor:n #1 { \cs_set_protected:Npn \@@_process_auxii:nn ##1##2 {#1} } % \end{macrocode} % \end{macro} % % \subsection{Content grabbing} % % \begin{macro}{\@@_grab_dollar:w} % Grab up to a single |$|, for inline math mode, suppressing % any processing if the first token is \tn{m@th}. % % \fmi{what's that test doing?} % % \car{It is some kind of fix, to avoid the remote % possibility that the math is empty, making the code % produce an unwanted \texttt{\$\$}.} % %\car{cf.~the code for this in \cs{@ensuredmath}} % %\car{It is harmless but unnecessary in the dollardollar grabbing below.} % % \begin{macrocode} \cs_new_protected:Npn \@@_grab_dollar:w % $ #1 $ { % \end{macrocode} % \fmi{what's that test doing?} % \begin{macrocode} \tl_if_blank:nF {#1} { \@@_process:nn { math } {#1} % $ % fairly simple this one % \end{macrocode} % We do not want math tagging in fakemath or when measuring, % so we imitate the test inside \cs{@@_process:nn} for now, % see https://github.com/latex3/tagging-project/issues/5 % TODO: use socket to get more control about typesetting variants (tagged, drop etc)? % \begin{macrocode} \legacy_if:nTF { measuring@ } { #1 $ } { \tl_if_in:nnTF {#1} { \m@th } { #1 $ } { \tagmcend %end P-chunk, in code: \tag_mc_end_push: \@kernel@math@begin #1 $ \@kernel@math@end \tagmcbegin{} % restart P-chunk (whatsits in pdftex) } } } } % \end{macrocode} % \end{macro} % \begin{macro}{\@@_grab_dollardollar:w} % And for the classical \TeX{} display structure. % \begin{macrocode} \skip_new:N \l_@@_tmpa_skip \cs_new_protected:Npn \@@_grab_dollardollar:w % $$ #1 $$ { \tl_if_blank:nF {#1} { \@@_tag_dollardollar_display:nn { equation* }{#1} #1 $$ } } % \end{macrocode} % To allow to use the code without tagging we guard. But probably tagpdf should % provide some tools for such manual para-ends. % \begin{macrocode} \cs_new_protected:Npn \@kernel@close@P { \tag_if_active:T { \tagmcend %end P-chunk, should perhaps be \tag_mc_end_push: ... \int_gincr:N \g__tag_para_end_int \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: } } \cs_new_protected:Npn \@@_tag_dollardollar_display:nn #1#2 { \@@_process:nn {#1} {#2} \@kernel@close@P \@kernel@math@begin % \skip_set:Nn \belowdisplayskip {-\belowdisplayskip} % \skip_set:Nn \belowdisplayshortskip {-\belowdisplayshortskip} % \int_set:Nn \postdisplaypenalty {10000} %% % \group_insert_after:N \@@_tag_dollardollar_display_end: } \cs_new_protected:Npn \@@_tag_dollardollar_display_end: { % \typeout{== tag dollarldollar display end} % \ShowTagging{struct-stack} \tagpdfparaOff \para_raw_end: \tagpdfparaOn \l_@@_tmpa_skip \lastskip \@kernel@math@end \penalty \postdisplaypenalty % \end{macrocode} % This reinserts the below display skips. It must be doubled to % get the right amount: % \begin{macrocode} \skip_vertical:n { -\l_@@_tmpa_skip * 2 } % \@doendpe % this has no \end{...} to take care of it } % \end{macrocode} % \end{macro} % % % \begin{macro}{\@@_grab_inline:w} % Collect inline math content and deal with the need to move to math mode. % \begin{macrocode} \cs_new_protected:Npn \@@_grab_inline:w % \( #1 \) { \tl_if_blank:nF {#1} { \@@_process:nn { math } {#1} $ #1 $ } \bool_set_false:N \l_@@_collected_bool } % \end{macrocode} % \end{macro} % \begin{macro}{\@@_grab_eqn:w} % For the most common use of \cs{[}/\cs{]}: turn into an environment. % \begin{macrocode} \cs_new_protected:Npn \@@_grab_eqn:w % \[ #1 \] { % \typeout{collected? = \bool_if:NTF \l_@@_collected_bool {true}{false}} \begin { equation* } #1 \end { equation* } } % \end{macrocode} % \end{macro} % % \subsection{Marking math environments} % % A general mechanism for math mode environments that do not grab their % content (\emph{cf.}~most \pkg{amsmath} environments). % % \begin{variable}{\l_@@_env_name_tl} % To allow us to carry out \enquote{special effects} % \begin{macrocode} \tl_new:N \l_@@_env_name_tl % \end{macrocode} % \end{variable} % % Here we set up specialised handling of environments. The idea for the % \texttt{arg-spec} key is that if an environment takes arguments, we % don't worry during the main grabbing. Rather, we remove the arguments % from the grabbed content and forward only the payload. That is done by % (ab)using \pkg{ltcmd}. % \begin{macrocode} \keys_define:nn { @@ } { arg-spec .code:n = { \ExpandArgs { c } \DeclareDocumentCommand { @@_env \l_@@_env_name_tl _aux: } {#1} { \@@_env_forward:w } } } % \end{macrocode} % % \begin{macro}{\math_register_env:nn} % \begin{macro}{\math_register_env:n} % \begin{macro}{\RegisterMathEnvironment} % Set up to capture environment content and make available. % % \begin{macrocode} \cs_new_protected:Npn \math_register_env:nn #1#2 { \tl_set:Nn \l_@@_env_name_tl {#1} \keys_set:nn { @@ } {#2} \cs_gset_eq:cc { @@_env_ #1 _begin: } {#1} \cs_gset_eq:cc { @@_env_ #1 _end: } { end #1 } % \ExpandArgs { nnx } \RenewDocumentEnvironment {#1} { b } { % \bool_set_true:N \exp_not:N \l_@@_collected_bool % \cs_if_exist:cTF { @@_env #1 _aux: } % { % \exp_not:c { @@_env #1 _aux: } % ####1 \exp_not:N \@@_env_end: {#1} % } % { \exp_not:N \@@_process:nn {#1} {####1} } \exp_not:N \bool_if:NTF \exp_not:N \l_@@_collected_bool { % \typeout{===>B1} } { % \typeout{===>B2} \cs_if_exist:cTF { @@_env #1 _aux: } { \exp_not:c { @@_env #1 _aux: } ####1 \exp_not:N \@@_env_end: {#1} } { \exp_not:N \@@_process:nn {#1} {####1} } \exp_not:n { \@kernel@math@registered@begin } \bool_set_true:N \exp_not:N \l_@@_collected_bool } % \exp_not:N \tracingall \exp_not:c { @@_env_ #1 _begin: } ####1 \exp_not:c { @@_env_ #1 _end: } % \exp_not:c { @@_env_ #1 _end: } % \exp_not:N \tracingnone % \exp_not:n { \@kernel@math@registered@end } } { } } \cs_set_protected:Npn \__cs_tmp:w #1 { \group_begin: \exp_args:No \__cs_generate_internal_variant:n { \tl_to_str:n {#1} } \group_end: } \__cs_tmp:w { nnxx } \cs_new_protected:Npn \math_register_halign_env:nn #1#2 { \tl_set:Nn \l_@@_env_name_tl {#1} \keys_set:nn { @@ } {#2} \cs_gset_eq:cc { @@_env_ #1 _begin: } {#1} \cs_gset_eq:cc { @@_env_ #1 _end: } { end #1 } % \ExpandArgs { nnxx } \RenewDocumentEnvironment {#1} { b } { % \bool_set_true:N \exp_not:N \l_@@_collected_bool % \cs_if_exist:cTF { @@_env #1 _aux: } % { % \exp_not:c { @@_env #1 _aux: } % ####1 \exp_not:N \@@_env_end: {#1} % } % { \exp_not:N \@@_process:nn {#1} {####1} } \exp_not:N \bool_if:NTF \exp_not:N \l_@@_collected_bool { % \typeout{===>B1} } { % \typeout{===>B2} \cs_if_exist:cTF { @@_env #1 _aux: } { \exp_not:c { @@_env #1 _aux: } ####1 \exp_not:N \@@_env_end: {#1} } { \exp_not:N \@@_process:nn {#1} {####1} } \exp_not:n { \@kernel@math@registered@begin } \bool_set_true:N \exp_not:N \l_@@_collected_bool } % \exp_not:N \tracingall \exp_not:c { @@_env_ #1 _begin: } ####1 % \exp_not:c { @@_env_ #1 _end: } % \exp_not:N \tracingnone } { \exp_not:c { @@_env_ #1 _end: } } } \cs_new_protected:Npn \math_register_odd_env:nn #1#2 { \tl_set:Nn \l_@@_env_name_tl {#1} \keys_set:nn { @@ } {#2} \cs_gset_eq:cc { @@_env_ #1 _begin: } {#1} \cs_gset_eq:cc { @@_env_ #1 _end: } { end #1 } % \ExpandArgs { nnxx } \RenewDocumentEnvironment {#1} { b } { \exp_not:N \bool_if:NTF \exp_not:N \l_@@_collected_bool { % \typeout{===>B1} } { % \typeout{===>B2} \cs_if_exist:cTF { @@_env #1 _aux: } { \exp_not:c { @@_env #1 _aux: } ####1 \exp_not:N \@@_env_end: {#1} } { \exp_not:N \@@_process:nn {#1} {####1} } \exp_not:n { \@kernel@math@registered@begin } \bool_set_true:N \exp_not:N \l_@@_collected_bool } % \exp_not:N \tracingall \exp_not:c { @@_env_ #1 _begin: } ####1 } { \exp_not:c { @@_env_ #1 _end: } % needed if we don't have $$...$$ % \exp_not:n { \typeout{---> @kernel@math@registered@end }} \exp_not:n { \@kernel@math@registered@end } } } % FMi: compare with block change! % % \DeclareRobustCommand*\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 % \@endpefalse % tmp!!! is it ok to drop this here? % \reserved@a} \cs_new:Npn \@kernel@math@registered@begin { % \ShowTagging{struct-stack} %\typeout{==>A1}\ShowTagging{struct-stack,mc-current} \mode_if_vertical:TF { % \legacy_if:nTF { @endpe } % { \legacy_if_set_false:n { @endpe } } % { \__block_list_beginpar_vmode: } % % \typeout{==>~ at:~ \g__tag_struct_tag_tl} % \exp_args:Noo\str_if_eq:nnF \g__tag_struct_tag_tl { \l__tag_para_main_tag_tl } % needs correction! { % \typeout{==>A2} \__block_beginpar_vmode: } % needs correction! } { % \typeout{==>A3} \@kernel@close@P % \tagmcend % needs correction! } \@kernel@math@begin \tagpdfparaOff % \typeout{==>MC1}\ShowTagging{mc-current} } \cs_new:Npn \@kernel@math@registered@end { % \typeout{==>MC2}\ShowTagging{mc-current} \para_raw_end: \tagpdfparaOn \@kernel@math@end % \typeout{==>MC3}\ShowTagging{mc-current} \@endpetrue } \cs_new_protected:Npn \math_register_env:n #1 { \math_register_env:nn {#1} { } } \NewDocumentCommand \RegisterMathEnvironment { O{} m } { \math_register_env:nn {#2} {#1} } % \end{macrocode} % \end{macro} % \end{macro} % \end{macro} % % \begin{macro}{\@@_env_forward:w} % \begin{macrocode} \cs_new_protected:Npn \@@_env_forward:w #1 \@@_env_end: #2 { \@@_process:nn {#2} {#1} } % \end{macrocode} % \end{macro} % % \subsection{Document commands} % % \car*{Add one more here: \texttt{displaymath}, which % is equivalent to \cs{[} , \cs{]}\\ % and hence to the basic \texttt{equation*}.\\ % Added in more recent branch.} % % \begin{macro} % {\equation, \@@_equation_begin:, \equation*, \@@_equation_star_begin:} % \begin{macro} % {\endequation, \@@_equation_end:, \endequation*, \@@_equation_star_end:} % These environments are not set up by \pkg{amsmath} to collect their body, % so we do that here. This has to be done \emph{after} we can be sure % \pkg{amsmath} is loaded. % % \car*{Note that with \pkg{amsmath} loaded, \texttt{equation*} and \texttt{equation}\\ % are the two basics: they are used to define the other single-row\\ % display environments, etc.} % % \begin{macrocode} \tl_gput_right:Nn \@kernel@before@begindocument { \math_register_env:n { equation } \math_register_env:n { equation* } % at the moment register_env can only do display math % \math_register_env:n { math } \RenewDocumentEnvironment{math} {b}{$#1$}{} % and this one doesn't work either % \math_register_env:n { displaymath } \RenewDocumentEnvironment{displaymath} {b}{\[#1\]}{} } % \end{macrocode} % \end{macro} % \end{macro} % % \begin{macro}{\(, \)} % If math mode has not been collected, we need to do that; otherwise, worry % about whether we are in math mode or not. The closing command here can only % occur inside a collected math block: otherwise it will be simply used as % a delimiter. % \begin{macrocode} \cs_gset_protected:Npn \( % \) { \bool_if:NTF \l_@@_collected_bool { \mode_if_math:TF { \@badmath } { $ } } { \@@_grab_inline:w } } % \( \cs_gset_protected:Npn \) { \mode_if_math:TF { $ } { \@badmath } } % \end{macrocode} % \end{macro} % % \begin{macro}{\[, \]} % Again, we need to watch for when \pkg{amsmath} is loaded after this code. % The flag usage here is to cover the case where \cs{[}/\cs{]} is hidden % inside another environment. In this case the grabbing happens on % the outer level and should not be repeated. % \begin{macrocode} \tl_gput_right:Nn \@kernel@before@begindocument { \cs_gset_protected:Npn \[ % \] { \bool_if:NTF \l_@@_collected_bool { \begin { equation* } } { \@@_grab_eqn:w } } % \[ \cs_gset_protected:Npn \] { \bool_if:NTF \l_@@_collected_bool { \end{ equation* } } { \@badmath } } } % \end{macrocode} % \end{macro} % % % \begin{macro}{\ensuremath} % A bit of nesting fun to make sure we collect only if required. % \fmi{why does ensuremath need handling at all?} % % \car{Indeed! Currently, this is setup to process the math that % it has anyways already captured as its argument; thus it is more % efficient than leaving the capture to be repeated by the \cs{everymath}} % % \begin{macrocode} %\cs_gset_protected:Npn \ensuremath #1 % { % \mode_if_math:TF % {#1} % { % \bool_if:NTF \l_@@_collected_bool % { \@ensuredmath {#1} } % { % \bool_set_true:N \l_@@_collected_bool % \@@_process:nn { math } {#1} % \@ensuredmath {#1} % \bool_set_false:N \l_@@_collected_bool % } % } % } % \end{macrocode} % \end{macro} % % \subsection{\cs{everymath} and \cs{everydisplay}} % % The business end for grabbing inline math and \enquote{raw} \TeX{} % display. Most display math mode is actually handled elsewhere, as we % have macro control. % \begin{macrocode} \tl_new:N\tmpmathcontent \def\@kernel@math@begin { % \typeout{==>~math~begin} % needs different handling if we support nesting \tl_gset:Nx\tmpmathcontent { LaTeX~ formula~ starts~ \exp_not:N\begin{\g_@@_grabbed_env_tl} \space \exp_not:V\g_@@_grabbed_math_tl \space \exp_not:N\end{\g_@@_grabbed_env_tl} \space LaTeX~ formula~ ends~ } \tagstructbegin{tag=Formula, AFinline-o=\tmpmathcontent, title-o=\g_@@_grabbed_env_tl, actualtext=\tmpmathcontent % alt=\tmpmathcontent } % inner formula if multiple parts (not really implemented yet) \grabaformulapartandstart % the above does: % \tagstructbegin{tag=Formula}\tagmcbegin{} % or just % \tagmcbegin{} } \def\@kernel@math@end { % \typeout{==>~math~end} % \ShowTagging{struct-stack} \tagmcend \if@subformulas \tagstructend \else \fi \tagstructend % \ShowTagging{struct-stack} } \exp_args:No \tex_everymath:D { \tex_the:D \tex_everymath:D \bool_if:NF \l_@@_collected_bool { \bool_set_true:N \l_@@_collected_bool \@@_grab_dollar:w } } \exp_args:No \tex_everydisplay:D { \tex_the:D \tex_everydisplay:D \iftrue % this may have to be a settable flag! % { % \typeout{==>~ in~ everydisplay} % \end{macrocode} % flipping the \cs{belowdisplay} values is done so that we get (assumption) % a negative skip and not make the page bigger then we take that out, % then we add the tagging code (in \cs{@@_tag_dollardollar_display_end} ) and % then we put a real \cs{postdisplaypenalty} in and % the right skip (of which we don't know if it is short or a % normal \cs{belowdisplayskip}). This might need some refinement if that skip % is actually negative from the start % (not sure it ever is and is worth bothering about) % \begin{macrocode} \skip_set:Nn \belowdisplayskip {-\belowdisplayskip} \skip_set:Nn \belowdisplayshortskip {-\belowdisplayshortskip} \int_set:Nn \postdisplaypenalty {10000} % \group_insert_after:N \@@_tag_dollardollar_display_end: % } \fi \bool_if:NF \l_@@_collected_bool { \bool_set_true:N \l_@@_collected_bool \@@_grab_dollardollar:w } } % \end{macrocode} % % \subsection{Modifying kernel environments} % % We need to cover this even though it is, of course, not encouraged. % \begin{macrocode} \math_register_env:n { eqnarray } \math_register_env:n { eqnarray* } % \end{macrocode} % % Places where math mode is (ab)used. % \begin{macrocode} \clist_map_inline:nn { tabular } { \AddToHook{ env / #1 / begin } { \bool_set_true:N \l_@@_collected_bool } } % \end{macrocode} % % \begin{macro}{\@@_m@th:, \m@th} % Handle non-math use of math mode. At present nesting isn't supported as % \cs{m@th} pops up in a few places that \emph{are} math mode! % \begin{macrocode} \cs_new_eq:NN \@@_m@th: \m@th \cs_gset_protected:Npn \m@th { \bool_set_true:N \l_@@_collected_bool \@@_m@th: } % \end{macrocode} % \end{macro} % % \subsection{Modifying \pkg{amsmath}} % % \begin{macro}{\@@_amsmath_align@:nn} % \begin{macro} % { % \@@_amsmath_gather@:n , % \@@_amsmath_multline@:n % } % \begin{macro}{\align@} % \begin{macro}{\gather@, \multline@} % Mark up all of the display environments as the content is captured anyway. % We then use an internal macro in each environment type to insert the % processing code. Each of these is slightly different, so we cannot use a % simple loop here. The test for \cs{split@tag} is required as the % \texttt{split} environment internally uses \texttt{gather} \emph{when not % within an \pkg{amsmath}} environment, for example inside \texttt{equation}. % Without the precaution, we'd get two copies of the grabbed math, the second % of which would start with \cs{split@tag}. % \begin{macrocode} \tl_gput_right:Nn \@kernel@before@begindocument { % \renewenvironment{gather*}{% \start@gather\st@rredtrue } {% % this redirection doesn't work if we alter "gather"! % \endgather % so replace it with its real meaning \math@cr \black@\totwidth@ \egroup $$\ignorespacesafterend } % \end{macrocode} % % \begin{macrocode} \def\common@align@ending { \math@cr \black@\totwidth@ \egroup \ifingather@ \restorealignstate@ \egroup \nonumber \ifnum0=`{\fi\iffalse}\fi \else $$% \fi \ignorespacesafterend } \renewenvironment{alignat}{% \start@align\z@\st@rredfalse }{% \common@align@ending } \renewenvironment{alignat*}{% \start@align\z@\st@rredtrue }{% \common@align@ending } \renewenvironment{xalignat}{% \start@align\@ne\st@rredfalse }{% \common@align@ending } \renewenvironment{xalignat*}{% \start@align\@ne\st@rredtrue }{% \common@align@ending } \renewenvironment{xxalignat}{% \start@align\tw@\st@rredtrue }{% \common@align@ending } \renewenvironment{align}{% \start@align\@ne\st@rredfalse\m@ne }{% \common@align@ending } \renewenvironment{align*}{% \start@align\@ne\st@rredtrue\m@ne }{% \common@align@ending } \renewenvironment{flalign}{% \start@align\tw@\st@rredfalse\m@ne }{% \common@align@ending } \renewenvironment{flalign*}{% \start@align\tw@\st@rredtrue\m@ne }{% \common@align@ending } % \renewenvironment{multline*}{\start@multline\st@rredtrue} {% \iftagsleft@ \@xp\lendmultline@ \else \@xp\rendmultline@ \fi \ignorespacesafterend } % \end{macrocode} % Also for "false?" % \begin{macrocode} \def\measuring@true{\let\ifmeasuring@\iftrue\tag_stop:} % \end{macrocode} % % \begin{macrocode} % \math_register_halign_env:nn {align}{} \math_register_halign_env:nn {align*}{} \math_register_halign_env:nn {flalign}{} \math_register_halign_env:nn {flalign*}{} \math_register_halign_env:nn {gather}{} \math_register_halign_env:nn {gather*}{} \math_register_halign_env:nn {multline}{} \math_register_halign_env:nn {multline*}{} \math_register_halign_env:nn {xalignat}{} \math_register_halign_env:nn {xalignat*}{} \math_register_halign_env:nn {xxalignat}{} % \@namedef{maketag @ @ @} #1{% % \typeout{--->maketag @ @ @} \ifmeasuring@ \hbox{\m@th\normalfont#1}% \else \tagmcend \tagstructbegin{tag=Lbl}% \tagmcbegin{tag=Lbl}% \hbox{\m@th\normalfont#1}% \tagmcend \tagstructend \tagmcbegin{}% \fi } % \end{macrocode} % % \begin{macrocode} \def\intertext@{% \def\intertext##1{% \ifvmode\else\\\@empty\fi \noalign{% % we have to flip the sign on the skip because we flipped it on the outside \penalty\postdisplaypenalty\vskip-\belowdisplayskip \vbox{ % \end{macrocode} % Stop tagging when measuring: % \begin{macrocode} \ifmeasuring@\tag_stop:\fi \normalbaselines \ifdim\linewidth=\columnwidth \else \parshape\@ne \@totalleftmargin \linewidth \fi % \end{macrocode} % End the previous mc:\fmi{if we use 2 levels of formulas this would % need changing} % \begin{macrocode} \tag_mc_end_push: % \end{macrocode} % We are already in a par so we change now to Span:\fmi{not true any longer} % \begin{macrocode} \tagpdfsetup{paratag=P}% \tagpdfparaOn \noindent\ignorespaces##1\par % \end{macrocode} % Restart the MC % \begin{macrocode} \tag_mc_begin_pop:n{}}% \penalty\predisplaypenalty\vskip\abovedisplayskip% }% } } % \end{macrocode} % % \begin{macrocode} \@namedef{math@cr @ @ @ gather}{% \ifst@rred\nonumber\fi &\relax \make@display@tag % \maybestartnewformulatag % \ifst@rred\else\global\@eqnswtrue\fi \global\advance\row@\@ne \cr } % \end{macrocode} % % \begin{macrocode} \@namedef{math@cr @ @ @ align}{% \ifst@rred\nonumber\fi \if@eqnsw \global\tag@true \fi \global\advance\row@\@ne \add@amps\maxfields@ \omit \kern-\alignsep@ \iftag@ \setboxz@h{\@lign\strut@{\make@display@tag}}% \place@tag \fi % \maybestartnewformulatag % \ifst@rred\else\global\@eqnswtrue\fi \global\lineht@\z@ \cr } % \end{macrocode} % % \begin{macrocode} \def\restore@math@cr{\@namedef{math@cr @ @ @}{ % \maybestartnewformulatag % \cr}} \restore@math@cr % \end{macrocode} % % \begin{macrocode} } % \end{macrocode} % \end{macro} % \end{macro} % \end{macro} % \end{macro} % % % % \begin{macro}{} % % \begin{macrocode} \cs_new:Npn \@@_split_at_nl_first:w #1 \\ #2 \\ #3 \s_stop { \quark_if_nil:nTF {#2} { {#1} { } } { \@@_split_chk_if_begin:ww #1 \begin \q_nil \s_mark #2 \\ #3 \s_stop } } \cs_new:Npn \@@_split_chk_if_begin:ww #1 \begin #2 #3 \s_mark #4 \\ \q_nil \\ \s_stop { \quark_if_nil:nTF {#2} { {#1} {#4} } { \exp_after:wN \@@_split_collect_one_end:w \@@_split_cleanup_begin_q_nil:w #1 \begin{#2} #3 \\ #4 \s_stop { } { 1 } } } \cs_new:Npn \@@_split_cleanup_begin_q_nil:w #1 \begin \q_nil {#1} \cs_new:Npn \@@_split_collect_one_end:w #1 \end #2 #3 \s_stop #4 #5 { \exp_args:Nf \@@_split_check_count_begins:nnnn { \@@_split_count_begins:n { #4 #1 } } {#5} { #4 #1 \end{#2} } {#3} } \cs_new:Npn \@@_split_count_begins:n #1 { \int_eval:n { 0 \@@_split_count_begins:w #1 \begin \q_nil } } \cs_new:Npn \@@_split_count_begins:w #1 \begin #2 { \quark_if_nil:nF {#2} { +1 \@@_split_count_begins:w } } \cs_new:Npn \@@_split_check_count_begins:nnnn #1 #2 #3 #4 { \int_compare:nNnTF {#1} = {#2} { \exp_last_unbraced:Nf \@@_split_final_cleanup:nn { \split:n { \@@_split_guard:n {#3} #4 } } } { \exp_args:No \use_ii_i:nn { \exp_after:wN { \int_value:w \int_eval:n { #2 + 1 } } } { \@@_split_collect_one_end:w #4 \s_stop {#3} } } } \cs_new:Npn \@@_split_final_cleanup:nn #1 #2 { \exp:w \@@_split_final_cleanup:w #1 \@@_split_guard:n \q_nil \s_mark { } {#2} } \cs_new:Npn \@@_split_final_cleanup:w #1 \@@_split_guard:n #2 #3 \s_mark #4 { \quark_if_nil:nTF {#2} { \exp_end: { #4 #1 } } { \@@_split_final_cleanup:w #3 \s_mark { #4 #1 #2 } } } \NewDocumentCommand \splitnl { mm +m } { \tl_set:Nf \l_tmpa_tl { \split:n {#3} } \show \l_tmpa_tl \exp_after:wN \__splitnl_aux:nnNN \l_tmpa_tl #1 #2 } \cs_new:Npn \split:n #1 { \@@_split_at_nl_first:w #1 \\ \q_nil \\ \s_stop } \cs_new:Npn \@@_split_at_nl:NN #1#2 { \tl_set:Nf \l_tmpa_tl { \exp_after:wN \@@_split_at_nl_first:w #1 \\ \q_nil \\ \s_stop } \exp_after:wN \@@_split_at_nl_aux:nnNN \l_tmpa_tl #1 #2 } \cs_new_protected:Npn \@@_split_at_nl_aux:nnNN #1 #2 #3 #4 { \tl_gset:Nn #4 {#1} \tl_gset:Nn #3 {#2} } % \end{macrocode} % \end{macro} % % % \begin{macro}{\maybestartnewformulatag} % % \begin{macrocode} \newif\if@subformulas \tl_new:N \result \cs_new_protected:Npn\grabaformulapartandstart { \@@_split_at_nl:NN \g_@@_grabbed_math_tl \result \typeout{====>first-result=\meaning\result} \typeout{====>first-tmpmathcontent=\meaning\g_@@_grabbed_math_tl} \tl_if_empty:NTF \g_@@_grabbed_math_tl { \typeout{====>formula~ has~ no~ subparts} \global\@subformulasfalse } { \typeout{====>formula~ has~ subparts} \global\@subformulastrue \edef\resulttitle{\g_@@_grabbed_env_tl\space (part)} \tagstructbegin{tag=Formula, % \end{macrocode} % For now we don't put anything in /alt or /ActualText on subformulas % \begin{macrocode} % alt=\result, title-o=\resulttitle } } \tagmcbegin{} } \cs_new_protected:Npn\grabaformulapartandmayberestart { \@@_split_at_nl:NN \g_@@_grabbed_math_tl \result \typeout{====>result=\meaning\result} \typeout{====>tmpmathcontent=\meaning\g_@@_grabbed_math_tl} % \tl_if_empty:NTF \g_@@_grabbed_math_tl % { % \typeout{====>tmpmathcontent=empty} % } % { % \typeout{====>tmpmathcontent=not-empty} \edef\resulttitle{\g_@@_grabbed_env_tl\space (part)} \tagstructbegin{tag=Formula, alt=\result, title-o=\resulttitle } % } \tagmcbegin{} } % \end{macrocode} % \end{macro} % % % % % \begin{macrocode} \def\maybestartnewformulatag { \if@subformulas \ifmeasuring@\else % \tl_if_empty:NF \g_@@_grabbed_math_tl { \tagmcend \tagstructend \grabaformulapartandmayberestart } \fi \fi } % \end{macrocode} % % The breqn packages changes catcodes and that isn't yet covered % by our mechanism. % \begin{macrocode} %\AddToHook{package/breqn/after}{ % \typeout{===>~ in~ hook} % \math_register_halign_env:nn {dmath}{} % \math_register_halign_env:nn {dgroup*}{} %} % \end{macrocode} % % \begin{macrocode} \ExplSyntaxOff % \end{macrocode} % % \begin{macrocode} %<@@=> % \end{macrocode} % % \begin{macrocode} % % \end{macrocode} % % \begin{macrocode} % % \end{macrocode} % % \Finale % %