6

I am writing a set of lecture notes for which there should be two versions: one for me with all of the text, and one for the students with certain sections replaced by a grid for them to make their own notes.

To achieve this, I am trying to define a function \gap which can enclose some object (paragraph/figure/theorem/etc) of my lecture notes, and when a Boolean gaps is off, it does nothing, and when the Boolean is on, it replaces the object with a grid the width of the text and of an appropriate height.

Taking into account the fact that hand writing is bigger than typeset text, the height of the grid should be some scaled up multiple of the object being replaced. I am defining \heightmultiplier as that scale factor which I have set to 2.5.

On the face of it, this is easy to achieve using the \fillwithgrid function which can be copied from the exam class exam.cls. This function takes a single numerical argument - the height of the grid to be drawn. The problem is that the grid this produces is not breakable and so the result leaves lots of white space in the lecture notes. Therefore, I am trying to adapt that function to create a breakable grid.

The first function I define is \fillwithgridbreakable which, like \fillwithgrid takes as argument the height of the grid to be drawn. First it compares this height to the amount of space left on the current page \remainingspace, which I define to be \textheight-\pagetotal. If there is enough space it draws the grid, otherwise it draws as big a grid as it can before calling itself again to draw a grid of whatever size is left:

\newcommand{\fillwithgridbreakable}[1]{
    \newlength{\remainingspace}%
    \setlength{\remainingspace}{\textheight-\pagetotal}%
    \ifdim #1>\remainingspace%
    \fillwithgrid{\remainingspace}%
    \fillwithgridbreakable{#1-\remainingspace}%
    \else%
    \fillwithgrid{#1}%
    \fi
}

Then I define the function \gap which takes some LaTeX code as input. If the Boolean gaps is off it jest runs the code, and otherwise, it measures the height \height of whatever that code would produce, and then calls \fillwithgridbreakable with argument \heightmultiplier\height.

\newcommand{\gap}[1]{%
     \ifthenelse{\boolean{gaps}}{%
        \newdimen\height
        \setbox0=\vbox{#1}
        \height=\ht0 \advance\height by \dp0
        \fillwithgridbreakable{\heightmultiplier\height}
     }
     {%
        #1
     }
 }

My problem is that I get errors saying that \gap is missing a number and units treated as 0, and that the grid it draws is not breakable.

Here is a MWE

\documentclass{exam}

\usepackage{lipsum} 

\newboolean{gaps}
\setboolean{gaps}{true}

\newcommand{\heightmultiplier}{2.5}

\newcommand{\fillwithgridbreakable}[1]{
    \newlength{\remainingspace}%
    \setlength{\remainingspace}{\textheight-\pagetotal}%
    \ifdim #1>\remainingspace%
    \fillwithgrid{\remainingspace}%
    \fillwithgridbreakable{#1-\remainingspace}%
    \else%
    \fillwithgrid{#1}%
    \fi
}

\newcommand{\gap}[1]{%
     \ifthenelse{\boolean{gaps}}{%
        \newdimen\height
        \setbox0=\vbox{#1}
        \height=\ht0 \advance\height by \dp0
        \fillwithgridbreakable{\heightmultiplier\height}
     }
     {%
        #1
     }
 }

\begin{document}

\lipsum[1-5]

\gap{\lipsum[1-3]}

\end{document}

The output of this, at the first page break, looks like this: Output of MWE showing large whitespace at the bottom of the first page, and the grid drawn only at the start of the second page I would be very grateful for any insight into why this doesn't work and what I can do to fix it. I have searched SE for posts on similar topics. That's how I found out how to measure the height of something as I do in the \gap function and how to compute \remainingspace and use \ifdim...\else. But I couldn't find anything that makes breakable environments from scratch.

This is also my first post on this SE, so let me know if there's anything I can do to improve it.

Solution

Drawing almost entirely on John Kormylo's solution below, I thought I would set out a working solution here with a couple of bugs fixed and more freedom to adjust things like grid size and colour.

\usepackage{xcolor} %to adjust the grid colour
\usepackage{ifthen} %to create the boolean for turning the functionality of \gaps on and off

\newboolean{gaps}
\setboolean{gaps}{true}

\newcommand{\heightmultiplier}{2.5} %how much taller the grid is than the object it is replacing
\newcommand{\mygridsize}{.5cm} %the size of the grid squares
\colorlet{gridcolor}{gray!50} %the colour of the gridlines
\newcommand{\rulewidth}{0.8pt} %the width of the vertical rules

\newsavebox{\row}
\savebox{\row}{\begin{minipage}[b]{\textwidth}
  {\color{gridcolor}\hrule}
  \dimen9=\mygridsize\relax
  \count9=\numexpr \textwidth / \dimen9\relax
  {\color{gridcolor}\rule{\rulewidth}{\dimen9}}%
  \loop\ifnum\count9>1
    \advance\count9 by -1
    \hfill{\color{gridcolor}\rule{\rulewidth}{\dimen9}}%
  \repeat
  {\color{gridcolor}\hrule}
\end{minipage}}

\newcommand{\fillwithgridbreakable}[1]{% #1 = total height
  \setstretch{1} %if setstretch is changed elsewhere in the document, this prevents it from messing with the grid row spacing
  \setlength{\parskip}{0pt} %If included inside a list environment, the prevents the paragraph spacing from messing up with the grid row spacing
  \vfill
  \dimen8=#1\relax
  \dimen9=\mygridsize\relax
  \count8=\numexpr \dimen8 / \dimen9\relax
  \loop\ifnum\count8>0
    \advance\count8 by -1
    \noindent\usebox\row\par\vskip-\lineskip
  \repeat}
  
\newcommand{\gap}[1]{% #1 = text to be replaced
     \ifthenelse{\boolean{gaps}}{%
        \setbox0=\vbox{#1}%
        \fillwithgridbreakable{\dimexpr \heightmultiplier\ht0 + \heightmultiplier\dp0}%
     }
     {%
        #1
     }
 }
5
  • 2
    Nice first question ...
    – Mensch
    Commented Sep 10, 2023 at 13:59
  • 1
    it should be possible to create a breakable grid by making each "row" a separate paragraph and slightly overlapping the them. Commented Sep 10, 2023 at 15:26
  • 1
    You should always put \newlength in the preamble. Otherwise, every time you expand \fillwithgridbreakable you will use up another dimen register and assign it to \remainingspace. Commented Sep 10, 2023 at 16:08
  • Thanks @JohnKormylo, that is very useful to know about \newlength, I guess the same goes for \newdem too. Having separate rows is an interesting idea, but a bit of a bodge I think - getting the overlap correct would be fiddly, and if you used \vspace{-?? pt} you would probably end up with the first now on the new page too high. Commented Sep 10, 2023 at 17:48
  • 1
    Actually, \vskip and \vspace are ignored at the top of a page. I'm more worried about the width. You may need to add \hspace*{-\csname @totalleftmargin\endcsname} at the beginning of each line. Commented Sep 11, 2023 at 0:02

2 Answers 2

3

This shows how to create a truly breakable grid (rather than a broken grid).

Registers 0-9 are supposedly free for use with some exceptions, \hrule is a TeX primitive which (a) draws a line, (b) acts like \par and (c) ignores \baselineskip and \lineskip. \loop\if... \repeat does precisely that. \repeat takes the place of \fi for the \if....

Note that \fullwidth is not breakable.

\documentclass{exam}

\usepackage{lipsum}
\usepackage{showframe}

\newboolean{gaps}
\setboolean{gaps}{true}

\newcommand{\heightmultiplier}{2.5}

\newcommand{\fillwithgridbreakable}[1]% #1 = total height
{\bgroup
  \sbox9{\begin{minipage}[b]{\linewidth}% one row of grid
    \hrule
    \dimen9=\heightmultiplier\baselineskip\relax
    \count9=\numexpr \textwidth / \dimen9\relax
    \rule{0.4pt}{\dimen9}%
    \loop\ifnum\count9>1
      \advance\count9 by -1
      \hfill\rule{0.4pt}{\dimen9}%
    \repeat
    \hrule
  \end{minipage}}%
  \ht9=\dimexpr \ht9-0.4pt\relax% overlap borders
  \vfill
  \dimen8=#1\relax
  \count8=\numexpr \dimen8 / \ht9\relax
  \loop\ifnum\count8>0
    \advance\count8 by -1
    \noindent\usebox9\hrule height0pt
  \repeat
\egroup}
  
\newcommand{\gap}[1]{% #1 = text to be replaced
     \ifthenelse{\boolean{gaps}}{%
        \setbox0=\vbox{#1}%
        \fillwithgridbreakable{\dimexpr \heightmultiplier\ht0 + \heightmultiplier\dp0}%
     }
     {%
        #1
     }
 }

\begin{document}

\lipsum[1-5]

\gap{\lipsum[1-3]}

\end{document}
4
  • Thanks for sharing this answer. It's not clear to me how to adjust the grid size because I don't understand your code. Please could you add a little more detail to clarify this? Commented Sep 10, 2023 at 18:00
  • I think I've worked it out. The grid size is determined by \dimen9 in each function, and changing this to .5cm (or indeed whatever height/width one may wish), has the desired result Commented Sep 10, 2023 at 18:44
  • 1
    Used inside list environments, the rows are separated by extra whitespace. To fix this, once can add \setlength{\parskip}{0pt} at the start of the definition of \fillwithgridbreakable Commented Sep 10, 2023 at 21:12
  • I replaced \par with \hrule height0pt, which also ignores \lineskip. Commented Sep 11, 2023 at 15:04
3

This is not really an answer to your nice question, but too long for a comment, and it might be helpful. I suggest to do something conceptually simpler(?): To (ab)use a tabular environment that can split over several pages (longtable? Maybe there is something better available now?). Then you will get almost all for free, in particular the breaks over the page. You will not measure the other alternative and multiply by 2.5, but isn't it easier to say that you want a certain number of grid lines anyways?

I apologize, but I don't know LaTeX well enough, so I don't know how to do the coding for what I suggest there, but below you find a ConTeXt version:

There is not too much to say about it. The first two settings only set the paper size and the width of the text block, not important. Then we define a macro \MyGrid that takes the number of grid lines as an argument. It typesets a table that can split. Inside the macro, the natural table mechanism is set up and used, and there is a loop. The \samplefiles are just there to get some text in.

\setuppapersize[A5]

\setuplayout
  [width=10cm]

\starttexdefinition MyGrid #1
\startplacefloat
  [table]
  [location={force,nonumber,split}]
  \setupTABLE
    [width=0.5cm,
     height=0.5cm,
     split=yes]
  \bTABLE
    \dorecurse{#1}{\bTR\dorecurse{20}{\bTD\eTD}\eTR}
  \eTABLE
\stopplacefloat
\stoptexdefinition

\starttext
\samplefile{ward}

\MyGrid {10}

\samplefile{knuthmath}

\MyGrid {40}

\samplefile{ward}
\stoptext

context result

1
  • Thanks for your answer and idea. If the input is a figure/block of text/etc, the easiest thing is to find it's height, so knowing how many rows are required isn't necessarily so easy. That said, it should be a relatively straightforward extra step to implement. Commented Sep 10, 2023 at 17:56

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .