13

The following is actually a simplified version from a large job.

\documentclass{memoir}

\AtBeginDocument{First}

\AtBeginDocument{Second}

\AtBeginDocument{Third}

% I want to cancel the above at this point

\AtBeginDocument{New First}

\AtBeginDocument{New Second}

\AtBeginDocument{New Third}

\begin{document}

\end{document}

I need to cancel the current \AtBeginDocument accumulations at some midpoint (coming from another homegrown style file from which I need to use some of the macros) and then add some more.

How do I do this?

10

I'm not sure I understand your use case precisely. Left to myself, I'd be hugely reluctant to fiddle with \AtBeginDocument (or \@begindocumenthook) directly, because lots of packages (and the internal workings of LaTeX itself) use them for various purposes, and I don't see how I could be sure that I wouldn't break something unintentionally.

Might it be better to define a separate accumulator macro of your own, and then to have that executed \AtBeginDocument? You can then safely mess with that macro to your heart's content, without clobbering anything else that has been innocently making use of \AtBeginDocument.

Something like:

\documentclass{memoir}

\makeatletter
\def\MyAtBeginDocument{\g@addto@macro\my@begindocumenthook}
\let\my@begindocumenthook\@empty
\def\MyResetBeginDocument{\global\let\my@begindocumenthook\@empty}
\AtBeginDocument{\my@begindocumenthook}
\makeatother

\AtBeginDocument{THAT\par}
\MyAtBeginDocument{NOT THIS\par}
\MyResetBeginDocument
\MyAtBeginDocument{THIS\par}

\begin{document}
We should see THIS then THAT!
\end{document}

Note that THIS comes before THAT because \my@begindocument is executed as part of \@begindocumenthook before the additional material added by \AtBeginDocument{THAT\par} because it comes in via \my@begindocumenthook which in turn got added to \@begindocumenthook first. You might need to (and could) delay that addition until later.

  • I particularly liked the idea of not messing with \@begindocumenthook. Otherwise, packages innocently using \AtBeginDocument would never know what hit them. For my scenario, your solution turned out to be the most suitable one. The one from Peter Grill was a very close second. There is no special reason behind this, I had started working on your's first. – Masroor Dec 14 '18 at 2:10
11

\AtBeginDocument just adds (appends) code to the 'hook' \@begindocumenthook, at the beginning of the document the collected code is executed.

In theory you can clear the hook at any time with

\makeatletter
\let\@begindocumenthook\@empty
\makeatother

but that might be a bit risky since many packages expect \AtBeginDocument to work properly.

If there are only a few \AtBeginDocuments you want to skip over, it might be safer to disable the command temporarily with

\makeatletter
\let\AtBeginDocument\@gobble
\makeatother

Of course you can also save the contents of the hook

\makeatletter
\let\@masroor@saved@begindocumenthook\@begindocumenthook
\makeatother

and then after a few \AtBeginDocuments you want to ignore restore it again

\makeatletter
\let\@begindocumenthook\@masroor@saved@begindocumenthook
\makeatother
  • Your solution is a very nice one. It also let me understand how exactly \AtBeginDocument works. But eventually I failed to use it since in the homegrown style file, many packages were innocently using \AtBeginDocument. And not all can be ignored. – Masroor Dec 13 '18 at 18:00
5

This is similar to Paul Stanley's answer but with slightly different UI. This defines \AllowAtBeginDocumentToBeResetable which makes all subsequent uses of \AtBeginDocument resetable. To reset these you invoke \ClearResetableAtBeginDocumentList. Thus your example code would look like:

\AllowAtBeginDocumentToBeResetable%% ALL EXTERNAL PACKAGES INCLUDED BEFORE HERE

\AtBeginDocument{First\par}
\AtBeginDocument{Second\par}
\AtBeginDocument{Third\par}

\ClearResetableAtBeginDocumentList% I want to cancel the above at this point

\AtBeginDocument{New First\par}
\AtBeginDocument{New Second\par}
\AtBeginDocument{New Third\par}

which produces:

enter image description here

Notes:

  • Note that this requires that all external packages MUST be included before you issue \AllowAtBeginDocumentToBeResetable.

Code:

\documentclass{memoir}

