Skip to content

Commit

Permalink
reordered the execution of shipout hooks so that code in shipout/firs…
Browse files Browse the repository at this point in the history
…tpage really executes first
  • Loading branch information
FrankMittelbach committed Jan 19, 2021
1 parent b21646c commit 3db2fc5
Show file tree
Hide file tree
Showing 4 changed files with 213 additions and 30 deletions.
5 changes: 5 additions & 0 deletions base/changes.txt
Expand Up @@ -6,6 +6,11 @@ completeness or accuracy and it contains some references to files that
are not part of the distribution.
================================================================================

2021-01-19 Frank Mittelbach <Frank.Mittelbach@latex-project.org>

* ltshipout.dtx: Reordered execution of shipout hooks so that code
in one can influence code in later ones in a more natural manner.

2021-01-12 Frank Mittelbach <Frank.Mittelbach@latex-project.org>

* ltshipout.dtx: Make sure that the shipout/firstpage hook material
Expand Down
135 changes: 105 additions & 30 deletions base/ltshipout.dtx
Expand Up @@ -32,8 +32,8 @@
%%% From File: ltshipout.dtx
%
% \begin{macrocode}
\providecommand\ltshipoutversion{v1.0g}
\providecommand\ltshipoutdate{2021/01/18}
\providecommand\ltshipoutversion{v1.0h}
\providecommand\ltshipoutdate{2021/01/19}
% \end{macrocode}
%
%<*driver>
Expand Down Expand Up @@ -899,19 +899,28 @@
% \begin{macrocode}
\@@_get_box_size:N #1
% \end{macrocode}
% The first hook we run is the \hook{shipout/firstpage} hook. This
% is only done once, then the \cs{@@_run_firstpage_hook:}
% command redefines itself to do nothing. If the hook contains
% \cs{special}s for integration at the top of the page they will be
% temporarily stored in a safe place and added later with
% \cs{@@_add_firstpage_specials:}.
% \begin{macrocode}
\@@_run_firstpage_hook:
% \end{macrocode}
% Run the hooks for background and foreground or, if this
% is called by \cs{RawShipout}, copy the box \cs{l_@@_raw_box} to
% \cs{l_shipout_box} so that firstpage and lastpage material gets
% added if necessary (that is always done to \cs{l_shipout_box}.
% \begin{macrocode}
#3
% \end{macrocode}
% We then run \cs{@@_add_firstpage_hook:} that adds
% We then run \cs{@@_add_firstpage_specials:} 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}
\@@_add_firstpage_hook:
\@@_add_firstpage_specials:
% \end{macrocode}
% Then we check if we have to add the \hook{shipout/lastpage} hook
% or the corresponding kernel hook
Expand Down Expand Up @@ -946,7 +955,7 @@
% \cs{l_shipout_box} to its earlier state if that is necessary. On
% later pages this is then a no-op.
% \begin{macrocode}
\@@_drop_firstpage_hook:
\@@_drop_firstpage_specials:
% \end{macrocode}
% The \hook{shipout/after} hook (if in \verb=#4=) needs to run with
% \cs{protect}ed commands again being executed, because that hook
Expand Down Expand Up @@ -1052,57 +1061,123 @@
% \end{macro}
%
%
% \begin{macro}{\@@_add_firstpage_hook:,\@@_drop_firstpage_hook}
% \begin{macro}{\@@_run_firstpage_hook:}
%
% There are three commands to handle the \hook{shipout/firstpage}
% hook:
% \cs{@@_run_firstpage_hook:}, \cs{@@_add_firstpage_specials:} and
% \cs{@@_drop_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.
% That hook is supposed to contain \cs{special}s and similar
% material to be placed at the very beginning of the output page
% and so it needs careful placing to avoid that anything else gets
% in front of it. And this means we have to wait with this until
% other hooks such as \hook{shipout/background} have added their
% bits. It is also important that such \cs{special}s show up only
% on the very first page, so if this page gets saved before
% \cs{shipout} for later reuse, we have to make sure that they
% aren't in the saved version.
%
% In addition the hook may also contain code to be executed ``first'', e.g.,
% visible from code in \hook{shipout/background} and this conflicts
% with adding the \cs{special}s late.
%
% Therefore the processing is split into different parts:
% \cs{@@_run_firstpage_hook:} is done early and checks if there is
% any material in the hook.
% \begin{macrocode}
\cs_new:Npn \@@_add_firstpage_hook: {
\cs_new:Npn \@@_run_firstpage_hook: {
\hook_if_empty:nTF {shipout/firstpage}
% \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.
% If not then we define the other two commands to do nothing.
% \begin{macrocode}
\hook_if_empty:nF {shipout/firstpage}
{
\cs_gset_eq:NN \@@_add_firstpage_specials: \prg_do_nothing:
\cs_gset_eq:NN \@@_drop_firstpage_specials: \prg_do_nothing:
}
% \end{macrocode}
% First we make a copy of the \cs{l_shipout_box} that we can
% restore it later on.
% If there is material we execute inside a box, which means any
% \cs{special} will end up in that box and any other code is
% executed and can have side effects (as long as they are global).
% \begin{macrocode}
\box_set_eq:NN \l_@@_raw_box \l_shipout_box
\@@_add_background_box:n { \UseHook{shipout/firstpage} }
% \end{macrocode}
% After the actual shipout \cs{@@_drop_firstpage_hook:} is
% run. Normally that does nothing but now it is used (once) to
% restore the earlier content of \cs{l_shipout_box} and then
% redefines itself again to do nothing.
% \begin{macrocode}
\cs_gset:Npn \@@_drop_firstpage_hook: {
\box_set_eq:NN \l_shipout_box \l_@@_raw_box
\cs_gset_eq:NN \@@_drop_firstpage_hook: \prg_do_nothing:
}
{
\hbox_set:Nn \l_@@_firstpage_box { \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 \@@_add_firstpage_hook: \prg_do_nothing:
\cs_gset_eq:NN \@@_run_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}{\@@_add_firstpage_specials:,\@@_drop_firstpage_hook}
% The \cs{@@_add_firstpage_specials:} then adds the \cs{special}s
% stored in \cs{l_@@_firstpage_box} to the page to be shipped out
% when the time is ready. Note that if there was no material in the
% \hook{shipout/firstpage} hook then this command gets redefined to
% do nothing. But for most documents there is something, e.g., some
% PostScript header, or some meta data declaration, etc.\ so by
% default we assume there is something to do.
% \begin{macrocode}
\cs_new:Npn \@@_add_firstpage_specials: {
% \end{macrocode}
% First we make a copy of the \cs{l_shipout_box} that we can
% restore it later on.
% \begin{macrocode}
\box_set_eq:NN \l_@@_raw_box \l_shipout_box
% \end{macrocode}
% Adding something to the beginning means adding it to the
% background as that layer is done first in the output.
% \begin{macrocode}
\@@_add_background_box:n { \hbox_unpack:N \l_@@_firstpage_box }
% \end{macrocode}
% After the actual shipout \cs{@@_drop_firstpage_specials:} is
% run to
% restore the earlier content of \cs{l_shipout_box} and then
% redefines itself again to do nothing.
%
% As a final act we change the definition to do nothing next time.
% \begin{macrocode}
\cs_gset_eq:NN \@@_add_firstpage_specials: \prg_do_nothing:
}
% \end{macrocode}
%
% The \cs{@@_drop_firstpage_specials:} is run after the shipout has
% accured but before the \hook{shipout/afterpage} hook is executed.
% That is the point where we have to restore the \cs{ShipoutBox} to
% its state without the \hook{shipout/firstpage} material.
% \begin{macrocode}
\cs_new_eq:NN \@@_drop_firstpage_hook: \prg_do_nothing:
\cs_new:Npn \@@_drop_firstpage_specials: {
\box_set_eq:NN \l_shipout_box \l_@@_raw_box
% \end{macrocode}
% If there was no such material then \cs{@@_run_firstpage_hook:}
% will have changed the definition to a no-op already. Otherwise
% this is what we do here.
% \begin{macrocode}
\cs_gset_eq:NN \@@_drop_firstpage_specials: \prg_do_nothing:
}
% \end{macrocode}
% \end{macro}
%

% \begin{macro}{\l_@@_firstpage_box}
% The box to hold any firstpage \cs{special}s.
% \begin{macrocode}
\box_new:N \l_@@_firstpage_box
% \end{macrocode}
% \end{macro}

%
% \begin{macro}{\g_@@_lastpage_handled_bool}
% A boolean to signal if we have already handled the
Expand Down
32 changes: 32 additions & 0 deletions base/testfiles-lthooks2/shipout2-016.lvt
@@ -0,0 +1,32 @@
\RequirePackage[enable-debug]{expl3}
\ExplSyntaxOn
\debug_on:n { check-declarations , deprecation }
\ExplSyntaxOff

\documentclass[a4paper]{article}

\input{regression-test}

\AddToHook{shipout/foreground}{\show\foreground}
\AddToHook{shipout/background}{\show\background}
\AddToHook{shipout/firstpage}{\show\firstpage}
\makeatletter
\g@addto@macro \@kernel@after@shipout@background
{\show\kernel}
\AddToHook{shipout/lastpage}{\show\lastpage}

\DebugShipoutsOn

\showoutput


\begin{document}

\START

blub

\end{document}



71 changes: 71 additions & 0 deletions base/testfiles-lthooks2/shipout2-016.tlg
@@ -0,0 +1,71 @@
This is a generated file for the l3build validation system.
Don't change this file in any respect.
Absolute page = 1 (target: 1)
> \firstpage=undefined.
\__hook_toplevel shipout/firstpage ... \firstpage
l. ...\end{document}
> \foreground=undefined.
\__hook_toplevel shipout/foreground ...foreground
l. ...\end{document}
> \background=undefined.
\__hook_toplevel shipout/background ...background
l. ...\end{document}
> \kernel=undefined.
\@kernel@after@shipout@background ->\show \kernel
l. ...\end{document}
Executing lastpage hook on page 1
> \lastpage=undefined.
\__hook_toplevel shipout/lastpage ...ow \lastpage
l. ...\end{document}
Completed box being shipped out [1]
\vbox(682.0+0.0)x398.0
.\hbox(0.0+0.0)x0.0
.\hbox(0.0+0.0)x0.0
..\kern -72.27
..\vbox(0.0+0.0)x0.0, glue set 72.27fil
...\kern -72.27
...\hbox(0.0+0.0)x0.0
....\glue 0.0 plus 1.0fil minus 1.0fil
...\glue 0.0 plus 1.0fil minus 1.0fil
.\glue 17.0
.\vbox(665.0+0.0)x345.0, shifted 53.0
..\vbox(12.0+0.0)x345.0, glue set 12.0fil
...\glue 0.0 plus 1.0fil
...\hbox(0.0+0.0)x345.0
..\glue 25.0
..\glue(\lineskip) 0.0
..\vbox(598.0+0.0)x345.0, glue set 587.9372fil
...\write-{}
...\glue(\topskip) 3.05556
...\hbox(6.94444+0.0)x345.0, glue set 310.5555fil
....\hbox(0.0+0.0)x15.0
....\OT1/cmr/m/n/10 b
....\OT1/cmr/m/n/10 l
....\OT1/cmr/m/n/10 u
....\OT1/cmr/m/n/10 b
....\penalty 10000
....\glue(\parfillskip) 0.0 plus 1.0fil
....\glue(\rightskip) 0.0
...\glue 0.0 plus 1.0fil
...\glue 0.0
...\glue 0.0 plus 0.0001fil
..\glue(\baselineskip) 23.55556
..\hbox(6.44444+0.0)x345.0, glue set 170.0fil
...\glue 0.0 plus 1.0fil
...\OT1/cmr/m/n/10 1
...\glue 0.0 plus 1.0fil
.\kern -682.0
.\hbox(0.0+0.0)x0.0
..\kern -72.27
..\vbox(0.0+0.0)x0.0, glue set 72.27fil
...\kern -72.27
...\hbox(0.0+0.0)x0.0
....\glue 0.0 plus 1.0fil minus 1.0fil
...\glue 0.0 plus 1.0fil minus 1.0fil
.\kern 682.0
.\kern 0.0
.\kern 0.0
.\kern -682.0
.\hbox(0.0+0.0)x0.0
.\kern 682.0
(shipout2-016.aux)

0 comments on commit 3db2fc5

Please sign in to comment.