For our purposes, \begin{#1}
is roughly
\begingroup \@endpefalse \csname #1\endcsname
and \end{#1}
is roughly
\csname end#1\endcsname\expandafter \endgroup \if@endpe \@doendpe \fi
Since \expandafter
expands \if@endpe
before the group ends, the value of the switch that is used is the one within the environment. If the \csname #1\endcsname
part of a \begin{#1}
features \@endpetrue
, then \@doendpe
will be expanded. However, it will be expanded outside the environment, where the switch has been returned to its former value.
Consider the following document. The environment parenvA
suppresses the indentation afterwards. In the second case, \@endparenv
is called within two groups, and causes \@doendpe
to be called still within the parenvB
environment. If the definition of \@doendpe
did not have \@endpetrue
, then it would simply define \par
and \everypar
within the parenvB
environment. These definitions would disappear at the end of the group, and the next paragraph would be indented. So instead, \@doendpe
makes sure to carry the truth of the switch with it beyond environment boundaries, until the next \par
or \everypar
.
\documentclass{article}
\makeatletter
\newenvironment{parenvA}{}{\par\@endparenv}
\newenvironment{parenvB}{}{}
\makeatother
\usepackage{lipsum}
\begin{document}
% case 1
\begin{parenvA}
\lipsum[1]
\end{parenvA}
\lipsum[2]
% case 2
\begin{parenvB}
\begin{parenvA}
\lipsum[3]
\end{parenvA}
\end{parenvB}
\lipsum[4]
% case 3
\begin{parenvB}
\begin{parenvA}
\lipsum[5]
\end{parenvA}
\lipsum[6]
\end{parenvB}
\lipsum[7]
\end{document}
Once this is established, both \par
and \everypar
must have \@endpefalse
, otherwise the last paragraph in the example above would lose its indentation: indeed, the \@doendpe
triggered just outside the parenvA
environment contains \@endpetrue
, but we don't want the indentation to be suppressed after that \end{parenvB}
, because there has already been a paragraph.
Now why do we need to work with both \everypar
and \par
? Because there are actually two cases. If the user leaves no blank line after \end{parenvA}
, then the first letter, inherently horizontal for TeX, causes \everypar
to be inserted, and to eat the indentation (with \lastbox
). However, if the user leaves a blank line, TeX's eyes replace that by a \par
token, which resets the normal definitions of \par
and \everypar
, and do not eat any indentation (try adding some blank lines in the file above and see what happens).
For your bonus question, well, if you remove those lines, the indentation will only be removed if the next \par
(blank line) or \everypar
(triggered by horizontal material) is within the same group/environment. Not necessarily what you want.
There are a few packages that can help with this- personally I would choose the newfloat
package because it is from the same author as the excellent caption
package, which means that the two packages work very well together.
Below is a mock up of your original code, but using these two packages; the important lines are
\DeclareFloatingEnvironment[fileext=frm,placement={!ht},name=Frame]{myfloat}
\captionsetup[myfloat]{labelfont=bf}
which sets up a new floating environment- it also creates \jobname.frm
which contains information that will be useful if you want to create a list of your frames, using the command
\listofmyfloats
which the newfloat
package created for us.
I also moved the hyperref
package to be loaded last; this is important, as discussed in Which packages should be loaded after hyperref instead of before?
\documentclass{article}
\usepackage{newfloat}
\usepackage{caption}
\DeclareFloatingEnvironment[fileext=frm,placement={!ht},name=Frame]{myfloat}
\captionsetup[myfloat]{labelfont=bf}
\usepackage{lipsum}
\usepackage[framemethod=TikZ]{mdframed}
\usepackage{multicol}
\usepackage[colorlinks=true]{hyperref}
\newenvironment{frameenv}[1]
{\begin{myfloat}[tb]
\begin{mdframed}[roundcorner=10pt,backgroundcolor=blue!10]
\caption{#1}
\begin{multicols*}{2}
}
{\end{multicols*}\end{mdframed}\end{myfloat}
}
\begin{document}
\lipsum
\begin{frameenv}{More details}\label{frame1}
\lipsum[1]
\end{frameenv}
See frame \ref{frame1} or figure \ref{fig1}.
\begin{figure}
\caption{A real figure}\label{fig1}
\end{figure}
\end{document}
Another thought is to make the argument to your environment optional so that you can have an empty caption
. This is easily done, using
\newenvironment{frameenv}[1][]
{\begin{myfloat}[tb]
...
which means that you use, noting the square brackets and not braces,
\begin{frameenv}[Details for the caption]\label{frame1}
or
\begin{frameenv}\label{frame2}
You could also remove the caption
from the definition of the environment and specify it manually each time, but that's up to you.
Best Answer
You should search for
\document
(and/or\enddocument
) insidelatex.ltx
, since an environmentenv
consists of the macro-pair\env
and\endenv
:Amongst a host of things, the
document
environment initiates the following:\documentclass
options;.aux
file;.aux
;\normalsize
);\AtBeginDocument
(\@begindocumenthook
);\@preamblecmds
, which collects commands that are defined as being usable in the preamble only via\@onlypreamble
).Note that this is the default definition used in LaTeX. Some documents may alter or append to this definition, depending on the requirements.
Fundamentally, packages are meant to do a host of things, including intervene with things done during the initiation of the
document
environment (mentioned above). Clearly,geometry
is one, as it needs to set up the page layout and dimensions before you can start writing certain content. However, as a more general rule, it's better to separate structure from content, and since package provide structural interface, they're better-suited for the preamble. You can load a fairly trivial packagemypackage
(say) within thedocument
environment, usingThe
\makeatletter
...\makeatother
pair avoid@
-in-macro problems. However, some packages are written using commands that are itself only usable within the preamble. Circumventing that would be a tedious process and is not necessary.