\makeatletter
    \let\@OldAtBeginDocument\AtBeginDocument
    \newcommand{\@ResetableBegindDoumentItems}{}%
    \newcommand{\AllowAtBeginDocumentToBeResetable}{%
        \ifdefined\@AtBeginDocumentIncludesResetableList\else
            \gdef\@AtBeginDocumentIncludesResetableList{}% Don't execute this again
            \AtBeginDocument{\@ResetableBegindDoumentItems}%
        \fi
        \renewcommand{\AtBeginDocument}[1]{%
            \g@addto@macro\@ResetableBegindDoumentItems{{##1}}%
        }%
    }
    \newcommand*{\ClearResetableAtBeginDocumentList}{%
        \gdef\@ResetableBegindDoumentItems{}%
    }
\makeatother


\AllowAtBeginDocumentToBeResetable%% ALL EXTERNAL PACKAGES INCLUDED BEFORE HERE

\AtBeginDocument{First\par}
\AtBeginDocument{Second\par}
\AtBeginDocument{Third\par}

\ClearResetableAtBeginDocumentList% I want to cancel the above at this point

\AtBeginDocument{New First\par}
\AtBeginDocument{New Second\par}
\AtBeginDocument{New Third\par}

\begin{document}

\end{document}
4

I can offer a few routines for introducing sub-hooks within the \AtBeginDocument-hook and maintaining them.

1) \AtBeginDocumentSubHook

Syntax:

\AtBeginDocumentSubHook{⟨name of \AtBeginDocument-sub-hook⟩}%
                       {⟨tokens⟩}%

Adds a new \AtBeginDocument-sub-hook whose name is ⟨name of \AtBeginDocument-sub-hook⟩ to the \AtBeginDocument-hook and defines that \AtBeginDocument-sub-hook to deliver ⟨tokens⟩.

2) \AddToContentOfAtBeginDocumentSubHook

Syntax:

\AddToContentOfAtBeginDocumentSubHook{⟨name of \AtBeginDocument-sub-hook⟩}%
                                     {⟨prepend-tokens⟩}%
                                     {⟨append-tokens⟩}%

Redefines the \AtBeginDocument-sub-hook whose name is ⟨name of \AtBeginDocument-sub-hook⟩ to additionally deliver ⟨prepend-tokens⟩ in front of the tokens that are already in this sub-hook and to additionally deliver ⟨append-tokens⟩ behind the tokens that are already in this sub-hook.

3) \ReplaceContentOfAtBeginDocumentSubHook

Syntax:

\ReplaceContentOfAtBeginDocumentSubHook{⟨name of \AtBeginDocument-sub-hook⟩}%
                                       {⟨tokens⟩}%

Redefines the \AtBeginDocument-sub-hook whose name is ⟨name of \AtBeginDocument-sub-hook⟩ to deliver ⟨tokens⟩.

4) \RemoveAtBeginDocumentSubHook

Syntax:

\RemoveAtBeginDocumentSubHook{⟨name of \AtBeginDocument-sub-hook⟩}%

Removes the \AtBeginDocument-sub-hook whose name is ⟨name of \AtBeginDocument-sub-hook⟩ from the \AtBeginDocument-hook.

\documentclass{memoir}

