% \iffalse meta-comment %% %% File: ltshipout.dtx (C) Copyright 2020,2021 Frank Mittelbach, LaTeX Team %% % % This file is part of the LaTeX base system. % ------------------------------------------- % % It may be distributed and/or modified under the % conditions of the LaTeX Project Public License, either version 1.3c % of this license or (at your option) any later version. % The latest version of this license is in % https://www.latex-project.org/lppl.txt % and version 1.3c or later is part of all distributions of LaTeX % version 2008 or later. % % This file has the LPPL maintenance status "maintained". % % The list of all files belonging to the LaTeX base distribution is % given in the file `manifest.txt'. See also `legal.txt' for additional % information. % % The list of derived (unpacked) files belonging to the distribution % and covered by LPPL is defined by the unpacking scripts (with % extension .ins) which are part of the distribution. % % \fi % % \iffalse % %%% From File: ltshipout.dtx % % \begin{macrocode} \providecommand\ltshipoutversion{v1.0d} \providecommand\ltshipoutdate{2020/12/06} % \end{macrocode} % %<*driver> \documentclass{l3doc} % bug fix fo l3doc.cls \ExplSyntaxOn \cs_set_protected:Npn \__codedoc_macro_typeset_one:nN #1#2 { \vbox_set:Nn \l__codedoc_macro_box { \vbox_unpack_drop:N \l__codedoc_macro_box \hbox { \llap { \__codedoc_print_macroname:nN {#1} #2 \MacroFont % <----- without it the \ is in lmr10 if a link is made \ } } } \int_incr:N \l__codedoc_macro_int } \ExplSyntaxOff \EnableCrossrefs \CodelineIndex \begin{document} \DocInput{ltshipout.dtx} \end{document} % % % \fi % % % \long\def\fmi#1{\begin{quote}\itshape Todo: #1\end{quote}} % % \providecommand\hook[1]{\texttt{#1}} % \providecommand\pkg[1]{\texttt{#1}} % % % \title{The \texttt{ltshipout} package\thanks{This package has version % \ltshipoutversion\ dated \ltshipoutdate, \copyright\ \LaTeX\ % Project.}} % % \author{Frank Mittelbach, \LaTeX{} Project Team} % % \maketitle % % % \tableofcontents % % \section{Introduction} % % The code provides an interface to the \cs{shipout} primitive of % \TeX{} which is called when a finished pages is finally % \enquote{shipped out} to the target output file, e.g., the % \texttt{.dvi} or \texttt{.pdf} file. % A good portion of the code is based on ideas by Heiko Oberdiek % implemented in his packages \pkg{atbegshi} and \pkg{atenddvi} % even though the interfaces are somewhat % different.\footnote{Heiko's interfaces are emulated by the kernel % code, if a document requests his packages, so older documents % will continue to work.} % % \subsection{Overloading the \cs{shipout} primitive} % % % \begin{function}{\shipout} % With this implementation \TeX's shipout primitive is no longer % available for direct use. Instead \cs{shipout} is running some % (complicated) code that picks up the box to be shipped out % regardless of how that is done, i.e., as a constructed \cs{vbox} % or \cs{hbox} or as a box register. % % It then stores it in a named box register. This box can then be % manipulated through a set of hooks after which it is shipped out % for real. % \end{function} % % \begin{variable}{\ShipoutBox,\l_shipout_box} % This box register is called \cs{ShipoutBox} (alternatively available via the % L3 name \cs{l_shipout_box}). % \end{variable} % % % \begin{variable}{\l_shipout_box_ht_dim, % \l_shipout_box_dp_dim,\l_shipout_box_wd_dim, % \l_shipout_box_ht_plus_dp_dim} % The shipout box dimensions are available in the L3 registers % \cs{l_shipout_box_ht_dim}, etc.\ (there are no \LaTeXe{} % names).\footnotemark{} These variables can be used % inside the hook code for \hook{shipout/before}, % \hook{shipout/foreground} and \hook{shipout/background} if needed. % \end{variable} % \footnotetext{Might need changing, but HO's version as strings % is not really helpful I think).} % % % % % \subsection{Provided hooks} % % \begin{variable}{shipout/before, % shipout/foreground,shipout/background, % shipout/firstpage, % shipout/lastpage} % The code offers a number of hooks into which packages (or the % user) can add code to support different use cases. % These are: % \begin{description} % \item[\hook{shipout/before}] % % This hook is executed after the finished page has been stored in % \cs{ShipoutBox} / \cs{l_shipout_box}). % It can be used to alter that box content or to discard it % completely (see \cs{DiscardShipoutBox} below). % % \item[\hook{shipout/background}] % % This hook adds a picture environment into the background of % the page with the \texttt{(0,0)} coordinate in the top-left % corner using a \cs{unitlength} of \texttt{1pt}. % % It should therefore only receive \cs{put} commands or other % commands suitable in a \texttt{picture} environment and the % vertical coordinate values would normally be % negative. % % Technically this is implemented by adding a zero-sized % \cs{hbox} as the very first item into the \cs{ShipoutBox} % containing that \texttt{picture} environment. Thus the rest of % the box content will overprint what ever is typeset by that hook. % % % \item[\hook{shipout/foreground}] % % This hook adds a picture environment into the foreground of % the page with the \texttt{(0,0)} coordinate in the top-left % corner using a \cs{unitlength} of \texttt{1pt}. % % Technically this is implemented by adding a zero-sized % \cs{hbox} as the very last item into the \cs{ShipoutBox} and % raising it up so that it still has its \texttt{(0,0)} point in % the top-left corner. % But being placed after the main box content it will be typeset % later and thus overprints it (i.e., is in the foreground). % % % \item[\hook{shipout/firstpage}] % % The material from this hook is executed only once at the very % beginning of the first output page that is shipped out (i.e., % not discarded at the last minute). It should only contain % \cs{special} or similar commands needed to direct post processors % handling the \texttt{.dvi} or \texttt{.pdf} output.\footnotemark % % ^^A \fmi{not sure it has to be that restrictive.} % % % \item[\hook{shipout/lastpage}] % % The corresponding hook to add \cs{special}s at the very end of % the output file. It is only executed on the very last page of % the output file --- % or rather on the page that \LaTeX{} believes is the last one. % % It may not be possible for \LaTeX{} to correctly determine % which page is the last one without several reruns. If this % happens and the hook is non-empty then \LaTeX{} will add an % extra page to place the material and also request a rerun to % get the correct placement sorted out. % % \end{description} % \end{variable}\footnotetext{In % \LaTeXe{} that was already existing, but implemented using a box % register with the name \cs{@begindvibox}.} % % As mentioned above the hook \hook{shipout/before} is executed % first and can manipulate the prepared shipout box stored in % \cs{ShipoutBox} or set things up for use in \cs{write} during the % actual shipout. The other hooks are added inside hboxes to the % box being shipped out in the following % order: % \begin{center} % \begin{tabular}{ll} % \hook{shipout/firstpage} & only on the first page \\ % \hook{shipout/background} & \\ % \meta{boxed content of \cs{ShipoutBox}} & \\ % \hook{shipout/foreground} & \\ % \hook{shipout/lastpage} & only on the last page \\ % \end{tabular} % \end{center} % If any of the hooks has no code then that particular no box is % added at that point. % % In a document that doesn't produce pages, e.g., only makes % \cs{typeout}s, none of the hooks are executed (as there is no % \cs{shipout}) not even the \hook{shipout/lastpage} hook. % % % \subsection{Legacy \LaTeX{} commands} % % % \begin{function}{\AtBeginDvi,\AtEndDvi} % \cs{AtBeginDvi} is the existing \LaTeXe{} interface to fill the % \hook{shipout/firstpage} hook. This is not really a good name % as it is not just supporting \texttt{.dvi} but also \texttt{.pdf} % output or \texttt{.xdv}. % % \cs{AtEndDvi} is the counterpart that was not available in the % kernel but only through the package \pkg{atenddvi}. It fills the % \hook{shipout/lastpage} hook. % \end{function} % % As these two wrappers have been available for a long time we % continue offering them. However, for new code we suggest using % the high-level hook management commands directly instead of % ``randomly-named'' wrappers. This will lead to code that is % easier to understand and to maintain. For this reason we do not % provide any other wrapper commands for the above hooks in the % kernel. % % % \subsection{Special commands for use inside the hooks} % % \begin{function}{\DiscardShipoutBox,\shipout_discard_box:} % \begin{syntax} % \cs{AddToHookNext} \texttt{\{shipout/before\} \{...\cs{DiscardShipoutBox}...\}} % \end{syntax} % The \cs{DiscardShipoutBox} declaration (L3 name % \cs{shipout_discard_box:}) % requests that on the next % shipout the page box is thrown away instead of being shipped to % the \texttt{.dvi} or \texttt{.pdf} file. % % Typical applications wouldn't do this unconditionally, but have % some processing logic that decides to use or not to use the page. % % Note that if this declaration is used directly in the document it % may depend on the placement to which page it applies, given that % \LaTeX{} output routine is called in an asynchronous manner! % % \fmi{Once we have a new mark mechanism available we can improve % on that and make sure that the declaration applies to the page % that contains it.} % \end{function} % % In the \pkg{atbegshi} package there are a number of additional % commands for use inside the \hook{shipout/before} hook. They % should normally not be needed any more as one can instead simply % add code to the hooks \hook{shipout/before}, % \hook{shipout/background} or % \hook{shipout/foreground}.\footnote{If that assumption turns out to % be wrong it would be trivial to change them to public functions % (right now they are private).} If \pkg{atbegshi} gets loaded then % those commands become available as public functions with their original % names as given below. % % % \subsection{Information counters} % % % \begin{variable}{\ReadonlyShipoutCounter,\g_shipout_readonly_int} % \begin{syntax} % \cs{ifnum}\cs{ReadOnlyShipoutCounter}\texttt{=...} % \cs{int_use:N} \cs{g_shipout_readonly_int} \texttt{\% expl3 usage} % \end{syntax} % This integer holds the number of pages shipped out up to now % (including the one to be shipped out when inside the output % routine). More precisely, it is incremented only after it is % clear that a page will be shipped out, i.e., after the % \hook{shipout/before} hook (because that might discard the page)! % % Just like with the \texttt{page} counter its value is % only accurate within the output routine. In the body of the % document it may be off by one as the output routine is called % asynchronously! % % Also important: it \emph{must not} be set, only read. There are % no provisions to prevent that but if you do, chaos will be the % result. To emphasize this fact it is not provided as a \LaTeX{} % counter but as a \TeX{} counter (i.e., a command), so % \cs{Alph}\Arg{\cs{ReadonlyShipoutCounter}} etc, would not work. % \end{variable} % % \begin{variable}{totalpages,\g_shipout_totalpages_int} % \begin{syntax} % \cs{arabic}\texttt{\{totalpages\}} % \cs{int_use:N} \cs{g_shipout_totalpage_int} \texttt{\% expl3 usage} % \end{syntax} % In contrast to \cs{ReadonlyShipoutCounter}, the % \texttt{totalpages} counter is a \LaTeX{} counter and incremented % for each shipout attempt including those pages that are discarded % for one or the other reason. Again \hook{shipout/before} sees % the counter before it is incremented). % % Furthermore, while it is incremented for each page, its value is % never used by \LaTeX. It can therefore be freely reset or changed by user % code, for example, to additionally count a number of pages that % are not build by \LaTeX\ but are added in a later part of the % process, e.g., cover pages or picture pages made externally. % % Important: as this is a page-related counter its value is only % reliable inside the output routine! % \end{variable} % % \begin{variable}{\PreviousTotalPages} % \begin{syntax} % \cs{thetotalpages}/\cs{PreviousTotalPages} % \end{syntax} % Command that expands to the number of total pages from the % previous run. If there was no previous run or if used in the % preamble it expands to % \texttt{0}. Note that this is a command and not a counter, so in order % to display the number in, say, Roman numerals you have to assign % its value to a counter and then use \cs{Roman} on that counter. % \end{variable} % % % \subsection{Debugging shipout code} % % \begin{function}{\DebugShipoutsOn,\DebugShipoutsOff, % \shipout_debug_on:,\shipout_debug_off:} % \begin{syntax} % \cs{DebugShipoutsOn} % \end{syntax} % Turn the debugging of shipout code on or off. This displays % changes made to the shipout data structures. \fmi{This needs % some rationalizing and may not stay this way.} % \end{function} % % % % % \section{Emulating commands from other packages} % % The packages in this section are no longer necessary, but as they % are used by other packages, they are emulated when they are % explicitly loaded with \cs{usepackage} or \cs{RequirePackage}. % % Please note that the emulation only happens if the package is % explicitly requested, i.e., the commands documented below are not % automatically available in the \LaTeX{} kernel! If you write a % new package we suggest to use the appropriate kernel hooks % directly instead of loading the emulation. % % % \subsection{Emulating \pkg{atbegshi}} % % % \begin{function}{\AtBeginShipoutUpperLeft,\AtBeginShipoutUpperLeftForeground} % \begin{syntax} % \cs{AddToHook} \texttt{\{shipout/before\} \{...\cs{AtBeginShipoutUpperLeft}}\Arg{code}\texttt{...\}} % \end{syntax} % This adds a \texttt{picture} environment into the background of the shipout % box expecting \meta{code} to contain \texttt{picture} % commands. The same effect can be obtained by simply using kernel % features as follows: % \begin{quote} % \cs{AddToHook}\texttt{\{shipout/background\}}\Arg{code} % \end{quote} % There is one technical difference: if % \cs{AtBeginShipoutUpperLeft} is used several times each % invocation is put into its own box inside the shipout box whereas % all \meta{code} going into \hook{shipout/background} ends up % all in the same box in the order it is added or sorted based on % the rules for the hook chunks. % % \cs{AtBeginShipoutUpperLeftForeground} is similar with the % difference that the \texttt{picture} environment is placed in the % foreground. To model it with the kernel functions use the hook % \hook{shipout/foreground} instead. % \end{function} % % % \begin{function}{\AtBeginShipoutAddToBox,\AtBeginShipoutAddToBoxForeground} % \begin{syntax} % \cs{AddToHook} \texttt{\{shipout/before\} \{...\cs{AtBeginShipoutAddToBox}}\Arg{code}\texttt{...\}} % \end{syntax} % These work like \cs{AtBeginShipoutUpperLeft} and % \cs{AtBeginShipoutUpperLeftForeground} with the difference that % \meta{code} is directly placed into an \cs{hbox} inside the % shipout box and not surrounded by a \texttt{picture} environment. % % To emulate them using \hook{shipout/background} or % \hook{shipout/foreground} you may have to wrap \meta{code} into % a \cs{put} statement but if the code is not doing any typesetting % just adding it to the hook should be sufficient. % \end{function} % % % % \begin{function}{\AtBeginShipoutBox} % This is the name of the shipout box as \pkg{atbegshi} knows it. % \end{function} % % \begin{function}{\AtBeginShipoutInit} % By default \pkg{atbegshi} delayed its action until % \verb=\begin{document}=. This command was forcing it in an earlier % place. With the new concept it does nothing. % \end{function} % % \begin{function}{\AtBeginShipout,\AtBeginShipoutNext} % \begin{syntax} % \cs{AtBeginShipout}\Arg{code} $\equiv$ \cs{AddToHook}\texttt{\{shipout/before\}}\Arg{code} % \cs{AtBeginShipoutNext}\Arg{code} $\equiv$ \cs{AddToHookNext}\texttt{\{shipout/before\}}\Arg{code} % \end{syntax} % This is equivalent to filling the \hook{shipout/before} hook % by either using \cs{AddToHook} or \cs{AddToHookNext}, respectively. % \end{function} % % \begin{function}{\AtBeginShipoutFirst,\AtBeginShipoutDiscard} % The \pkg{atbegshi} names for \cs{AtBeginDvi} and \cs{DiscardShipoutBox}. % \end{function} % % % % % \subsection{Emulating \pkg{everyshi}} % % The \pkg{everyshi} package is providing commands to run arbitrary % code just before the shipout starts. % One point of difference: in the new shipout hooks the page is % available as \cs{ShipoutBox} for inspection of change, one should % not manipulate box 255 directly inside \hook{shipout/before}, so % old code doing this would change to use \cs{ShipoutBox} instead % of \texttt{255} or \cs{@cclv}. % % \begin{function}{\EveryShipout} % \begin{syntax} % \cs{EveryShipout}\Arg{code} $\equiv$ \cs{AddToHook}\texttt{\{shipout/before\}}\Arg{code} % \end{syntax} % \end{function} % % \begin{function}{\AtNextShipout} % \begin{syntax} % \cs{AtNextShipout}\Arg{code} $\equiv$ \cs{AddToHookNext}\texttt{\{shipout/before\}}\Arg{code} % \end{syntax} % \end{function} % % However, most use cases for \pkg{everyshi} are attempts to put % some picture or text into the background or foreground of the page % and that can be done today simply by using the % \hook{shipout/background} and % \hook{shipout/foreground} hooks without any need to coding. % % % \subsection{Emulating \pkg{atenddvi}} % % The \pkg{atenddvi} package implemented only a single command: % \cs{AtEndDvi} and that is now available out of the box so the % emulation makes the package a no-op. % % % % \subsection{Emulating \pkg{everypage}} % % This package patched the original \cs{@begindvi} hook and replaced % it with its own version. % Its functionality is now covered by the hooks offered by the % kernel so that there is no need for such patching any longer. % % \begin{function}{\AddEverypageHook} % \begin{syntax} % \cs{AddEverypageHook}\Arg{code} $\equiv$ % \qquad\cs{AddToHook}\texttt{\{shipout/background\}\{\cs{put}(1in,-1in)\Arg{code}\}} % \end{syntax} % \cs{AddEverypageHook} is adding something into the % background of every page at a position of 1in to the right and % 1in down from the top left corner of the page. % By using the kernel hook directly you can put your material % directly to the right place, i.e., use other coordinates in the % \cs{put} statement above. % \end{function} % % \begin{function}{\AddThispageHook} % \begin{syntax} % \cs{AddThispageHook}\Arg{code} $\equiv$ % \qquad\cs{AddToHookNext}\texttt{\{shipout/background\}\{\cs{put}(1in,-1in)\Arg{code}\}} % \end{syntax} % The \cs{AddThispageHook} wrapper is similar but uses % \cs{AddToHookNext}. % \end{function} % % % \StopEventually{\setlength\IndexMin{200pt} \PrintIndex } % % % \section{The Implementation} % \begin{macrocode} %<@@=shipout> % \end{macrocode} % % At the moment the whole module rolls back in one go, but if we % make any modifications in later releases this will then need % splitting. % \begin{macrocode} %<*2ekernel|latexrelease> %\IncludeInRelease{2020/10/01}% % {\shipout}{Hook management (shipout)}% % \end{macrocode} % % % \begin{macrocode} \ExplSyntaxOn % \end{macrocode} % % % \subsection{Debugging} % % \begin{macro}{\g_@@_debug_bool} % Holds the current debugging state. % \begin{macrocode} \bool_new:N \g_@@_debug_bool % \end{macrocode} % \end{macro} % % \begin{macro}{\shipout_debug_on:,\shipout_debug_off:} % \begin{macro}{\@@_debug:n} % \begin{macro}{\@@_debug_gset:} % Turns debugging on and off by redefining \cs{@@_debug:n}. % \begin{macrocode} \cs_new_eq:NN \@@_debug:n \use_none:n \cs_new_protected:Npn \shipout_debug_on: { \bool_gset_true:N \g_@@_debug_bool \@@_debug_gset: } \cs_new_protected:Npn \shipout_debug_off: { \bool_gset_false:N \g_@@_debug_bool \@@_debug_gset: } \cs_new_protected:Npn \@@_debug_gset: { \cs_gset_protected:Npx \@@_debug:n ##1 { \bool_if:NT \g_@@_debug_bool {##1} } } % \end{macrocode} % \end{macro} % \end{macro} % \end{macro} % % % % % % \begin{macro}{\ShipoutBox,\l_shipout_box} % The box filled with the page to be shipped out (both L3 and % \LaTeXe{} name). % \begin{macrocode} \box_new:N \l_shipout_box % \end{macrocode} % % \begin{macrocode} \cs_set_eq:NN \ShipoutBox \l_shipout_box % \end{macrocode} % \end{macro} % % % % % \begin{macro}{\@@_execute:} % This is going to the be the code run by \cs{shipout}. The code % follows closely the ideas from \pkg{atbegshi}, so not % documenting that here for now. % \begin{macrocode} \cs_set_protected:Npn \@@_execute: { \tl_set:Nx \l_@@_group_level_tl { \int_value:w \tex_currentgrouplevel:D } \tex_afterassignment:D \@@_execute_test_level: \tex_setbox:D \l_shipout_box } % \end{macrocode} % \end{macro} % % % \begin{macro}{\shipout} % Overloading the \cs{shipout} primitive: % \begin{macrocode} \cs_gset_eq:NN \shipout \@@_execute: % \end{macrocode} % \end{macro} % % % \begin{macro}{\l_@@_group_level_tl} % Helper token list to record the group level at which % \cs{@@_execute:} is encountered. \begin{macrocode} \tl_new:N \l_@@_group_level_tl % \end{macrocode} % \end{macro} % % % \begin{macro}{\@@_execute_test_level:} % If the group level has changed then we are still constructing % \cs{l_shipout_box} and to continue we need to wait until the % current group has finished, hence the \cs{tex_aftergroup:D}. % \begin{macrocode} \cs_new:Npn \@@_execute_test_level: { \int_compare:nNnT \l_@@_group_level_tl < \tex_currentgrouplevel:D \tex_aftergroup:D \@@_execute_cont: } % \end{macrocode} % \end{macro} % % % \begin{macro}{\@@_execute_cont:} % When we have reached this point the shipout box has been % processed and is available in \cs{l_shipout_box} and ready for % real ship out (perhaps).. % % First we quickly check if it is void (can't happen in the % standard \LaTeX{} output routine but \cs{shipout} might be called % from a package that has some special processing logic). If it is % void we aren't shipping anything out and processing ends.\footnote{In that % case we don't reset the deadcyles, that would be up to the OR % processing logic to do.} % \begin{macrocode} \cs_new:Npn \@@_execute_cont: { \box_if_empty:NTF \l_shipout_box { \PackageWarning{ltshipout}{Ignoring~ void~ shipout~ box} } { % \end{macrocode} % Otherwise we assume that we will ship something and prepare for % final adjustments (in particular setting the state of % \cs{protect} while we are running the hook code). % We also save the current \cs{protect} state to restore it later. % \begin{macrocode} \bool_gset_false:N \g_@@_discard_bool \cs_set_eq:NN \@@_saved_protect: \protect \set@typeset@protect % \end{macrocode} % We also store the current shipout box dimension in registers, so that % they can be used in the hook code.\footnote{This is not really % necessary as the code could access them via \cs{box_ht:N}, etc., % but it is perhaps convenient.} % \begin{macrocode} \@@_get_box_size:N \l_shipout_box % \end{macrocode} % Then we execute the \hook{shipout/before} hook. % \begin{macrocode} \hook_use:n {shipout/before} % \end{macrocode} % In \cs{g_shipout_totalpages_int} we count all shipout attempts so % we increment that counter already here (the other one is % incremented later when we know for sure that we do a % \cs{shipout}. % % We increment it after running the above hook so that the values % for \cs{g_shipout_totalpages_int} and \cs{g_shipout_readonly_int} % are in sync while the % hook is executed (in the case that totalpages isn't manually % altered or through discarding pages that is). % \begin{macrocode} \int_gincr:N \g_shipout_totalpages_int % \end{macrocode} % The above hook might contain code that requests the page to be discarded so % we now test for it. % \begin{macrocode} \bool_if:NTF \g_@@_discard_bool { \PackageInfo{ltshipout}{Completed~ page~ discarded} \bool_gset_false:N \g_@@_discard_bool % \end{macrocode} % As we are discarding the page box and not shipping anything out, % we need to do some house cleaning and reset \TeX's deadcycles so % that it doesn't complain about too many calls to the OR without % any shipout. % \begin{macrocode} \tex_deadcycles:D \c_zero_int % \end{macrocode} % \fmi{In \pkg{atbegshi} the box was dropped but is that actually % needed? Or the resetting of \cs{protect} to its kernel value?} % \begin{macrocode} % \group_begin: % \box_set_eq_drop:NN \l_shipout_box \l_shipout_box % \group_end: % \cs_set_eq:NN \protect \exp_not:N } % \end{macrocode} % Even if there was no explicit request to discard the box it is % possible that the code for the hook \hook{shipout/before} has % voided the box (by mistake or deliberately). We therefore test % once more but this time make it a warning, because the best % practice way is to use the request mechanism. % \begin{macrocode} { \box_if_empty:NTF \l_shipout_box { \PackageWarning{ltshipout}{ Shipout~ box~ was~ voided~ by~ hook,\MessageBreak ignoring~ shipout~ box } } % \end{macrocode} % Finally, if the box is still non-empty we are nearly ready to % ship it out. % First we increment the total page counter so that we can later % test if we have reached the final page according to our available % information.\footnote{Doing that earlier would be wrong because we % might end up with the last page counted but discard and then we % have no place to add the final objects into the output file.} % \begin{macrocode} { \int_gincr:N \g_shipout_readonly_int \@@_debug:n { \typeout{Absolute~ page~ =~ \int_use:N \g_shipout_readonly_int \space (target:~ \@abspage@last)} } % \end{macrocode} % Then we store the box sizes again (as they may have % changed) and then look at the hooks \hook{shipout/foreground} % and \hook{shipout/background}. If either or both are non-empty % we add a \texttt{picture} environment to the box (in the % foreground and or in the background) and execute the hook code % inside that environment. % % \begin{macrocode} \@@_get_box_size:N \l_shipout_box \hook_if_empty:nF {shipout/foreground} { \@@_add_foreground_picture:n { \hook_use:n {shipout/foreground} } } % \end{macrocode} % If there is no user hook, there might still code in the kernel hook. % \changes{v1.0d}{2020/11/23}{Check for both kernel and user hook (gh/431)} % \begin{macrocode} \bool_lazy_and:nnF { \hook_if_empty_p:n {shipout/background} } { \tl_if_empty_p:N \@kernel@before@shipout@background } { \@@_add_background_picture:n { \@kernel@before@shipout@background \hook_use:n {shipout/background} } } % \end{macrocode} % We then run \cs{@@_execute_firstpage_hook:} that adds % the content of the hook \hook{shipout/firstpage} to the % start of the first page (if non-empty). It is then redefined to % do nothing on later pages. % \begin{macrocode} \@@_execute_firstpage_hook: % \end{macrocode} % The we check if we have to add the \hook{shipout/lastpage} hook % or the corresponding kernel hook % because we have reached the last page. This test will be false for % all but one (and hopefully the correct) page. % \changes{v1.0d}{2020/11/23}{Check for both kernel and user hook (gh/431)} % \begin{macrocode} \int_compare:nNnT \@abspage@last = \g_shipout_readonly_int { \bool_lazy_and:nnF { \hook_if_empty_p:n {shipout/lastpage} } { \tl_if_empty_p:N \@kernel@after@shipout@lastpage } { \@@_debug:n { \typeout{Executing~ lastpage~ hook~ on~ page~ \int_use:N \g_shipout_readonly_int } } \_@@_add_foreground_box:n { \UseHook{shipout/lastpage} \@kernel@after@shipout@lastpage } } \bool_gset_true:N \g_@@_lastpage_handled_bool } % \end{macrocode} % Finally we run the actual \TeX{} primitive for shipout. As that will % expand delayed \cs{write} statements inside the page in which % protected commands should not expand we first change \cs{protect} % to the appropriate definition for that case. % \begin{macrocode} \cs_set_eq:NN \protect \exp_not:N \tex_shipout:D \box_use:N \l_shipout_box } } % \end{macrocode} % Restore the value of \cs{protect} in case \cs{shipout} is called % outside of the output routine (where it is automatically restored % because of the implicit group). % \begin{macrocode} \cs_set_eq:NN \protect \@@_saved_protect: } } % \end{macrocode} % \end{macro} % % % \begin{macro}{\@@_saved_protect:} % Remember the current \cs{protect} state. % \begin{macrocode} \cs_new_eq:NN \@@_saved_protect: \protect % \end{macrocode} % \end{macro} % % \begin{macro}{shipout/before, % shipout/foreground,shipout/background, % shipout/firstpage, % shipout/lastpage} % Declaring all hooks for the shipout code. % \begin{macrocode} \hook_new:n{shipout/before} \hook_new:n{shipout/foreground} \hook_new:n{shipout/background} \hook_new:n{shipout/firstpage} \hook_new:n{shipout/lastpage} % \end{macrocode} % \end{macro} % % % % \begin{macro}{\@kernel@after@shipout@lastpage, % \@kernel@before@shipout@background} % And here are the internal kernel hooks going before or after the % public ones where needed. % \begin{macrocode} \let\@kernel@after@shipout@lastpage\@empty \let\@kernel@before@shipout@background\@empty % \end{macrocode} % \end{macro} % % % \begin{macro}{\@@_execute_firstpage_hook: } % This command adds any specials into a box and adds that to the % very beginning of the first box shipped out. After that we % redefine it to do nothing on later pages. % \begin{macrocode} \cs_new:Npn \@@_execute_firstpage_hook: { % \end{macrocode} % Adding something to the beginning means adding it to the % background as that layer is done first in the output. Of course % that is only needed if the hook actually contains anything. % \begin{macrocode} \hook_if_empty:nF {shipout/firstpage} { \@@_add_background_box:n { \UseHook{shipout/firstpage} } } % \end{macrocode} % Once we are here we change the definition to do nothing next time % and we also change the command used to implement \cs{AtBeginDvi} % to become a warning and not add further material to a hook that % is never used again. % \begin{macrocode} \cs_gset_eq:NN \@@_execute_firstpage_hook: \prg_do_nothing: \cs_gset:Npn \@@_add_firstpage_material:Nn ##1 ##2 { \@latex@warning{ First~ page~ is~ already~ shipped~ out,~ ignoring\MessageBreak \string##1 } } } % \end{macrocode} % \end{macro} % % % \begin{macro}{\g_@@_lastpage_handled_bool} % A boolean to signal if we have already handled the % \hook{shipout/lastpage} hook. % \begin{macrocode} \bool_new:N \g_@@_lastpage_handled_bool % \end{macrocode} % \end{macro} % % % % \begin{macro}{\@@_add_firstpage_material:Nn} % This command adds material to the % \hook{shipout/firstpage} hook. It is used in % \cs{AtBeginDvi}, etc. The first argument is the % command through which is it called. Initially this is ignored but % once we are passed the first page it can be used to generate a % warning message mentioning the right user command. % \begin{macrocode} \cs_new:Npn \@@_add_firstpage_material:Nn #1#2 { \AddToHook{shipout/firstpage}{#2} } % \end{macrocode} % \end{macro} % % % % % % % % \begin{macro}{\@@_get_box_size:N} % Store the box dimensions in dimen registers. % \fmi{This could/should perhaps be generalized to set height depth and % width given an arbitrary box.} % \begin{macrocode} \cs_new:Npn \@@_get_box_size:N #1 { \dim_set:Nn \l_shipout_box_ht_dim { \box_ht:N #1 } \dim_set:Nn \l_shipout_box_dp_dim { \box_dp:N #1 } \dim_set:Nn \l_shipout_box_wd_dim { \box_wd:N #1 } \dim_set:Nn \l_shipout_box_ht_plus_dp_dim { \l_shipout_box_ht_dim + \l_shipout_box_dp_dim } } % \end{macrocode} % \end{macro} % % \begin{macro}{\l_shipout_box_ht_dim, % \l_shipout_box_dp_dim,\l_shipout_box_wd_dim, % \l_shipout_box_ht_plus_dp_dim} % And here are the variables set by \cs{@@_get_box_size:N}. % \begin{macrocode} \dim_new:N \l_shipout_box_ht_dim \dim_new:N \l_shipout_box_dp_dim \dim_new:N \l_shipout_box_wd_dim \dim_new:N \l_shipout_box_ht_plus_dp_dim % \end{macrocode} % \end{macro} % % % % % % \begin{macro}{\g_@@_discard_bool} % Indicate whether or not the current page box should be discarded % \begin{macrocode} \bool_new:N \g_@@_discard_bool % \end{macrocode} % \end{macro} % % % % \begin{macro}{\l_@@_tmp_box,\l_@@_saved_badness_tl} % We need a box for the background and foreground material and a % token register to remember badness settings as we disable them % during the buildup below. % \begin{macrocode} \box_new:N \l_@@_tmp_box \tl_new:N \l_@@_saved_badness_tl % \end{macrocode} % \end{macro} % % % \begin{macro}{\@@_add_background_box:n} % In standard \LaTeX{} the shipout box is always a \cs{vbox} but % here we are allow for other usage as well, in case some package % has its own output routine. % \begin{macrocode} \cs_new:Npn \@@_add_background_box:n #1 { \@@_get_box_size:N \l_shipout_box % \end{macrocode} % But we start testing for a vertical box as that should be the % normal case. % \begin{macrocode} \box_if_vertical:NTF \l_shipout_box { % \end{macrocode} % Save current values of \cs{vfuzz} and \cs{vbadness} then change % them to allow box manipulations without warnings. % \begin{macrocode} \tl_set:Nx \l_@@_saved_badness_tl { \vfuzz=\the\vfuzz\relax \vbadness=\the\vbadness\relax } \vfuzz=\c_max_dim \vbadness=\c_max_int % \end{macrocode} % Then we reconstruct \cs{l_shipout_box} \ldots % \begin{macrocode} \vbox_set_to_ht:Nnn \l_shipout_box \l_shipout_box_ht_plus_dp_dim { % \end{macrocode} % \ldots{} the material in \verb=#1= is placed into a horizontal % box with zero dimensions. % \begin{macrocode} \hbox_set:Nn \l_@@_tmp_box { \l_@@_saved_badness_tl #1 } \box_set_wd:Nn \l_@@_tmp_box \c_zero_dim \box_set_ht:Nn \l_@@_tmp_box \c_zero_dim \box_set_dp:Nn \l_@@_tmp_box \c_zero_dim % \end{macrocode} % The we typeset that box followed by whatever was in % \cs{l_shipout_box} before (unpacked). % \begin{macrocode} \skip_zero:N \baselineskip \skip_zero:N \lineskip \skip_zero:N \lineskiplimit \box_use:N \l_@@_tmp_box \vbox_unpack:N \l_shipout_box % \end{macrocode} % The \cs{kern} ensures that the box has no depth which is % afterwards explicitly corrected. % \begin{macrocode} \kern \c_zero_dim } \box_set_ht:Nn \l_shipout_box \l_shipout_box_ht_dim \box_set_dp:Nn \l_shipout_box \l_shipout_box_dp_dim % \end{macrocode} % \fmi{The whole boxing maneuver looks a bit like overkill to me, but for % the moment I leave.} % \begin{macrocode} \l_@@_saved_badness_tl } { % \end{macrocode} % A horizontal box is handled in a similar way. The last case would % be a void box in which case we do nothing hence the missing % \texttt{F} branch. % \begin{macrocode} \box_if_horizontal:NT \l_shipout_box { \tl_set:Nx \l_@@_saved_badness_tl { \hfuzz=\the\hfuzz\relax \hbadness=\the\hbadness\relax } \hfuzz=\c_max_dim \hbadness=\c_max_int \hbox_set_to_wd:Nnn \l_shipout_box \l_shipout_box_wd_dim { \hbox_set:Nn \l_@@_tmp_box { \l_@@_saved_badness_tl #1 } \box_set_wd:Nn \l_@@_tmp_box \c_zero_dim \box_set_ht:Nn \l_@@_tmp_box \c_zero_dim \box_set_dp:Nn \l_@@_tmp_box \c_zero_dim \box_move_up:nn \l_shipout_box_ht_dim { \box_use:N \l_@@_tmp_box } \hbox_unpack:N \l_shipout_box } \l_@@_saved_badness_tl } } } % \end{macrocode} % \end{macro} % % % % % \begin{macro}{\@@_add_foreground_box:n} % Foreground boxes are done in the same way, only the order and % placement of boxes has to be done differently. % \begin{macrocode} \cs_new:Npn \@@_add_foreground_box:n #1 { \box_if_vertical:NTF \l_shipout_box { \tl_set:Nx \l_@@_saved_badness_tl { \vfuzz=\the\vfuzz\relax \vbadness=\the\vbadness\relax } \vfuzz=\c_max_dim \vbadness=\c_max_int \vbox_set_to_ht:Nnn \l_shipout_box \l_shipout_box_ht_plus_dp_dim { \hbox_set:Nn \l_@@_tmp_box { \l_@@_saved_badness_tl #1 } \box_set_wd:Nn \l_@@_tmp_box \c_zero_dim \box_set_ht:Nn \l_@@_tmp_box \c_zero_dim \box_set_dp:Nn \l_@@_tmp_box \c_zero_dim \skip_zero:N \baselineskip \skip_zero:N \lineskip \skip_zero:N \lineskiplimit \vbox_unpack:N \l_shipout_box \kern -\l_shipout_box_ht_plus_dp_dim \box_use:N \l_@@_tmp_box \kern \l_shipout_box_ht_plus_dp_dim } \l_@@_saved_badness_tl \box_set_ht:Nn \l_shipout_box \l_shipout_box_ht_dim \box_set_dp:Nn \l_shipout_box \l_shipout_box_dp_dim } { \box_if_horizontal:NT \l_shipout_box { \tl_set:Nx \l_@@_saved_badness_tl { \hfuzz=\the\hfuzz\relax \hbadness=\the\hbadness\relax } \hfuzz=\c_max_dim \hbadness=\c_max_int \hbox_set_to_wd:Nnn \l_shipout_box \l_shipout_box_wd_dim { \hbox_unpack:N \l_shipout_box \kern -\box_wd:N \l_shipout_box \hbox_set:Nn \l_@@_tmp_box { \l_@@_saved_badness_tl #1 } \box_set_wd:Nn \l_@@_tmp_box \c_zero_dim \box_set_ht:Nn \l_@@_tmp_box \c_zero_dim \box_set_dp:Nn \l_@@_tmp_box \c_zero_dim \box_move_up:nn { \box_ht:N \l_shipout_box } { \box_use:N \l_@@_tmp_box } \kern \box_wd:N \l_shipout_box }% \l_@@_saved_badness_tl } } } % \end{macrocode} % \end{macro} % % % % % \begin{macro}{\@@_init_page_origins:,\c_@@_horigin_tl,\c_@@_vorigin_tl} % Two constants holding the offset of the top-left with respect to % the media box. % % Setting the constants this way is courtesy of Bruno. % % We delay setting the constants to the last possible place as % there might be updates in the preamble or even in the % \hook{begindocument} hook that affects their setup. % \begin{macrocode} \cs_new:Npn \@@_init_page_origins: { \tl_const:Nx \c_@@_horigin_tl { \cs_if_exist_use:NTF \pdfvariable { horigin } { \cs_if_exist_use:NF \pdfhorigin { 1in } } } \tl_const:Nx \c_@@_vorigin_tl { \cs_if_exist_use:NTF \pdfvariable { vorigin } { \cs_if_exist_use:NF \pdfvorigin { 1in } } } % \end{macrocode} % After the constants have been set there is no need to execute % this command again, in fact it would raise an error, so we % redefine it to do nothing. % \begin{macrocode} \cs_gset_eq:NN \@@_init_page_origins: \prg_do_nothing: } % \end{macrocode} % \end{macro} % % % \begin{macro}{\@@_picture_overlay:n} % Put the argument into a \texttt{picture} environment that doesn't take up % any size and uses \texttt{1pt} for \cs{unitlength}. % \fmi{Could perhaps be generalized as it might be useful elsewhere. For % now it is not.} % \begin{macrocode} \cs_new:Npn \@@_picture_overlay:n #1 { % \end{macrocode} % The very first time this is executed we have to initialize (and % freeze) the origins. % \begin{macrocode} \@@_init_page_origins: % \end{macrocode} % % \begin{macrocode} \kern -\c_@@_horigin_tl \scan_stop: \vbox_to_zero:n { \kern -\c_@@_vorigin_tl \scan_stop: \unitlength 1pt \scan_stop: % \end{macrocode} % This mimics a simple zero-sized picture environment. The \cs{hss} % is need in case there is horizontal material (without using % \cs{put} with a positive width. % \changes{v1.0b}{2020/09/09} % {Prevent overfull box warnings (gh/387)} % \begin{macrocode} \hbox_set_to_wd:Nnn \l_@@_tmp_box \c_zero_dim { \ignorespaces #1 \hss } \box_set_ht:Nn \l_@@_tmp_box \c_zero_dim \box_set_dp:Nn \l_@@_tmp_box \c_zero_dim \box_use:N \l_@@_tmp_box \tex_vss:D } } % \end{macrocode} % \end{macro} % % % \begin{macro}{\@@_add_background_picture:n} % Put a \texttt{picture} env in the background of the shipout box % with its reference point in the top-left corner. % % \begin{macrocode} \cs_new:Npn \@@_add_background_picture:n #1 { \@@_add_background_box:n { \@@_picture_overlay:n {#1} } } % \end{macrocode} % \end{macro} % % % % \begin{macro}{\@@_add_foreground_picture:n} % % Put a \texttt{picture} env in the foreground of the shipout box % with its reference point in the top-left corner. % \begin{macrocode} \cs_new:Npn \@@_add_foreground_picture:n #1 { \@@_add_foreground_box:n { \@@_picture_overlay:n {#1} } } % \end{macrocode} % \end{macro} % % % \begin{macro}{\shipout_discard:} % Request that the next shipout box should be discarded. At the % moment this is just setting a boolean, but we may want to augment % this behavior that the position of the call is taken into account % (in case \LaTeX{} looks ahead and is not using the position for % on the next page). % \begin{macrocode} \cs_new_protected:Npn \shipout_discard: { \bool_gset_true:N \g_@@_discard_bool } % \end{macrocode} % \end{macro} % % % % % \subsection{Handling the end of job hook} % % At the moment this is partly solved by using the existing hooks. % But rather than putting the code into these hooks it should be % moved to the right place directly as we shouldn't prefill hooks % with material unless it needs to interact with other code. % % % % \begin{macro}{\g_shipout_readonly_int,\ReadonlyShipoutCounter} % We count every shipout activity that makes a page (but not those % that are discarded) in order to know how many pages got produced. % \begin{macrocode} \int_new:N \g_shipout_readonly_int % \end{macrocode} % For \LaTeXe{} it is available as a command (i.e., a \TeX{} % counter only. % \begin{macrocode} \cs_new_eq:NN \ReadonlyShipoutCounter \g_shipout_readonly_int % \end{macrocode} % \end{macro} % % \begin{macro}{\g_shipout_totalpages_int,\c@totalpages} % We count every shipout attempt (even those that are discarded) in % this counter. It is not used in the code but may get used in user % code. % \begin{macrocode} \int_new:N \g_shipout_totalpages_int % \end{macrocode} % For \LaTeXe{} this is offered as a \LaTeX{} counter so can be % easily typeset inside the output routine to display things like % \enquote{\cs{thepage}\texttt{/}\cs{thetotalpages}}, etc. % \begin{macrocode} \cs_new_eq:NN \c@totalpages \g_shipout_totalpages_int \cs_new:Npn \thetotalpages { \arabic{totalpages} } % \end{macrocode} % \end{macro} % % % % % \begin{macro}{\@abspage@last} % In \cs{@abspage@last} record the number of pages from the last % run. This is written to the \texttt{.aux} and this way made % available to the next run. In case there is no \texttt{.aux} file % or the statement is missing from it we initialize it with the % largest possible number in \TeX{}. We use this as the default % because then we are inserting the \hook{shipout/lastpage} on % the last page (or after the last page) but not on page 1 for a % multipage document. % \begin{macrocode} \xdef\@abspage@last{\number\maxdimen} % \end{macrocode} % \end{macro} % % % \begin{macro}{\enddocument} % % Instead of using the hooks \hook{enddocument} and % \hook{enddocument/afterlastpage} we add this code to private % kernel hooks to be 100\% sure when it is executed and to avoid % cluttering the hooks with data that is always there. % % Inside \cs{enddocument} there is a \cs{clearpage}. Just before % that we execute this code here. There is a good chance that we % are on the last page. Therefore, if we don't know the value from % the last run, we assume that the current page is the right % one. So we set \cs{@abspage@last} and as a result the next % shipout will run the \hook{shipout/lastpage} code. Of course, % if there are floats that still need a placement this guess will % be wrong but then rerunning the document will give us the correct % value next time around. % % \begin{macro}{\@kernel@after@enddocument} % \begin{macrocode} \g@addto@macro \@kernel@after@enddocument { \int_compare:nNnT \@abspage@last = \maxdimen { % \end{macrocode} % We use \LaTeXe{} coding as \cs{@abspage@last} is not an L3 name. % \begin{macrocode} \xdef\@abspage@last{ \int_eval:n {\g_shipout_readonly_int + 1} } } } % \end{macrocode} % \end{macro} % % \begin{macro}{\@kernel@after@enddocument@afterlastpage} % Once the \cs{clearpage} has done its work inside \cs{enddocument} % we know for sure how many pages this document has, so we record % that in the \texttt{.aux} file for the next run. % % \begin{macrocode} \g@addto@macro \@kernel@after@enddocument@afterlastpage { % \end{macrocode} % There is one special case: If no output is produced then there is % no point in a) recording the number as 0 will never match the % page number of a real page and b) adding an extra page to ran the % \hook{shipout/lastpage} is pointless as well (as it would % remain forever). So we test for this and run the code only if % there have been pages. % \begin{macrocode} \int_compare:nNnF \g_shipout_readonly_int = 0 { % \end{macrocode} % This ends up in the \texttt{.aux} so we use \LaTeXe{} names here. % \fmi{This needs an interface for \cs{nofiles} in expl3, doesn't at the moment!} % \begin{macrocode} \if@filesw \iow_now:Nx \@auxout { \gdef\string\@abspage@last {\int_use:N \g_shipout_readonly_int}} \fi % \end{macrocode} % But we may have guessed wrongly earlier and we still have to run the % \hook{shipout/lastpage} even though there is no page to place % it into. If that is the case we make a trivial extra page and put % it there. This temporary page will then vanish again on the next % run but helps to keep pdf viewers happy. % \begin{macrocode} \bool_if:NF \g_@@_lastpage_handled_bool { % \end{macrocode} % However, making this extra page in case the hook is actually % empty would be forcing a rerun without any reason, so we check % that condition and also check if % \cs{@kernel@after@shipout@lastpage} contains any code. If both % are empty we omit the page generation. % \begin{macrocode} \bool_lazy_and:nnF { \hook_if_empty_p:n {shipout/lastpage} } { \tl_if_empty_p:N \@kernel@after@shipout@lastpage } { \tex_shipout:D\vbox to\textheight { \hbox:n { \UseHook{shipout/lastpage} \@kernel@after@shipout@lastpage } % \end{macrocode} % This extra page could be totally empty except for the hook % content, but to help the user understanding why it is there we % put some text into it. % \begin{macrocode} \@@_excuse_extra_page: \null } % \end{macrocode} % At this point we also signal to \LaTeX{}'s endgame that a rerun is % necessary so that an appropriate message can be shown on the % terminal. We do this by simply defining a command used as a flag and % tested in \cs{enddocument}. % \begin{macrocode} \cs_gset_eq:NN \@extra@page@added \relax } } } } % \end{macrocode} % \end{macro} % \end{macro} % % % % \begin{macro}{\@@_excuse_extra_page:} % Say mea culpa \ldots % \begin{macrocode} \cs_new:Npn \@@_excuse_extra_page: { \vfil \begin{center} \bfseries Temporary~ page! \end{center} \LaTeX{}~ was~ unable~ to~ guess~ the~ total~ number~ of~ pages~ correctly.~ ~ As~ there~ was~ some~ unprocessed~ data~ that~ should~ have~ been~ added~ to~ the~ final~ page~ this~ extra~ page~ has~ been~ added~ to~ receive~ it. \par If~ you~ rerun~ the~ document~ (without~ altering~ it)~ this~ surplus~ page~ will~ go~ away,~ because~ \LaTeX{}~ now~ knows~ how~ many~ pages~ to~ expect~ for~ this~ document. \vfil } % \end{macrocode} % \end{macro} % % % \begin{macro}{\PreviousTotalPages,\@kernel@before@begindocument} % In the preamble before the \texttt{aux} file was read % \cs{PreviousTotalPages} is always zero. % \begin{macrocode} \def\PreviousTotalPages{0} % \end{macrocode} % In the \texttt{aux} file there should be an update for % \cs{@abspage@last} recording the number of pages from the % previous run. If not that macro holds the value of % \cs{maxdimen}. So we test for it and update % \cs{PreviousTotalPages} if there was a real value. This should % happen just before the \hook{begindocument} hook is executed so % that the value can be used inside that hook. % \begin{macrocode} \g@addto@macro\@kernel@before@begindocument {\ifnum\@abspage@last<\maxdimen \xdef\PreviousTotalPages{\@abspage@last}\fi} % \end{macrocode} % \end{macro} % % % \section{Legacy \LaTeXe{} interfaces} % % % % \begin{macro}{\DiscardShipoutBox} % Request that the next shipout box is to be discarded. % \begin{macrocode} \cs_new_eq:NN \DiscardShipoutBox \shipout_discard: % \end{macrocode} % \end{macro} % % % \begin{macro}{\AtBeginDvi} % If we roll forward from an earlier kernel \cs{AtBeginDvi} is % defined so we better not use \cs{cs_new_protected:Npn} here. % \changes{v1.0d}{2020/11/24}{Support for roll forward (gh/434)} % \begin{macrocode} \cs_set_protected:Npn \AtBeginDvi {\@@_add_firstpage_material:Nn \AtBeginDvi} % \end{macrocode} % \end{macro} % % % \begin{macro}{\DebugShipoutsOn,\DebugShipoutsOff} % % \begin{macrocode} \cs_new_eq:NN \DebugShipoutsOn \shipout_debug_on: \cs_new_eq:NN \DebugShipoutsOff \shipout_debug_off: % \end{macrocode} % \end{macro} % % % \section{Internal commands needed elsewhere} % % These internal commands use double and triple \texttt{@} signs so % we need to stop getting them translated to the module name. % \begin{macrocode} %<@@=> % \end{macrocode} % % \begin{macro}{\@expl@@@shipout@add@firstpage@material@@Nn, % \@expl@@@shipout@add@background@box@@n, % \@expl@@@shipout@add@foreground@box@@n, % \@expl@@@shipout@add@background@picture@@n, % \@expl@@@shipout@add@foreground@picture@@n} % Some internals needed elsewhere. % % \begin{macrocode} \cs_set_eq:NN \@expl@@@shipout@add@firstpage@material@@Nn \__shipout_add_firstpage_material:Nn % \end{macrocode} % % \begin{macrocode} \cs_set_eq:NN \@expl@@@shipout@add@background@box@@n \__shipout_add_background_box:n % \end{macrocode} % % \begin{macrocode} \cs_set_eq:NN \@expl@@@shipout@add@foreground@box@@n \__shipout_add_foreground_box:n % \end{macrocode} % % \begin{macrocode} \cs_set_eq:NN \@expl@@@shipout@add@background@picture@@n \__shipout_add_background_picture:n % \end{macrocode} % % \begin{macrocode} \cs_set_eq:NN \@expl@@@shipout@add@foreground@picture@@n \__shipout_add_foreground_picture:n % \end{macrocode} % \end{macro} % % \begin{macrocode} \ExplSyntaxOff % %\EndIncludeInRelease % \end{macrocode} % % % Rolling back here doesn't undefine the interface commands as they % may be used in packages without rollback functionality. So we % just make them do nothing which may or may not work depending on % the code usage. % % \begin{macrocode} %\IncludeInRelease{0000/00/00}% % {\shipout}{Hook management (shipout)}% % % \end{macrocode} % If we roll forward then \cs{tex\_shipout:D} may not be defined in % which case \cs{shipout} does have it original definition and so % we must not \cs{let} it to something else which is \cs{relax}! % \changes{v1.0d}{2020/11/24}{Support for roll forward (gh/434)} % \begin{macrocode} %\ifcsname tex_shipout:D\endcsname %\expandafter\let\expandafter\shipout % \csname tex_shipout:D\endcsname %\fi % %\let \ShipoutBox\@undefined %\let \ReadonlyShipoutCounter \@undefined %\let \c@totalpages \@undefined %\let \thetotalpages \@undefined % %\let \DiscardShipoutBox \@undefined %\let \DebugShipoutsOn \@undefined %\let \DebugShipoutsOff \@undefined % %\DeclareRobustCommand \AtBeginDvi [1]{% % \global \setbox \@begindvibox % \vbox{\unvbox \@begindvibox #1}% %} % %\let \AtBeginShipout \@undefined %\let \AtBeginShipoutNext \@undefined % %\let \AtBeginShipoutFirst \@undefined % %\let \ShipoutBoxHeight \@undefined %\let \ShipoutBoxDepth \@undefined %\let \ShipoutBoxWidth \@undefined % %\let \AtBeginShipoutDiscard \@undefined % %\let \AtBeginShipoutAddToBox \@undefined %\let \AtBeginShipoutAddToBoxForeground \@undefined %\let \AtBeginShipoutUpperLeft \@undefined %\let \AtBeginShipoutUpperLeftForeground \@undefined % % \end{macrocode} % We do not undo a substitution when rolling back. As the file % support gets undone the underlying data is no longer used (and % sufficiently obscure that it should not interfere with existing % commands) and properly removing it would mean we need to make the % \cs{unclare@...} and its support macros available in all earlier % kernel releases which is pointless (and actually worse). % \begin{macrocode} %\undeclare@file@substitution{everyshi.sty} % \end{macrocode} % % \begin{macrocode} % %\let \AtEndDvi \@undefined % \end{macrocode} % We do not reenable a disabled package load when rolling back. As the file % support gets undone the underlying data is no longer checked (and % sufficiently obscure that it should not interfere with existing % commands) and properly removing it would mean we need to make the % \cs{reenable@package@load} command available in all earlier % kernel releases which is pointless (and actually worse). % \begin{macrocode} %\reenable@package@load{atenddvi} % \end{macrocode} % % \begin{macrocode} % %\EndIncludeInRelease %<*2ekernel> % \end{macrocode} % % % % % \section{Package emulation for compatibility} % % % \subsection{Package \pkg{atenddvi} emulation} % % % \begin{macro}{\AtEndDvi} % This package has only one public command to simulating it is easy % and actually sensible to provide as part of the kernel. % \begin{macrocode} % %<*2ekernel|latexrelease> %\IncludeInRelease{2020/10/01}% % {\AtEndDvi}{atenddvi emulation}% \ExplSyntaxOn \cs_new_protected:Npn \AtEndDvi {\AddToHook{shipout/lastpage}} \ExplSyntaxOff % \end{macrocode} % As the package is integrate we prevent loading (no need to roll that back): % \begin{macrocode} \disable@package@load{atenddvi} {\PackageWarning{atenddvi} {Functionality of this package is already\MessageBreak provided by LaTeX.\MessageBreak\MessageBreak It is there no longer necessary to load it\MessageBreak and you can safely remove it.\MessageBreak Found on}} % \end{macrocode} % % \begin{macrocode} % % \end{macrocode} % % \begin{macrocode} %\EndIncludeInRelease %\IncludeInRelease{0000/00/00}% % {\AtEndDvi}{atenddvi emulation}% %\let \AtEndDvi \@undefined %\EndIncludeInRelease %<*2ekernel> % \end{macrocode} % \end{macro} % % % % \begin{macrocode} % % \end{macrocode} % % % \subsection{Package \pkg{atbegshi} emulation} % % % % % % \begin{macrocode} %<*atbegshi-ltx> \ProvidesPackage{atbegshi-ltx} [2020/10/15 v1.0b Emulation of the original atbegshi^^Jpackage with kernel methods] % \end{macrocode} % % % \begin{macro}{\AtBeginShipoutBox} % \begin{macrocode} \let \AtBeginShipoutBox \ShipoutBox % \end{macrocode} % \end{macro} % % % \begin{macro}{\AtBeginShipoutInit} % Compatibility only, we aren't delaying \ldots % \begin{macrocode} \let \AtBeginShipoutInit \@empty % \end{macrocode} % \end{macro} % % % \begin{macro}{\AtBeginShipout,\AtBeginShipoutNext} % Filling hooks % \begin{macrocode} \protected \def \AtBeginShipout {\AddToHook{shipout/before}} \protected \def \AtBeginShipoutNext {\AddToHookNext{shipout/before}} % \end{macrocode} % \end{macro} % % % \begin{macro}{\AtBeginShipoutFirst} % Slightly more complex as we need to know the name of the command under which the % \hook{shipout/firstpage} hook is filled. % \begin{macrocode} \protected \def \AtBeginShipoutFirst {\@expl@@@shipout@add@firstpage@material@@Nn \AtBeginShipoutFirst} % \end{macrocode} % \end{macro} % % % \begin{macro}{\AtBeginShipoutDiscard} % Just a different name. % \begin{macrocode} \let \AtBeginShipoutDiscard \DiscardShipoutBox % \end{macrocode} % \end{macro} % % % \begin{macro}{\AtBeginShipoutAddToBox,\AtBeginShipoutAddToBoxForeground, % \AtBeginShipoutUpperLeft,\AtBeginShipoutUpperLeftForeground} % We don't expose them. % \begin{macrocode} \let \AtBeginShipoutAddToBox \@expl@@@shipout@add@background@box@@n \let \AtBeginShipoutAddToBoxForeground \@expl@@@shipout@add@foreground@box@@n % \end{macrocode} % % \begin{macrocode} \let \AtBeginShipoutUpperLeft \@expl@@@shipout@add@background@picture@@n \let \AtBeginShipoutUpperLeftForeground \@expl@@@shipout@add@foreground@picture@@n % \end{macrocode} % \end{macro} % % % % \begin{macro}{\ShipoutBoxHeight,\ShipoutBoxWidth,\ShipoutBoxDepth} % This is somewhat different from the original in \pkg{atbegshi} % where \cs{ShipoutBoxHeight} etc.\ only holds the % \verb=\the\ht= value. This may has some implications in some % use cases and if that is a problem then it might need changing. % \begin{macrocode} \ExplSyntaxOn \cs_new:Npn \ShipoutBoxHeight { \dim_use:N \l_shipout_box_ht_dim } \cs_new:Npn \ShipoutBoxDepth { \dim_use:N \l_shipout_box_dp_dim } \cs_new:Npn \ShipoutBoxWidth { \dim_use:N \l_shipout_box_wd_dim } \ExplSyntaxOff % \end{macrocode} % \end{macro} % % % \begin{macrocode} % % \end{macrocode} % % If the package is requested we substitute the one above: % \begin{macrocode} %<*2ekernel> \declare@file@substitution{atbegshi.sty}{atbegshi-ltx.sty} % % \end{macrocode} % % % % % % % \subsection{Package \pkg{everyshi} emulation} % % \begin{macrocode} %<*everyshi-ltx> % \end{macrocode} % % \begin{macrocode} \ProvidesPackage{everyshi-ltx} [2020/10/15 v1.0b Emulation of the original everyshi^^Jpackage with kernel methods] % \end{macrocode} % % \begin{macro}{\EveryShipout,\AtNextShipout} % This package has only two public commands so simulating it is easy: % \begin{macrocode} \protected \def \EveryShipout {\AddToHook{shipout/before}} \protected \def \AtNextShipout {\AddToHookNext{shipout/before}} % \end{macrocode} % \end{macro} % % \begin{macrocode} % This is one difference between \pkg{everyshi} and the kernel % implementation, the latter does not directly use box 255. % % For usage by ordinary users this makes no difference but of a % package use complicated code together with \pkg{everyshi} and % directly manipulates box 255 then this package needs updating. % In most cases the updates are simple because the kernel offers % hooks that makes such complicated code unnecessary. % % We therefore add a little file into the adjusted package % \begin{macrocode} %% %% In normal circumstances the above emulation is sufficient and in %% all known packages (we know of) that use everyshi it either works or %% the packages have been adjusted. %% %% Code that directly manipulates box 255, however, might fail. %% If that is the case look at the shipout hooks offered now as %% they are normally sufficient to avoid such manipulations (or %% replace box 255 with \ShipoutBox in the code. %% % % \end{macrocode} % % % If the package is requested we substitute the one above: % \begin{macrocode} %<*2ekernel> \declare@file@substitution{everyshi.sty}{everyshi-ltx.sty} % % \end{macrocode} % % % Rather important :-) % \begin{macrocode} %<@@=> % \end{macrocode} % % \Finale % % % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% \endinput %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%