About
Shop
LaTeX
Software
Books
Gallery
News
Contact
Blog
Settings
Account
Smile for the Camera: a new cybercrime short story ebook.


11.1 Writing a Class File for a Form

The above may be suitable for a short form to be filled in by hand, but it may be that you want to produce a more complex form to be filled in by LaTeX users. In this case, it may be more appropriate to write a class file that provides commands to fill in the form data. This section describes how to do this and is developed from an article I wrote on the LaTeX Community Forum [92]. The next section will look at interactive form elements.

§7.3 Displaying a Date briefly introduced package writing. There are similar commands for classes, and some of the package commands, such as \RequirePackage, may also be used in class files. As with packages, the class first identifies the TeX format using

\NeedsTeXFormat{format}[version]

and then identifies the class using

\ProvidesClass{name}[version]

This has the same syntax as \ProvidesPackage described in §7.3 Displaying a Date. The class code should be saved in a file called ⟨name.cls and placed somewhere on TeX's path.

Many classes load a parent class, which saves defining many common elements, such as the sectioning commands or list environments. The parent class is loaded using:

\LoadClass[options]{name}[version]

where ⟨name⟩ is the name of the parent class. The optional arguments are analogous to the optional arguments of \RequirePackage. Before you load a class, you can specify which options to pass to it using:

\PassOptionsToClass{option-list}{class-name}

where ⟨option-list⟩ is a comma-separated list of options to pass to the class specified by ⟨class-name⟩. An option is defined using:

\DeclareOption{option}{code}

where ⟨option⟩ is the option name and ⟨code⟩ is the code to perform for that option. The starred version of this command only has one argument:

\DeclareOption*{code}

This indicates the code to perform for an unknown option. The option name can be referenced within ⟨code⟩ using

\CurrentOption

Once all the options have been declared, they then need to be processed using:

\ProcessOptions

Here's the code for a trivial class called simple-form:

\NeedsTeXFormat{LaTeX2e}
\ProvidesClass{simple-form}[2014/10/11]

\DeclareOption*{\PassOptionsToClass{\CurrentOption}{article}}

\ProcessOptions

\LoadClass{article}

% class code

\endinput

This code needs to be saved in a file called simple-form.cls. At the moment this class doesn't provide anything in addition to the article class, but the new code will be added in the area between the \LoadClass line and the \endinput line.

In addition to \Square, which produces an empty square ☐, the wasysym package also defines

\XBox

which produces a box with a cross in it ☒, and

\CheckedBox

which produces a box with a tick in it ☑. These symbols will be useful for this new class, so the class code needs to load the wasysym package using

\RequirePackage{wasysym}

Information for the form can be gathered using the same type of mechanism as \author, \title and \date. These work by having an internal command that stores the information and a user command that sets the internal command. For example, if the form requires a person's name, the internal command could be called, say, \@name which is initially empty

\newcommand*{\@name}{}

and the user command could be called, say, \name which redefines the internal command:

