Printing Envelopes and Labels in L^{A}TEX 2ε: EnvLab Package ^{∗}^{†}

Boris Veytsman

1996/07/15

Contents

1 Introduction 2

2 Identification 2

3 Preliminary code 3 3.1 Switches, etc. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3 3.2 Lengths and numbers . . . . . . . . . . . . . . . . . . . . . . . . . 4 3.3 Main setting commands . . . . . . . . . . . . . . . . . . . . . . . . 4

4 Defining options 5 4.1 Envelope Sizes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5 4.2 Labels sizes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5 4.3 Optional switches . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6 4.4 Unknown options . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6 4.5 Default options . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6

5 Configuration file 6

6 Processing options and loading packages 7

7 Document layout 7 7.1 Printer specific commands . . . . . . . . . . . . . . . . . . . . . . . 7 7.2 Some useful counters for labels . . . . . . . . . . . . . . . . . . . . 8 7.3 Envelope Placement . . . . . . . . . . . . . . . . . . . . . . . . . . 9 7.4 Fonts . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9 7.5 Return address . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10 7.6 Margins, page styles, etc. . . . . . . . . . . . . . . . . . . . . . . . 10 7.7 Printing of the addresses . . . . . . . . . . . . . . . . . . . . . . . . 11 7.8 Label setup . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11 7.9 Envelope setup . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12 8 Printing of envelopes and labels 12

∗This file has version number v1.1, last revised 1996/07/15.

†\begin{matrix}c \\ ◯ \end{matrix}Boris Veytsman, 1996

8.1 Main Command . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12 8.2 Printing of one envelope . . . . . . . . . . . . . . . . . . . . . . . . 12 8.3 Printing of one label . . . . . . . . . . . . . . . . . . . . . . . . . . 12 8.4 Printing of return labels . . . . . . . . . . . . . . . . . . . . . . . . 13

9 Barcodes 13 9.1 Main command . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13 9.2 Extraction of barcodes . . . . . . . . . . . . . . . . . . . . . . . . . 14 9.3 Printing barcodes . . . . . . . . . . . . . . . . . . . . . . . . . . . . 16

10 Capitalization 17

References 20

1 Introduction

The standard \makelabels command in the L^{A}TEX 2ε letter.cls documentclass typesets labels on Avery 5352 sheets. A typical user may want more. EnvLab redefines \makelabels in^{1} a more useful and customizable way

The detailed usage of the package is described in the file elguide.tex. Here we just comment the macros.

2 Identification

First, we must say “Hello world.”

1 ⟨∗package⟩

 2 \NeedsTeXFormat{LaTeX2e}
 3 \ProvidesPackage{envlab}[1996/07/15 v1.1 Envelopes and Labels]

4 ⟨/package⟩

\envlab@ok \envlab@oops Now let us check whether we in the letter documentclass. Actually we will accept any class that has \makelabels defined (custom letter classes, etc.)

5 ⟨∗package⟩

 6 \def\envlab@oops{%
 7 \PackageError{envlab}%
 8 {Envlab is used outside of \MessageBreak%
 9 a letter-compatible documentclass}%
10 {You are trying to use Envelopes & Labels\MessageBreak%
11 package, but your documentclass does not\MessageBreak%
12 understand address formatting commands.\MessageBreak%
13 Try standard document class letter\MessageBreak}}
14 \def\envlab@ok{%
15 \PackageInfo{envlab}%
16 {Envelopes & Labels package: found makelabels...\MessageBreak%
17 Seems everything is OK. Good luck.}}
18 \expandafter\ifx\csname makelabels\endcsname \relax %
19 \envlab@oops\else\envlab@ok\fi

20 ⟨/package⟩

1hopefully

3 Preliminary code

3.1 Switches, etc.

\if@envelope First, we must say whether we want to print out envelopes (default) or labels. The differences are:

• Envelopes are printed on separate pages, labels are printed several per page

• Envelopes might have return address, labels might not

• Envelopes are rotated by default, labels are never rotated

21 ⟨∗package⟩

22 \newif\if@envelope
23 \@envelopetrue

24 ⟨/package⟩

\if@rotateenvelopes Now we must determine whether we want to rotate envelopes and whether to \if@printreturnaddress include return address (both yes by default).

25 ⟨∗package⟩

26 \newif\if@rotateenvelopes
27 \@rotateenvelopestrue
28 \newif\if@printreturnaddress
29 \@printreturnaddresstrue

30 ⟨/package⟩

\@envelopeposition Now let us decide how to print envelopes. They can be either centered (default) or shifted to the left or to the right of the paper tray. The counter \@envelopeposition can be, correspondingly, either 0 or 1 or 2. The value of 3 corresponds to the “custom placing”, when the user sets \EnvelopeLeftMargin manually.

31 ⟨∗package⟩

32 \newcount\@envelopeposition \@envelopeposition=0\relax

33 ⟨/package⟩

\if@pswait The switch \if@pswait controls optional manual feeding of envelopes and labels in Postscript printers (see Section 7.1 for details).

34 ⟨∗package⟩

35 \newif\if@pswait
36 \@pswaitfalse

37 ⟨/package⟩

\if@barcodes We can either print bar codes (default) or not. The second switch forces to \if@alwaysbarcodes print barcodes even if they are not last in the address (like Pa 16801\\USA)

38 ⟨∗package⟩

39 \newif\if@barcodes
40 \newif\if@alwaysbarcodes
41 \@barcodestrue
42 \@alwaysbarcodesfalse

43 ⟨/package⟩

\if@capitalizeaddress Also, we can either capitalize the address (default) or not.

44 ⟨∗package⟩

45 \newif\if@capitalizeaddress
46 \@capitalizeaddresstrue

47 ⟨/package⟩

3.2 Lengths and numbers

We want all lengths to be user settable, so no @ in the names.

\EnvelopeWidth An envelope has four basic lengths. The first two are self-evident. The third is

\EnvelopeHeight
\EnvelopeTopMargin
\EnvelopeLeftMargin

