3

I defined a new environment that draws a sort of answer box using fbox around a minipage. The environment also puts some text above the answer box.

Here is a minimal working example:

\documentclass{article}
\usepackage{listings}

\newsavebox{\mybox}
\newenvironment{ansbox}[1]{Write answer in the box:\newline
\begin{lrbox}{\mybox}\begin{minipage}[t][#1]{\linewidth}}
{\vfill\hfill\end{minipage}\end{lrbox}\fbox{\usebox{\mybox}}}

\setlength{\parskip}{\baselineskip}
\setlength{\parindent}{0ex}
\begin{document}

Description of problem.

\begin{ansbox}{2in}
%empty box
\end{ansbox}

What is your name?

\begin{ansbox}{2in}
My name is 
\end{ansbox}

Implement a factorial function.

\begin{ansbox}{2in}
\begin{lstlisting}
def factorial(n):
\end{lstlisting}
\end{ansbox}

\end{document}

For convenience I attach a screenshot of the first page:

enter image description here

As you can see, there is a vertical space when the box is empty, or when it begins immediately with a lstlisting environment. I tried searching around but have no idea how this space is added or how to get rid of it.

Also, how can I keep the "header" text together with the box when it breaks into the next page?

  • I believe that the empty minipage puts the baseline at the very top of the minipage, so \baselineskip will be placed between last line of text ant the start of the minipage. Otherwise, the baseline is somewhere inside the minipage and \lineskip will be placed between the bottom of the last line of text and the top of the \fbox. – John Kormylo Jul 7 '17 at 19:08
  • @JohnKormylo Seems to make sense for an empty minipage. But I am still having problems visualizing where the \lineskip is placed when there is text in the minipage. – welcomb Jul 8 '17 at 2:08
  • Since the last line of text has no descenders, it is possible that \baselineskip could fit between the last baseline and the first baseline in the minipage (despite the \fbox border). – John Kormylo Jul 8 '17 at 2:24
1

The problem is in how you ensure the width of the box; in this case it's better to add a zero height rule as wide as the box. You also need to trim off some width (for \fboxsep and \fboxrule).

\documentclass{article}
\usepackage{listings}

\newsavebox{\mybox}
\newenvironment{ansbox}[1]
 {%
  Write answer in the box:\\*
  \begin{lrbox}{\mybox}
  \begin{minipage}[t][#1]{\dimexpr\linewidth-2\fboxsep-2\fboxrule}
  \hrule width \textwidth height 0pt
 }
 {\end{minipage}\end{lrbox}\fbox{\usebox{\mybox}}}

\setlength{\parskip}{\baselineskip}
\setlength{\parindent}{0ex}
\begin{document}

Description of problem.

\begin{ansbox}{1.5in}
%empty box
\end{ansbox}

What is your name?

\begin{ansbox}{1.5in}
My name is 
\end{ansbox}

Implement a factorial function.

\begin{ansbox}{1.5in}
\begin{lstlisting}
def factorial(n):
\end{lstlisting}
\end{ansbox}

\end{document}

enter image description here

Your code, with \begin{ansbox}{1.5in}, became

\begin{minipage}[t][1.5in]{\linewidth}
<environment's contents>
\vfill\hfill
\end{minipage}

When the environment body is empty, this makes for

\vfill
\hfill
\par\vfil

(the last tokens are added because you want a [t] minipage; the \vfil is cancelled by \vfill). The reference point of the box is determined by the \vfill. On the other hand, with a nonempty contents, you have

My name is
\vfill
\hfill
\vfil

and the reference point is at the baseline of the top paragraph; now the height of the box will be measured as the height of M plus \fboxsep plus \fboxrule; this makes for 6.83331+3+0.4=10.23331 points. This clearly exceeds the threshold, so TeX will use \lineskip between the “Write answer in the box:” line and the box. In the former case the box height is just 3.4pt and the separation you see is (since the line above has no depth) \baselineskip minus 3.4pt.

With my solution the box will always have height 3.4pt, because the zero height rule determines the reference point.

  • Thanks @egreg. The zero height \hrule does the trick. I was trying with blank spaces or \hfill which did not do anything. Btw, is the width really the issue here? I do trim off the excess width in my actual document (just did not that include in the mwe) but the issue of the vertical spacing remains. But more than just fix the problem, I was hoping to understand why this happens to improve my understanding of latex. – welcomb Jul 8 '17 at 2:03
  • @welcomb I added the explanation – egreg Jul 8 '17 at 8:39
  • Wow that's a very detailed explanation. I sort of get it but I guess I am still not familiar enough with how latex works. But nonetheless your fix works. I also found that if I put a \medskip at the top of the minipage, it also causes a \baselineskip minus some length. – welcomb Jul 8 '17 at 14:46
  • @welcomb A \medskip adds some vertical space, less than \baselineskip, as you discovered. – egreg Jul 8 '17 at 14:47
0

Minipages usually follow box spacing rules (\lineskip instead of \baselineskip) so I like to add \struts at the borders.

There are problems other than the one noted. Frankly, I'm surprised the code ran at all.

First, you should not use \begin and \end in the definition of an environment, as they will be expanded out of order. This sort of thing is best done using the environ package.

Second, lstlisting does not like being inside of anything (except an \hbox). The package has its own enviroment software.

If you want to prevent a page break between the text and the box, you can use the needspace package.

\documentclass{article}
\usepackage{listings}
\usepackage{environ}

\newsavebox{\mybox}
\NewEnviron{ansbox}[1]{Write answer in the box:\strut\newline
\fbox{\begin{minipage}[t][#1][s]{\dimexpr \linewidth-2\fboxsep-2\fboxrule}
\strut\BODY\vfill\hfill
\end{minipage}}}

\lstnewenvironment{lstbox}{\global\setbox\mybox=\hbox\bgroup}{\egroup}

\setlength{\parskip}{\baselineskip}
\setlength{\parindent}{0ex}
\begin{document}

Description of problem.

\begin{ansbox}{2in}
\end{ansbox}

What is your name?

\begin{ansbox}{2in}
My name is 
\end{ansbox}

Implement a factorial function.

\begin{lstbox}
def factorial(n):
\end{lstbox}
\begin{ansbox}{1in}
\usebox{\mybox}
\end{ansbox}

\end{document}

full page image

  • There is absolutely nothing wrong in the OP's definition of the environment. Your definition will not allow a lstlisting in the body of the environment. – egreg Jul 7 '17 at 22:30
  • Yes I ran into the problem of lstlisting not being allowed when I tried using the environ package. In my actual document, I have to mix lstlisting with regular text as well as \if-\fi blocks in the environment. e.g. \begin{ansbox}{2in} \begin{lstlisting} def factorial(n):|\ifsolution| return 1 if n == 0 else return n * factorial(n-1)|\fi| \end{lstlisting} \ifsolution Uses recursion to solve \fi \end{ansbox} – welcomb Jul 8 '17 at 1:52
  • For a single line of code, \lstinline is more tolerant. – John Kormylo Jul 8 '17 at 19:19
  • Yes but this as only an example. I do need multi-line code hence the need for \lstlisting. – welcomb Jul 11 '17 at 6:56

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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