\newcommand*{\name}[1]{%
  \renewcommand*{\@name}{#1}%
}

Then a command analogous to \maketitle is required to typeset the form. For example, this command could be called \makeform and it would use the internal commands to fill in the required areas. A trivial example would be:

\newcommand{\makeform}{%
  Name: \@name\␣Date: \@date
}

(\@date is the internal command used by \date and is initially defined as \today.)

This definition of \makeform has a problem when \name isn't used. If \@name is empty it won't take up any space. A better solution is to put \@name inside a horizontal box with a fixed width:

\newcommand{\makeform}{%
  Name: \makebox[6em][l]{\@name}\␣Date: \@date
}

This will leave a blank space if the name hasn't been set. If you prefer a lined space you could make the initial definition of \@name use \hrulefill

\newcommand*{\@name}{\hrulefill}

Now a lined space will appear if \name hasn't been used, but the line won't be present if \name has been used. If you still want a line to appear even if \name has been used, then you could replace

\makebox[6em][l]{\@name}

with

\makebox[6em][l]{\rlap{\@name}\hrulefill}

If you have more than one blank area to fill in, then it's best to define a command to do this. For example:

\newcommand*{\form@fillin}[2]{%
  \makebox[#1][l]{\rlap{#2}\hrulefill}%
}

This has the syntax

\form@fillin{width}{text}

so the trivial definition of \makeform can now look something like:

\newcommand{\makeform}{%
  Name: \form@fillin{6em}{\@name}\␣Date: \form@fillin{4em}{\@date}%
}

Variations of \form@fillin could include

\newcommand*{\form@fillin}[2]{%
  \makebox[#1][l]{\rlap{#2}\dotfill}%
}

which uses a dotted line instead or

\newcommand*{\form@fillin}[2]{%
  \makebox[#1][c]{%
  \hrulefill\makebox[0pt][c]{#2}\hrulefill}%
}

which centres the text within the ruled area or

\newcommand*{\form@fillin}[2]{%
  \makebox[#1][r]{\hrulefill\llap{#2}}%
}

which right-aligns the text within the ruled area.

Check boxes require a different interface, but there are various methods you can use. For example, for a gender check box you might want a command called, say, \male that ticks the “male” box and a command called, say, \female that ticks the “female” box. Alternatively you might prefer a command called, say, \gender that takes an argument which can either be male or female. In both cases, internal commands are defined for each option that default to the unchecked case:

\newcommand*{\gender@male}{\Square}
\newcommand*{\gender@female}{\Square}

The user commands redefine these internal commands. In the first case:

\newcommand*{\male}{%
  \renewcommand*{\gender@male}{\XBox}%
}
\newcommand*{\female}{%
  \renewcommand*{\gender@female}{\XBox}%
}

In the second case:

\newcommand*{\gender}[1]{%
  \ifcsdef{gender@#1}%
  {\csdef{gender@#1}{\XBox}}
  {% unknown option produces an error
    \ClassError{simple-form}{Unknown gender `#1'}
      {Options: `male', `female'}%
  }%
}

This uses the etoolbox commands \ifcsdef and \csdef described in §2.1.1 Macro Definitions, and also uses

\ClassError{class-name}{error-message}{help-message}

to display an error message. The first argument is the class name (simple-form in this case) and the second argument is the error message. The third argument provides a help message if the user types “h” in TeX's interactive mode.

What if I later decide to use \CheckedBox instead of \XBox? Alternatively, I might decide to use a radio button style. To help with code maintenance it's better to define commands for the checked and unchecked status and use those commands for the form data. For example:

\newcommand*{\form@unchecked}{\Square}
\newcommand*{\form@checked}{\XBox}

\newcommand*{\gender@male}{\form@unchecked}
\newcommand*{\gender@female}{\form@unchecked}

\newcommand*{\male}{%
  \renewcommand*{\gender@male}{\form@checked}%
}
\newcommand*{\female}{%
  \renewcommand*{\gender@female}{\form@checked}%
}

Or

\newcommand*{\gender}[1]{%
  \ifcsdef{gender@#1}%
  {\csdef{gender@#1}{\form@checked}}
  {%
    \ClassError{simple-form}{Unknown gender `#1'}
      {Options: `male', `female'}%
  }%
}

Now there are only one or two lines to change if I want to use different symbols. For example, to use \CheckedBox instead of \XBox just requires one edit:

\newcommand*{\form@checked}{\CheckedBox}

If you can't find a symbol that suits you, it's possible to combine symbols using a command such as \rlap. For example, to make round radio style buttons, you could use the ifsym package [45] with the geometry option and combine \BigCircle with \FilledSmallCircle.

\newcommand*{\form@unchecked}{\BigCircle}
\newcommand*{\form@checked}{\rlap{\FilledSmallCircle}\BigCircle}

These produce the symbols [unfilled big circle] and [big circle with small filled circle inside] .

Take care if you want to load both ifsym and wasysym as they have conflicting command names when ifsym is loaded with the geometry option. For example, both define \Square. If you want both packages, load ifsym without the geometry option and use \textifsymbol to access the symbols. For example:

\newcommand*{\form@checked}{%
  \rlap{\textifsymbol[ifgeo]{117}}\textifsymbol[ifgeo]{37}}
\newcommand*{\form@unchecked}{\textifsymbol[ifgeo]{37}}

Alternatively, if you want fancier buttons you can use picture drawing code. The following example creates on and off buttons using tikz with the shadings and shadows libraries:

\RequirePackage[x11names]{xcolor}
\RequirePackage{tikz}
\usetikzlibrary{shadings}
\usetikzlibrary{shadows}

\newcommand*{\form@unchecked}{%
  \resizebox{!}{2ex}%
  {%
    \begin{tikzpicture}
    \path[fill=LightYellow4,circular glow] (0,0) circle(.5cm);
    \path[fill=LightYellow1,circular glow={fill=LightYellow3}]
      (0,0) circle(.35cm);
    \end{tikzpicture}%
  }%
}
\newcommand*{\form@checked}{%
  \resizebox{!}{2ex}%
  {%
    \begin{tikzpicture}
    \path[shade,inner color=LightYellow2,outer color=LightYellow4,
          circular glow] (0,0) circle(.5cm);
    \end{tikzpicture}%
  }%
}

This produces [dark 3D effect filled circle] and [circle with light interior].

Similarly, it's a good idea to provide a command to layout the check box or fill-in area and its accompanying text. For example:

\newcommand*{\form@layout@checkbox}[2]{#1 #2}

This has the syntax:

\form@layout@checkbox{symbol}{text}

For example:

\form@layout@checkbox{\gender@male}{Male}
\form@layout@checkbox{\gender@female}{Female}

This means that if, say, you want to change all your check boxes so that the text is to the left of the check box symbol, then all you need to do is change the definition of \form@layout@checkbox. Similarly for the fill-in text fields:

\newcommand*{\form@layout@fillin}[3]{#3: \form@fillin{#1}{#2}}

This has the syntax

\form@layout@fillin{width}{value}{text}

For example:

\form@layout@fillin{6em}{\@name}{Name}

Example 56. A Simple Form Class

Here's a simple form class with two fill-in areas (for the name and date) and two check boxes (for the gender). The article class is loaded with the options a4paper and 12pt as this example is simulating a form with specific paper size and font requirements.

The contents of the file simple-form.cls are as follows:

\NeedsTeXFormat{LaTeX2e}
\ProvidesClass{simple-form}[2014/10/11]

\DeclareOption*{\PassOptionsToClass{\CurrentOption}{article}}

\ProcessOptions

\LoadClass[a4paper,12pt]{article}

\RequirePackage{etoolbox}
\RequirePackage{wasysym}

\newcommand*{\form@fillin}[2]{%
  \makebox[#1][l]{\rlap{#2}\hrulefill}%
}

\newcommand*{\form@checked}{\XBox}
\newcommand*{\form@unchecked}{\Square}

\newcommand*{\form@layout@checkbox}[2]{#1 #2}
\newcommand*{\form@layout@fillin}[3]{#3: \form@fillin{#1}{#2}}

\newcommand*\@name{}
\newcommand*{\name}[1]{\renewcommand*{\@name}{#1}}

\newcommand*{\gender@male}{\form@unchecked}
\newcommand*{\gender@female}{\form@unchecked}

\newcommand*{\gender}[1]{%
  \ifcsdef{gender@#1}%
  {\csdef{gender@#1}{\form@checked}}
  {%
    \ClassError{simple-form}{Unknown gender `#1'}%
    {Options: `male', `female'}%
  }%
}

\newcommand{\makeform}{%
  \form@layout@fillin{8em}{\@name}{Name}\qquad
  \form@layout@fillin{12em}{\@date}{Date}
  \par
  \bigskip
  \par
  \form@layout@checkbox{\gender@male}{Male}\qquad
  \form@layout@checkbox\gender@female{Female}
}

\endinput

An example document:

\documentclass{simple-form}

\name{Mabel Canary}
\gender{female}

\begin{document}

\makeform

\end{document}

The result is shown in Figure 11.1. You can download or view this example.

Figure 11.1: A Simple Form with Two Fill-In Areas and Two Check Boxes
 

Name: Mabel Canary Date: October 12, 2014

☐ Male ☒ Female

End of Image.

The above example doesn't test if \gender has already been used, so it's possible for a user to do:

\gender{male}\gender{female}

which would cause both boxes to be checked. If you want to prevent this from happening you could either produce an error message if the command is used more than once or make each subsequent use of the command reset the boxes before setting the new choice.

Here's a possible way of implementing the first case. It uses the \let assignment described in §2.1.1 Macro Definitions.

\newcommand*{\@gendererror}[1]{%
  \ClassError{simple-form}
  {\string\gender\space may only be used once}
  {}%
}

\newcommand*{\gender}[1]{%
  \let\gender\@gendererror
  \ifcsdef{gender@#1}%
  {\csdef{gender@#1}{\form@checked}}
  {%
    \ClassError{simple-form}{Unknown gender `#1'}%
    {Options: `male', `female'}%
  }%
}

This works as follows: the first time \gender is used, it redefines itself to have the same definition as \@gendererror, so the next time \gender is used, it's now equivalent to \@gendererror, which ignores its argument and produces an error message. (\string is a TeX primitive that converts the following control sequence into a list of characters, which provides an easy way of printing the control sequence in the transcript file or console.)

Here's a possible way of implementing the second case that defines a reset command:

\newcommand*{\@resetgender}{%
 \renewcommand*{\gender@male}{\form@unchecked}%
 \renewcommand*{\gender@female}{\form@unchecked}%
}

\newcommand*{\gender}[1]{%
  \@resetgender
  \ifcsdef{gender@#1}%
  {\csdef{gender@#1}{\form@checked}}
  {%
    \ClassError{simple-form}{Unknown gender `#1'}%
    {Options: `male', `female'}%
  }%
}

The \male/\female version is simpler:

\newcommand*{\male}{%
  \renewcommand*{\gender@female}{\form@unchecked}
  \renewcommand*{\gender@male}{\form@checked}
}
\newcommand*{\female}{%
  \renewcommand*{\gender@male}{\form@unchecked}
  \renewcommand*{\gender@female}{\form@checked}
}

However the other method is neater for a large set of check boxes. If you do have many choices, you may find it easier to use a list-based approach. For example, suppose I want to produce the following:

Which project would you like to enrol on?


Mind-Controlling Cookies Telepathic Cakes
Exploding Chocolates Ray Gun

A convenient user command might be called, say, \project where the argument may be one of: cookies, cakes, chocolates or raygun. The internal commands are called \project@label⟩ where ⟨label⟩ is the argument of \project. These commands can be reset using:

\csdef{project@⟨label}{\form@unchecked}

and set using

\csdef{project@⟨label}{\form@checked}

These can be wrapped up in two commands that each take the label as the argument:

\newcommand*{\reset@project}[1]{%
  \csdef{project@#1}{\form@unchecked}%
}
\newcommand*{\set@project}[1]{%
  \ifcsdef{project@#1}
  {\csdef{project@#1}{\form@checked}}
  {%
    \ClassError{simple-form}{Unknown project `#1'}{}%
  }%
}

It's also useful to provide a command to use the internal \project@label⟩ command:

\newcommand*{\use@project}[1]{%
  \ifcsdef{project@#1}{\csuse{project@#1}}{\form@unchecked}%
}

This will produce an unchecked box if the label hasn't been defined, which means that the internal commands don't need to be initialised if the user wants a blank form to fill in by hand.

Here's a comma-separated list where each element contains two groups. The first is the label that will be used in the argument of \project and the second is the text to appear next to the check box in the form:

\newcommand*{\@projectlist}{%
  {cookies}{Mind-Controlling Cookies},%
  {cakes}{Telepathic Cakes},%
  {chocolates}{Exploding Chocolates},%
  {raygun}{Ray Gun}%
}

Various list-iteration commands were discussed in §2.7.2 Iterating Over a Comma-Separated List, but in the examples from that section all of the lists had an element that could be used as a single argument to a command such as \do. However in this list each element needs to be treated as two arguments. For example, the command to reset the check boxes should iterate through this list but only grab the first group (the label) of each element.

Consider first:

\@for\this@element:=\@projectlist\do{%
  \reset@project\this@element 
}

This won't work because it's equivalent to doing

\reset@project{{cookies}{Mind-Controlling Cookies}}
and so on. I could try using \expandafter described in §2.7.2 Iterating Over a Comma-Separated List:

\@for\this@element:=\@projectlist\do{%
  \expandafter\reset@project\this@element 
}

This is an improvement as this is now equivalent to doing

\reset@project{cookies}{Mind-Controlling Cookies}
and so on. Now \reset@project picks up the label correctly, but the text after the label is left dangling and needs to be discarded. There are various ways to deal with this. The simplest solution is just to make \reset@project take two arguments and ignore the second argument:

\newcommand*{\reset@project}[2]{%
  \csdef{project@#1}{\form@unchecked}%
}

A more generic approach is to leave \reset@project with just one argument as before and use the LaTeX kernel command

\@firstoftwo{first}{second}

which does ⟨first⟩ and discards ⟨second⟩. This requires \expandafter to expand \this@element before applying \@firstoftwo:

\@for\this@element:=\@projectlist\do{%
  \reset@project{\expandafter\@firstoftwo\this@element}%
}

A similar method can be used to display the check boxes and their associated text within the form. There is an analogous LaTeX kernel command that grabs the second argument and discards the first:

\@secondoftwo{first}{second}

Here's a simple example that just displays the check boxes with their associated text without any tabulation:

\@for\this@element:=\@projectlist\do{%
  \use@project{\expandafter\@firstoftwo\this@element}% check box
  \space
  \expandafter\@secondoftwo\this@element
  \qquad
}

Or using the layout command \form@layout@checkbox defined earlier:

\@for\this@element:=\@projectlist\do{%
  \form@layout@checkbox
    {\use@project{\expandafter\@firstoftwo\this@element}}% check box
    {\expandafter\@secondoftwo\this@element}% text
  \qquad
}

This can be converted into a tabular environment but we need a way to track which column we're in. One way to do this is to define a register (recall §2.1.3 Arithmetic).

% initialise
\newcount\form@columncount
\form@columncount=1\relax
\def\form@precolumn{}%
% layout check boxes and text:
\begin{tabular}{ll}
\@for\this@element:=\@projectlist\do{%
  \global\let\this@element\this@element
  \form@precolumn
  \form@layout@checkbox
    {\use@project{\expandafter\@firstoftwo\this@element}}%
    {\expandafter\@secondoftwo\this@element}%
  \global\advance\form@columncount by 1\relax
  \ifnum\form@columncount>2\relax
    \global\form@columncount=1\relax
    \gdef\form@precolumn{\\}%
  \else
    \gdef\form@precolumn{&}%
  \fi
}%
\end{tabular}%

(\global is required because of the local scoping effect of tabular cells.) This uses a similar method to those discussed in §2.7.5 Iteration Tips and Tricks.

If you are likely to have more than one group of check boxes, then it makes more sense to create generic commands. First, we need generic versions of the above \reset@project, \set@project and \use@project where the first argument is the element label (such as cakes) and the second argument is the block label (such as project):

\newcommand*{\reset@element}[2]{%
  \csdef{#2@#1}{\form@unchecked}%
}

\newcommand*{\set@element}[2]{%
  \ifcsdef{#2@#1}%
  {\csdef{#2@#1}{\form@checked}}%
  {%
    \ClassError{simple-form}{Unknown #2 `#1'}{}%
  }%
}

\newcommand*{\use@element}[2]{%
  \ifcsdef{#2@#1}{\csuse{#2@#1}}{\form@unchecked}%
}

So now instead of

\reset@project{label}

I need to use

\reset@element{label}{project}

and so on. It's also convenient to provide a command that can iterate over the {label}{text} list (such as \@projectlist) for the block:

\newcommand*{\for@block}[3]{%
  \ifcsdef{@#2list}%
  {%
    \expandafter\@for\expandafter
      #1\expandafter:\expandafter=\csname @#2list\endcsname\do{#3}%
  }%
  {%
    \ClassError{simple-form}{Unknown block `#2'}{}%
  }%
}

(The \expandafters are required because the list control sequence provided by \csname @#2list\endcsname needs to be expanded to the actual control sequence \@⟨block-label⟩list, for example \@projectlist, before \@for tries to iterate over it.) This has the syntax:

\for@block{cs}{block-label}{body}

where ⟨cs⟩ is assigned to the {label}{text} element for the current iteration.

All elements within a block can be reset using \reset@block, which is defined as:

\newcommand*{\reset@block}[1]{%
  \for@block{\this@element}{#1}%
  {%
    \reset@element{\expandafter\@firstoftwo\this@element}{#1}%
  }%
}

This means that \project can now be defined as

\newcommand*{\project}[1]{%
  \reset@block{project}%
  \set@element{#1}{project}%
}

The generic two-column tabulated block of elements used by \makeform can be defined as follows:

\newcount\form@columncount

\newcommand*{\form@block}[1]{%
  \def\form@precolumn{}%
  \form@columncount=1\relax
  \begin{tabular}{ll}
  \for@block{\this@element}{#1}%
  {%
    \global\let\this@element\this@element
    \form@precolumn
    \form@layout@checkbox
      {\use@element{\expandafter\@firstoftwo\this@element}{#1}}%
      {\expandafter\@secondoftwo\this@element}%
    \global\advance\form@columncount by 1\relax
    \ifnum\form@columncount>2\relax
      \global\form@columncount=1\relax
      \gdef\form@precolumn{\\}%
    \else
      \gdef\form@precolumn{&}%
    \fi
  }%
  \end{tabular}%
}

This custom command has the syntax:

\form@block{block-label}

So for the project example, this would just require

\form@block{project}

This is hard-coded for two columns, but it would be more flexible to allow an arbitrary number of columns. For example if the command had the syntax

\form@block{block-label}{columns}

then the project block could be generated using

\form@block{project}{2}

In this case, the hard-coded conditional in \form@block

\ifnum\form@columncount>2\relax

can now have the total column count replaced with #2:

\ifnum\form@columncount>#2\relax

However the column specifier argument for the tabular environment is a little more complicated as it now requires #2 lots of l (or whatever alignment specifier you want).

Recall TeX's \loop command from §2.7.4 General Iteration with TeX's \loop and the hook management commands from §2.1.2 Hook Management. These can be used to generate the argument for the tabular environment:

% initialise
\def\form@columnargs{}%
\form@columncount=0\relax
% iterate #2 times
\loop
 \appto\form@columnargs{l}%
 \advance\form@columncount by 1\relax
\ifnum\form@columncount<#2
\repeat

This will store the column specifiers in \form@columnargs which can now be used in the tabular environment argument:

\begin{tabular}{\form@columnargs}

Therefore the new two-argument version of \form@block can be defined as:

\newcount\form@columncount

\newcommand*{\form@block}[2]{%
  \def\form@columnargs{}%
  \form@columncount=0\relax
  \loop
   \appto\form@columnargs{l}%
   \advance\form@columncount by 1\relax
  \ifnum\form@columncount<#2
  \repeat
  \def\form@precolumn{}%
  \form@columncount=1\relax
  \begin{tabular}{\form@columnargs}
  \for@block\this@element{#1}%
  {%
    \global\let\this@element\this@element
    \form@precolumn
    \form@layout@checkbox
      {\use@element{\expandafter\@firstoftwo\this@element}{#1}}%
      {\expandafter\@secondoftwo\this@element}%
    \global\advance\form@columncount by 1\relax
    \ifnum\form@columncount>#2\relax
      \global\form@columncount=1\relax
      \gdef\form@precolumn{\\}%
    \else
      \gdef\form@precolumn{&}%
    \fi
  }%
  \end{tabular}%
}

The form check box elements are now much simpler to define:

\newcommand*{\@genderlist}{{male}{Male},{female}{Female}}

\newcommand*{\gender}[1]{%
  \reset@block{gender}%
  \set@element{#1}{gender}%
}

\newcommand*{\@projectlist}{%
  {cookies}{Mind-Controlling Cookies},%
  {cakes}{Telepathic Cakes},%
  {chocolates}{Exploding Chocolates},%
  {raygun}{Ray Gun}%
}

\newcommand*{\project}[1]{%
  \reset@block{project}%
  \set@element{#1}{project}%
}

If multiple selections are permitted, then the \reset@block command needs to be moved outside the user command definition to initialise all the elements. For example, if multiple projects may be selected:

\reset@block{project}

\newcommand*{\project}[1]{%
  \set@element{#1}{project}%
}

Exercise 30. Simple Form Class with Check Boxes

Adapt the class file simple-form.cls from Example 56 so that the form shown in Figure 11.2 can be created with the following document:

\documentclass{simple-form}

\name{Mabel Canary}
\date{2014-10-13}

\gender{female}
\project{cakes}
\icecream{vanilla}
\icecream{fudge}
\icecream{other}

\begin{document}

\makeform

\end{document}

For the More Adventurous

Add a fill-in area for the “Other” ice-cream option so that instead of the user writing:

\icecream{other}

they can use a new command:

\othericecream{Neapolitan}

which both checks the “Other” box and fills in the area, as shown in Figure 11.3. You can download or view a solution to this exercise.

Figure 11.2: A Simple Form with Multiple Check Box Areas
 

Name: Mabel Canary Date: 2014-10-13

☐ Male ☒ Female

Which project would you like to enrol on? (Tick one box.)

☐ Mind-Controlling Cookies☒ Telepathic Cakes
☐ Exploding Chocolates☐ Ray Gun

Which ice-cream flavours do you like? (Tick all that apply.)

☒ Vanilla☐ Mint☐ Toffee
☒ Fudge☐ Guaraná☐ Strawberry
☐ Raspberry Ripple☐ Chilli☒ Other
End of Image.

Figure 11.3: A Simple Form with Multiple Check Box Areas and a Fill-In Other Area
 

Name: Mabel Canary Date: 2014-10-13

☐ Male ☒ Female

Which project would you like to enrol on? (Tick one box.)

☐ Mind-Controlling Cookies☒ Telepathic Cakes
☐ Exploding Chocolates☐ Ray Gun

Which ice-cream flavours do you like? (Tick all that apply.)

☒ Vanilla☐ Mint☐ Toffee
☒ Fudge☐ Guaraná☐ Strawberry
☐ Raspberry Ripple☐ Chilli☒ Other: Neapolitan
End of Image.

If you have a large text area that needs to be filled in, you may prefer to use an environment to collect the information. For example, instead of creating a command to specify, say, a project description:

\projectdescription{Several paragraphs of text}

which can be defined using

\newcommand{\@projectdescription}{}
\newcommand{\projectdescription}[1]{%
  \renewcommand{\@projectdescription}{#1}%
}

you may prefer to have the user interface:

\begin{ProjectDescription}Several paragraphs of text\end{ProjectDescription}

This is more complicated to define, as you can't simply gather the contents of an environment when you use \newenvironment. There are a number of ways to achieve this.

If the environment contents are being gathered so that they can then be stored in a command definition, then the limitations applied to command definitions also apply to the environment contents. [Why doesn't verbatim work within…?] This includes the usual problems with verbatim code in a command argument.

The collect package [77] provides the collectinmacro environment:

\begin{collectinmacro}{macro}{before}{after}
body
\end{collectinmacro}

This defines the command ⟨macro⟩ to be ⟨before⟩⟨body⟩⟨after⟩. For example,

\begin{collectinmacro}{\mycommand}{Before. }{ After.}
Some text here.
\end{collectinmacro}

This is equivalent to:

\newcommand{\mycommand}{Before. Some text here. After.}

If you want to define an environment that uses this method, you can't use the environment form \begin{collectinmacro} and \end{collectinmacro}, but must instead use the commands \collectinmacro and \endcollectinmacro.

Example:

\newcommand{\@projectdescription}{}
\newenvironment{ProjectDescription}%
{\collectinmacro{@projectdescription}{}{}}%
{\endcollectinmacro}

Another possibility is to use the amsmath package's

\collect@bodycs

command. This gathers the contents of the current environment ⟨body⟩ and applies ⟨cs{body}.

Example:

\newcommand{\@projectdescription}{}
\newcommand{\projectdescription}[1]{%
  \gdef\@projectdescription{#1}%
}
\newenvironment{ProjectDescription}%
{\collect@body\projectdescription}%
{}

This now means that the user can do either

\begin{ProjectDescription}
This will be an interesting project.
\end{ProjectDescription}

or

\projectdescription{This will be an interesting project.}

Note that I had to use \gdef instead of \renewcommand otherwise the change will be scoped by the encasing environment.

The \collect@body command uses short internal commands to gather the environment contents, which means that the environment can't contain paragraph breaks. If you want to allow paragraph breaks, you can use an analogous command provided by the environ package [75]:

\Collect@Bodycs

Note that the unstarred version of \newcommand allows a paragraph break to be present within #1 so \projectdescription can be used by \Collect@Body in the following:

\newcommand{\@projectdescription}{}
\newcommand{\projectdescription}[1]{%
  \gdef\@projectdescription{#1}%
}
\newenvironment{ProjectDescription}%
{\Collect@Body\projectdescription}%
{}

Example 57. A Simple Form Class (Gathering Environment Contents)

This example uses the \Collect@Body command from the environ package to allow the user to enter multi-paragraph data in a form. First the class file, sample-form.cls:

\NeedsTeXFormat{LaTeX2e}
\ProvidesClass{sample-form}[2014/11/03]

\DeclareOption*{\PassOptionsToClass{\CurrentOption}{article}}

\ProcessOptions

\LoadClass[a4paper,12pt]{article}

\RequirePackage{environ}

\newcommand*{\form@fillin}[2]{%
  \makebox[#1][l]{\rlap{#2}\hrulefill}%
}

\newcommand*{\form@layout@fillin}[3]{#3: \form@fillin{#1}{#2}}

\newcommand*\@name{}
\newcommand*{\name}[1]{%
  \renewcommand*{\@name}{#1}%
}

\newcommand*\@projectdescription{}
\newcommand{\projectdescription}[1]{%
  \long\gdef\@projectdescription{#1}%
}

\newenvironment{ProjectDescription}{\Collect@Body\projectdescription}{}

\newcommand{\makeform}{%
  \section{Applicant Details}
  \form@layout@fillin{8em}{\@name}{Name}
  \section{Project Description}
  \@projectdescription
}

\endinput

Here's an example document:

\documentclass{sample-form}

\name{Mabel Canary}

\begin{ProjectDescription}
This project will be very interesting.

This is another paragraph.
\end{ProjectDescription}

\begin{document}
\makeform
\end{document}

The result is shown in Figure 11.4. You can download or view this example document.

Figure 11.4: A Simple Form with a Text Area.
 

1 Applicant Details

Name: Mabel Canary

2 Project Description

This project will be very interesting.

This is another paragraph.

End of Image.


This book is also available as A4 PDF or 12.8cm x 9.6cm PDF or paperback (ISBN 978-1-909440-07-4).

© 2015 Dickimaw Books. "Dickimaw", "Dickimaw Books" and the Dickimaw parrot logo are trademarks. The Dickimaw parrot was painted by Magdalene Pritchett.

Terms of Use Privacy Policy Cookies Site Map FAQs