the distance between the edge of the paper and the leading edge of the envelope. All pre-defined envelope sizes set this to zero. The fourth is the distance between the left edge of the paper and the envelope. Its value depends on the value of the \@envelopeposition variable. We will preset it to zero

48 ⟨∗package⟩

49 \newlength{\EnvelopeWidth}
50 \newlength{\EnvelopeHeight}
51 \newlength{\EnvelopeTopMargin}
52 \newlength{\EnvelopeLeftMargin}
53 \setlength{\EnvelopeLeftMargin}{0pt}

54 ⟨/package⟩

\LabelWidth A label has more parameters. The first two are the same as for the envelopes.

\LabelHeight
\LabelTopMargin
\LabelLeftMargin
\LabelRightMargin

The next two define the distances from the paper edges to the beginning of the labels. The last one describes the distance between the labels.

55 ⟨∗package⟩

56 \newlength{\LabelWidth}
57 \newlength{\LabelHeight}
58 \newlength{\LabelTopMargin}
59 \newlength{\LabelLeftMargin}
60 \newlength{\LabelRightMargin}

61 ⟨/package⟩

LabelMaxCol The following numbers define, how many labels are in each row and how many LabelMaxRow rows are on each page

62 ⟨∗package⟩

63 \newcounter{LabelMaxCol}
64 \newcounter{LabelMaxRow}

65 ⟨/package⟩

The lengths above are external parameters that determine an envelope or a label. Now we will describe internal lengths. We define them here because the commands \SetEnvelope and \SetLabel determine them basing on the given envelope or label type.

\FromAddressTopMargin The following lengths are self-evident.

\FromAddressLeftMargin
\FromAddressHeight
\FromAddressWidth
\ToAddressTopMargin
\ToAddressLeftMargin
\ToAddressWidth

66 ⟨∗package⟩

67 \newlength{\FromAddressTopMargin}
68 \newlength{\FromAddressLeftMargin}
69 \newlength{\FromAddressHeight}
70 \newlength{\FromAddressWidth}
71 \newlength{\ToAddressTopMargin}
72 \newlength{\ToAddressLeftMargin}
73 \newlength{\ToAddressWidth}

74 ⟨/package⟩

3.3 Main setting commands

OK, we are ready to set up envelopes and labels.

\SetEnvelope The command \SetEnvelope has three parameters: optional top Margin, width and height of the envelope.

75 ⟨∗package⟩