\makeatletter
%%----------------------------------------------------------------------
%% Paraphernalia
%%----------------------------------------------------------------------
\newcommand\UD@firstoftwo[2]{#1}%
\newcommand\UD@secondoftwo[2]{#2}%
\newcommand\UD@Exchange[2]{#2#1}%
\newcommand\UD@name{}\long\def\UD@name#1#{\romannumeral0\UD@innername{#1}}%
\newcommand\UD@innername[2]{%
  \expandafter\UD@Exchange\expandafter{\csname#2\endcsname}{ #1}%
}%
%%----------------------------------------------------------------------
%% Check whether argument is empty:
%%......................................................................
%% \UD@CheckWhetherNull{<Argument which is to be checked>}%
%%                     {<Tokens to be delivered in case that argument
%%                       which is to be checked is empty>}%
%%                     {<Tokens to be delivered in case that argument
%%                       which is to be checked is not empty>}%
%%
%% The gist of this macro comes from Robert R. Schneck's \ifempty-macro:
%% <https://groups.google.com/forum/#!original/comp.text.tex/kuOEIQIrElc/lUg37FmhA74J>
\newcommand\UD@CheckWhetherNull[1]{%
  \romannumeral0\expandafter\UD@secondoftwo\string{\expandafter
  \UD@secondoftwo\expandafter{\expandafter{\string#1}\expandafter
  \UD@secondoftwo\string}\expandafter\UD@firstoftwo\expandafter{\expandafter
  \UD@secondoftwo\string}\expandafter\expandafter\UD@firstoftwo{ }{}%
  \UD@secondoftwo}{\expandafter\expandafter\UD@firstoftwo{ }{}\UD@firstoftwo}%
}%
%%----------------------------------------------------------------------
%% Remove \AtBeginDocument-sub-hook from \AtBeginDocument-hook:
%%......................................................................
%%  \RemoveAtBeginDocumentSubHook{<name of \AtBeginDocument-sub-hook>}
%%  removes the \AtBeginDocument-sub-hook whose name is
%%  <name of \AtBeginDocument-sub-hook> from the \AtBeginDocument-hook.
\newcommand*\RemoveAtBeginDocumentSubHook[1]{%
   \begingroup
   \UD@name\long\def\keep##1{@begindocumentsubhook@#1}{##1}%
   \UD@name\long\def\remove##1{@begindocumentsubhook@#1}{}%
   \expandafter\expandafter\expandafter\expandafter\expandafter\expandafter\expandafter
   \expandafter\expandafter\expandafter\expandafter\expandafter\expandafter\expandafter
   \expandafter
   \UD@CheckWhetherNull
   \expandafter\expandafter\expandafter\expandafter\expandafter\expandafter\expandafter
   \expandafter\expandafter\expandafter\expandafter\expandafter\expandafter\expandafter
   \expandafter
   {\UD@name\expandafter\remove\@begindocumenthook{@begindocumentsubhook@#1}}%
   {%
     \endgroup
     \@latex@info{Removal of undefined \string\AtBeginDocument-sub-hook\MessageBreak`#1' obsolete}%
   }{%
      \@latex@info{Removing \string\AtBeginDocument-sub-hook\MessageBreak`#1'}%
      \expandafter\UD@Exchange\expandafter{%
        \expandafter\toks@\expandafter{\the\toks@}%
      }{%
        \expandafter\expandafter\expandafter\expandafter\expandafter\expandafter\expandafter
        \endgroup
        \expandafter\expandafter\expandafter\expandafter\expandafter\expandafter\expandafter
        \toks@
        \expandafter\expandafter\expandafter\expandafter\expandafter\expandafter\expandafter
        {\expandafter\expandafter\expandafter\@gobble\expandafter\keep\expandafter.\@begindocumenthook}%
        \xdef\@begindocumenthook{\the\toks@}%
      }%
   }%
   \UD@name\global\let{@begindocumentsubhook@#1}=\UndEFineD
}%
%%----------------------------------------------------------------------
%% Introduce new \AtBeginDocument-sub-hook:
%%......................................................................
%%  \AtBeginDocumentSubHook{<name of \AtBeginDocument-sub-hook>}%
%%                         {<tokens>}%
%%  Adds a new \AtBeginDocument-sub-hook whose name is
%%  <name of \AtBeginDocument-sub-hook> to the \AtBeginDocument-hook
%%  and defines that \AtBeginDocument-sub-hook to deliver <tokens>.
\newcommand\AtBeginDocumentSubHook[2]{%
  \@ifundefined{@begindocumentsubhook@#1}{%
    \@latex@info{Introducing \string\AtBeginDocument-sub-hook\MessageBreak`#1'}%
    \UD@name\newcommand{@begindocumentsubhook@#1}{}%
    \expandafter\UD@Exchange\expandafter{%
      \expandafter\toks@\expandafter{\the\toks@}%
      \UD@name\AtBeginDocument{@begindocumentsubhook@#1}%
    }{%
      \toks@{#2}%
      \UD@name\xdef{@begindocumentsubhook@#1}{\the\toks@}%
    }%
  }{%
    \@latex@error{Sub-Hook `#1' already defined\on@line}{%
      Use either \string\RemoveAtBeginDocumentSubHook\space for removing the sub-hook,\MessageBreak
      or \string\ReplaceContentOfAtBeginDocumentSubHook\space for replacing the content of the\MessageBreak
      sub-hook,\MessageBreak
      or \string\AddToContentOfAtBeginDocumentSubHook\space for adding tokens to the sub-hook.%
    }%
  }%
}%
%%----------------------------------------------------------------------
%% Replace all tokens of an \AtBeginDocument-sub-hook:
%%......................................................................
%%  \ReplaceContentOfAtBeginDocumentSubHook{<name of \AtBeginDocument-sub-hook>}%
%%                                         {<tokens>}%
%%  Redefines the \AtBeginDocument-sub-hook whose name is
%%  <name of \AtBeginDocument-sub-hook> to deliver <tokens>.
\newcommand\ReplaceContentOfAtBeginDocumentSubHook[2]{%
  \@ifundefined{@begindocumentsubhook@#1}{%
    \@latex@info{To be replaced \string\AtBeginDocument-sub-hook\MessageBreak`#1' does not exist}%
    \AtBeginDocumentSubHook{#1}{#2}%
  }{%
    \@latex@info{Replacing \string\AtBeginDocument-sub-hook\MessageBreak`#1'}%
    \expandafter\UD@Exchange\expandafter{%
      \expandafter\toks@\expandafter{\the\toks@}%
    }{%
      \toks@{#2}%
      \UD@name\xdef{@begindocumentsubhook@#1}{\the\toks@}%
    }%
  }%
}%
%%----------------------------------------------------------------------
%% Add tokens to an \AtBeginDocument-sub-hook:
%%......................................................................
%%  \AddToContentOfAtBeginDocumentSubHook{<name of \AtBeginDocument-sub-hook>}%
%%                                       {<prepend-tokens>}%
%%                                       {<append-tokens>}%
%%  Redefines the \AtBeginDocument-sub-hook whose name is
%%  <name of \AtBeginDocument-sub-hook> to additionally deliver <prepend-tokens>
%%  in front of the tokens that are already in this hook and to additionally
%%  deliver <append-tokens> behind the tokens that are already in this hook.
\newcommand\AddToContentOfAtBeginDocumentSubHook[3]{%
  \@ifundefined{@begindocumentsubhook@#1}{%
    \@latex@info{\string\AtBeginDocument-sub-hook `#1' where tokens\MessageBreak shall be added does not exist}%
    \AtBeginDocumentSubHook{#1}{#2#3}%
  }{%
    \@latex@info{Adding tokens to \string\AtBeginDocument-sub-hook\MessageBreak`#1'}%
    \expandafter\UD@Exchange\expandafter{%
      \expandafter\toks@\expandafter{\the\toks@}%
    }{%
      \toks@{#2}%
      \toks@\expandafter\expandafter\expandafter
            \expandafter\expandafter\expandafter\expandafter{%
        \UD@name\the\expandafter\toks@{@begindocumentsubhook@#1}#3%
      }%
      \UD@name\xdef{@begindocumentsubhook@#1}{\the\toks@}%
    }%
  }%
}%
\AtBeginDocument{%
  \renewcommand\RemoveAtBeginDocumentSubHook[1]{%
    \@bsphack
    \UD@name\global\let{@begindocumentsubhook@#1}=\UndEFineD
    \@latex@info{Removal of \string\AtBeginDocument-sub-hook `#1'\MessageBreak obsolete as not in preamble any more}%
    \@esphack
  }%
  \renewcommand\AtBeginDocumentSubHook[2]{%
    \@bsphack
    \@latex@info{Introducing \string\AtBeginDocument-sub-hook `#1'\MessageBreak obsolete as not in preamble any more}%
    \@esphack
    #2%
  }%
  \renewcommand\ReplaceContentOfAtBeginDocumentSubHook[2]{%
    \@bsphack
    \@latex@info{Replacing \string\AtBeginDocument-sub-hook `#1'\MessageBreak obsolete as not in preamble any more}%
    \@esphack
    #2%
  }%
  \renewcommand\AddToContentOfAtBeginDocumentSubHook[3]{%
    \@bsphack
    \@latex@info{Adding to \string\AtBeginDocument-sub-hook `#1'\MessageBreak obsolete as not in preamble any more}%
    \@esphack
    #2#3%
  }%
}%
\makeatother

%\expandafter\show\csname @begindocumenthook\endcsname

\AtBeginDocumentSubHook{SomeSubHook}{Some\par}

\AtBeginDocumentSubHook{FirstSubHook}{First\par}

\AtBeginDocumentSubHook{SecondSubHook}{Second\par}

\AtBeginDocumentSubHook{ThirdSubHook}{Third\par}

\AtBeginDocumentSubHook{FourthSubHook}{Fourth\par}

% Let's cancel/change some of the above at this point

\RemoveAtBeginDocumentSubHook{SomeSubHook}

\ReplaceContentOfAtBeginDocumentSubHook{FirstSubHook}{New First\par}

\ReplaceContentOfAtBeginDocumentSubHook{SecondSubHook}{New Second\par}

\ReplaceContentOfAtBeginDocumentSubHook{ThirdSubHook}{Thi}

\AddToContentOfAtBeginDocumentSubHook{ThirdSubHook}{New }{rd\par}

%\expandafter\show\csname @begindocumenthook\endcsname

\begin{document}

\end{document}

enter image description here

Your Answer

By clicking "Post Your Answer", you acknowledge that you have read our updated terms of service, privacy policy and cookie policy, and that your continued use of the website is subject to these policies.

Not the answer you're looking for? Browse other questions tagged or ask your own question.