76 \DeclareRobustCommand{\SetEnvelope}[3][0pt]{%
77 \@envelopetrue%
78 \setlength{\EnvelopeTopMargin}{#1}%
79 \setlength{\EnvelopeWidth}{#2}%
80 \setlength{\EnvelopeHeight}{#3}%
81 \setlength{\FromAddressTopMargin}{0.5in}%
82 \setlength{\FromAddressLeftMargin}{0.5in}%
83 \setlength{\FromAddressHeight}{0.33\EnvelopeHeight}%
84 \setlength{\FromAddressWidth}{0.5\EnvelopeWidth}%
85 \setlength{\ToAddressTopMargin}{0.5in}%
86 \setlength{\ToAddressLeftMargin}{0.5in}%
87 \setlength{\ToAddressWidth}{3in}}

88 ⟨/package⟩

\SetLabel The command \SetLabel has seven parameters: five lengths and two numbers. All are mandatory:

89 ⟨∗package⟩

90 \DeclareRobustCommand{\SetLabel}[7]{%
91 \@envelopefalse%
92 \setlength{\LabelWidth}{#1}%
93 \setlength{\LabelHeight}{#2}%
94 \setlength{\LabelTopMargin}{#3}%
95 \setlength{\LabelLeftMargin}{#4}%
96 \setlength{\LabelRightMargin}{#5}%
97 \setcounter{LabelMaxCol}{#6}%
98 \setcounter{LabelMaxRow}{#7}%
99 \setlength{\ToAddressTopMargin}{0.1in}%
100 \setlength{\ToAddressLeftMargin}{0.2in}%
101 \setlength{\ToAddressWidth}{\LabelWidth}%
102 \addtolength{\ToAddressWidth}{-\ToAddressLeftMargin}%
103 \addtolength{\ToAddressWidth}{-\LabelRightMargin}}

104 ⟨/package⟩

4 Defining options

4.1 Envelope Sizes

105 ⟨∗package⟩

106 \DeclareOption{businessenvelope}{\SetEnvelope{9.5in}{4.125in}}
107 \DeclareOption{executiveenvelope}{\SetEnvelope{7.5in}{3.875in}}
108 \DeclareOption{bookletenvelope}{\SetEnvelope{10.5in}{7.5in}}
109 \DeclareOption{personalenvelope}{\SetEnvelope{6.5in}{3.625in}}
110 \DeclareOption{c6envelope}{\SetEnvelope{162mm}{114mm}}
111 \DeclareOption{c65envelope}{\SetEnvelope{224mm}{114mm}}
112 \DeclareOption{c5envelope}{\SetEnvelope{229mm}{162mm}}

113 ⟨/package⟩

4.2 Labels sizes

114 ⟨∗package⟩

115 \DeclareOption{avery5160label}{%
116 \SetLabel{2.75in}{1in}{0.5in}{0.19in}{0.12in}{3}{10}}

117 \DeclareOption{avery5161label}{%
118 \SetLabel{4.19in}{1in}{0.5in}{0.16in}{0.19in}{2}{10}}
119 \DeclareOption{avery5162label}{%
120 \SetLabel{4.19in}{1.33in}{0.83in}{0.16in}{0.19in}{2}{7}}
121 \DeclareOption{avery5163label}{%
122 \SetLabel{4.19in}{2in}{0.5in}{0.16in}{0.19in}{2}{5}}
123 \DeclareOption{avery5164label}{%
124 \SetLabel{4.19in}{3.33in}{0.5in}{0.16in}{0.19in}{2}{3}}

125 ⟨/package⟩

4.3 Optional switches

All this should be evident…

126 ⟨∗package⟩

127 \DeclareOption{rotateenvelopes}{\@rotateenvelopestrue}
128 \DeclareOption{norotateenvelopes}{\@rotateenvelopesfalse}
129 \DeclareOption{centerenvelopes}{\@envelopeposition=0\relax}
130 \DeclareOption{leftenvelopes}{\@envelopeposition=1\relax}
131 \DeclareOption{rightenvelopes}{\@envelopeposition=2\relax}
132 \DeclareOption{customenvelopes}{\@envelopeposition=3\relax}
133 \DeclareOption{printbarcodes}{\@barcodestrue}
134 \DeclareOption{noprintbarcodes}{\@barcodesfalse\@alwaysbarcodesfalse}
135 \DeclareOption{alwaysbarcodes}{\@alwaysbarcodestrue\@barcodestrue}
136 \DeclareOption{noalwaysbarcodes}{\@alwaysbarcodesfalse}
137 \DeclareOption{capaddress}{\@capitalizeaddresstrue}
138 \DeclareOption{nocapaddress}{\@capitalizeaddressfalse}
139 \DeclareOption{printreturnaddress}{\@printreturnaddresstrue}
140 \DeclareOption{noprintreturnaddress}{\@printreturnaddressfalse}
141 \DeclareOption{pswait}{\@pswaittrue}
142 \DeclareOption{nopswait}{\@pswaitfalse}

143 ⟨/package⟩

4.4 Unknown options

All options we did not declare above are probably the options for the graphics package; let us send them there.

144 ⟨∗package⟩

145 \DeclareOption*{\PassOptionsToPackage{\CurrentOption}{graphics}}

146 ⟨/package⟩

4.5 Default options

147 ⟨∗package⟩

148 \ExecuteOptions{businessenvelope,rotateenvelopes,centerenvelopes}
149 \ExecuteOptions{printbarcodes,capaddress}
150 \ExecuteOptions{nopswait,printreturnaddress}

151 ⟨/package⟩

5 Configuration file

At this point we will look for the configuration file. This file is named envlab.cfg. The options declared in this file will supersede the ones declared in Section 4.5, but will be in their turn be superseded by the options explicitly defined when the package is loaded.

152 ⟨∗package⟩

153 \InputIfFileExists{envlab.cfg}{%
154 \typeout{Loading configuration file envlab.cfg}}{%
155 \typeout{Configuration file envlab.cfg is not found}}

156 ⟨/package⟩

Now let us discuss the structure of the configuration file. We want it to contain default options, and therefore to be loaded before the \ProcessOptions command. On the other hand we want it to contain some definitions that supersede the ones in this package. The hook \AtEndOfPackage helps to do this: we will just delay all definitions until later.

OK, let’s construct an example of envlab.cfg file. The commands here essentially repeat Section 4.5, but we want to be foolproof…

157 ⟨∗cfg⟩ 158 %%

159 %% The default options go here

160 %%

161 \ExecuteOptions{businessenvelope,rotateenvelopes,centerenvelopes}
162 \ExecuteOptions{printbarcodes,capaddress}
163 \ExecuteOptions{nopswait,printreturnaddress}

164 %%

165 \AtEndOfPackage{\relax % Customization goes here

166 } 167 ⟨/cfg⟩

6 Processing options and loading packages

168 ⟨∗package⟩

169 \ProcessOptions
170 \IfFileExists{graphics.sty}{%
171 \RequirePackage{graphics}}{%
172 \PackageWarning{envlab}{%
173  You don't have the graphics package!\MessageBreak
174  Probably you will not be able to print\MessageBreak
175  envelopes sidewise. \MessageBreak}}

176 ⟨/package⟩

7 Document layout

7.1 Printer specific commands

\@beginlabelshook The command \@beginlabelshook is called at the beginning of the printing of envelopes and labels. We define it to be a no-op, but it is possible to introduce some printer-specific commands (like paper change).

177 ⟨∗package⟩

178 \def\@beginlabelshook{\relax}

179 ⟨/package⟩

\@beginlabelpagehook  The command \@beginlabelpagehook is like \@beginlabelshook, but is

called at the beginning of each page of envelopes and labels.

180 ⟨∗package⟩

181 \def\@beginlabelpagehook{\relax}

182 ⟨/package⟩

\AtBeginLabels \AtBeginLabelPage The hooks \AtBeginLabels and \AtBeginLabelPage redefine \@beginlabelshook

and \@beginlabelpagehook. They are built like the standard L^{A}TEX 2ε hooks \AtBeginDocument, \AtBeginDvi, etc. In the implementation we use the internal L^{A}TEX 2ε command \g@addto@macro. In the current (June 1996) L^{A}TEX 2ε release

\g@addto@macro it is defined as:
\long\def\g@addto@macro#1#2{{%
\toks@\expandafter{#1#2}%
\xdef#1{\the\toks@}}}

We quote this definition in case They change Their minds…

183 ⟨∗package⟩

184 \def\AtBeginLabels{\g@addto@macro\@beginlabelshook}
185 \def\AtBeginLabelPage{\g@addto@macro\@beginlabelpagehook}

186 ⟨/package⟩

\PSwait PostScript printers can be switched to the manual feeding mode with the following code by William Slough <cfwas@eiu.edu>.

187 ⟨∗package⟩

188 \def\PSwait{\special{ps: clear grestore @manualfeed 0 0 bop}}

189 ⟨/package⟩

The author explains his code in the following way:

Here is a possible explanation:

The clear removes operands from the PostScript stack, which has the effect of reversing some actions from the *previous* bop. Unfortunately, it reverses other important actions too (such as font size), but the grestore seems to get these back. Then the desired @manualfeed, followed by the “bop” for the beginning of page. The pair of 0’s are used for bop and are completely bogus. However, from what I could detect, 0’s are as good as anything. The values that DVIPS provides for bop seem to be related to DVI page numbers.

I make no guarantee about the reliability of this solution, but initial tests indicate it will work for my environment.

The option pswait puts the \PSwait code in the beginning of each page.

190 ⟨∗package⟩

191 \if@pswait
192 \AtBeginLabels{\PSwait}%
193 \fi

194 ⟨/package⟩

7.2 Some useful counters for labels

LabelCountCol These counters store the position of the currently printed label:

LabelCountRow

195 ⟨∗package⟩

196 \newcounter{LabelCountCol}
197 \newcounter{LabelCountRow}

198 ⟨/package⟩

LabelOffsetCol And these counters provide the offset for the label printed on a partially used

LabelOffsetRow sheet:

199 ⟨∗package⟩

200 \newcounter{LabelOffsetCol}
201 \newcounter{LabelOffsetRow}
202 \setcounter{LabelOffsetCol}{1}
203 \setcounter{LabelOffsetRow}{1}

204 ⟨/package⟩

\FirstLabel The command \FirstLabel{⟨Row⟩}{⟨Col⟩} sets the counters LabelOffsetRow
 and LabelOffsetCol.

205 ⟨∗package⟩

206 \DeclareRobustCommand{\FirstLabel}[2]{%
207 \setcounter{LabelOffsetRow}{#1}%
208 \setcounter{LabelOffsetCol}{#2}}

209 ⟨/package⟩

7.3 Envelope Placement

 Now we can calculate \EnvelopeLeftMargin

210 ⟨∗package⟩

211 \ifcase\the\@envelopeposition
212 \setlength{\EnvelopeLeftMargin}{\paperwidth}
213 \if@rotateenvelopes
214  \addtolength{\EnvelopeLeftMargin}{-\EnvelopeHeight}
215 \else
216  \addtolength{\EnvelopeLeftMargin}{-\EnvelopeWidth}
217 \fi
218 \setlength{\EnvelopeLeftMargin}{0.5\EnvelopeLeftMargin}
219 \or
220 \setlength{\EnvelopeLeftMargin}{0pt}
221 \or
222 \setlength{\EnvelopeLeftMargin}{\paperwidth}
223 \if@rotateenvelopes
224  \addtolength{\EnvelopeLeftMargin}{-\EnvelopeHeight}
225 \else
226  \addtolength{\EnvelopeLeftMargin}{-\EnvelopeWidth}
227 \fi
228 \else
229 \relax
230 \fi

231 ⟨/package⟩

7.4 Fonts

\@toaddressfont \@fromaddressfont We want the address to be printed in 12pt sans serif font. The return address will be printed in 10pt normal font.

232 ⟨∗package⟩

233 \def\@toaddressfont{%
234 \ifcase\@ptsize \large\or\normalsize\or\small\fi%
235 \sffamily\selectfont}
236 \def\@fromaddressfont{%

237 \ifcase\@ptsize \normalsize\or\small\or\footnotesize\fi%
238 \normalfont}

239 ⟨/package⟩

7.5 Return address

\returnaddress The standard letter class defines \returnaddress to be null. This is sensible if we are printing labels, but not so good if we are printing envelopes. Therefore let us redefine it:

240 ⟨∗package⟩

241 \def\returnaddress{\if@envelope \fromaddress\else\relax\fi}

242 ⟨/package⟩

7.6 Margins, page styles, etc.

\startlabels The command \startlabels is the internal command that prepares the paper for labels or envelopes, resets the internal counters and calls \@beginlabelshook.

243 ⟨∗package⟩

244 \def\startlabels{%
245 \pagestyle{empty}
246 \setlength{\topmargin}{-1.0in}
247 \if@envelope
248  \addtolength{\topmargin}{\EnvelopeTopMargin}
249  \else \addtolength{\topmargin}{\LabelTopMargin}
250 \fi
251 \setlength{\headheight}{0pt}
252 \setlength{\headsep}{0pt}
253 \setlength{\footskip}{0pt}
254 \setlength{\textheight}{200in}
255 \setlength\paperheight{\textheight}
256 \global\vsize=200in\relax
257 \addtolength{\textheight}{-\topmargin}
258 \addtolength{\textheight}{-1.0in}
259 \setlength{\oddsidemargin}{-1.0in}
260 \setlength{\evensidemargin}{\oddsidemargin}
261 \setlength{\textwidth}{20in}
262 \hsize=20in
263 \baselineskip=0pt
264 \lineskip=0pt
265 \parindent=0pt
266 \if@envelope \relax \else
267  \setcounter{LabelCountCol}{\theLabelOffsetCol}
268  \setcounter{LabelCountRow}{\theLabelOffsetRow}
269  \ifnum\theLabelOffsetRow>1
270  \null
271  \loop \vspace*{\LabelHeight}
272  \addtocounter{LabelOffsetRow}{-1} \ifnum\theLabelOffsetRow>1
273  \repeat
274  \fi
275  \ifnum\theLabelOffsetCol>1
276  \loop \hspace*{\LabelWidth}\nolinebreak
277  \addtocounter{LabelOffsetCol}{-1} \ifnum\theLabelOffsetCol>1
278  \repeat

279  \fi
280  \nopagebreak
281 \fi
282 \spaceskip 0pt\relax
283 \xspaceskip 0pt\relax
284 \clubpenalty=0
285 \widowpenalty=0
286 \raggedbottom
287 \sloppy
288 \setlength\hfuzz{5in}
289 \setlength\vfuzz{5in}
290 \ignorespaces
291 \@beginlabelshook
292 \@beginlabelpagehook
293 \nopagebreak}

294 ⟨/package⟩

7.7 Printing of the addresses

\PrintReturnAddress This macro uses the text as an argument and prints it according to the conventions.

295 ⟨∗package⟩

296 \newcommand{\PrintReturnAddress}[1]{%
297 \vspace*{\FromAddressTopMargin}
298 \null\hspace{\FromAddressLeftMargin}
299 \parbox[t][\FromAddressHeight]{\FromAddressWidth}%
300  {\@fromaddressfont \lineskip=1pt
301  \if@printreturnaddress #1\else\relax\fi}}

302 ⟨/package⟩

\PrintAddress This macro works like \PrintReturnAddress, with several important differences: it prints barcodes if necessary and capitalizes the address.

303 ⟨∗package⟩

304 \newcommand{\PrintAddress}[1]{%
305 \vspace*{\ToAddressTopMargin}
306 \leavevmode
307 \null\hspace*{\ToAddressLeftMargin}
308 \parbox[t]{\ToAddressWidth}{%
309  \lineskip=1pt
310  \if@barcodes \PrintBarCode{#1} \fi
311  \@toaddressfont
312  \if@capitalizeaddress \@make@capitalize{#1} \else #1 \fi}}

313 ⟨/package⟩

7.8 Label setup

\PrintLabel This macro prints a label in a parbox

314 ⟨∗package⟩

315 \newcommand{\PrintLabel}[1]{%
316 \parbox[t][\LabelHeight]{\LabelWidth}{%
317  \PrintAddress{#1}}}

318 ⟨/package⟩

7.9 Envelope setup

Labels include one box per label, so their setup is simple. The situation with envelopes is different: they contain several boxes, and could be rotated, centered, etc.

\PrintEnvelope This macro makes a minipage with addresses on it.

319 ⟨∗package⟩

320 \newcommand{\PrintEnvelope}[2]{%
321 \begin{minipage}[t][\EnvelopeHeight]{\EnvelopeWidth}
322  \baselineskip=0pt
323  \lineskip=0pt
324  \parindent=0pt
325  \PrintReturnAddress{#1}\\
326  \begin{center}
327  \PrintAddress{#2}
328  \end{center}
329 \end{minipage}}

330 ⟨/package⟩

\@PrintEnvelope The following macro checks for rotation:

331 ⟨∗package⟩

332 \newcommand{\@PrintEnvelope}[2]{%
333 \if@rotateenvelopes\rotatebox{90}{\PrintEnvelope{#1}{#2}}
334 \else\PrintEnvelope{#1}{#2}
335 \fi}

336 ⟨/package⟩

8 Printing of envelopes and labels

8.1 Main Command

\mlabel Now we are prepared to print actual envelopes and labels. It is done by the \mlabel command. It has two forms: for labels and envelopes.

337 ⟨∗package⟩

338 \renewcommand{\mlabel}[2]{\ignorespaces%
339 \spaceskip 0pt\relax%
340 \xspaceskip 0pt\relax%

341 ⟨/package⟩

8.2 Printing of one envelope

342 ⟨∗package⟩

343 \if@envelope%
344  \leavevmode%
345  \hspace*{\EnvelopeLeftMargin}%
346  \@PrintEnvelope{#1}{#2}%
347  \clearpage%
348  \@beginlabelpagehook%

349 ⟨/package⟩

8.3 Printing of one label

350 ⟨∗package⟩

351 \else%
352  \ignorespaces%
353  \ifnum\theLabelCountCol>\theLabelMaxCol%
354  \\\nopagebreak%
355  \stepcounter{LabelCountRow}%
356  \setcounter{LabelCountCol}{1}%
357  \fi%
358  \ifnum\theLabelCountRow>\theLabelMaxRow%
359  \vfill\eject\@beginlabelpagehook%
360  \setcounter{LabelCountRow}{1}%
361  \setcounter{LabelCountCol}{1}%
362  \fi%
363  \PrintLabel{#2}\ignorespaces\nolinebreak%
364  \stepcounter{LabelCountCol}%
365 \fi}

366 ⟨/package⟩

8.4 Printing of return labels

We print only mailing addresses on labels. The user is supposed to have preprinted return labels. Here we describe a utility for printing them. This utility should be used in a separate document.

\@numreturnlabels The counter \@numreturnlabels stores the number of return labels to be printed. Note that it is a TEX counter, not a L^{A}TEX one.

367 ⟨∗package⟩

368 \newcount\@numreturnlabels

369 ⟨/package⟩

\printreturnlabels This macro has two parameters: the number of labels to be printed and the text that is printed. It is the same on all labels.

370 ⟨∗package⟩

371 \newcommand{\printreturnlabels}[2]{%
372 \@numreturnlabels=#1
373 \def\@toaddressfont{\@fromaddressfont}
374 \@capitalizeaddressfalse
375 \@barcodesfalse
376 \startlabels
377 \loop \mlabel{\relax}{#2} \advance\@numreturnlabels by -1
378  \ifnum\@numreturnlabels>0\repeat}

379 ⟨/package⟩

9 Barcodes

9.1 Main command

The USPS Postnet codes are printed accordingly to the specifications Ref. [2]. The scanning algorithm is stolen from David Carlisle’s enumerate package [1].

\PrintBarCode First, we extract barcodes by the command \@extractbarcode. Then we print

 them by \@printbarcode.

380 ⟨∗package⟩

381 \newcommand{\PrintBarCode}[1]{%
382 \@extractbarcode{#1}

383 \@printbarcode}

384 ⟨/package⟩

9.2 Extraction of barcodes

We define zipcode as a sequence of digits (0–9) that:

• Has no characters other than digits and dashes (-) inside it

• Has no bracketed groups inside it and is not bracketed itself

• Is the last in the address field unless \if@alwaysbarcodes=true

We print this sequence plus the control character. The latter is defined as minus sum of digits of the zip code modulo 10 (that is, the complement of the sum of digits to a multiple of 10).

\@zipcode First, some internal registers. The token list \@zipcode contains barcode found

\@zipcodesum
\@zipcodefound

so far. The register \@zipcodesum first contains the sum of digits of the barcode, and then the control character. The switch \@zipcodefound shows whether we found zip code so far.

385 ⟨∗package⟩

386 \newtoks\@zipcode
387 \newcount\@zipcodesum
388 \newif\if@zipcodefound

389 ⟨/package⟩

There are two modes for gobbling tokens:

State A: We are outside a potential zipcode sequence (\if@zipcodefound=false)

State B: We are inside a potential zipcode sequence (\if@zipcodefound=true)

\@endaddress • If we meet in any state the special token \@endaddress, we gobble it and

\@finishzipcode  finish the loop.

390 ⟨∗package⟩

391  \long\def\@finishzipcode#1{}

392 ⟨/package⟩

\@firstzipcode • If we meet a number (0–9) in state A, we initialize registers, process the token and go to state B

393 ⟨∗package⟩

394  \long\def\@firstzipcode#1{%
395  \global\@zipcode{#1}
396  \global\@zipcodesum=#1\relax
397  \global\@zipcodefoundtrue
398  \@zipcodeloop}

399 ⟨/package⟩

\@continuezipcode • If we meet a number (0–9) in state B, we just process it.

400 ⟨∗package⟩

401  \long\def\@continuezipcode#1{%
402  \global\@zipcode=\expandafter{\the\@zipcode#1}
403  \global\advance\@zipcodesum by #1
404  \@zipcodeloop}

405 ⟨/package⟩

\@dashzipcode • If we meet a dash in state B, we gobble it.

406 ⟨∗package⟩

407  \long\def\@dashzipcode#1{\@zipcodeloop}

408 ⟨/package⟩

\@spacezipcode • If we meet a space in any state, we gobble it and go to the state A. The trick is from Carlisle’s enumerate package.

409 ⟨∗package⟩

410  \def\@spacezipcode{%
411  \global\@zipcodefoundfalse
412  \afterassignment\@zipcodeloop\let\@temp= }

413 ⟨/package⟩

\@abortzipcode • If we meet anything else in any mode, we gobble it and go to state A

414 ⟨∗package⟩

415  \long\def\@abortzipcode#1{%
416  \global\@zipcodefoundfalse
417  \@zipcodeloop}

418 ⟨/package⟩

\@zipcodeloop This macro is simple. We just put the next token into \@temp and process it

\@temp through \@zipcodeloop@.

419 ⟨∗package⟩

420 \def\@zipcodeloop{\futurelet\@temp\@zipcodeloop@}

421 ⟨/package⟩

\@zipcodeloop@ This macro performs actual processing… We put the command that gobbles

\@tempa the next token into \@tempa

422 ⟨∗package⟩

423 \def\@zipcodeloop@{%
424 \ifx \@endaddress\@temp      \def\@tempa{\@finishzipcode}    \else
425 \ifx 0\@temp \if@zipcodefound \def\@tempa{\@continuezipcode}
426  \else           \def\@tempa{\@firstzipcode} \fi \else
427 \ifx 1\@temp \if@zipcodefound \def\@tempa{\@continuezipcode}
428  \else           \def\@tempa{\@firstzipcode} \fi \else
429 \ifx 2\@temp \if@zipcodefound \def\@tempa{\@continuezipcode}
430  \else           \def\@tempa{\@firstzipcode} \fi \else
431 \ifx 3\@temp \if@zipcodefound \def\@tempa{\@continuezipcode}
432  \else           \def\@tempa{\@firstzipcode} \fi \else
433 \ifx 4\@temp \if@zipcodefound \def\@tempa{\@continuezipcode}
434  \else           \def\@tempa{\@firstzipcode} \fi \else
435 \ifx 5\@temp \if@zipcodefound \def\@tempa{\@continuezipcode}
436  \else           \def\@tempa{\@firstzipcode} \fi \else
437 \ifx 6\@temp \if@zipcodefound \def\@tempa{\@continuezipcode}
438  \else           \def\@tempa{\@firstzipcode} \fi \else
439 \ifx 7\@temp \if@zipcodefound \def\@tempa{\@continuezipcode}
440  \else           \def\@tempa{\@firstzipcode} \fi \else
441 \ifx 8\@temp \if@zipcodefound \def\@tempa{\@continuezipcode}
442  \else           \def\@tempa{\@firstzipcode} \fi \else
443 \ifx 9\@temp \if@zipcodefound \def\@tempa{\@continuezipcode}
444  \else           \def\@tempa{\@firstzipcode} \fi \else
445 \ifx -\@temp \if@zipcodefound \def\@tempa{\@dashzipcode}
446  \else           \def\@tempa{\@abortzipcode} \fi \else

447 \ifx \@sptoken\@temp         \def\@tempa{\@spacezipcode}     \else
448  \def\@tempa{\@abortzipcode}
449 \fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi
450 \@tempa}

451 ⟨/package⟩

\@extractbarcode  The command \@extractbarcode puts barcode into the \@zipcode, and cal-

culates the control character (10 minus sum of the digits of the barcode).

452 ⟨∗package⟩

453 \long\def\@extractbarcode#1{%
454 \@zipcodefoundfalse
455 \@zipcodeloop#1\@endaddress
456 \if@alwaysbarcodes \@zipcodefoundtrue \fi
457 \if@zipcodefound
458  \ifnum\the\@zipcodesum>0
459  \loop \global\advance \@zipcodesum by -10 \ifnum\the\@zipcodesum>0
460  \repeat
461  \fi
462  \global\multiply\@zipcodesum by -1
463 \fi}

464 ⟨/package⟩

9.3 Printing barcodes

\@barcodewidth First, some lengths. “L” and “S” below refer to “long” and “short” bars corre-

\@barcodeLheight
\@barcodeSheight
\@barcodeskip

spondingly.

465 ⟨∗package⟩

466 \newlength{\@barcodewidth}
467 \newlength{\@barcodeLheight}
468 \newlength{\@barcodeSheight}
469 \newlength{\@barcodeskip}
470 \setlength{\@barcodewidth}{0.020in}
471 \setlength{\@barcodeLheight}{0.125in}
472 \setlength{\@barcodeSheight}{0.050in}
473 \setlength{\@barcodeskip}{0.026in}

474 ⟨/package⟩

\@barL The following macros print long and short bars.

\@barS

475 ⟨∗package⟩

476 \DeclareRobustCommand{\@barL}{%
477 \rule{\@barcodewidth}{\@barcodeLheight}\hspace{\@barcodeskip}}
478 \DeclareRobustCommand{\@barS}{%
479 \rule{\@barcodewidth}{\@barcodeSheight}\hspace{\@barcodeskip}}

480 ⟨/package⟩

\@printonezip The scanning of \@zipcode is simpler than the scanning of the address: the \@printbarcode only tokens we can meet are digits. Well, we will add an end marking token to

the list. Let it be the letter “S” (from “Stop”).

481 ⟨∗package⟩

482 \def\@printonezip#1{%
483 \ifx1#1\@barS\@barS\@barS\@barL\@barL\else
484 \ifx2#1\@barS\@barS\@barL\@barS\@barL\else
485 \ifx3#1\@barS\@barS\@barL\@barL\@barS\else
486 \ifx4#1\@barS\@barL\@barS\@barS\@barL\else

487 \ifx5#1\@barS\@barL\@barS\@barL\@barS\else
488 \ifx6#1\@barS\@barL\@barL\@barS\@barS\else
489 \ifx7#1\@barL\@barS\@barS\@barS\@barL\else
490 \ifx8#1\@barL\@barS\@barS\@barL\@barS\else
491 \ifx9#1\@barL\@barS\@barL\@barS\@barS\else
492 \ifx0#1\@barL\@barL\@barS\@barS\@barS\else
493 \ifx S#1\def\@tempa{\relax}%
494 \fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi%
495 \@tempa}
496 \def\@printbarcode{%
497 \if@zipcodefound
498 \mbox{%
499  \@barL%
500  \def\@tempa{\@printonezip}%
501  \expandafter\@tempa\the\@zipcode S%
502  \def\@tempa{\@printonezip}%
503  \expandafter\@tempa\the\@zipcodesum S%
504  \@barL}
505  \\[1ex]
506 \fi}

507 ⟨/package⟩

10 Capitalization

These macros process the address (actually, any string) according to the USPS recommendations. Specifically, they:

• Strip dots (.) and commas (,) from the address unless they are enclosed in brackets

• Make all letters uppercase

• Add 1pt space between letters

• Add 1em space between words

An interesting question is whether we should de-accent accented letters. USPS says nothing about it. In the present version accents are not stripped. However due to the scanning algorithm they should be enclosed by brackets, like this:

{\u S}andor {\c C}edi.

\@addr@cap We store the capitalized address in the token list \@addr@cap.

508 ⟨∗package⟩

509 \newtoks\@addr@cap

510 ⟨/package⟩

The following macros process the tokens one by one.

\@finishaddrcap If we meet the special token \@endaddress, we gobble it and stop.

511 ⟨∗package⟩

512 \long\def\@finishaddrcap#1{}

513 ⟨/package⟩

\@dotcommaaddrcap If we meet comma or dot, we gobble it and do not stop. This macro is also useful for gobbling L^{A}TEX 2ε letter commands like \voidb@x and \unhbox.

514 ⟨∗package⟩

515 \long\def\@dotcommaaddrcap#1{%
516 \@addrcaploop}

517 ⟨/package⟩

\@newlineaddrcap If we meet \\, we add it to the list

518 ⟨∗package⟩

519 \long\def\@newlineaddrcap#1{%
520 \global\@addr@cap=\expandafter{\the\@addr@cap #1}
521 \@addrcaploop}

522 ⟨/package⟩

\@bgroupaddrcap If we meet \bgroup, we add it to the list the complete group (uppercase)

523 ⟨∗package⟩

524 \long\def\@bgroupaddrcap#1{%
525 \global\@addr@cap=\expandafter{\the\@addr@cap {\MakeUppercase{#1}}}
526 \@addrcaploop}

527 ⟨/package⟩

\@spaceaddrcap If we meet a space we gobble it (oh-oh) and add it to the list.

528 ⟨∗package⟩

529 \def\@spaceaddrcap{%
530 \global\@addr@cap=\expandafter{\the\@addr@cap\hspace{0.6em}}
531 \afterassignment\@addrcaploop\let\@temp= }

532 ⟨/package⟩

\@otheraddrcap And if we meet anything else, we make it uppercase and add to the list

533 ⟨∗package⟩

534 \def\@otheraddrcap#1{%
535 \global\@addr@cap=\expandafter{\the\@addr@cap%
536  \MakeUppercase{#1}\kern1pt\relax}
537 \@addrcaploop}

538 ⟨/package⟩

\@addrcaploop This macro is simple. We just put the next token into \@temp and process it

 through \@addrcaploop@.

539 ⟨∗package⟩

540 \def\@addrcaploop{\futurelet\@temp\@addrcaploop@}

541 ⟨/package⟩

\@addrcaploop@ This macro performs actual processing…

542 ⟨∗package⟩

543 \def\@addrcaploop@{%
544 \ifx \@endaddress\@temp      \def\@tempa{\@finishaddrcap}    \else
545 \ifx .\@temp                 \def\@tempa{\@dotcommaaddrcap}  \else
546 \ifx ,\@temp                 \def\@tempa{\@dotcommaaddrcap}  \else
547 \ifx \voidb@x\@temp          \def\@tempa{\@dotcommaaddrcap}  \else
548 \ifx \unhbox\@temp            \def\@tempa{\@dotcommaaddrcap} \else
549 \ifx \\\@temp                \def\@tempa{\@newlineaddrcap}   \else
550 \ifx \bgroup\@temp           \def\@tempa{\@bgroupaddrcap}    \else
551 \ifx \@sptoken\@temp         \def\@tempa{\@spaceaddrcap}     \else
552  \def\@tempa{\@otheraddrcap}
553 \fi\fi\fi\fi\fi\fi\fi\fi
554 \@tempa}

555 ⟨/package⟩

\@make@capitalize

556 ⟨∗package⟩

557 \long\def\@make@capitalize#1{%
558 \global\@addr@cap={\relax}
559 \@addrcaploop#1\@endaddress
560 \the\@addr@cap}

561 ⟨∗package⟩

References

[1] David Carlisle. The enumerate package. CTAN, v2.02, January 1994.

[2] USPS. Designing Business Letter Mail (Pub 25), August 1995.

Index

Numbers written in italic refer to the page where the corresponding entry is described, the ones underlined to the code line of the definition, the rest to the code lines where the entry is used.

abelCountCol=  \subitem *+LabelCountCol+, \usage{8}
abelCountRow=  \subitem *+LabelCountRow+, \usage{8}
abelMaxCol=  \subitem *+LabelMaxCol+, \usage{4}
abelMaxRow=  \subitem *+LabelMaxRow+, \usage{4}
abelOffsetCol=  \subitem *+LabelOffsetCol+, \usage{9}
abelOffsetRow=  \subitem *+LabelOffsetRow+, \usage{9}
AtBeginLabelPage=  \subitem *+\AtBeginLabelPage+, \main{183}
AtBeginLabels=  \subitem *+\AtBeginLabels+, \main{183}, 192
EnvelopeHeight=  \subitem *+\EnvelopeHeight+, \usage{4}, 50, 80, 83, 214, 224, 321
EnvelopeLeftMargin=  \subitem *+\EnvelopeLeftMargin+, \usage{4}, 52, 53, 212, 214, 216,218,

220, 222, 224, 226, 345

EnvelopeTopMargin=  \subitem *+\EnvelopeTopMargin+, \usage{4}, 51, 78, 248
EnvelopeWidth=  \subitem *+\EnvelopeWidth+, \usage{4}, 49, 79, 84, 216, 226, 321
ok=  \subitem ok+, \main{5}
oops=  \subitem oops+, \main{5}
FirstLabel=  \subitem *+\FirstLabel+, \main{205}
FromAddressHeight=  \subitem *+\FromAddressHeight+, \usage{4}, 69, 83, 299
FromAddressLeftMargin=  \subitem *+\FromAddressLeftMargin+, \usage{4}, 68, 82, 298
FromAddressTopMargin=  \subitem *+\FromAddressTopMargin+, \usage{4}, 67, 81, 297
FromAddressWidth=  \subitem *+\FromAddressWidth+, \usage{4}, 70, 84, 299

alwaysbarcodes=  \subitem alwaysbarcodes+, \usage{3}, 40, 456
barcodes=  \subitem barcodes+, \usage{3}, 39, 310
capitalizeaddress=  \subitem capitalizeaddress+, \usage{3}, 45, 312
envelope=  \subitem envelope+, \usage{3}, 22, 241, 247, 266, 343
printreturnaddress=  \subitem printreturnaddress+, \usage{3}, 28, 301
pswait=  \subitem pswait+, \usage{3}, 35, 191
rotateenvelopes=  \subitem rotateenvelopes+, \usage{3}, 26, 213, 223, 333
zipcodefound=  \subitem zipcodefound+, 388, 425, 427, 429, 431, 433, 435, 437, 439,441,

443, 445, 457, 497

IfFileExists=  \subitem *+\IfFileExists+, 170
LabelHeight=  \subitem *+\LabelHeight+, \usage{4}, 57, 93, 271, 316
LabelLeftMargin=  \subitem *+\LabelLeftMargin+, \usage{4}, 59, 95
LabelRightMargin=  \subitem *+\LabelRightMargin+, \usage{4}, 60, 96, 103
LabelTopMargin=  \subitem *+\LabelTopMargin+, \usage{4}, 58, 94, 249
LabelWidth=  \subitem *+\LabelWidth+, \usage{4}, 56, 92, 101, 276, 316
mlabel=  \subitem *+\mlabel+, \main{337}, 377
PackageWarning=  \subitem *+\PackageWarning+, 172
PrintAddress=  \subitem *+\PrintAddress+, \usage{11}, 304, 317, 327
PrintBarCode=  \subitem *+\PrintBarCode+, \usage{13}, 310, 381
PrintEnvelope=  \subitem *+\PrintEnvelope+, \usage{12}, 320, 333, 334
PrintLabel=  \subitem *+\PrintLabel+, \usage{11}, 315, 363

                          PrintReturnAddress=  \subitem *+\PrintReturnAddress+, \usage{11}, 296, 325
                          printreturnlabels=  \subitem *+\printreturnlabels+, \usage{13}, 371
                          PSwait=  \subitem *+\PSwait+, \main{187}, 192
                          repeat=  \subitem *+\repeat+, 273, 278, 378, 460
                          returnaddress=  \subitem *+\returnaddress+, \usage{10}, 241
                          rotatebox=  \subitem *+\rotatebox+, 333
                          SetEnvelope=  \subitem *+\SetEnvelope+, \usage{4}, 76, 106--112
                          SetLabel=  \subitem *+\SetLabel+, \usage{5}, 90, 116, 118, 120, 122, 124
                          special=  \subitem *+\special+, 188
                          startlabels=  \subitem *+\startlabels+, \main{243}, 376
                          theLabelCountCol=  \subitem *+\theLabelCountCol+, 353
                          theLabelCountRow=  \subitem *+\theLabelCountRow+, 358
                          theLabelMaxCol=  \subitem *+\theLabelMaxCol+, 353
                          theLabelMaxRow=  \subitem *+\theLabelMaxRow+, 358
                          theLabelOffsetCol=  \subitem *+\theLabelOffsetCol+, 267, 275, 277
                          theLabelOffsetRow=  \subitem *+\theLabelOffsetRow+, 268, 269, 272
                          ToAddressLeftMargin=  \subitem *+\ToAddressLeftMargin+, \usage{4}, 72, 86, 100, 102, 307
                          ToAddressTopMargin=  \subitem *+\ToAddressTopMargin+, \usage{4}, 71, 85, 99, 305
                          ToAddressWidth=  \subitem *+\ToAddressWidth+, \usage{4}, 73, 87, 101--103, 308
                          vfill=  \subitem *+\vfill+, 359