% \begin{meta-comment} % % $Id: mdwtab.dtx,v 1.8 1996/12/09 23:20:42 mdw Exp $ % % Another rewrite of the tabular environment, and maths alignments % % (c) 1996 Mark Wooding % %----- Revision history ----------------------------------------------------- % % $Log: mdwtab.dtx,v $ % Revision 1.8 1996/12/09 23:20:42 mdw % (\tab@setstrut): Fixed so that it uses \dimen\tw@ for the strut depth, % as advertised. % % Revision 1.7 1996/11/29 21:59:16 mdw % Fixed a little formatting mistake in a syntax diagram, and switched over % to the new syntax diagram commands on the grounds that they're slightly % less messy. Maybe. % % Revision 1.6 1996/11/19 20:54:33 mdw % Entered into RCS % % % \end{meta-comment} % % \begin{meta-comment} <general public licence> %% %% mdwtab package -- another rewrite of the tabular environment, etc. %% Copyright (c) 1996 Mark Wooding %% %% This program is free software; you can redistribute it and/or modify %% it under the terms of the GNU General Public License as published by %% the Free Software Foundation; either version 2 of the License, or %% (at your option) any later version. %% %% This program is distributed in the hope that it will be useful, %% but WITHOUT ANY WARRANTY; without even the implied warranty of %% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the %% GNU General Public License for more details. %% %% You should have received a copy of the GNU General Public License %% along with this program; if not, write to the Free Software %% Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. %% % \end{meta-comment} % % \begin{meta-comment} <Package preambles> %<+mdwtab>\NeedsTeXFormat{LaTeX2e} %<+mdwtab>\ProvidesPackage{mdwtab} %<+mdwtab> [1998/04/28 1.9 Table typesetting with style] %<+mathenv>\NeedsTeXFormat{LaTeX2e} %<+mathenv>\ProvidesPackage{mathenv} %<+mathenv> [1998/04/28 1.9 Various maths environments] % \end{meta-comment} % % \CheckSum{2758} %% \CharacterTable %% {Upper-case \A\B\C\D\E\F\G\H\I\J\K\L\M\N\O\P\Q\R\S\T\U\V\W\X\Y\Z %% Lower-case \a\b\c\d\e\f\g\h\i\j\k\l\m\n\o\p\q\r\s\t\u\v\w\x\y\z %% Digits \0\1\2\3\4\5\6\7\8\9 %% Exclamation \! Double quote \" Hash (number) \# %% Dollar \$ Percent \% Ampersand \& %% Acute accent \' Left paren \( Right paren \) %% Asterisk \* Plus \+ Comma \, %% Minus \- Point \. Solidus \/ %% Colon \: Semicolon \; Less than \< %% Equals \= Greater than \> Question mark \? %% Commercial at \@ Left bracket \[ Backslash \\ %% Right bracket \] Circumflex \^ Underscore \_ %% Grave accent \` Left brace \{ Vertical bar \| %% Right brace \} Tilde \~} %% % % \begin{meta-comment} % %<*driver> \input{mdwtools} \describespackage{mdwtab} \describespackage{mathenv} \addcontents{lot}{\listoftables} \mdwdoc %</driver> % % \end{meta-comment} % %^^A------------------------------------------------------------------------- % \renewcommand{\tabstyle}{\small} % % \section{User guide} % % % The \package{mdwtab} package contains a reimplementation of the standard % \LaTeX\ \env{tabular} and \env{array} environments. This is not just an % upgraded version: it's a complete rewrite. It has several advantages over % the official \package{array} package (not raw \LaTeX's, which is even less % nice), and it's more-or-less compatible. Most of these are rather % technical, I'll admit. % % \begin{itemize} % % \item The newcolumn system is properly and perfectly integrated into the % system. There are now \emph{no} `primitive' column types -- all the % standard types are created as user-defined columns. % % \item You can define entirely different table-like environments using the % equipment here. It's still hard work, although less so than before. % I'll do an example of this some time. % % \item Construction of the preamble is generally much tidier. I've used % token registers rather than |\edef|, and it's all done very nicely. % % \item Fine spacing before and after rules (described by DEK as `a mark of % quality') is now utterly trivial, since the preamble-generator will % store the appropriate information. % % \item You can use \env{array} in LR and paragraph modes without having % to surround it with `|$|' signs. % % \item Usually you don't want tables in the middle of paragraphs. For these % cases, I've provided a simpler way to position the table % horizontally. % % \item Footnotes work properly inside \env{tabular} environments (hoorah!). % You can `catch' footnotes using the \env{minipage} environment if % you like. (It uses an internal version of the \package{footnote} % package to handle footnotes, which doesn't provide extra goodies like % the \env{footnote} environment; you'll need to load the full package % explicitly to get them.) % % \item Standard \LaTeX\ tabular environments have a problem with lining up % ruled tables. The |\firsthline| command given in the \textit{\LaTeX\ % Companion} helps a bit, but it's not really good enough, and besides, % it doesn't \emph{actually} line the text up right after all. The % \package{mdwtab} package does the job properly to begin with, so you % don't need to worry. % % \end{itemize} % % I've tested the following packages with \package{mdwtab}, and they all % work. Some of the contortions required to make them work weren't pleasant, % but you don't need to know about them. By a strange coincidence, all the % packages were written by David Carlisle. Anyway, here's the list: % \begin{itemize} % \item The quite nice \package{dcolumn} package. % \item The more useful \package{delarray} package. % \item The rather spiffy \package{hhline} package. % \item The truly wonderful \package{tabularx} package. % \item The utterly magnificent \package{longtable} package. % \end{itemize} % % Note that I've looked at \package{supertabular} as well: it won't work, so % use \package{longtable} instead, 'cos it's much better. % % % \subsection{The downside} % % There's no such thing as a free lunch. The \package{mdwtab} environment % is not 100\% compatible with the \env{tabular} environment found in % \LaTeXe\ or the \package{array} package. % % The differences between \package{mdwtab} and \LaTeXe's \env{tabular} % environment are as follows: % % \begin{itemize} \synshorts \let\`=\lq % % \item The vertical spacing in \env{array} environments is different to % that in \env{tabular} environments. This produces more attractive % results in most mathematical uses of \env{array}s, in the author's % opinion. The spacing can be modified by playing with length % parameters. % % \item The presence of horizontal and vertical rules will alter the spacing % of the table (so a pair of columns separated by a `|' is wider than % a pair with no separation by "\\arrayrulewidth". This does mean that % horizontal and vertical rules match up properly -- the usual \LaTeX\ % environment makes the horizontal rules stop just short of the edge % of the table, making an ugly mess (check out the \textit{\LaTeX\ % book} if you don't believe me -- page~62 provides a good example). % The \package{array} package handles rules in the same way as % \package{mdwtab}. % % \setbox0=\hbox{\footnotesize`\\def\\xcs{\\tabskip=\\fill}'} % \setbox2=\hbox{\footnotesize`...@{\\span\\xcs}...'} % \item In common with the \package{array} package, there are some % restrictions on the use of the "\\extracolsep" command in preambles: % you may use at most one "\\extracolsep" command in each `@' or `!' % expression. Also, you can't say % \begin{listing} %\newcommand{\xcs}{\extracolsep{\fill}} % \end{listing} % and then expect something like `...@{\\xcs}...' to actually work -- % the "\\extracolsep" mustn't be hidden inside any other % commands. Because things like `@' expressions aren't expanded at % the time, "\\extracolsep" has to be searched and processed % \`by hand'.\footnote{^^A % All \cs{extracolsep} does is modify the \cs{tabskip} glue, so % if you were an evil \TeX\ hacker like me, you could just say % \unhbox0\ and put \unhbox2\ in your preamble. That'd work nicely. % It also works with the \package{array} package.} % % \item Control sequences (commands) in a table's preamble aren't expanded % before the preamble is read. In fact, commands in the preamble are % considered to be column types, and their names are entirely % independent of normal \LaTeX\ commands. No column types of this % nature have yet been defined\footnote{^^A % There used to be an internal \cs{@magic} type used by % \env{eqnarray}, but you're not supposed to know about that. % Besides, it's not there any more.} % but the possibility's always there. Use the "\\newcolumntype" or % "\\coldef" commands to define new column types. % % \item The preamble parsing works in a completely different way. There is % a certain amount of compatibility provided, although it's heavily % geared towards keeping \package{longtable} happy and probably won't % work with other packages. % % \item Obscure constructs which were allowed by the old preamble parser but % violate the syntax shown in the next section (e.g., `|@{}|' to % suppress the "\\doublerulesep" space between two vertical rules, % described in \textit{The \LaTeX\ Companion} as \`a misuse of the % `@{...}' qualifier') are now properly outlawed. You will be given % an error message if you attempt to use such a construction. % % \item The `*' forms (which repeat column types) are now expanded at a % different time. Previously, preambles like `c@*{4}{{:}@}{--}c' % were considered valid (the example would expand to % `c@{:}@{:}@{:}@{:}@{--}c'), because `*'s were expanded before the % preamble was actually parsed. In the new system, `*' is treated % just like any other preamble character (it just has a rather odd % action), and preambles like this will result in an error (and % probably a rather confusing one). % % \end{itemize} % % There are also several incompatibilities between \package{mdwtab} and % \package{array}: % % \begin{itemize} \synshorts \let\`=\lq % % \item Because of the way "\\newcolumntype" works in the \package{array} % package, a horrid construction like % \begin{listing} %\newcolumntype{x}{{:}} %\begin{tabular}{|c!xc|} % \end{listing} % is considered to be valid, and is interpreted as `|c!{:}c|'. My % reading of pages~54 and~55 of the \textit{\LaTeX\ book} tells me % that this sort of thing is forbidden in normal \LaTeX\ commands. % The \package{mdwtab} preamble parser now treats column type letters % much more like commands with the result that the hacking above won't % work any more. The construction above would actually be interpreted % as `|c!{x}c|' (i.e., the `x' column type wouldn't be expanded to % `{:}' because the parser noticed that it was the argument to the % `!' modifier\footnote{^^A % This is a direct result of the way \TeX\ treats undelimited % arguments. See chapters~5 and~20 of \textit{The \TeX book} for % more information about how grouping affects argument reading.}). % % \item Most of the points above, particularly those relating to the % handling of the preamble, also apply to the \package{array} package. % it's not such an advance over the \LaTeXe\ version as everyone said % it was. % % \end{itemize} % % % \subsection{Syntax} % % \DescribeEnv{tabular} % \DescribeEnv{tabular*} % \DescribeEnv{array} % So that everyone knows where I stand, here's a complete syntax for my % version of the \env{tabular} environment, and friends % % \begin{grammar} % % <tabular-env> ::= \[[ % "\\begin" % \begin{stack} % "{tabular}" \\ "{tabular*}" "{" <length> "}" \\ % "{array}" \\ "{smarray}" % \end{stack} % \[ "[" <position-arg> "]" \] % "{" <preamble> "}" <text> % "\\end" % \( "{tabular}" \\ "{tabular*}" \\ "{array}" \\ "{smarray}" \) % \]] % % <position-arg> ::= (see below) % % <preamble> ::= \[[ % <first-column> % \[ \< <column> \> \] % \]] % % <first-column> ::= \[[ \[ <rule> \] <column> \]] % % <column> ::= \[[ % \[ <spacing> \] \[ \< <user-pre-text> \> \] <column-type> % \[ \< <user-post-text> \> \] \[ <spacing> \] \[ <rule> \] % \]] % % <spacing> ::= \[[ "@" "{" <text> "}" \]] % % <user-pre-text> ::= \[[ ">" "{" <text> "}" \]] % % <column-type> ::= \[[ % \begin{stack} % \[ "T" \\ "M" \] \( "l" \\ "c" \\ "r" \) \\ % \( "p" \\ "m" \\ "b" \) "{" <length> "}" \\ % "#" "{" <raw-pre-text> "}" "{" <raw-post-text> "}" % \end{stack} % \]] % % <user-post-text> ::= \[[ "<" "{" <text> "}" \]] % % <rule> ::= \[[ \( "|" \\ "!" "{" <text> "}" \) \]] % % \end{grammar} % % If you examine the above very carefully, you'll notice a slight deviation % from the original -- an |@|-expression \emph{following} a rule is % considered to be part of the \emph{next} column, not the current one. This % is, I think, an almost insignificant change, and essential for some of the % new features. You'll also notice the new |#| column type form, which % allows you to define new real column types instead of just modifying % existing ones. It's not intended for direct use in preambles -- it's % there mainly for the benefit of people who know what they're doing and % insist on using |\newcolumntype| anyway. %% % The actual column types are shown in table~\ref{tbl:columns}. % % \begin{table} % \begin{tabular}[C]{| >{\synshorts} c | m{3in} |} \hlx{hv[1]} % % \multicolumn{2}{|c|}{\bf Column types} \\ \hlx{v[1]hv} % \bf Name & \bf Meaning \\ \hlx{vhv.} % "l" & Left aligned text (\env{tabular}) or % equation (\env{array}). \\ \hlx{.} % "c" & Centred text (\env{tabular}) or % equation (\env{array}). \\ \hlx{.} % "r" & Right aligned text (\env{tabular}) or % equation (\env{array}). \\ \hlx{vhv.} % "Ml", "Mc" and "Mr" & Left, centre and right aligned % equations.* \\ \hlx{.} % "Tl", "Tc" and "Tr" & Left, centre and right aligned % text.* \\ \hlx{vhv.} % "p{"<width>"}" & Top aligned paragraph with the given % width. \\ \hlx{.} % "m{"<width>"}" & Vertically centred paragraph with % the given width. \\ \hlx{.} % "b{"<width>"}" & Bottom aligned paragraph with the % given width. \\ \hlx{vhv.} % "#{"<pre>"}{"<post>"}" & User defined column type: % \<pre> is inserted before the % cell entry, \<post> is inserted % afterwards.* \\ \hlx{vhhv[1]} % % \multicolumn{2}{|c|}{\bf Other modifier characters} \\ \hlx{v[1]hv} % \bf Name & \bf Meaning \\ \hlx{vhv.} % "|" & Inserts a vertical rule between % columns. \\ \hlx{.} % "!{"<text>"}" & Inserts \<text> between columns, % treating it as a vertical rule. \\ \hlx{vhv.} % "@{"<text>"}" & Inserts \<text> instead of the % usual intercolumn space. \\ \hlx{vhv.} % ">{"<text>"}" & Inserts \<text> just before the % actual column entry. \\ \hlx{.} % "<{"<text>"}" & Inserts \<text> just after the % actual column entry. \\ \hlx{vhv.} % "*{"<count>"}{"<chars>"}" & Inserts \<count> % copies of the \<chars> into the % preamble. \\ \hlx{vhs} % % \multicolumn{2}{@{}l}{* This column type is a new feature} % \end{tabular} % % \caption{\package{array} and \package{tabular} column types and modifiers} % \label{tbl:columns} % \end{table} % % Now that's sorted everything out, there shouldn't be any arguments at all % about what a column means. % % The lowercase \<position-arg>s \lit{t}, \lit{c} and \lit{b} do exactly % what they did before: control the vertical positioning of the table. The % uppercase ones control the \emph{horizontal} positioning -- this is how you % create \emph{unboxed} tables. You can only create unboxed tables in % paragraph mode. % % Note that unboxed tables still can't be broken across pages. Use % the \package{longtable} package for this, because it already does an % excellent job. % % \DescribeMacro{\tabpause} % One thing you can to with unboxed tables, however, is to `interrupt' them, % do some normal typesetting, and then continue. This is achieved by the % |\tabpause| command: its argument is written out in paragraph mode, and % the table is continued after the argument finishes. % Note that it isn't a real argument as far as commands like |\verb| are % concerned -- they'll work inside |\tabpause| without any problems. % % \DescribeMacro{\vline} % The |\vline| command draws a vertical rule the height of the current table % cell (unless the current cell is being typeset in paragraph mode -- it % only works in the simple LR-mode table cells, or in \lit{@} or \lit{!} % modifiers). It's now been given an optional argument which gives the % width of the rule to draw: % % { \let\tabstyle=\relax % \begin{demo}{An example of \cmd\vline} %\large %\begin{tabular} % {| c !{\vline[2pt]} c | c |} % \hlx{hv} % \bf A & \it B & \sf C \\ % \hlx{vhv} % \bf D & \it E & \sf F \\ % \hlx{vh} %\end{tabular} % \end{demo} % } % % \DescribeMacro{smarray} % You've probably noticed that there's an unfamiliar environment mentioned % in the syntax shown above. The \env{smarray} environment produces a % `small' array, with script size cells rather than the normal full text % size cells. I've seen examples of this sort of construction\footnote{^^A % There's a nasty use of \env{smallmatrix} in the |testmath.tex| file which % comes with the \package{amslatex} distribution. It's actually there to % simulate a `smallcases' environment, which the \package{mathenv} package % includes, based around \env{smarray}.} % being implemented by totally unsuitable commands. Someone may find it % handy. % % % \subsection{An updated \cs{cline} command} % % \DescribeMacro{\cline} % The standard \LaTeX\ |\cline| command has been updated. As well as just % passing a range of columns to draw lines through, you can now pass a comma % separated list of column numbers and ranges: % % \begin{grammar} % <cline-cmd> ::= \[[ % "\\cline" "{" \< <number> \[ "-" <number> \] \\ "," \> "}" % \]] % \end{grammar} % % The positioning of the horizontal lines has also been improved a bit, so % that they meet up with the vertical lines properly. Displays like the one % in the example below don't look good unless this has been done properly. % % {\let\tabstyle\relax % \begin{demo}[w]{A \cs{cline} example} %\newcommand{\mc}{\multicolumn{1}} %\begin{tabular}[C]{|c|c|c|c|} \cline{2,4} % \mc{c|}{one} & two & three & four \\ \hline % five & six & seven & \mc{c}{eight} \\ \cline{1,3} %\end{tabular} % \end{demo} % } % % \subsection{Spacing control} % % One of the most irritating things about \LaTeX's tables is that there isn't % enough space around horizontal rules. Donald Knuth, in \textit{The % \TeX book}, describes addition of some extra vertical space here as `a mark % of quality', and since \TeX\ was designed to produce `beautiful documents' % it seems a shame that \LaTeX\ doesn't allow this to be done nicely. Well, % it does now. % % \DescribeMacro{\vgap} % The extra vertical space is added using a command |\vgap|, with the % following syntax: % % \begin{grammar} % % <vgap-cmd> ::= \[[ % "\\vgap" \[ "[" <which-cols> "]" \] "{" <length> "}" % \]] % % <which-cols> ::= \[[ \< <number> \[ "-" <number> \] \\ "," \> \]] % % \end{grammar} % % This command must appear either immediately after the beginning of the % table or immediately after the |\\| which ends a row. (Actually, there are % other commands which also have this requirement -- you can specify a % collection of them wherever you're allowed to give any one.) It adds some % vertical space (the amount is given by the \<length>) to the table, % making sure that the vertical rules of the table are extended correctly. % % The |\vgap| command relies on information stored while your table preamble % is being examined. However, it's possible that you might not want some % of the rules drawn (e.g., if you've used |\multicolumn|). The optional % \<which-cols> argument allows you to specify which rules are \emph{not} % to be drawn. You can specify either single column numbers or ranges. The % rule at the very left hand side is given the number~0; the rules at the % end of column~$n$ are numbered~$n$. It's easy really. % % \DescribeMacro{\hlx} % Using |\vgap| is all very well, but it's a bit cumbersome, and takes up a % lot of typing, especially when combined with |\hline| commands. The |\hlx| % command tries to tidy things. % % The syntax is simple: % \begin{grammar} % % <hlx-cmd> ::= \[[ % "\\hlx" "{" % \begin{rep} % \begin{stack} % "h" \\ % \tok{"v["<which-cols>"]["<length>"]"} \\ % \tok{"s["<length>"]"} \\ % \tok{"c{"<which-cols>"}"} \\ % "b" \\ % \tok{"/["<number>"]"} \\ % "." % \end{stack} % \end{rep} % "}" % \]] % % \end{grammar} % The argument works a bit like a table preamble, really. Each letter is a % command. The following are supported: % % \begin{description} % % \item [\lit*{h}] Works just like |\hline|. If you put two adjacent to each % other, a gap will be put between them. % % \item [\lit*{v[}\<which-cols>\lit*{][}\<length>\lit*{]}] Works % like \syntax{"\\vgap["<which-cols>"]{"<length>"}"}. If the % \<length> is omitted, the value of |\doublerulesep| is used. % This usually looks right. % % \item [\lit*{s[}\<length>\lit*{]}] Leaves a vertical gap with the % given size. If you omit the \<length> then |\doublerulesep| is % used. This is usually right. % % \item [\lit*{c\char`\{}\<which-cols>\lit*{\char`\}}] Works just like % |\cline|. % % \item [\lit*{b}] Inserts a backspace the width of a rule. This is useful % when doing \package{longtable}s. % % \item [\lit*{/[}\<number>\lit*{]}] Allows a page break in a table. Don't % use this except in a \env{longtable} environment. The \<number> % works exactly the same as it does in the |\pagebreak| command, % except that the default is 0, which just permits a break without % forcing it. % % \item [\lit*{.}] (That's a dot) Starts the next row of the table. No % more characters may follow the dot, and no |\hline|, |\hlx|, |\vgap| % or |\multicolumn| commands may be used after it. You don't have to % include it, and most of the time it's totally useless. It can be % handy for some macros, though. I used it in (and in fact added it % especially for) the table of column types. % % \end{description} % % An example of the use of |\hlx| is given, so you can see what's going on. % % \begin{figure} % \let\tabstyle\relax % \begin{demo}[w]{Beautiful table example} %\newcommand{\zerowidth}[1]{\hbox to 0pt{\hss#1\hss}} %\setlength{\tabcolsep}{1.5em} %\begin{tabular}[C]{| r | c | r |} \hlx{hv[1,2]} % \multicolumn{3}{|c|}{\bf AT\&T Common Stock} \\ \hlx{v[1,2]hv} % \multicolumn{1}{|c|}{\zerowidth{\bf Year}} & % \multicolumn{1}{c|}{\zerowidth{\bf Price}} & % \multicolumn{1}{c|}{\zerowidth{\bf Dividend}} \\ \hlx{vhv} % 1971 & 41--54 & \$2.60 \\ % 2 & 41--54 & 2.70 \\ % 3 & 46--55 & 2.87 \\ % 4 & 40--53 & 3.24 \\ % 5 & 45--52 & 3.40 \\ % 6 & 51--59 & .95\rlap{*} \\ \hlx{vhs} % \multicolumn{3}{@{}l}{* (first quarter only)} %\end{tabular} % \end{demo} % \end{figure} % % % \subsection{Creating beautiful long tables} % % You can use the |\vgap| and |\hlx| commands with David Carlisle's % stunning \package{longtable} package. However, there are some things you % should be away of to ensure that your tables always come out looking % lovely. % % The \package{longtable} package will break a table at an |\hline| command, % leaving a rule at the bottom of the page and another at the top of the % next page. This means that a constructions like |\hlx{vhv}| will be % broken into something like |\hlx{vh}| at the bottom of the page and % |\hlx{hv}| at the top of the next. You need to design the table headers % and footers with this in mind. % % However, there appears to be a slight problem:\footnote % {You might very well call it a bug. I couldn't possibly comment.} % if the footer starts with an |\hline|, and a page is broken at an |\hline|, % then you get an extra thick rule at the bottom of the page. This is a bit % of a problem, because if the rule isn't there in the footer and you get % a break between two rows \emph{without} a rule between them, then the page % looks very odd. % % If you want to do ruled longtables, I'd recommend that you proceed as % follows: % \begin{itemize} % \item End header sections with an |\hlx{vh}|. % \item Begin footer sections with an |\hlx{bh}|. % \item Begin the main table with |\hlx{v}|. % \item Insert |\hlx{vhv}| commands in the main table body as usual. % \end{itemize} % If \package{longtable} gets modified appropriately, the use of the \lit{b} % command won't be necessary. % % Here's an example of the sort of thing you'd type. % % \begin{listinglist} \listingsize % \verb"\begin{longtable}[c]{|c|l|} \hlx{hv}" \\ % \verb"\bf Heading & \bf Also heading \\ \hlx{vh}" \\ % \verb"\endhead" \\ % \verb"\hlx{bh}" \\ % \verb"\endfoot" \\ % \verb"\hlx{v}" \\ % \verb"First main & table line \\ \hlx{vhv}" \\ % \verb"Lots of text & like this \\ \hlx{vhv}" \\ % \null\quad\vdots \\ % \verb"Lots of text & like this \\ \hlx{vhv}" \\ % \verb"Last main & table line \\ \hlx{vh}" \\ % \verb"\end{longtable}" % \end{listinglist} % % % \subsection{Rules and vertical positioning} % % In the \LaTeXe\ and \package{array.sty} versions of \env{tabular}, you run % into problems if you try to use ruled tables together with the \lit{[t]} or % \lit{[b]} position specifiers -- the top or bottom rule ends up being % nicely lined up with the text baseline, giving you an effect which is % nothing like the one you expected. The \textit{\LaTeX\ Companion} gives % two commands |\firsthline| and |\lasthline| which are supposed to help with % this problem. (These commands have since migrated into the \package{array} % package.) Unfortunately, |\firsthline| doesn't do its job properly -- % it gets the text position wrong by exactly the width of the table rules. % % The \package{mdwtab} package makes all of this automatic. It gets the % baseline positions exactly right, whether or not you use rules. Earlier % versions of this package required that you play with a length parameter % called |\rulefudge|; this is no longer necessary (or even possible -- the % length parameter no longer exists). The package now correctly compensates % for all sorts of rules and |\vgap|s at the top and bottom of a table and % it gets the positioning right all by itself. You've never had it so good. % % % \subsection{User serviceable parts} % % There are a lot of parameters which you can modify in order to make arrays % and tables look nicer. They are all listed in table~\ref{tbl:config}. % % \begin{table} % \begin{tabular}[C]{| l | m{3in} |} \hlx{hv} % \bf Parameter & \bf Meaning \\ \hlx{vhv} % |\tabstyle| & A command executed at the beginning of % a \env{tabular} or \env{tabular$*$} % environment. By default does nothing. % Change using |\renewcommand|. \\ \hlx{vhv} % |\extrarowheight| & A length added to the height of every % row, used to stop table rules % overprinting ascenders. Default 0\,pt. % Usage is deprecated now: use |\hlx| % instead. \\ \hlx{vhv} % |\tabextrasep| & Extra space added between rows in a % \env{tabular} or \env{tabular$*$} % environment (added \emph{before} any % following |\hline|). Default 0\,pt. \\ % |\arrayextrasep| & Analogous to |\tabextrasep|, but for % \env{array} environments. Default % 1\,jot (3\,pt). \\ % |\smarrayextrasep| & Analogous to |\tabextrasep|, but for % \env{smarray} environments. Default % 1\,pt. \\ \hlx{vhv} % |\tabcolsep| & Space added by default on each side of % a table cell (unless suppressed by an % \lit{@}-expression) in \env{tabular} % environments. Default is defined by % your document class. \\ % |\arraycolsep| & Analogous to |\tabcolsep|, but for % \env{array} environments. Default is % defined by your document class. \\ % |\smarraycolsep| & Analogous to |\tabcolsep|, but for % \env{smarray} environments. Default % is 3\,pt. \\ \hlx{vhv} % |\arrayrulewidth| & The width of horizontal and vertical % rules in tables. \\ % |\doublerulesep| & Space added between two adjacent % vertical or horizontal rules. Also % used by |\hlx{v}|. \\ \hlx{vhv} % |\arraystretch| & Command containing a factor to % multiply the default row height. % Default is defined by your document % class (usually 1). \\ \hlx{vh} % \end{tabular} % % \caption{Parameters for configuring table environments} % \label{tbl:config} % % \end{table} % % % \subsection{Defining column types} % % \DescribeMacro{\newcolumntype} % The easy way to define new column types is using |\newcolumntype|. It % works in more or less the same way as |\newcommand|: % % \begin{grammar} % % <new-col-type-cmd> ::= \[[ % "\\newcolumntype" % "{" <column-name> "}" % \[ "[" <num-args> "]" \] % \[ "[" <default-arg> "]" \] % "{" <first-column> \[ \< <column> \> \] "}" % \]] % % \end{grammar} % % (The \env{array.sty} implementation doesn't accept the \<default-arg> % argument. I've no idea why not, 'cos it was very easy to implement.) % % \DescribeMacro{\colset} % This implementation allows you to define lots of different sets of columns. % You can change the current set using the |\colset| declaration: % \begin{grammar} % <colset-cmd> ::= \[[ "\\colset" "{" <set-name> "}" \]] % \end{grammar} % This leaves a problem, though: at any particular moment, the current % column set could be anything, since other macros and packages can change % it. % % \DescribeMacro{\colpush} % \DescribeMacro{\colpop} % What actually happens is that a stack of column sets is maintained. The % |\colset| command just replaces the item at the top of the stack. The % command |\colpush| pushes its argument onto the top of the stack, making % it the new current set. The corresponding |\colpop| macro (which doesn't % take any arguments) removes the top item from the stack, reinstating the % previous current column set. % % \begin{grammar} % <colpush-cmd> ::= \[[ "\\colpush" "{" <set-name> "}" \]] % <colpop-cmd> ::= \[[ "\\colpop" \]] % \end{grammar} % % The macros which manipulate the column set stack work \emph{locally}. % The contents of the stack are saved when you open a new group. % % To make sure everyone behaves themselves properly, these are the rules for % using the column set stack: % % \begin{itemize} % % \item Packages defining column types must ensure that they preserve the % current column set. Either they must push their own column type % and pop it off when they're finished defining columns, or they must % avoid changing the stack at all, and use the optional arguments to % |\coldef| and |\collet|. % % \item Packages must not assume that any particular column set is current % unless they have made sure of it themselves. % % \item Packages must ensure that they pop exactly as much as they push. % There isn't much policing of this (perhaps there should be more), % so authors are encouraged to behave responsibly. % % \item Packages must change the current column set (using |\colset|) when % they start up their table environment. This will be restored when % the environment closes. % % \end{itemize} % % \DescribeMacro{\coldef} % |\newcolumntype| is probably enough for most purposes. However, Real % \TeX nicians, and people writing new table-generating environments, require % something lower-level. % % \begin{grammar} % <coldef-cmd> ::= \[[ % "\\coldef" % \[ "[" <set-name> "]" \] % <col-name> <arg-template> "{" <replacement-text> "}" % \]] % \end{grammar} % % Note that this defines a column type in the current colset. It works % almost exactly the same way as \TeX's primitive |\def|. There is a % potential gotcha here: a |\tab@mkpream| token is inserted at the end of % your replacement text. If you need to read an optional argument or % something, you'll need to gobble this token before you carry on. The % |\@firstoftwo| macro could be handy here: % \begin{listing} %\coldef x{\@firstoftwo{\@ifnextchar[\@xcolumn@i\@xcolumn@ii}}} % \end{listing} % This isn't a terribly pretty state of affairs, and I ought to do something % about it. I've not seen any use for an optional argument yet, though. % Note that if you do gobble the |\tab@mkpream|, it's your responsibility to % insert another one at the very end of your macro's expansion (so that % further preamble characters can be read). % % The replacement text is inserted directly. It's normal to insert preamble % elements here. There are several to choose from: % % \begin{description} % % \item [Column items] provide the main `meat' of a column. You insert a % column element by saying % \syntax{"\\tabcoltype{"<pre-text>"}{"<post-text>"}"}. % The user's text gets inserted between these two. (So do user pre- % and post-texts. Bear this in mind.) % % \item [User pre-text items] work like the \lit{>} preamble command. You % use the \syntax{"\\tabuserpretype{"<text>"}"} command to insert it. % User pre-texts are written in \emph{reverse} order between the % pre-text of the column item and the text from the table cell. % % \item [User post-text items] work like the \lit{<} preamble command. You % use the \syntax{"\\tabuserposttype{"<text>"}"} command to insert it. % Like user pre-texts, user post-texts are written in reverse order, % between the table cell text and the column item post-text. % % \item [Space items] work like the \lit{@} preamble command. They're % inserted with the \syntax{"\\tabspctype{"<text>"}"} command. % % \item [Rule items] work like the `\verb"|"' and \lit{!} commands. You % insert them with the \syntax{"\\tabruletype{"<text>"}"} command. % Note that the text is inserted by |\vgap| too, so it should contain % things which adjust their vertical size nicely. If you really need % to, you can test |\iftab@vgap| to see if you're in a |\vgap|. % % \end{description} % % \DescribeMacro{\collet} % As well as defining columns, you can copy definitions (rather like |\let| % allows you to copy macros). The syntax is like this: % % \begin{grammar} % % <collet-cmd> ::= \[[ % \[ "[" <set-name> "]" \] <col-name> \[ "=" \] \[ "[" <set-name> "]" \] % <col-name> % \]] % % \end{grammar} % % (In other words, you can copy defintions from other column sets.) % % % \subsection{Defining new table-generating environments} % % Quite a few routines are provided specifically to help you to define new % environments which do alignment in a nice way. % % \subsubsection{Reading preambles} % % The main tricky bit in doing table-like environments is parsing preambles. % No longer. % % \DescribeMacro{\tab@readpreamble} % \DescribeMacro{\tab@doreadpream} % The main parser routine is called |\tab@doreadpream|. Given a user % preamble string as an argument, it will build an |\halign| preamble to % return to you. However, the preamble produced won't be complete. This is % because you can actually make multiple calls to |\tab@doreadpream| with % bits of user preambles. The |\newcolumntype| system uses this mechanism, % as does the \lit{*} (repeating) modifier. When there really is no more % preamble to read, you need to \emph{commit} the heldover tokens to the % output. The |\tab@readpreamble| routine will do this for you -- given a % user preamble, it builds a complete output from it. % % A token register |\tab@preamble| is used to store the generated preamble. % Before starting, you must iniitialise this token list to whatever you want. % There's another token register, |\tab@shortline|, which is used to store % tokens used by |\vgap|. For each column in the table, the list contains % an |\omit| (to override the standard preamble) and an |\hfil| space taking % up most of the column. Finally, for each rule item in the user preamble, % the shortline list contains an entry of the form: % \begin{quote} \synshorts % "\\tab@ckr{"<column-number>"}{"<rule-text>"}" % \end{quote} % This is used to decide whether to print the rule or an empty thing of the % same width. You probably ought to know that the very first column does % \emph{not} have a leading |\omit| -- this is supplied by |\vgap| so that % it can then look for optional arguments. % % \DescribeMacro{\tab@initread} % As well as initialising |\tab@preamble| and emptying |\tab@shortline|, % there are several other operations required to initialise a preamble read. % These are all performed by the |\tab@initread| macro, although you may want % to change some of the values for your specific application. For reference, % the actions performed are: % \begin{itemize} % \item initialising the parser state by setting $|\tab@state| = % |\tab@startstate|$; % \item clearing the token lists |\tab@preamble| and |\tab@shortlist|; % \item initialising the macros |\tab@tabtext|, |\tab@midtext|, and % |\tab@multicol| to their default values of `|&|', % `|\ignorespaces#\unskip|' and the empty token list respectively.^^A % \footnote{^^A % These are macros rather than token lists to avoid hogging all % the token list registers. Actually, the package only allocates % two, although it does use almost all of the temporary registers as % well. Also, there's a lie: \cs{unskip} is too hamfisted to remove % trailing spaces properly; I really use a macro called % \cs{@maybe@unskip}} % \item clearing the internal token list registers |\tab@pretext|, % |tab@userpretext| and |\tab@posttext|; % \item clearing the column counter |\tab@columns| to zero; % \item clearing the action performed when a new column is started (by making % the |\tab@looped| macro equal to |\relax|; this is used to make % |\multicolumn| macro raise an error if you try to do more than one % column); and % \item setting up some other switches used by the parser (|\iftab@rule|, % |\iftab@initrule| and |\iftab@firstcol|, all of which are set to be % |true|). % \end{itemize} % % The macro |\tab@multicol| is used by the |\multicolumn| command to insert % any necessary items (e.g., struts) before the actual column text. If you % set this to something non-empty, you should probably consider adding a % call to the macro to the beginning of |\tab@preamble|. % % When parsing is finally done, the count register |\tab@columns| contains % the number of columns in the alignment. Don't corrupt this value, because % it's used for handling |\hline| commands. % % \subsubsection{Starting new lines} % % The other messy bit required by table environments is the newline command % |\\|. There are nasty complications involved with starting new lines, some % of which can be handled by this package, and some on which I can only give % advice. % % \DescribeMacro{\tab@cr} % The optional arguments and star-forms etc. can be read fairly painlessly % using the |\tab@cr| command: % % \begin{grammar} % <tabcr-cmd> ::= \[[ % "\\tab@cr" <command> "{" <non-star-text> "}" "{" <star-text> "}" % \]] % \end{grammar} % % This will call your \<command> with two arguments. The first is the % contents of the optional argument, or `|\z@|' if there wasn't one. The % second is either \<star-text> or \<non-star-text> depending on % whether the user wrote the $*$-form or not. % % Somewhere in your \<command>, you'll have to use the |\cr| primitive to % end the table row. After you've done this, you \emph{must} ensure that you % don't do anything that gets past \TeX's mouth without protecting it -- % otherwise |\hline| and co.\ won't work. I usually wrap things up in a % |\noalign| to protect them, although there are other methods. Maybe. % % You might like to have a look at the \env{eqnarray} implementation provided % to see how all this gets put into practice. % % % \subsection{The \env{mathenv} package alignment environments} % % The \env{mathenv} package provides several environments for aligning % equations in various ways. They're mainly provided as a demonstration of % the table handling macros in \package{mdwtab}, so don't expect great % things. If you want truly beautiful mathematics, use % \package{amsmath}.\footnote{^^A % Particularly since nice commands like \cmd\over\ are being reactivated % in a later release of \package{amsmath}.} % However, the various environments do nest in an approximately useful way. % I also think that the \env{matrix} and \env{script} environments provided % here give better results than their \package{amsmath} equivalents, and % they are certainly more versatile. % % \subsubsection{The new \env{eqnarray} environment} % % \DescribeEnv{eqnarray} % \DescribeEnv{eqnarray*} % As an example of the new column defining features, and because the original % isn't terribly good, I've included a rewritten version of the % \env{eqnarray} environment. The new implementation closes the gap between % \env{eqnarray} and \AmSTeX\ alignment features. It's in a separate, % package called \package{mathenv}, to avoid wasting your memory. % % \begin{grammar} % % <eqnarray-env> ::= \[[ % <begin-eqnarray> \< <row> \\ "\\\\" \> <end-eqnarray> % \]] % % <begin-eqnarray> ::= \[[ % "\\begin" \( "{eqnarray}" \\ "{eqnarray*}" \) % \[ "[" \< <eqa-column> \> "]" \] % \]] % % <eqa-column> ::= \[[ % \[ "q" \\ ":" \] % \[ \< ">" "{" <pre-text> "}" \> \] % \begin{stack} % \[ "T" \] \( "r" \\ "c" \\ "l" \) \\ % "L" \\ % "x" % \end{stack} % \[ \< "<" "{" <post-text> "}" \> \] % \]] % % <end-eqnarray> ::= \[[ % "\\end" \begin{stack} "{eqnarray}" \\ "{eqnarray*}" \end{stack} % \]] % % \end{grammar} % % Descriptions of the various column types are given in % table~\ref{tbl:eqnarray}. % % \begin{table} % \begin{tabular}[C]{| >{\synshorts} c | m{3in} |} \hlx{hv[1]} % % \multicolumn{2}{|c|}{\bf Column types} \\ \hlx{v[1]hv} % \bf Name & \bf Meaning \\ \hlx{vhv.} % "l" & Left aligned piece of equation. \\ \hlx{.} % "c" & Centred piece of equation. \\ \hlx{.} % "x" & Centred or flush-left whole equation % (depending on \textsf{fleqn} option). \\ \hlx{.} % "r" & Right aligned piece of equation. \\ \hlx{vhv.} % "L" & Left aligned piece of equation whose % width is considered to be 2\,em. \\ \hlx{vhv.} % "Tl", "Tc" and "Tr" & Left, centre and right aligned % text. \\ \hlx{vhhv[1]} % % \multicolumn{2}{|c|}{\bf Other modifier characters} \\ \hlx{v[1]hv} % \bf Name & \bf Meaning \\ \hlx{vhv.} % ":" & Leaves a big gap between equations. % By default, the `chunks' separated by % \lit{:}s are equally spaced on the % line. \\ \hlx{.} % "q" & Inserts 1\,em of space \\ \hlx{vhv.} % ">{"<text>"}" & Inserts \<text> just before the % actual column entry. \\ \hlx{.} % "<{"<text>"}" & Inserts \<text> just after the % actual column entry. \\ \hlx{vhv.} % "*{"<count>"}{"<chars>"}" & Inserts \<count> % copies of the \<chars> into the % preamble. \\ \hlx{vh} % \end{tabular} % % \caption{\package{eqnarray} column types and modifiers} % \label{tbl:eqnarray} % \end{table} % % The default preamble, if you don't supply one of your own, is \lit{rcl}. % Most of the time, \lit{rl} is sufficient, although compatibility is more % important to me. % % By default, there is no space between columns, which makes formul\ae\ in an % \env{eqnarray} environment look just like formul\ae\ typeset on their own, % except that things get aligned in columns. This is where the default % \env{eqnarray} falls down: it leaves |\arraycolsep| space between each % column making the thing look horrible. % % An example would be good here, I think. This one's from exercise 22.9 of % the \textit{\TeX book}. % % \begin{demo}[w]{Simultaneous equations} %\begin{eqnarray}[*3{rc}rl] % 10w & + & 3x & + & 3y & + & 18z & = 1 \\ % 6w & - & 17x & & & - & 5z & = 2 %\end{eqnarray} % \end{demo} % % Choosing a more up-to-date example, here's some examples from the % \textit{\LaTeX\ Companion}. % % \begin{demo}[w]{Lots of equations} %\begin{eqnarray}[rl:rl:lq] % V_i &= v_i - q_i v_j, & X_i &= x_i - q_i x_j, & % U_i = u_i, \qquad \mbox{for $i \ne j$} \\ % V_j &= v_j, & X_j &= x_j & % U_j u_j + \sum_{i \ne j} q_i u_i. \label{eq:A} %\end{eqnarray} % \end{demo} % % \begin{figure} % \begin{demo}[w]{Plain text column and \cs{tabpause}} %\begin{eqnarray}[rlqqTl] % x &= y & by (\ref{eq:A}) \\ % x' &= y' & by definition \\ %\tabpause{and} % x + x' &= y + y' & by Axiom~1 %\end{eqnarray} % \end{demo} % \end{figure} % % The new features also mean that you don't need to mess about with % |\lefteqn| any more. This is handled by the \lit{L} column type: % % \begin{demo}{Splitting example} %\begin{eqnarray*}[Ll] % w+x+y+z = \\ % & a+b+c+d+e+ \\ % & f+g+h+i+j %\end{eqnarray*} % \end{demo} % % Finally, just to prove that the spacing's right at last, here's another one % from the \textit{Companion}. % % \begin{demo}{Spacing demonstration} %\begin{equation} % x^2 + y^2 = z^2 %\end{equation} %\begin{eqnarray}[rl] % x^2 + y^2 &= z^2 \\ % y^2 &< z^2 %\end{eqnarray} % \end{demo} % % Well, that was easy enough. Now on to numbering. As you've noticed, the % equations above are numbered. You can use the \env{eqnarray$*$} % environment to turn off the numbering in the whole environment, or say % |\nonumber| on a line to suppress numbering of that one in particular. % % \DescribeMacro{\eqnumber} % More excitingly, you can say |\eqnumber| to enable numbering for a % particular equation, or \syntax{"\\eqnumber["<text>"]"} to choose what to % show instead of the line number. This works for both starred and unstarred % versions of the environment. Now |\nonumber| becomes merely a synonym for % `|\eqnumber[]|'. % % A note for cheats: you can use the sparkly new \env{eqnarray} for simple % equations by specifying \lit{x} as the column description. Who needs % \AmSTeX?\ |;-)| % % \DescribeEnv{eqlines} % \DescribeEnv{eqlines*} % In fact, there's a separate environment \env{eqlines}, which is equivalent % to \env{eqnarray} with a single \lit{x} column; the result is that you can % insert a collection of displayed equations separated by |\\| commands. If % you don't like numbering, use \env{eqlines$*$} insead. % % \subsubsection{The \env{eqnalign} environment} % % \DescribeEnv{eqnalign} % There's a new environment, \env{eqnalign}, which does almost the same % thing as \env{eqnarray} but not quite. It doesn't do equation numbers, % and it wraps its contents up in a box. The result of this is that: % % \begin{itemize} % % \item You can use \env{eqnalign} for just a part of a formula. % The \env{eqnarray} environment must take up the whole display. % % \item You can use \env{eqnalign} within \env{eqnarray} for extra fine % alignment of subsidiary bits. % % \item You can break off from doing an \env{eqnarray} using the |\tabpause| % command. You can't use |\tabpause| inside % \env{eqnalign}.\footnote{^^A % Well, technically speaking there's nothing to stop you. However, % the results won't be pretty.} % % \end{itemize} % % The \env{eqnalign} environment works like this: % % \begin{grammar} % % <eqnalign-env> ::= \[[ % <begin-eqnalign> <contents> <end-eqnalign> % \]] % % <begin-eqnalign> ::= \[[ % "\\begin" "{eqnalign}" % \[ "[" \< <eqa-column> \> "]" \] % \[ "[" \( "t" \\ "c" \\ "b" \) "]" \] % \]] % % <end-eqnalign> ::= \[[ "\\end" "{eqnalign}" \]] % % \end{grammar} % % As the syntax suggests, the preamble for the \env{eqnalign} environment % works exactly the same way as for \env{eqnarray}. Example time: another % one from the \textit{\TeX book}. % % \begin{figure} % \begin{demo}[w]{Example of \env{eqnalign}} %\[ % \left\{ \begin{eqnalign}[rl] % \alpha &= f(z) \\ \beta &= f(z^2) \\ % \gamma &= f(z^3) % \end{eqnalign} \right\} % \qquad % \left\{ \begin{eqnalign}[rl] % x &= \alpha^2 - \beta \\ y &= 2\gamma % \end{eqnalign} \right\}. %\] % \end{demo} % \end{figure} % % \DescribeMacro{\multicolumn} % The |\multicolumn| command works correctly in both the \env{eqnarray} and % \env{eqnalign} environments, although you should bear in mind that you % should give \env{eqnarray} column types, not \env{array} ones. % % \subsubsection{A note on spacing in alignment environments} % % Most of the time, equations in \env{eqnarray} and \env{eqnalign} % environments will be beautiful. However, there are some things you should % bear in mind when you produce beautiful equations. % % The main problem with spacing is making sure that binary relations and % binary operators have the correct amount of space on each side of them. % The alignment environments insert `hidden' objects at the ends of table % cells to assist with the spacing: \lit{l} column types have a hidden object % on the left, \lit{r} types have a hidden object on the right, and \lit{c} % types have a hidden object on \emph{both} ends. These hidden objects add % the correct space when there's a binary operator or relation next to them. % If some other sort of object is lurking there, no space is added. So far, % so good. % % The only problem comes when you have something like this: % % \begin{demo}{How not to do an \env{eqnarray}} %\begin{eqnarray*}[rcl] % x + y & = & 12 \\ % 2x - 5y & = & -6 %\end{eqnarray*} % \end{demo} % % The `$-$' sign in the second equation has been treated as a binary operator % when really it should be a unary prefix operator, but \TeX\ isn't clever % enough to know the difference. (Can you see the difference in the spacing % between $-6$~and~${}-6$?) There are two possible solutions to the % problem. You could wrap the `|-6|' up in a group (`|{-6}|'), or just the % $-$ sign (`|{-}6|'). A better plan, though, is to get rid of the middle % column altogether: % % \begin{demo}{How to do an \env{eqnarray}} %\begin{eqnarray*}[rl] % x + y & = 12 \\ % 2x - 5y & = -6 %\end{eqnarray*} % \end{demo} % % Since the things in the middle column were the same width, it's not % actually doing any good. Also, now that \TeX\ can see that the thing on % the left of the `$-$' sign is a relation (the `$=$' sign), it will space % the formula correctly. % % In this case, it might be even better to add some extra columns, and line % up the $x$ and $y$ terms in the left hand side: % % \begin{demo}{Extra beautiful \env{eqnarray}} %\begin{eqnarray*}[rrl] % x + & y & = 12 \\ % 2x - & 5y & = -6 %\end{eqnarray*} % \end{demo} % % ^^A Some hacking now to display box sizes. % % { % \catcode`p=12 \catcode`t=12 % \gdef\magni#1pt{#1} % } % % \newcommand{\widthof}[1]{^^A % \settowidth{\dimen0 }{#1}^^A % \expandafter\magni\the\dimen0\,pt^^A % } % % ^^A The text below makes an assumption which looks correct to me (I asked % ^^A TeX, and it agreed with me), although in case anything changes, I want % ^^A to be informed. % % \sbox0{$+$} \sbox2{$-$} \ifdim\wd0=\wd2\else% % \errmessage{Assertion failed: `+' and `-' are different widths!} % \fi % % There's no need to put the `$+$' and `$-$' operators in their own column % here, because they're both \widthof{$+$} wide, even though they don't % look it. % % \subsubsection{Configuring the alignment environments} % % There are a collection of parameters you can use to make the equation % alignment environments (\env{eqnarray} and \env{eqnalign}) look the way % you like them. These are all shown in table~\ref{tbl:eqnparms}. % % \begin{table} % \begin{tabular}[C]{| l | p{3in} |} \hlx{hv} % \bf Parameter & \bf Use \\ \hlx{vhv} % |\eqaopenskip| & Length put on the left of an % \env{eqnarray} environment. By % default, this is |\@centering| (to % centre the alignment) or |\mathindent| % (to left align) depending on whether % you're using the \textsf{fleqn} % document class option. \\ % |\eqacloseskip| & Length put on the right of an % \env{eqnarray} environment. By % default, this is |\@centering|, to % align the environment correctly. \\ \hlx{vhv} % |\eqacolskip| & Space added by the \lit{:} column % modifier. This should be a rubber % length, although it only stretches in % \env{eqnarray}, not in \env{eqnalign}. % The default value is 1\smallf1/2\,em % with 1000\,pt of stretch. \\ % |\eqainskip| & Space added at each side of a normal % column. By default this is 0\,pt. \\ \hlx{vhv} % |\eqastyle| & The maths style used in the alignment. % By default, this is |\textstyle|, % and you probably won't want to change % it. \\ \hlx{vh} % \end{tabular} % % \caption{Parameters for the \env{eqnarray} and \env{eqnalign} environments} % \label{tbl:eqnparms} % \end{table} % % % \subsection{Other multiline equations} % % Sometimes there's no sensible alignment point for splitting equations. The % normal thing to do under these circumstances is to put the first line way % over to the left of the page, and the last line over to the right. (If % there are more lines, I imagine we put them in the middle.) % % \DescribeEnv{spliteqn} % \DescribeEnv{spliteqn*} % The \env{spliteqn} environment allows you to do such splitting of % equations. Rather than tediously describe it, I'll just give an example, % because it's really easy. The $*$-version works the same, except it % doesn't put an equation number in. % % \begin{figure} % \begin{demo}[w]{A split equation} %\begin{spliteqn} % \sum_{1\le j\le n} % \frac {1} { (x_j - x_1) \ldots (x_j - x_{j-1}) % (x - x_j) (x_j - x_{j+1}) \ldots (x_j - x_n) } % \\ % = \frac {1} { (x - x_1) \ldots (x - x_n) }. %\end{spliteqn} % \end{demo} % \end{figure} % % \DescribeEnv{subsplit} % If you have a very badly behaved equation, you might want to split a part % of it (say, a bit of a fraction), particularly if you're doing things in % narrow columns. % % \begin{figure} % \begin{demo}[w]{A \env{subsplit} environment} %\begin{equation} % \frac{ % \begin{subsplit} % q^{\frac{1}{2} n(n+1)}(ea; q^2)_\infty (eq/a; q^2)_\infty \\ % (caq/e; q^2)_\infty (cq^2/ae; q^2)_\infty % \end{subsplit} % }{ % (e; q)_\infty (cq/e; q)_\infty % } %\end{equation} % \end{demo} % \end{figure} % % \subsection{Matrices} % % Also included in the \package{mathenv} package is a collection of things % for typesetting matrices. The standard \env{array} doesn't (in my opinion) % provide the right sort of spacing for matrices. \PlainTeX\ provides some % quite nice matrix handling macros, but they don't work in the appropriate % \LaTeX\ way. % % \textbf{Warning:} These definitions will make old versions of % \package{plain.sty} unhappy; newer versions correctly restore the % Plain~\TeX\ macros |\matrix| and |\pmatrix|. % % \DescribeEnv{matrix} % The simple way to do matrices is with the \env{matrix} environment. % % \begin{grammar} % % <matrix-env> ::= \[[ <begin-matrix> <contents> <end-matrix> \]] % % <begin-matrix> ::= \[[ "\\begin{matrix}" \[ "[" <matrix-cols> "]" \] \]] % % <matrix-cols> ::= \[[ % \< \[ "[" \] \[ "T" \] \( "l" \\ "c" \\ "r" \) \> % \]] % % <end-matrix> ::= \[[ "\\end{stack}" \]] % % \end{grammar} % % The \lit{l}, \lit{c} and \lit{r} columns are fairly obvious -- they align % their contents in the appropriate way. The \lit{[} character is more % complicated. It means `repeat the remaining column types forever', so a % preamble of \lit{cc[lr} means `two centred columns, then alternating left- % and right-aligned columns for as often as needed'. The default preamble, % if you don't specify one, is \lit{[c} -- `any number of centred columns'. % % \DescribeMacro{\multicolumn} % The |\multicolumn| command works correctly in matrices, although you should % bear in mind that you should give \env{matrix} column types, not % \env{array} ones. % % \DescribeEnv{pmatrix} % The standard \env{matrix} environment doesn't put any delimiters around the % matrix. You can use the standard |\left| and |\right| commands, although % this is a bit nasty. The \env{pmatrix} environment will put parentheses % around the matrix it creates; it's otherwise exactly the same as % \env{matrix}. % % \DescribeEnv{dmatrix} % A \env{dmatrix} environment is also provided. It takes two extra % arguments: the left and right delimiter characters (without |\left| or % |\right|). % % \begin{figure} % \begin{demo}[w]{Various \env{matrix} environments} %\[ \begin{matrix} 1 & 0 \\ 0 & -1 \end{matrix} \quad % \begin{pmatrix} % \cos\theta & \sin\theta \\ % -\sin\theta & \cos\theta % \end{pmatrix} \quad % \begin{dmatrix}[] 0 & -i \\ i & 0 \end{dmatrix} %\] % \end{demo} % \end{figure} % % \DescribeEnv{smatrix} % Normal matrices always come out the same size; they don't change size % according to the surrounding context (unfortunately). However, it can be % occasionally useful to put matrices in running text, so you can talk about % $A$ being $\bigl( \begin{smatrix} a & b \\ b & c \end{smatrix} \bigr)$ % being its own transpose (i.e., $A = A^T$). This is accomplished using the % \env{smatrix} (the `s' stands for `small' -- I thought that `smallmatrix' % was too big to type inline). As well as inline text, the \env{smatrix} % can be useful in displays, if the matrix is deep in a subformula. I can't % think of any examples offhand, though. % % \DescribeEnv{spmatrix} % \DescribeEnv{sdmatrix} % The \env{smatrix} environment doesn't supply any delimiters, like % \env{matrix}. There are \env{spmatrix} and \env{sdmatrix} environments % which do, though. Note that delimiters have a tendency to get too big and % mess up the line spacing -- I had to use explicitly |\big| delimiters % in the above example. % % \DescribeEnv{pmatrix*} % \DescribeEnv{spmatrix*} % \DescribeEnv{sdmatrix*} % All the small matrix environments have starred versions, which are more % suitable for use in displays, since they have more space between the rows. % They're intended for typesetting really big matrices in displays. % % \DescribeMacro{\ddots} % \DescribeMacro{\vdots} % The standard |\vdots| and |\ddots| commands don't produce anything at all % nice in small matrices, so this package redefines them so that they scale % properly to smaller sizes. % % \DescribeEnv{genmatrix} % Actually, all these environments are special cases of one: \env{genmatrix}. % This takes oodles of arguments: % \begin{quote} \synshorts % "\\begin{genmatrix}{"<matrix-style>"}{"<outer-style>"}" \\ % \null \qquad "{"<spacing>"}{"<left-delim>"}{"<right-delim>"}" \\ % \null \quad\vdots \\ % "\\end{genmatrix}" % \end{quote} % The two `style' arguments should be things like |\textstyle| or % |\scriptstyle|; the first, \<matrix-style>, is the style to use for the % matrix elements, and the second, \<outer-style>, is the style to assume % for the surrounding text (this affects the spacing within the matrix; it % should usually be the same as \<matrix-style>). The \<spacing> is inserted % between the matrix and the delimiters, on each side of the matrix. It's % usually `|\,|' in full-size matrices, and blank for small ones. The % delimiters are inserted around the matrices, and sized appropriately. % % \DescribeEnv{newmatrix} % You can create your own matrix environments if you like, using the % |\newmatrix| command. It takes two arguments, although they're a bit % odd. The first is the name of the environment, and the second contains % the arguments to pass to \env{genmatrix}. For example, the \env{pmatrix} % environment was defined by saying % % \begin{listing} %\newmatrix{pmatrix}{{\textstyle}{\textstyle}{\,}{(}{)}} % \end{listing} % % If you don't pass all three arguments, then you end up requiring the % user to specify the remaining ones. This is how \env{dmatrix} works. % % \DescribeEnv{script} % Finally, although it's not really a matrix, stacked super- and subscripts % follow much the same sorts of spacing rules. The \env{script} environment % allows you to do this sort of thing very easily. It essentially provides % a `matrix' with the right sort of spacing. The default preamble string is % \lit{c}, giving you centred scripts, although you can say % |\begin{script}[l]| for left-aligned scripts, which is better if the % script is being placed to the right of its operator. If you're really % odd, you can have more than one column. % % \begin{demo}{Example of \env{script}} %\[ \mathop{{\sum}'}_{x \in A} % f(x) % \stackrel{\mathrm{def}}{=} % \sum_{\begin{script} % x \in A \\ x \ne 0 % \end{script}} f(x) %\] % \end{demo} % % % \subsection{Other \package{mathenv} environments} % % The \package{mathenv} package contains some other environments which may % be useful, based on the enhanced \env{tabular} and \env{array} % environments. % % \DescribeEnv{cases} % The \env{cases} environment lets you say things like the following: % % \begin{demo}[w]{Example of \env{cases}} %\[ P_{r-j} = \begin{cases} % 0 & if $r-j$ is odd \\ % r!\,(-1)^{(r-j)/2} & if $r-j$ is even % \end{cases} %\] % \end{demo} % % The spacing required for this is a bit messy, so providing an environment % for it is quite handy. % % \DescribeEnv{smcases} % The \env{smcases} environment works the same way as \env{cases}, but with % scriptsize lettering. % % \implementation % % %^^A------------------------------------------------------------------------- % \section{Implementation of table handling} % % % Here we go. It starts horrid and gets worse. However, it does stay nicer % than the original, IMHO. % % \begin{macrocode} %<*mdwtab> % \end{macrocode} % % % \subsection{Registers, switches and things} % % We need lots of these. It's great fun. % % The two count registers are simple enough: % % \begin{description} % \item [\cs{tab@state}] contains the current parser state. Since we % probably won't be parsing preambles recursively, this is a global % variable. % \item [\cs{tab@columns}] contains the number of the current column. % \item [\cs{tab@hlstate}] contains the state required for hline management. % \end{description} % % \begin{macrocode} \newcount\tab@state \newcount\tab@columns % \end{macrocode} % % We need \emph{lots} of token registers. Fortunately, most of them are only % used during parsing. We'll use \PlainTeX's scratch tokens for this. Note % that |\toks\tw@| isn't used here. It, and |\toks@|, are free for use by % column commands. % % \begin{macrocode} \newtoks\tab@preamble \newtoks\tab@shortline \toksdef\tab@pretext 4 \toksdef\tab@posttext 6 \toksdef\tab@userpretext 8 % \end{macrocode} % % The dimens are fairly straightforward. The inclusion of |\col@sep| is a % sacrifice to compatibility -- judicious use of |\let| in \package{array} % would have saved a register. % % \begin{macrocode} \newdimen\extrarowheight \newdimen\tabextrasep \newdimen\arrayextrasep \newdimen\smarraycolsep \newdimen\smarrayextrasep \newdimen\tab@width \newdimen\col@sep \newdimen\tab@endheight % \end{macrocode} % % Some skip registers too. Phew. % % \begin{macrocode} \newskip\tab@leftskip \newskip\tab@rightskip % \end{macrocode} % % And some switches. The first three are for the parser. % % \begin{macrocode} \newif\iftab@firstcol \newif\iftab@initrule \newif\iftab@rule \newif\iftab@vgap % \end{macrocode} % % Now assign some default values to new dimen parameters. These definitions % are essentially the equivalent of an |\openup 1\jot| in \env{array}, but % not in \env{tabular}. This looks nice, I think. % % \begin{macrocode} \tabextrasep\z@ \arrayextrasep\jot \smarraycolsep\thr@@\p@ \smarrayextrasep\z@ % \end{macrocode} % % Set some things up for alien table environments. % % \begin{macrocode} \let\tab@extrasep\tabextrasep \let\tab@penalty\relax % \end{macrocode} % % % \subsection{Some little details} % % \begin{macro}{\@maybe@unskip} % % This macro solves a little problem. In an alignment (and in other places) % it's desirable to suppress trailing space. The usual method, to say % |\unskip|, is a little hamfisted, because it removes perfectly reasonable % aligning spaces like |\hfil|s. While as a package writer I can deal with % this sort of thing by saying |\kern\z@| in appropriate places, it can % annoy users who are trying to use |\hfill| to override alignment in funny % places. % % My current solution seems to be acceptable. I'll remove the natural width % of the last glue item, so that it can still stretch and shrink if % necessary. The implementation makes use of the fact that multiplying % a \<skip> by a \<number> kills off the stretch. (Bug fix: don't do this % when we're in vertical mode.) % % \begin{macrocode} \def\@maybe@unskip{\ifhmode\hskip\m@ne\lastskip\relax\fi} % \end{macrocode} % % \end{macro} % % \begin{macro}{\q@delim} % % Finally, for the sake of niceness, here's a delimiter token I can use % for various things. It's a `quark', for what it's worth (i.e., it expands % to itself) although I'm not really sure why this is a good thing. As far % as I'm concerned, it's important that it has a unique meaning (i.e., that % it won't be |\ifx|-equal to other things, or something undefined) and that % it won't be used where I don't expect it to be used. \TeX\ will loop % horridly if it tries to expand this, so I don't think that quarks are % wonderfully clever thing to use. (Maybe it should really expand to % something like `\syntax{<quark>"."}', which will rapdly fill \TeX's memory % if it gets accidentally expanded. Still, I'll leave it as it is until % such time as I understand the idea more.) % % \begin{macrocode} \def\q@delim{\q@delim} % \end{macrocode} % % \end{macro} % % % \subsection{Parser states} % % Now we start on the parser. It's really simple, deep down. We progress % from state to state, extracing tokens from the preamble and building % command names from them. Each command calls one of the element-building % routines, which works out which state it should be in. We go through each % of the states in between (see later) doing default things for the ones we % missed out. % % Anyway, here's some symbolic names for the states. It makes my life % easier. % % \begin{macrocode} \chardef\tab@startstate 0 \chardef\tab@loopstate 1 \chardef\tab@rulestate 1 \chardef\tab@prespcstate 2 \chardef\tab@prestate 3 \chardef\tab@colstate 4 \chardef\tab@poststate 5 \chardef\tab@postspcstate 6 \chardef\tab@limitstate 7 % \end{macrocode} % % % \subsection{Adding things to token lists} % % Define some macros for adding stuff to the beginning and end of token % lists. This is really easy, actually. Here we go. % % \begin{macrocode} \def\tab@append#1#2{#1\expandafter{\the#1#2}} \def\tab@prepend#1#2{% \toks@{#2}#1\expandafter{\the\expandafter\toks@\the#1}% } % \end{macrocode}% % % % \subsection{Committing a column to the preamble} % % Each time we pass the `rule' state, we `commit' the tokens we've gathered % so far to the main preamble token list. This is how we do it. Note the % icky use of |\expandafter|. % % \begin{macrocode} \def\tab@commit{% % \end{macrocode} % % If this isn't the first column, then we need to put in a column separator. % % \begin{macrocode} \iftab@firstcol\else% \expandafter\tab@append\expandafter\tab@preamble% \expandafter{\tab@tabtext}% \fi% % \end{macrocode} % % Now we spill the token registers into the main list in a funny order (which % is why we're doing it in this strange way in the first place. % % \begin{macrocode} \toks@\expandafter{\tab@midtext}% \tab@preamble\expandafter{% \the\expandafter\tab@preamble% \the\expandafter\tab@pretext% \the\expandafter\tab@userpretext% \the\expandafter\toks@% \the\tab@posttext% }% % \end{macrocode} % % Now reset token lists and things for the next go round. % % \begin{macrocode} \tab@firstcolfalse% \tab@pretext{}% \tab@userpretext{}% \tab@posttext{}% } % \end{macrocode} % % % \subsection{Playing with parser states} % % \begin{macro}{\tab@setstate} % % This is how we set new states. The algorithm is fairly simple, really. % % ^^A Let's see how good my TeX really is... ;-) % ^^A Actually, it doesn't seem to have worked out too badly. Maybe I should % ^^A write a package to do this automatically. It's rather tricky, though. % % \def\qq{\mbox{\quad}} % \sbox{0}{\itshape\textunderscore}\def\_{\usebox{0}} % % \begin{quote} % {\bf while} $\it tab\_state \ne s$ {\bf do} \\ % \qq $\mathit{tab\_state = tab\_state}+1$; \\ % \qq {\bf if} $\it tab\_state = tab\_limitState$ {\bf then} % $\it tab\_state=tab\_loopState$; \\ % \qq {\bf if} $\it tab\_state = tab\_preSpcState$ {\bf then} \\ % \qq \qq {\bf if} $\it tab\_initRule$ {\bf then} \\ % \qq \qq \qq $\it tab\_initRule = {\bf false}$; \\ % \qq \qq {\bf else} \\ % \qq \qq \qq {\bf if} $\it tab\_inMultiCol$ {\bf then moan}; \\ % \qq \qq \qq $\it commit$; \\ % \qq \qq \qq $\it append(tab\_shortLine,\hbox{`|&\omit|')}$; \\ % \qq \qq {\bf end\,if}; \\ % \qq {\bf end\,if}; \\ % \qq {\bf if} $\it tab\_state \ne s$ {\bf then} % $\it do\_default(tab\_state)$; \\ % {\bf end\,while}; % \end{quote} % % First we decide if there's anything to do. If so, we call another macro to % do it for us. % % \begin{macrocode} \def\tab@setstate#1{% \ifnum#1=\tab@state\else% \def\@tempa{\tab@setstate@i{#1}}% \@tempa% \fi% } % \end{macrocode} % % This is where the fun is. First we bump the state by one, and loop back % if we fall off the end. % % \begin{macrocode} \def\tab@setstate@i#1{% \global\advance\tab@state\@ne% \ifnum\tab@state>\tab@limitstate% \global\tab@state\tab@loopstate% \fi% % \end{macrocode} % % Now, if we've just passed the ruleoff state, we commit the current text % \emph{unless} this was the strange initial rule at the very beginning. We % provide a little hook here so that |\multicolumn| can moan if you try and % give more than one column there. We also add another tab/omit pair to the % list we use for |\vgap|. % % \begin{macrocode} \ifnum\tab@state=\tab@prespcstate% \iftab@initrule% \tab@initrulefalse% \else% \tab@looped% \tab@commit% \tab@append\tab@shortline{&\omit}% \fi% \fi% % \end{macrocode} % % Now we decide whether to go round again. If not, we do the default thing % for this state. This is mainly here so that we can put the |\tabcolsep| or % whatever in if the user didn't give an \lit{@} expression. % % \begin{macrocode} \ifnum#1=\tab@state% \let\@tempa\relax% \else% \csname tab@default@\number\tab@state\endcsname% \fi% \@tempa% } % \end{macrocode} % % \end{macro} % % Now we set up the default actions for the various states. % % In state~2 (pre-space) we add in the default gap if either we didn't have % an \lit{@} expression in the post-space state or there was an explicit % intervening rule. % % \begin{macrocode} \@namedef{tab@default@2}{% \iftab@rule% \tab@append\tab@pretext{\hskip\col@sep}% \fi% } % \end{macrocode} % % If the user omits the column type, we insert an `l'-type column and moan % a lot. % % \begin{macrocode} \@namedef{tab@default@4}{% \tab@err@misscol% \tab@append\tab@pretext{\tab@bgroup\relax}% \tab@append\tab@posttext{\relax\tab@egroup\hfil}% \tab@append\tab@shortline{\hfil}% \advance\tab@columns\@ne% } % \end{macrocode} % % Finally we deal with the post-space state. We set a marker so that we % put in the default space in the pre-space state later too. % % \begin{macrocode} \@namedef{tab@default@6}{% \tab@append\tab@posttext{\hskip\col@sep}% \tab@ruletrue% } % \end{macrocode} % % % \subsection{Declaring token types} % % \begin{macro}{\tab@extracol} % % Before we start, we need to handle |\extracolsep|. This is a right pain, % because the original version of \env{tabular} worked on total expansion, % which is a Bad Thing. On the other hand, turning |\extracolsep| into a % |\tabskip| is also a major pain. % % \begin{macrocode} \def\tab@extracol#1#2{\tab@extracol@i#1#2\extracolsep{}\extracolsep\end} \def\tab@extracol@i#1#2\extracolsep#3#4\extracolsep#5\end{% \ifx @#3@% \def\@tempa{#1{#2}}% \else% \def\@tempa{#1{#2\tabskip#3\relax#4}}% \fi% \@tempa% } % \end{macrocode} % % \end{macro} % % This is where we do the work for inserting preamble elements. % % \begin{macro}{\tabruletype} % % Inserting rules is interesting, because we have to decide where to put % them. If this is the funny initial rule, it goes in the pre-text list, % otherwise it goes in the post-text list. We work out what to do first % thing: % % \begin{macrocode} \def\tabruletype#1{\tab@extracol\tabruletype@i{#1}}% \def\tabruletype@i#1{% \iftab@initrule% \let\tab@tok\tab@pretext% \else% \let\tab@tok\tab@posttext% \fi% % \end{macrocode} % % Now if we're already in the rule state, we must have just done a rule. % This means we must put in the |\doublerulesep| space, both here and in the % shortline list. Otherwise we just stick the rule in. % % This is complicated, because |\vgap| needs to be able to remove some bits % of rule. We pass each one to a macro |\tab@ckr|, together with the column % number, which is carefully bumped at the right times, and this macro will % vet the rules and output the appropriate ones. There's lots of extreme % |\expandafter| nastiness as a result. Amazingly, this actually works. % % \begin{macrocode} \ifnum\tab@state=\tab@rulestate% \tab@append\tab@tok{\hskip\doublerulesep\begingroup#1\endgroup}% \expandafter\tab@append\expandafter\tab@shortline\expandafter{% \expandafter\hskip\expandafter\doublerulesep% \expandafter\tab@ckr\expandafter{\the\tab@columns}% {\begingroup#1\endgroup}% }% \else% \tab@setstate\tab@rulestate% \tab@append\tab@tok{\begingroup#1\endgroup}% \expandafter\tab@append\expandafter\tab@shortline\expandafter{% \expandafter\tab@ckr\expandafter{\the\tab@columns}% {\begingroup#1\endgroup}% }% \fi% % \end{macrocode} % % Finally, we say there was a rule here, so that default space gets put in % after this. Otherwise we lose lots of generality. % % \begin{macrocode} \tab@ruletrue% } % \end{macrocode} % % \end{macro} % % \begin{macro}{\tabspctype} % % We need to work out which space-state we should be in. Then we just put % the text in. Easy, really. % % \begin{macrocode} \def\tabspctype#1{\tab@extracol\tabspctype@i{#1}}% \def\tabspctype@i#1{% \tab@rulefalse% \ifnum\tab@state>\tab@prespcstate% \tab@setstate\tab@postspcstate% \let\tab@tok\tab@posttext% \else% \tab@setstate\tab@prespcstate% \let\tab@tok\tab@pretext% \fi% \tab@append\tab@tok{\begingroup#1\endgroup}% } % \end{macrocode} % % \end{macro} % % \begin{macro}{\tabcoltype} % % If we're already in the column state, we bump the state and loop round % again, to get all the appropriate default behaviour. We bump the column % counter, and add the bits of text we were given to appropriate token lists. % We also add the |\hfil| glue to the shortline list, to space out the rules % properly. % % \begin{macrocode} \def\tabcoltype#1#2{% \ifnum\tab@state=\tab@colstate% \global\advance\tab@state\@ne% \fi% \advance\tab@columns\@ne% \tab@setstate\tab@colstate% \tab@append\tab@pretext{#1}% \tab@append\tab@posttext{#2}% \tab@append\tab@shortline{\hfil}% } % \end{macrocode} % % \end{macro} % % \begin{macro}{\tabuserpretype} % \begin{macro}{\tabuserposttype} % % These are both utterly trivial. % % \begin{macrocode} \def\tabuserpretype#1{% \tab@setstate\tab@prestate% \tab@prepend\tab@userpretext{#1}% } % \end{macrocode} % % \begin{macrocode} \def\tabuserposttype#1{% \tab@setstate\tab@poststate% \tab@prepend\tab@posttext{#1}% } % \end{macrocode} % % \end{macro} % \end{macro} % % % \subsection{The colset stack} % % Let's start with something fairly easy. We'll keep a stack of column sets % so that users don't get confused by package authors changing the current % column set. This is fairly easy, really. % % \begin{macro}{\tab@push} % \begin{macro}{\tab@pop} % \begin{macro}{\tab@head} % % These are the stack management routines. The only important thing to note % is that |\tab@head| must take place \emph{only} in \TeX's mouth, so we can % use it in |\csname|\dots|\endcsname| constructions. % % \begin{macrocode} \def\tab@push#1#2{% \toks@{{#2}}% \expandafter\def\expandafter#1\expandafter{\the\expandafter\toks@#1}% } \def\tab@pop#1{\expandafter\def\expandafter#1\expandafter{\@gobble#1}} \def\tab@head#1{\expandafter\tab@head@i#1\relax} \def\tab@head@i#1#2\relax{#1} % \end{macrocode} % % \end{macro} % \end{macro} % \end{macro} % % \begin{macro}{\colset} % \begin{macro}{\colpush} % \begin{macro}{\colpop} % % Now we can define the user macros. % % \begin{macrocode} \def\tab@colstack{{tabular}} \def\colset{\colpop\colpush} \def\colpush{\tab@push\tab@colstack} \def\colpop{\tab@pop\tab@colstack} % \end{macrocode} % % \end{macro} % \end{macro} % \end{macro} % % \begin{macro}{\tab@colset} % % Now we define a shortcut for reading the top item off the stack. % % \begin{macrocode} \def\tab@colset{\tab@head\tab@colstack} % \end{macrocode} % % \end{macro} % % % \subsection{The main parser routine} % % \begin{macro}{\tab@initread} % % This macro sets up lots of variables to their normal states prior to % parsing a preamble. Some things may need changing, but not many. % % \begin{macrocode} \def\tab@initread{% % \end{macrocode} % % First, reset the parser state to the start state. % % \begin{macrocode} \global\tab@state\tab@startstate% % \end{macrocode} % % We clear the token lists to sensible values, mostly. The midtext macro % contains what to put in the very middle of each template -- |\multicolumn| % will insert its argument here. % % \begin{macrocode} \tab@preamble{}% \tab@shortline{}% \def\tab@tabtext{&}% \def\tab@midtext{\ignorespaces####\@maybe@unskip}% \tab@pretext{}% \tab@userpretext{}% \tab@posttext{}% \let\tab@multicol\@empty% \def\tab@startpause{\penalty\postdisplaypenalty\medskip}% \def\tab@endpause{\penalty\predisplaypenalty\medskip}% % \end{macrocode} % % Finally, reset the column counter, don't raise errors when we loop, and set % some parser flags to their appropriate values. % % \begin{macrocode} \tab@columns\z@% \let\tab@looped\relax% \tab@ruletrue% \tab@initruletrue% \tab@firstcoltrue% } % \end{macrocode} % % \end{macro} % % \begin{macro}{\tab@readpreamble} % % This is the main macro for preamble handling. Actually, all it does is % gobble its argument's leading brace and call another macro, but it does it % with style. % % \begin{macrocode} \def\tab@readpreamble#1{% \tab@doreadpream{#1}% \iftab@initrule\global\tab@state\tab@prespcstate\fi% \tab@setstate\tab@rulestate% \tab@commit% } % \end{macrocode} % % \end{macro} % % \begin{macro}{\tab@doreadpream} % % The preamble is in an argument. Previous versions used a nasty trick using % |\let| and |\afterassignment|. Now we use an explicit end token, to allow % dodgy column type handlers to scoop up the remaining preamble tokens % and process them. Not that anyone would want to do that, oh no (see % the \lit{[} type in the \env{eqnarray} environment |;-)|). % % \begin{macrocode} \def\tab@doreadpream#1{\tab@mkpreamble#1\q@delim} % \end{macrocode} % % \end{macro} % % \begin{macro}{\tab@mkpreamble} % % This is the main parser routine. It takes each token in turn, scrutinises % it carefully, and does the appropriate thing with it. % % The preamble was given as an argument to |\tab@doreadpream|, and that has % helpfully stripped off the initial |{| character. We need to pick off the % next token (whatever it is) so we can examine it. We'll use |\futurelet| % so we can detect groups and things in funny places. % % \begin{macrocode} \def\tab@mkpreamble{\futurelet\@let@token\tab@mkpreamble@i} % \end{macrocode} % % If we find a space token, we'll go off and do something a bit special, % since spaces are sort of hard to handle. Otherwise we'll do it in the old % fashioned way. % % \begin{macrocode} \def\tab@mkpreamble@i{% \ifx\@let@token\@sptoken% \expandafter\tab@mkpreamble@spc% \else% \expandafter\tab@mkpreamble@ii% \fi% } % \end{macrocode} % % If we find a |\@@endpreamble| token, that's it and we're finished. We just % gobble it and return. Otherwise, if it's an open group character, we'll % complain because someone's probably tried to put an argument in the wrong % place. Finally, if none of the other things apply, we'll deal with the % character below. % % \begin{macrocode} \def\tab@mkpreamble@ii{% \ifx\@let@token\q@delim% \def\@tempa{\let\@let@token}% \else% \ifcat\bgroup\noexpand\@let@token% \tab@err@oddgroup% \def\@tempa##1{\tab@mkpreamble}% \else% \let\@tempa\tab@mkpreamble@iii% \fi% \fi% \@tempa% } % \end{macrocode} % % Handle a character. This involves checking to see if it's actually % defined, and then doing it. Doing things this way means we won't get % stranded in mid-preamble unless a package author has blown it. % % \begin{macrocode} \def\tab@mkpreamble@iii#1{% \@ifundefined{\tab@colset!col.\string#1}{% \tab@err@undef{#1}\tab@mkpreamble% }{% \@nameuse{\tab@colset!col.\string#1}% }% } % \end{macrocode} % % If we get given a space character, we'll look up the command name as % before. If no-one's defined the column type we'll just skip it silently, % which lets users do pretty formatting if they like. % % \begin{macrocode} \@namedef{tab@mkpreamble@spc} {% \@ifundefined{\tab@colset!col. }{% \tab@mkpreamble% }{% \@nameuse{\tab@colset!col. }% }% } % \end{macrocode} % % \end{macro} % % \begin{macro}{\coldef} % % Here's how to define column types the nice way. Some dexterity is required % to make everything work right, but it's simple really. % % \begin{macrocode} \def\coldef{\@ifnextchar[\coldef@i{\coldef@i[\tab@colset]}} \def\coldef@i[#1]#2#3#{\coldef@ii[#1]{#2}{#3}} \def\coldef@ii[#1]#2#3#4{% \expandafter\def\csname#1!col.\string#2\endcsname#3{% #4\tab@mkpreamble% }% } % \end{macrocode} % % \end{macro} % % \begin{macro}{\collet} % % We'd like to let people copy column types from other places. This is how % to do it. % % \begin{macrocode} \def\collet{\@ifnextchar[\collet@i{\collet@i[\tab@colset]}} \def\collet@i[#1]#2{% \@ifnextchar=% {\collet@ii[#1]{#2}}% {\collet@ii[#1]{#2}=}% } \def\collet@ii[#1]#2={% \@ifnextchar[% {\collet@iii[#1]{#2}}% {\collet@iii[#1]{#2}[\tab@colset]}% } \def\collet@iii[#1]#2[#3]#4{% \expandafter\let\csname#1!col.\string#2\expandafter\endcsname% \csname#3!col.\string#4\endcsname% } % \end{macrocode} % % \end{macro} % % \begin{macro}{\newcolumntype} % % We just bundle the text off to |\newcommand| and expect it to cope. It % ought to. The column type code inserts the user's tokens directly, rather % than calling |\tab@doreadpream| recursively. The magic control sequence % is the one looked up by the parser. % % There's some additional magic here for compatiblity with the obscure way % that \package{array} works. % % \begin{macrocode} \def\newcolumntype#1{\@ifnextchar[{\nct@i{#1}}{\nct@i#1[0]}} \def\nct@i#1[#2]{\@ifnextchar[{\nct@ii{#1}[#2]}{\nct@iii{#1}{[#2]}}} \def\nct@ii#1[#2][#3]{\nct@iii{#1}{[#2][#3]}} \def\nct@iii#1#2#3{% \expandafter\let\csname\tab@colset!col.\string#1\endcsname\relax% \expandafter\newcommand\csname\tab@colset!col.\string#1\endcsname#2{% \tab@deepmagic{#1}% \tab@mkpreamble% #3% }% } % \end{macrocode} % % Now for some hacking for compatibility with \package{tabularx}. % % \begin{macrocode} \def\newcol@#1[#2]{\nct@iii{#1}{[#2]}} % \end{macrocode} % % And now some more. This is seriously deep magic. Hence the name. % % \begin{macrocode} \def\tab@deepmagic#1{% \csname NC@rewrite@\string#1\endcsname\NC@find\tab@@magic@@% } \def\NC@find#1\tab@@magic@@{} % \end{macrocode} % % \end{macro} % % % \subsection{Standard column types} % % First, make sure we're setting up the right columns. This also sets the % default for the user. Other packages must not use the |\colset| command % for defining columns -- they should use the stack operations defined above. % % \begin{macrocode} \colset{tabular} % \end{macrocode} % % Now do the simple alignment types. These are fairly simple. The % mysterious kern in the \lit{l} type is to stop the |\col@sep| glue from % vanishing due to the |\unskip| inserted by the standard |\tab@midtext| if % the column contains no text. (Thanks for spotting this bug go to that % nice Mr~Carlisle.) % % \begin{macrocode} \coldef l{\tabcoltype{\kern\z@\tab@bgroup}{\tab@egroup\hfil}} \coldef c{\tabcoltype{\hfil\tab@bgroup}{\tab@egroup\hfil}} \coldef r{\tabcoltype{\hfil\tab@bgroup}{\tab@egroup}} % \end{macrocode} % % Some extensions now. These are explicitly teextual or mathematical % columns. Can be useful if you're providing column types for other people. % I've inserted a kern here for exactly the same reason as for the \lit{l} % column type above. % % \begin{macrocode} \coldef T#1{\tab@aligncol{#1}{\tab@btext}{\tab@etext}} \coldef M#1{\tab@aligncol{#1}{\tab@bmaths}{\tab@emaths}} \def\tab@aligncol#1#2#3{% \if#1l\tabcoltype{\kern\z@#2}{#3\hfil}\fi% \if#1c\tabcoltype{\hfil#2}{#3\hfil}\fi% \if#1r\tabcoltype{\hfil#2}{#3}\fi% } % \end{macrocode} % % Now for the default rules. % % \begin{macrocode} \coldef |{\tabruletype{\vrule\@width\arrayrulewidth}} \coldef !#1{\tabruletype{#1}} % \end{macrocode} % % Deal with \lit{@} expressions. % % \begin{macrocode} \coldef @#1{\tabspctype{#1}} % \end{macrocode} % % And the paragraph types. I've added things to handle footnotes here. % % \begin{macrocode} \coldef p#1{\tabcoltype% {\savenotes\vtop\tab@bpar{#1}}% {\tab@epar\spewnotes\hfil}} \coldef m#1{\tabcoltype% {\savenotes$\vcenter\tab@bpar{#1}}% {\tab@epar$\spewnotes\hfil}} \coldef b#1{\tabcoltype% {\savenotes\vbox\tab@bpar{#1}}% {\tab@epar\spewnotes\hfil}} % \end{macrocode} % % Phew. Only a few more left now. The user text ones. % % \begin{macrocode} \coldef >#1{\tabuserpretype{#1}} \coldef <#1{\tabuserposttype{#1}} % \end{macrocode} % % The strange column type. % % \begin{macrocode} \coldef ##1#2{\tabcoltype{#1}{#2}} % \end{macrocode} % % And \lit{*}, which repeats a preamble spec. This is really easy, and not % at all like the original one. % % \begin{macrocode} \coldef *#1#2{% \count@#1% \loop\ifnum\count@>0\relax% \tab@doreadpream{#2}% \advance\count@\m@ne% \repeat% } % \end{macrocode} % % % \subsection{Paragraph handling} % % First of all, starting new paragraphs: the vbox token is already there, and % we have the width as an argument. % % \begin{macro}{\tab@bpar} % % There are some gymnastics to do here to support lists which form the % complete text of the parbox. One of the odd things I'll do here is to % not insert a strut on the first line: instead, I'll put the text into a % box register so that I can inspect it later. So that I have access to % the height of the first line, I'll use a |\vtop| -- I can get at the % final depth by using |\prevdepth|, so this seems to be the most general % solution. % % \begin{macrocode} \def\tab@bpar#1{% \bgroup% \hsize#1\relax% \@arrayparboxrestore% \setbox\z@\vtop\bgroup \global\@minipagetrue% \everypar{% \global\@minipagefalse% \everypar{}% }% } % \end{macrocode} % % \end{macro} % % \begin{macro}{\tab@epar} % % To end the paragraph, close the box. That sounds easy, doesn't it? % I need to space out the top and bottom of the box so that it looks as if % struts have been applied. % % \begin{macrocode} \def\tab@epar{% % \end{macrocode} % % Anyway, I should end the current paragraph if I'm still in horizontal % mode. A simple |\par| will do this nicely. I'll also remove any trailing % vertical glue (which may be left there by a list environment), because % things will look very strange otherwise. % % \begin{macrocode} \ifhmode\@maybe@unskip\par\fi% \unskip% % \end{macrocode} % % Now I'll look at the depth of the last box: if it's less deep than my % special strut, I'll cunningly backpedal by a bit, and add a box with the % appropriate depth. Since this will lie on the previous baseline, it won't % alter the effective height of the box. There's a snag here. |\prevdepth| % may be wrong for example if the last thing inserted was a rule, or the % box is just empty. Check for this specially. (Thanks to Rowland for % spotting this.) % % \begin{macrocode} \ifdim\prevdepth>-\@m\p@\ifdim\prevdepth<\dp\@arstrutbox% \kern-\prevdepth% \nointerlineskip% \vtop to\dp\@arstrutbox{}% \fi\fi% % \end{macrocode} % % I've finished the bottom of the box now: I'll close it, and start work on % the top again. % % \begin{macrocode} \egroup% % \end{macrocode} % % For top-alignment to work, the first item in the box must be another box. % (This is why I couldn't just set |\prevdepth| at the beginning.) If the % box isn't high enough, I'll add a box of the right height and then kern % backwards so that the `real' first box ends up in the right place. % % \begin{macrocode} \ifdim\ht\z@<\ht\@arstrutbox% \vbox to\ht\@arstrutbox{}% \kern-\ht\z@% \fi% \unvbox\z@% \egroup% } % \end{macrocode} % % \end{macro} % % % \subsection{Gentle persuasion} % % To persuade \package{longtable} to work, we emulate some features of % the \package{array} way of doing things. It's a shame, but we have to do % it, because \package{longtable} came first. % % Note the horribleness with the grouping here. In order to get everything % expanded at the right time, |\@preamble| just replaces itself with the (not % expanded!) preamble string, using |\the|. This means that the preamble % string must be visible in the group just above us. Now, % \package{longtable} (and \package{array} for that matter) does % |\@mkpreamble| immediately after opening a new group. So all we need to do % is close that group, do our stuff, and reopen the group again. (Evil % laughter\dots) % % \begin{macrocode} \def\@mkpream#1{% \endgroup% \colset{tabular}% \tab@initread% \def\tab@multicol{\@arstrut}% \tab@preamble{\tab@multicol}% \def\tab@midtext{\ignorespaces\@sharp\@sharp\@maybe@unskip}% \tab@readpreamble{#1}% \gdef\@preamble{\the\tab@preamble}% \let\tab@bgroup\begingroup% \let\tab@egroup\endgroup% \begingroup% } % \end{macrocode} % % % \subsection{Debugging} % % This macro just parses a preamble and displays it on the terminal. It % means I can see whether the thing's working. % % \begin{macrocode} \def\showpream#1{% \tab@initread% \tab@readpreamble{#1}% \showthe\tab@preamble% \showthe\tab@shortline% } % \end{macrocode} % % A quick macro for showing column types. % % \begin{macrocode} \def\showcol#1{% \expandafter\show\csname\tab@colset!col.\string#1\endcsname% } % \end{macrocode} % % % \subsection{The \env{tabular} and \env{array} environments} % % This is where we define the actual environments which users play with. % % \subsubsection{The environment routines} % % The real work is done in the |\@array| macro later. We just set up lots % (and I mean \emph{lots}) of parameters first, and then call |\@array|. % % \begin{macro}{\tab@array} % % The |\tab@array| macro does most of the common array things. % % \begin{macrocode} \def\tab@array{% \tab@width\z@% \let\tab@bgroup\tab@bmaths% \let\tab@egroup\tab@emaths% \@tabarray% } % \end{macrocode} % % \end{macro} % % \begin{macro}{\tab@btext} % \begin{macro}{\tab@bmaths} % \begin{macro}{\tab@etext} % \begin{macro}{\tab@emaths} % % These macros contain appropriate things to use when typesetting % text or maths macros. They're all trivial. They're here only for % later modification by funny things like the \env{smarray} environment. % % \begin{macrocode} \def\tab@btext{\begingroup} \def\tab@bmaths{$} \def\tab@etext{\endgroup} \def\tab@emaths{\m@th$} % \end{macrocode} % % \end{macro} % \end{macro} % \end{macro} % \end{macro} % % \begin{environment}{array} % % Now for the \env{array} environment. The `|$|' signs act as a group, so we % don't need to do extra grouping this time. Closing the environment is % easy. % % \begin{macrocode} \def\array{% \col@sep\arraycolsep% \let\tab@extrasep\arrayextrasep% \tab@normalstrut% \tab@array% } \def\endarray{% \crcr% \egroup% \tab@right% \tab@restorehlstate% } % \end{macrocode} % % \end{environment} % % \begin{environment}{smarray} % % Now for something a little different. The \env{smarray} environment % gives you an array with lots of small text. % % \begin{macrocode} \def\smarray{% \extrarowheight\z@% \col@sep\smarraycolsep% \let\tab@extrasep\smarrayextrasep% \def\tab@bmaths{$\scriptstyle}% \def\tab@btext{\begingroup\scriptsize}% \setbox\z@\hbox{\scriptsize\strut}% \dimen@\ht\z@\dimen\tw@\dp\z@\tab@setstrut% \tab@array% } \let\endsmarray\endarray % \end{macrocode} % % \end{environment} % % \begin{macro}{\tabstyle} % % This is a little hook that document designers can use to modify the % appearance of tables throughout a document. For example, I've set it to % make the text size |\small| in all tables in this document. Macro writers % shouldn't try to use it as a hook for their own evilness, though. I've % used |\providecommand| to avoid nobbling an existing definition. % % \begin{macrocode} \providecommand\tabstyle{} % \end{macrocode} % % \end{macro} % % \begin{macro}{\@tabular} % % The two \env{tabular} environments share lots of common code, so we % separate that out. (This needs to be done better.) All we really do here % is set up the |\tab@bgroup| and |\tab@egroup| to localise things properly, % and then go. % % \begin{macrocode} \def\@tabular#1{% \tabstyle% \tab@width#1% \let\tab@bgroup\tab@btext% \let\tab@egroup\tab@etext% \col@sep\tabcolsep% \let\tab@extrasep\tabextrasep% \tab@normalstrut% \@tabarray% } % \end{macrocode} % % \end{macro} % % \begin{environment}{tabular} % \begin{environment}{tabular*} % % These environments just call a macro which does all the common stuff. % % \begin{macrocode} \def\tabular{\@tabular\z@} \expandafter\let\csname tabular*\endcsname\@tabular \let\endtabular\endarray \expandafter\let\csname endtabular*\endcsname\endarray % \end{macrocode} % % \end{environment} % \end{environment} % % \subsubsection{Setting the strut height} % % \begin{macro}{\tab@setstrut} % % We use a magical strut, called |\@arstrut|, which keeps the table from % collapsing around our heads. This is where we set it up. % % It bases the array strut size on the given values of |\dimen@| and % |\dimen\tw@|, amended by various appropriate fiddle values added in by % various people. % % \begin{macrocode} \def\tab@setstrut{% \setbox\@arstrutbox\hbox{% \vrule% \@height\arraystretch\dimen@% \@depth\arraystretch\dimen\tw@% \@width\z@% }% } % \end{macrocode} % % \end{macro} % % \begin{macro}{\tab@normalstrut} % % This sets the strut the normal way, from the size of |\strutbox|. % % \begin{macrocode} \def\tab@normalstrut{% \dimen@\ht\strutbox\advance\dimen@\extrarowheight% \dimen\tw@\dp\strutbox% \tab@setstrut% } % \end{macrocode} % % \end{macro} % % \subsubsection{Setting up the alignment} % % The following bits are mainly for other packages to hook themselves onto. % % \begin{macrocode} \let\@arrayleft\relax% \let\@arrayright\relax% % \end{macrocode} % % \begin{macrocode} \def\@tabarray{% \let\@arrayleft\relax% \let\@arrayright\relax% \@ifnextchar[\@array{\@array[c]}% } % \end{macrocode} % % \begin{macro}{\@array} % % The |\@array| macro does most of the real work for the environments. The % first job is to set up the row strut, which keeps the table rows at the % right height. We just take the normal strut box, and extend its height by % the |\extrarowheight| length parameter. % % \begin{macrocode} \def\@array[#1]#2{% % \end{macrocode} % % Sort out the hline state variable. We'll store the old value in a % control sequence to avoid wasting any more count registers. % % \begin{macrocode} \edef\tab@restorehlstate{% \global\tab@endheight\the\tab@endheight% \gdef\noexpand\tab@hlstate{\tab@hlstate}% }% \def\tab@hlstate{n}% % \end{macrocode} % % Now we read the preamble. All the clever things we've already done are % terribly useful here. % % The |\tab@setcr| sets up |\\| to be a newline even if users have changed it % using something like |\raggedright|. % % \begin{macrocode} \colset{tabular}% \tab@initread% \def\tab@midtext{\tab@setcr\ignorespaces####\@maybe@unskip}% \def\tab@multicol{\@arstrut\tab@startrow}% \tab@preamble{\tab@multicol\tabskip\z@skip}% \tab@readpreamble{#2}% % \end{macrocode} % % Set up the default tabskip glue. This is easy: there isn't any. % % \begin{macrocode} \tab@leftskip\z@skip% \tab@rightskip\z@skip% % \end{macrocode} % % Now set up the positioning of the table. This is put into a separate macro % because it's rather complicated. % % \begin{macrocode} \tab@setposn{#1}% % \end{macrocode} % % Now work out how to start the alignment. % % \begin{macrocode} \ifdim\tab@width=\z@% \def\tab@halign{}% \else% \def\tab@halign{to\tab@width}% \fi% % \end{macrocode} % % Finally, do all the normal things we need to do before an alignment. Note % that we define |\tabularnewline| first, then set |\\| from that (using % |\tab@setcr|). Since |\\| is reset in the |\tab@midtext| of every table % cell, it becomes secondary to |\tabularnewline|. Doing things this way % avoids the problems with declarations like |\raggedright| which redefine % |\\| in their own (usually rather strange) way, so you don't need to mess % about with things like the |\PreserveBackslash| command given in the % \textit{\LaTeX\ Companion}. % % \begin{macrocode} \lineskip\z@\baselineskip\z@% \m@th% \def\tabularnewline{\tab@arraycr\tab@penalty}% \tab@setcr% \let\par\@empty% \everycr{}\tabskip\tab@leftskip% \tab@left\halign\tab@halign\expandafter\bgroup% \the\tab@preamble\tabskip\tab@rightskip\cr% } % \end{macrocode} % % \end{macro} % % You've no doubt noticed the |\tab@left| and |\tab@right| macros above. % These are set up here and elsewhere to allow other things to gain control % at various points of the table (they include and take the place of the % |\@arrayleft| and |\@arrayright| hooks in \package{array}, put in for % \package{delarray}'s use. % % \subsubsection{Positioning the table} % % \begin{macro}{\tab@setposn} % % This macro sets everything up for the table's positioning. It's rather % long, but not all that complicated. Honest. % % First, we set up some defaults (for centring). If anything goes wrong, we % just do the centring things. % % \begin{macrocode} \def\tab@setposn#1{% \def\tab@left{% \savenotes% \leavevmode\hbox\bgroup$\@arrayleft\vcenter\bgroup% }% \def\tab@right{% \egroup% \m@th\@arrayright$\egroup% \spewnotes% }% \global\tab@endheight\z@% % \end{macrocode} % % For the standard positioning things, we just do appropriate boxing things. % Note that the dollar signs are important, since \package{delarray} might % want to put its delimiters in here. % % The |\if@tempswa| switch it used to decide if we're doing an unboxed % tabular. We'll set it if we find an unbox-type position code, and then % check that everything's OK for this. % % \begin{macrocode} \@tempswafalse% \let\tab@penalty\relax% \if#1t% \def\tab@left{% \savenotes% \leavevmode\setbox\z@\hbox\bgroup$\@arrayleft\vtop\bgroup% }% \def\tab@right{% \egroup% \m@th\@arrayright$\egroup% \tab@raisebase% \spewnotes% }% \gdef\tab@hlstate{t}% \global\tab@endheight\ht\@arstrutbox% \else\if#1b% \def\tab@left{% \savenotes% \leavevmode\setbox\z@\hbox\bgroup$\@arrayleft\vbox\bgroup% }% \def\tab@right{% \egroup% \m@th\@arrayright$\egroup% \tab@lowerbase% \spewnotes% }% \gdef\tab@hlstate{b}% \else% \if#1L\@tempswatrue\fi% \if#1C\@tempswatrue\fi% \if#1R\@tempswatrue\fi% \fi\fi% % \end{macrocode} % % Now for some tests to make sure we're allowed to do the unboxing. We text % for |\@arrayleft| being defined, because people trying to hook us won't % understand unboxed tabulars. % % \begin{macrocode} \if@tempswa\ifhmode% \ifinner\tab@err@unbrh\@tempswafalse\else\par\fi% \fi\fi% \if@tempswa\ifmmode\tab@err@unbmm\@tempswafalse\fi\fi% \if@tempswa\ifx\@arrayleft\relax\else% \tab@err@unbext\@tempswafalse% \fi\fi% % \end{macrocode} % % Finally, if we're still doing an unboxed alignment, we need to sort out the % spacing. We know that no-one's tried to hook on to the environment, so we % clear |\tab@left| and |\tab@right|. % % \begin{macrocode} \if@tempswa% \def\tab@left{\vskip\parskip\medskip}% \def\tab@right{\par\@endpetrue\global\@ignoretrue}% % \end{macrocode} % % Now we need to sort out the alignment. The only way we can do this is by % playing with tabskip glue. There are two possiblities: % % \begin{itemize} % % \item If this is a straight \env{tabular} or an \env{array}, we just use % infinite glue. This is reasonable, I think. % % \item If we have a width for the table, we calculate the fixed values of % glue on either side. This is fairly easy, and forces the table to % the required width. % % \end{itemize} % % First, set up the left and right glues to represent the prevailing % margins set up by \env{list} environments. I think this is the right % thing to do. % % \begin{macrocode} \tab@leftskip\@totalleftmargin% \tab@rightskip\hsize% \advance\tab@rightskip-\linewidth% \advance\tab@rightskip-\@totalleftmargin% % \end{macrocode} % % First of all, deal with the simple case. I'm using 10000\,fill glue here, % in an attempt to suppress |\extracolsep| glue from making the table the % wrong width. It can always use filll glue if it really needs to, though. % % \begin{macrocode} \ifdim\tab@width=\z@% \if#1L\else\advance\tab@leftskip\z@\@plus10000fill\fi% \if#1R\else\advance\tab@rightskip\z@\@plus10000fill\fi% % \end{macrocode} % % Now for the fun bit. This isn't too hard really. The extra space I must % add around the table adds up to $|\linewidth| - |\tab@width|$. I just % need to add this onto the appropriate sides of the table. % % \begin{macrocode} \else% \dimen@\linewidth% \advance\dimen@-\tab@width% \if#1L\advance\tab@rightskip\dimen@\fi% \if#1R\advance\tab@leftskip\dimen@\fi% \if#1C% \advance\tab@leftskip.5\dimen@% \advance\tab@rightskip.5\dimen@% \fi% \fi% % \end{macrocode} % % Don't allow page breaks. David Carlisle's wonderful \env{longtable} % package does page breaks far better than I could possibly do here, and % we're compatible with it (wahey!). % % \begin{macrocode} \def\tab@penalty{\penalty\@M}% % \end{macrocode} % % Finally, set the new width of the table, and leave. % % \begin{macrocode} \tab@width\hsize% \fi% } % \end{macrocode} % % \end{macro} % % \subsubsection{Handling tops and bottoms} % % This is how the tops and bottoms of tables are made to line up with the % text on the same line, in the presence of arbitrary rules and space. The % old method, based on the way the \package{array} package worked, wasn't % terribly good. This new version copes much better with almost anything % that gets thrown at it. % % I'll keep a state in a macro (|\tab@hlstate|), which tells me what I'm % meant to be doing. The possible values are \lit{n}, which means I don't % have to do anything, \lit{t}, which means that I'm meant to be handling % top-aligned tables, and \lit{b}, which means that I'm meant to be lining % up the bottom. There are several other `substates' which have various % magic meanings. % % \begin{macrocode} \def\tab@hlstate{n} % \end{macrocode} % % When all's said and done, I extract the box containing the table, and % play with the height and depth to try and make it correct. % % \begin{macro}{\tab@addruleheight} % % This macro is called by `inter-row' things to add their height to our % dimen register. % % Only do this if the state indicates that it's sensible. % % \begin{macrocode} \def\tab@addruleheight#1{% \if\tab@hlstate n\else% \global\advance\tab@endheight#1\relax% \fi% } % \end{macrocode} % % \end{macro} % % \begin{macro}{\tab@startrow} % % This is called at the start of a row, from within the array preamble. % Currently, this assumes that the rows aren't bigger than their struts: % this is reasonable, although slightly limiting, and it could be done better % if I was willing to rip the alignment apart and put it back together % again. % % \begin{macrocode} \def\tab@startrow{% \if\tab@hlstate t% \gdef\tab@hlstate{n}% \else\if\tab@hlstate b% \global\tab@endheight\dp\@arstrutbox% \fi\fi% } % \end{macrocode} % % \end{macro} % % \begin{macro}{\tab@raisebase} % % This macro is called at the end of it all, to set the height and depth % of the box correctly. It sets the height to |\tab@endheight|, and the % depth to everything else. The box is in |\box|~0 currently. % % \begin{macrocode} \def\tab@raisebase{% \global\advance\tab@endheight-\ht\z@% \raise\tab@endheight\box\z@% } % \end{macrocode} % % \end{macro} % % \begin{macro}{\tab@lowerbase} % % And, for symmetry's sake, here's how to set the bottom properly instead. % % \begin{macrocode} \def\tab@lowerbase{% \global\advance\tab@endheight-\dp\z@% \lower\tab@endheight\box\z@% } % \end{macrocode} % % \end{macro} % % % \subsection{Breaking tables into bits} % % Unboxed tables have a wonderful advantage over boxed ones: you can stop % halfway through and do something else for a bit. Here's how: % % \begin{macro}{\tabpause} % % I'd like to avoid forbidding catcode changes here. I'll use |\doafter| % now I've got it, to ensure that colour handling and things occur % \emph{inside} the |\noalign| (otherwise they'll mess up the alignment % very seriously). % % We have to be careful here to ensure that everything works correctly within % lists. (The \package{amsmath} package had this problem in its % |\intertext| macro, so I'm not alone here.) % % \begin{macrocode} \def\tabpause#{% \noalign{\ifnum0=`}\fi% \@parboxrestore% \tab@startpause% \vskip-\parskip% \parshape\@ne\@totalleftmargin\linewidth% \noindent% \doafter\tabpause@i% } \def\tabpause@i{% \nobreak% \tab@endpause% \ifnum0=`{\fi}% } % \end{macrocode} % % \end{macro} % % % \subsection{The wonderful world of \cmd\multicolumn} % % \begin{macro}{\multicolumn} % % This is actually fantasitcally easy. Watch and learn. Make sure you % notice the |\long|s here: remember that some table cells can contain % paragraphs, so it seems sensible to allow |\par| into the argument. % (As far as I know, most other |\multicolumn| commands don't do this, % which seems a little silly. Then again, I forgot to do it the first % time around.) % % \begin{macrocode} \long\def\multicolumn#1#2#3{% \multispan{#1}% \begingroup% \tab@multicol% \tab@initread% \tab@preamble{}% \long\def\tab@midtext{#3}% \let\tab@looped\tab@err@multi% \tab@readpreamble{#2}% \the\tab@preamble% \endgroup% \ignorespaces% } % \end{macrocode} % % \end{macro} % % % \subsection{Interlude: range lists} % % For processing arguments to |\vgap| and |\cline|, we need to be able to % do things with lists of column ranges. To save space, and to make my % fingers do less typing, here's some routines which do range handling. % % \begin{macro}{\ranges} % % Given a macro name and a comma separated list of ranges and simple numbers, % this macro will call the macro giving it each range in the list in turn. % Single numbers~$n$ will be turned into ranges $n$--$n$. % % The first job is to read the macro to do (which may already have some % arguments attached to it). We'll also start a group to make sure that % our changes to temp registers don't affect anyone else. % % There's a space before the delimiting |\q@delim| to stop numbers being % parsed to far and expanding our quark (which will stop \TeX\ dead in its % tracks). Since we use |\@ifnextchar| to look ahead, spaces in range lists % are perfectly all right. % % \begin{macrocode} \def\ranges#1#2{% \gdef\ranges@temp{#1}% \begingroup% \ranges@i#2 \q@delim% } % \end{macrocode} % % % We're at the beginning of the list. We expect either the closing marker % (if this is an empty list) or a number, which we can scoop up into a % scratch register. % % \begin{macrocode} \def\ranges@i{% \@ifnextchar\q@delim\ranges@done{\afterassignment\ranges@ii\count@}% } % \end{macrocode} % % We've read the first number in the range. If there's another number, we'll % expect a `|-|' sign to be next. If there is no `|-|', call the user's code % with the number duplicated and then do the rest of the list. % % \begin{macrocode} \def\ranges@ii{% \@ifnextchar-\ranges@iii{\ranges@do\count@\count@\ranges@v}% } % \end{macrocode} % % Now we strip the `|-|' off and read the other number into a temporary % register. % % \begin{macrocode} \def\ranges@iii-{\afterassignment\ranges@iv\@tempcnta} % \end{macrocode} % % We have both ends of the range now, so call the user's code, passing it % both ends of the range. % % \begin{macrocode} \def\ranges@iv{\ranges@do\count@\@tempcnta\ranges@v} % \end{macrocode} % % We've finished doing an item now. If we have a `|,|' next, then start % over with the next item. Otherwise, if we're at the end of the list, % we can end happily. Finally, if we're totally confused, raise an % error. % % \begin{macrocode} \def\ranges@v{% \@ifnextchar,% \ranges@vi% {% \@ifnextchar\q@delim% \ranges@done% {\tab@err@range\ranges@vi,}% }% } % \end{macrocode} % % We had a comma, so gobble it, read the next number, and go round again. % % \begin{macrocode} \def\ranges@vi,{\afterassignment\ranges@ii\count@} % \end{macrocode} % % Here's how we call the user's code, now. We close the group, so that the % user's code doesn't have to do global things to remember its results, and % we expand the two range ends from their count registers. We also ensure % that the range is the right way round. % % \begin{macrocode} \def\ranges@do#1#2{% \ifnum#1>#2\else% \expandafter\endgroup% \expandafter\ranges@temp% \expandafter{% \the\expandafter#1% \expandafter}% \expandafter{% \the#2% }% \begingroup% \fi% } % \end{macrocode} % % And finishing the scan is really easy. We close the group after gobbling % the close token. % % \begin{macrocode} \def\ranges@done\q@delim{\endgroup} % \end{macrocode} % % \end{macro} % % \begin{macro}{\ifinrange} % % Something a little more useful, now. |\ifinrange| takes four arguments: % a number, a range list (as above), and two token lists which I'll call % \emph{then} and \emph{else}. If the number is in the list, I'll do % \emph{then}, otherwise I'll do \emph{else}. % % \begin{macrocode} \def\ifinrange#1#2{% \@tempswafalse% \count@#1% \ranges\ifinrange@i{#2}% \if@tempswa% \expandafter\@firstoftwo% \else% \expandafter\@secondoftwo% \fi% } \def\ifinrange@i#1#2{% \ifnum\count@<#1 \else\ifnum\count@>#2 \else\@tempswatrue\fi\fi% } % \end{macrocode} % % \end{macro} % % % \subsection{Horizontal rules OK} % % This is where all the gubbins for |\vgap| and friends is kept, lest it % contaminate fairly clean bits of code found elsewhere. % % \subsubsection{Drawing horizontal rules} % % \begin{macro}{\hline} % % Note the funny use of |\noalign| to allow \TeX\ stomach ops like % |\futurelet| without starting a new table row. This lets us see if there's % another |\hline| coming up, so we can see if we need to insert extra % vertical space. % % \begin{macrocode} \def\hline{% \tab@dohline% \noalign{\ifnum0=`}\fi% \tab@penalty% \futurelet\@let@token\hline@i% } % \end{macrocode} % % We check here for another |\hline| command, and insert glue if there is. % This looks terrible, though, and |\hlx{hvh}| is much nicer. Still\dots % % \begin{macrocode} \def\hline@i{% \ifx\@let@token\hline% \vskip\doublerulesep% \tab@addruleheight\doublerulesep% \fi% \ifnum0=`{\fi}% } % \end{macrocode} % % \end{macro} % % \begin{macro}{\tab@dohline} % % This is where hlines actually get drawn. % Drawing lines is more awkward than it used to be, particularly in unboxed % tables. It used to be a case simply of saying |\noalign{\hrule}|. % However, since unboxed tables are actually much wider than they look, this % would make the rules stretch right across the page and look generally % horrible. % % The solution is simple: we basically do a dirty big |\cline|. % % \begin{macrocode} \def\tab@dohline{% \multispan{\tab@columns}% \leaders\hrule\@height\arrayrulewidth\hfil% \tab@addruleheight\arrayrulewidth% \cr% } % \end{macrocode} % % \end{macro} % % \subsubsection{Vertical rules} % % I couldn't fit these in anywhere else, so they'll have to go here. I'll % provide a new optional argument which specifies the width of the rule; this % gets rid of the problem described in the \emph{Companion}, where to get % an unusually wide vertical rule, you have to play with things like % \syntax{"\\vrule width" <dimen>} which really isn't too nice. % % \begin{macro}{\vline} % % The new |\vline| has an optional argument which gives the width of the % rule. The |\relax| stops \TeX\ trying to parse a \<rule-specification> for % too long, in case someone says something like `|\vline depthcharges|' or % something equally unlikely. % % \begin{macrocode} \renewcommand\vline[1][\arrayrulewidth]{\vrule\@width#1\relax} % \end{macrocode} % % \end{macro} % % \subsubsection{Drawing bits of lines} % % Just for a bit of fun, here's an extended version of |\cline| which takes % a list of columns to draw lines under, rather than just a single range. % % \begin{macro}{\cline} % % Not a single line of code written yet, and we already have a dilemma on % our hands. Multiple consecutive |\cline| commands are meant to draw % on the same vertical bit of table. But horizontal lines are meant to have % thickness now. Oh, well [sigh], we'll skip back on it after all. % % Now the problem remains how best to do the job. The way I see it, there % are three possibilities: % % \begin{itemize} % % \item We can start a table row, and then for each column of the table % (as recorded in |\tab@columns|) we look to see if that column is % listed in the range list and if so draw the rule. This requires % lots of scanning of the range list. % % \item We can take each range in the list, and draw rules appropriately, % just like the old |\cline| used to do, and starting a new table row % for each. % % \item We can start a table row, and then for each range remember where we % stopped drawing the last row, move to the start of the new one, and % draw it. If we start moving backwards, we close the current row % and open a new one. % % \end{itemize} % % The last option looks the most efficient, and the most difficult. This % is therefore what I shall do |;-)|. % % The first thing to do is to add in a little negative space, and start a % table row (omitting the first item). Then scan the range list, and finally % close the table row and add some negative space again. % % We need a global count register to keep track of where we are. Mixing % local and global assignments causes all sorts of tragedy, so I shall hijack % |\tab@state|. % % \begin{macrocode} \def\cline#1{% \noalign{\kern-.5\arrayrulewidth\tab@penalty}% \omit% \global\tab@state\@ne% \ranges\cline@i{#1}% \cr% \noalign{\kern-.5\arrayrulewidth\tab@penalty}% } % \end{macrocode} % % Now for the tricky bit. When we're given a range, we look to see if the % first number is less than |\tab@state|. If so, we quickly close the % current row, kern backwards and start again with an |\omit| and reset % |\tab@state| to 1, and try again. % % \begin{macrocode} \def\cline@i#1#2{% \ifnum#1<\tab@state\relax% \tab@@cr% \noalign{\kern-\arrayrulewidth\tab@penalty}% \omit% \global\tab@state\@ne% \fi% % \end{macrocode} % % We are now either at or in front of the column position required. If % we're too far back, we must |\hfil&\omit| our way over to the correct% % column. % % \begin{macrocode} \@whilenum\tab@state<#1\do{% \hfil\tab@@tab@omit% \global\advance\tab@state\@ne% }% % \end{macrocode} % % We've found the start correctly. We must deal with a tiny problem now: % if this is not the first table cell, the left hand vertical rule is in the % column to the left, so our horizontal rule won't match up properly. So % we skip back by a bit to compensate. If there isn't actually a vertical % rule to line up with, no-one will notice, because the rules are so thin. % This adds a little touch of quality to the whole thing, which is after all % the point of this whole exercise. % % \begin{macrocode} \ifnum\tab@state>\@ne% \kern-\arrayrulewidth% \fi% % \end{macrocode} % % Now we must stretch this table cell to the correct width. % % \begin{macrocode} \@whilenum\tab@state<#2\do{% \tab@@span@omit% \global\advance\tab@state\@ne% }% % \end{macrocode} % % We're ready. Draw the rule. Note that this is |\hfill| glue, just in case % we start putting in |\hfil| glue when we step onto the next cell. % % \begin{macrocode} \leaders\hrule\@height\arrayrulewidth\hfill% } % \end{macrocode} % % Some alignment primitives are hidden inside macros so they don't get seen % at the wrong time. This is what they look like: % % \begin{macrocode} \def\tab@@cr{\cr} \def\tab@@tab@omit{&\omit} \def\tab@@span@omit{\span\omit} % \end{macrocode} % % \end{macro} % % \subsubsection{Drawing short table rows} % % Before I start on a description of more code, I think I'll briefly discuss % my reasons for leaving the |\vgap| command in its current state. There's a % reasonable case for introducing an interface between |\vgap| and % |\multicolumn|, to avoid all the tedious messing about with column % ranges. There are good reasons why I'm not going to do this: % % \begin{itemize} % % \item It's very difficult to do: it requires either postprocessing of % the table or delaying processing of each row until I know exactly % what's in it; a |\multicolumn| in a row should be able to affect % a |\vgap| before the row, which gets very nasty. This package is % probably far too large already, and adding more complexity and % running the risk of exhausting \TeX's frustratingly finite capacity % for the sake of relieving the user of a fairly trivial job doesn't % seem worthwhile. % % \item Perhaps more importantly, there are perfectly valid occasions when % it's useful to have the current vgap behaviour. For example, the % \texttt{MIX} word layout diagrams found in \emph{The Art of % Computer Programming} use the little `stub lines' to show where % data items cross byte boundaries: % % ^^A This actually looks terrifyingly similar to the original. % ^^A The leading @{} is there to stop the table looking off-centre, % ^^A because there's no left hand rule telling you where the table % ^^A starts, like there is on the right, just the \tabcolsep glue. % % \begingroup % \newcommand{\wide}[2]{\multicolumn{#1}{c|}{\ttfamily #2}} % \begin{tabular}[C]{@{} r @{\qquad} | Mc | *{5}{c|}} \hlx{c{2-7} v} % empty & - & 1 & 0 & 0 & 0 & 0 \\ \hlx{v c{2-7} v} % occupied & + & \wide{2}{LINK} & \wide{3}{KEY} \\ \hlx{v c{2-7}} % \end{tabular} % \endgroup % % \end{itemize} % % That's my excuses out of the way; now I'll press on with the actual % programming. % % \begin{macro}{\tab@checkrule} % % We have a range list in |\tab@xcols| and a number as an argument. If we % find the number in the list, wejust space out the following group, % otherwise we let it be. % % \begin{macrocode} \def\tab@checkrule#1{% \count@#1\relax% \expandafter\ifinrange% \expandafter\count@% \expandafter{\tab@xcols}% {\tab@checkrule@i}% {}% } \def\tab@checkrule@i#1{\setbox\z@\hbox{#1}\hb@xt@\wd\z@{}} % \end{macrocode} % % \end{macro} % % \begin{macro}{\vgap} % % We must tread carefully here. A single misplaced stomach operation can % cause error messages. We therefore start with an |\omit| so we can search % for optional arguments. % % So that |\hlx| can get control after |\vgap| has finished, we provide a % hook called |\vgap@after| which is expanded after |\vgap| has finished. % Here we make it work like |\@empty|, which expands to nothing. (Note that % |\relax| will start a new table row, so we can't use that.) There are % some penalty items here to stick the |\vgap| row to the text row and % |\hline| that are adjacent to it. The \package{longtable} package will % split an |\hline| in half, so this is the correct thing to do. % % \begin{macrocode} \def\vgap{% \noalign{\nobreak}% \omit% \global\let\vgap@after\@empty% \iffalse{\fi\ifnum0=`}\fi% \@ifnextchar[\vgap@i\vgap@simple% } % \end{macrocode} % % We set up two different sorts of |\vgap| -- a simple one which allows all % rules to be passed through, and a specific one which carefully vets each % one (and is therefore slower). We decide which to so based on the presence % of an optional argument. % % The optional argument handler just passes its argument to an interface % routine which is used by |\hlx|. % % \begin{macrocode} \def\vgap@i[#1]{\vgap@spec{#1}} % \end{macrocode} % % Now we handle specified columns. Since we're in an omitted table cell, we % must set things up globally. Assign the column spec to a macro, and set up % vetting by the routine above. Then just go and do the job. % % \begin{macrocode} \def\vgap@spec#1#2{% \gdef\tab@xcols{#1}% \global\let\tab@ckr\tab@checkrule% \vgap@do{#2}% } % \end{macrocode} % % Handle all columns. Just gobble the column number for each rule, and let % the drawing pass unharmed. Easy. % % \begin{macrocode} \def\vgap@simple#1{% \global\let\tab@ckr\@gobble% \vgap@do{#1}% } % \end{macrocode} % % This is where stuff actually gets done. We set the |\vgap| flag on while % we do the short row. Then just expand the token list we built while % scanning the preamble. % % Note that the flag is cleared at the end of the last column, to allow other % funny things like |\noalign| and |\omit| before a new row is started. % % \begin{macrocode} \def\vgap@do#1{% \ifnum0=`{}\fi% \global\tab@vgaptrue% \the\tab@shortline% \vrule\@height#1\@width\z@% \global\tab@vgapfalse \tab@addruleheight{#1}% \cr% \noalign{\nobreak}% \vgap@after% } % \end{macrocode} % % \end{macro} % % \subsubsection{Prettifying syntax} % % \begin{macro}{\hlx} % % This is like a poor cousin to the preamble parser. The whole loop is % carefully written to take place \emph{only} in \TeX's mouth, so the % alignment handling bits half way down the gullet don't see any of this. % % First, pass the string to another routine. % % \begin{macrocode} \def\hlx#1{\hlx@loop#1\q@delim} % \end{macrocode} % % Now peel off a token, and dispatch using |\csname|. We handle % undefinedness of the command in a fairly messy way, although it probably % works. Maybe. % % \begin{macrocode} \def\hlx@loop#1{% \ifx#1\q@delim\else% \@ifundefined{hlx@cmd@\string#1}{% \expandafter\hlx@loop% }{% \csname hlx@cmd@\string#1\expandafter\endcsname% }% \fi% } % \end{macrocode} % % \end{macro} % % \begin{macro}{\hlxdef} % % New |\hlx| commands can be defined using |\hlxdef|. This is a simple % abbreviation. % % \begin{macrocode} \def\hlxdef#1{\@namedef{hlx@cmd@#1}} % \end{macrocode} % % \end{macro} % % \begin{macro}{\hlx h} % % Handle an \lit{h} character. Just do an |\hline| and return to the loop. % We look ahead to see if there's another \lit{h} coming up, and if so % insert two |\hline| commands. This strange (and inefficient) behaviour % keeps packages which redefine |\hline| happy. % % \begin{macrocode} \hlxdef h#1{% \noalign{% \ifx#1h% \def\@tempa{\hline\hline\hlx@loop}% \else% \def\@tempa{\hline\hlx@loop#1}% \fi% \expandafter }% \@tempa% } % \end{macrocode} % % \end{macro} % % \begin{macro}{\hlx b} % % The \lit{b} character does a nifty backspace, for \package{longtable}'s % benefit. % % \begin{macrocode} \hlxdef b{\noalign{\kern-\arrayrulewidth}\hlx@loop} % \end{macrocode} % % \end{macro} % % \begin{macro}{\hlx /} % % The `"/"' character allows a page break at the current position. % % \begin{macrocode} \hlxdef /{% \noalign{\ifnum0=`}\fi% \@ifnextchar[\hlx@cmd@break@i{\hlx@cmd@break@i[0]}% } \def\hlx@cmd@break@i[#1]{\ifnum0=`{\fi}\pagebreak[0]\hlx@loop} % \end{macrocode} % % \end{macro} % % \begin{macro}{\hlx v} % % Handle a \lit{v} character. This is rather like the |\vgap| code above, % although there are syntactic differences. % % \begin{macrocode} \hlxdef v{% \noalign{\nobreak}% \omit% \iffalse{\fi\ifnum0=`}\fi% \global\let\vgap@after\hlx@loop% \@ifnextchar[\hlx@vgap@i{\hlx@vgap@ii\vgap@simple}% } \def\hlx@vgap@i[#1]{% \ifx!#1!% \def\@tempa{\hlx@vgap@ii\vgap@simple}% \else% \def\@tempa{\hlx@vgap@ii{\vgap@spec{#1}}}% \fi% \@tempa% } \def\hlx@vgap@ii#1{% \@ifnextchar[{\hlx@vgap@iii{#1}}{\hlx@vgap@iii{#1}[\doublerulesep]}% } \def\hlx@vgap@iii#1[#2]{#1{#2}} % \end{macrocode} % % \end{macro} % % \begin{macro}{\hlx s} % % Allow the user to leave a small gap using the \lit{s} command. % % \begin{macrocode} \hlxdef s{% \noalign{\ifnum0=`}\fi% \nobreak% \@ifnextchar[\hlx@space@i{\hlx@space@i[\doublerulesep]}% } \def\hlx@space@i[#1]{% \vskip#1% \tab@addruleheight{#1}% \ifnum0=`{\fi}% \hlx@loop% } % \end{macrocode} % % \end{macro} % % \begin{macro}{\hlx c} % % We might as well allow a \lit{c} command to do a |\cline|. % % \begin{macrocode} \hlxdef c#1{\cline{#1}\hlx@loop} % \end{macrocode} % % \end{macro} % % \begin{macro}{\hlx .} % % The \lit{.} character forces a start of the new column. There's a little % problem here. Since the \lit{.} character starts the next column, we need % to gobble any spaces following the |\hlx| command before the cell contents % actually starts. Unfortunately, |\ignorespaces| will start the column for % us, so we can't put it in always. We'll handle it here, then. We'll take % the rest of the `preamble' string, and warn if it's not empty. Then we'll % |\ignorespaces| -- this will start the column for us, so we don't need to % |\relax| any more. % % \begin{macrocode} \hlxdef .#1\q@delim{% \ifx @#1@\else% \PackageWarning{mdwtab}{% Ignoring \protect\hlx\space command characters following a `.'\MessageBreak command% }% \fi% \ignorespaces% } % \end{macrocode} % % \end{macro} % % % \subsection{Starting new table rows} % % We take a break from careful mouthery at last, and start playing with % newlines. The standard one allows pagebreaks in unboxed tables, which % isn't really too desirable. % % Anyway, we'll try to make this macro rather more reusable than the standard % one. Here goes. % % \begin{macro}{\@arraycr} % % We pass lots of information to a main parser macro, and expect it to cope. % % \begin{macrocode} \def\@arraycr{\tab@arraycr{}} \def\tab@arraycr#1{\tab@cr{\tab@tabcr{#1}}{}{}} % \end{macrocode} % % Now to actually do the work. |\tab@cr| passes us the skip size, and the % appropriate one of the two arguments given above (both of which are empty) % depending on the presence of the $*$. % % \begin{macrocode} \def\tab@tabcr#1#2{% % \end{macrocode} % % If the total height I need to add between rows (from the optional argument % and the `extrasep' parameter) is greater than zero, I'll handle this by % extending the strut slightly. I'm not actually sure whether this is the % right thing to do, to be honest, although it's easier than trying to % to an automatic |\vgap|, because I need to know which columns to skip. % If the space is less than zero, I'll just insert the vertical space with % in a |\noalign|. % % First, to calculate how much space needs adding. % % \begin{macrocode} \dimen@#2% \advance\dimen@\tab@extrasep% % \end{macrocode} % % If the height is greater than zero, I need to play with the strut. I must % bear in mind that the current table cell (which I'm still in, remember) % may be in vertical mode, and I may or may not be in a paragraph. % % If I am in vertical mode, I'll backpedal to the previous box and put the % strut in an hbox superimposed on the previous baseline. Otherwise, I can % just put the strut at the end of the text. (This works in either LR % or paragraph mode as long as I'm not between paragraphs.) Again, Rowland's % empty cell bug strikes. (See |\tab@epar| for details.) % % \begin{macrocode} \ifdim\dimen@>\z@% \ifvmode% \unskip\ifdim\prevdepth>-\@m\p@\kern-\prevdepth\fi% \nointerlineskip\expandafter\hbox% \else% \@maybe@unskip\expandafter\@firstofone% \fi% {\advance\dimen@\dp\@arstrutbox\vrule\@depth\dimen@\@width\z@}% \fi% % \end{macrocode} % % This table cell works as a group (which is annoying here). I'll copy the % interrow gap into a global register so that I can use it in the |\noalign|. % % \begin{macrocode} \global\dimen\@ne\dimen@% \cr% \noalign{% #1% \ifdim\dimen\@ne<\z@\vskip\dimen\@ne\relax\fi% }% \@gobble% } % \end{macrocode} % % \end{macro} % % \begin{macro}{\tab@setcr} % % To set the |\\| command correctly in each table cell, we make it a part of % the preamble (in |\tab@midtext|) to call this routine. It's easy -- just % saves the preamble from being huge. % % \begin{macrocode} \def\tab@setcr{\let\\\tabularnewline} % \end{macrocode} % % \end{macro} % % \begin{macro}{\tab@cr} % % Now we do the parsing work. This is fun. Note the revenge of the funny % braces here. Nothing to worry about, honest. The tricky bit is to keep % track of which arguments are which. (Thanks to David Carlisle for pointing % out that I'd missed out the |\relax| here.) % % \begin{macrocode} \def\tab@cr#1#2#3{% \relax% \iffalse{\fi\ifnum0=`}\fi% \@ifstar{\tab@cr@i{#1}{#3}}{\tab@cr@i{#1}{#2}}% } \def\tab@cr@i#1#2{% \@ifnextchar[{\tab@cr@ii{#1}{#2}}{\tab@cr@ii{#1}{#2}[\z@]}% } \def\tab@cr@ii#1#2[#3]{% \ifnum0=`{}\fi% #1{#3}{#2}% } % \end{macrocode} % % \end{macro} % % % \subsection{Gratuitous grotesquery} % % So far we've had an easy-ish ride (or should that be \emph{queasy}?). Now % for something unexplainably evil. We convince \LaTeX\ that it's loaded the % \package{array} package, so that packages which need it think they've got % it. % % The bogus date is the same as the date for the \package{array} package I've % got here -- this will raise a warning if Frank updates his package which % should filter back to me telling me that there's something I need to % know about. % % The messing with |\xdef| and the funny parsing ought to insert the current % \package{mdwtab} version and date into the fake \package{array} version % string, giving a visible clue to the user that this isn't the real % \package{array} package. % % \begin{macrocode} \begingroup \catcode`.=11 \def\@tempa#1 #2 #3\@@{#1 #2} \xdef\ver@array.sty {1995/11/19 [mdwtab.sty \expandafter\@tempa\ver@mdwtab.sty\@@]} \endgroup % \end{macrocode} % % % \subsection{Error messages} % % I've put all the error messages together, where I can find them, translate % them or whatever. % % First, some token-space saving (which also saves my fingers): % % \begin{macrocode} \def\tab@error{\PackageError{mdwtab}} % \end{macrocode} % % Now do the error messages. % % \begin{macrocode} \def\tab@err@misscol{% \tab@error{Missing column type}{% I'm lost. I was expecting something describing^^J% the type of the current column, but you seem to^^J% have missed it out. I've inserted a type `l'^^J% column here in the hope that this makes sense.% }% } % \end{macrocode} % % \begin{macrocode} \def\tab@err@oddgroup{% \tab@error{Misplaced group in table preamble}{% I've found an open brace character in your preamble^^J% when I was expecting a specifier character. I'm^^J% going to gobble the whole group and carry on as if^^J% I'd never seen it.% }% } % \end{macrocode} % % \begin{macrocode} \def\tab@err@undef#1{% \tab@error{Unknown `\tab@colset' preamble character `\string#1'}{% I don't understand what you meant by typing this^^J% character. Anyway, I'll ignore it this time around.^^J% Just don't you do it again.% }% } % \end{macrocode} % % \begin{macrocode} \def\tab@err@unbrh{% \tab@error{Can't use unboxed tabular in LR mode}{% You've asked for a tabular or array environment with^^J% `L', `C' or `R' as the position specifier, but you're^^J% in LR (restricted horizontal) mode, so it won't work.^^J% I'll assume you really meant `c' and soldier on.% }% } % \end{macrocode} % % \begin{macrocode} \def\tab@err@unbmm{% \tab@error{Can't use unboxed tabular in maths mode}{% You've asked for a tabular or array environment with^^J% `L', `C' or `R' as the position specifier, but you're^^J% in maths mode, so it won't work. I'll pretend that^^J% you really typed `c', and that this is all a bad dream.% }% } % \end{macrocode} % % \begin{macrocode} \def\tab@err@unbext{% \tab@error{Can't extend unboxed tabulars}{% You're trying to use kludgy extensions (e.g.,^^J% `delarray') on an array or tabular with `L', `C'^^J% or `R' as the position specifier. I'll assume you^^J% subconsciously wanted a `c' type all along.% }% } % \end{macrocode} % % \begin{macrocode} \def\tab@err@multi{% \tab@error{More than one column in a \protect\multicolumn}{% You've put more than one column into a \string\multicolumn^^J% descriptor. It won't work. I have no idea what^^J% will happen, although it won't be pleasant. Hold^^J% on tight now...% }% } % \end{macrocode} % % \begin{macrocode} \def\tab@err@range{% \tab@error{Expected `,' or `<end>' in range list}{% I was expecting either the end of the range list,^^J% or a comma, followed by another range. I've^^J% inserted a comma to try and get me back on track.^^J% Good luck.% }% } % \end{macrocode} % % That's it. No more. Move along please. % % \begin{macrocode} %</mdwtab> % \end{macrocode} % % %^^A------------------------------------------------------------------------- % \section{Implementation of \package{mathenv}} % % % This is in a separate package, mainly to avoid wasting people's memory. % % \begin{macrocode} %<*mathenv> % \end{macrocode} % % % \subsection{Options handling} % % We need to be able to cope with \textsf{fleqn} and \textsf{leqno} options. % This will adjust our magic modified \env{eqnarray} environment % appropriately. % % \begin{macrocode} \newif\if@fleqn \newif\if@leqno \DeclareOption{fleqn}{\@fleqntrue} \DeclareOption{leqno}{\@leqnotrue} \ProcessOptions % \end{macrocode} % % We use the \package{mdwtab} package for all its nice table handling things. % (Oh, and to inflict it on users who want to do nice equations and don't % care about our tables.) % % \begin{macrocode} \RequirePackage{mdwtab} % \end{macrocode} % % % \subsection{Some useful registers} % % The old \LaTeX\ version puts the equation numbers in by keeping a count of % where it is in the alignment. Since I don't know how may columns there are % going to be, I'll just use a switch in the preamble to tell me to stop % tabbing. % % \begin{macrocode} \newif\if@eqalast % \end{macrocode} % % Now define some useful length parameters. First allocate them: % % \begin{macrocode} \newskip\eqaopenskip \newskip\eqacloseskip \newskip\eqacolskip \newskip\eqainskip \newskip\splitleft \newskip\splitright % \end{macrocode} % % Now assign some default values. Users can play with these if they really % want although I can't see the point myself. % % \begin{macrocode} \AtBeginDocument{% \eqacloseskip\@centering% \eqacolskip1.5em\@plus\@m\p@ \eqainskip\z@% \if@fleqn% \eqaopenskip\mathindent% \splitleft\mathindent\relax% \splitright\mathindent\@minus\mathindent\relax% \else% \eqaopenskip\@centering% \splitleft2.5em\@minus2.5em% \splitright\splitleft% \fi% \relax% } % \end{macrocode} % % % \subsection{A little display handling} % % I'm probably going a little far here, and invading territory already % claimed by the \package{amsmath} stuff (and done a good deal better than % I can be bothered to do), but just for completeness, this is how we handle % attempts to put displays inside other displays without screwing up the % spacing. % % \begin{macro}{\dsp@startouter} % % This is how we start an outermost display. It's fairly easy really. We % make |\dsp@start| start an inner display, and make |\dsp@end| close the % outer display. % % \begin{macrocode} \def\dsp@startouter{% \let\dsp@end\dsp@endouter% $$% } % \end{macrocode} % % \end{macro} % % \begin{macro}{\dsp@endouter} % % Ending the outer display is utterly trivial. % % \begin{macrocode} \def\dsp@endouter{$$} % \end{macrocode} % % \end{macro} % % \begin{macro}{\dsp@startinner} % % Starting inner displays is done in a vbox (actually I choose |\vbox| or % |\vtop| depending on the setting of \textsf{leqno} to put the equation % number the right way round). % % \begin{macrocode} \def\dsp@startinner{% \let\dsp@end\dsp@endinner% \if@fleqn\kern-\mathindent\fi% \if@leqno\vtop\else\vtop\fi\bgroup% } % \end{macrocode} % % \end{macro} % % \begin{macro}{\dsp@endinner} % % Ending an inner display is also really easy. % % \begin{macrocode} \def\dsp@endinner{\egroup} % \end{macrocode} % % \end{macro} % % \begin{macro}{\dsp@start} % % This is what other bits of code uses to start displays. It's one of the % start macros up above, and outer by default. % % \begin{macrocode} \def\dsp@start{% \ifmmode% \ifinner\mth@err@mdsp\fi% \expandafter\dsp@startinner% \else% \ifhmode\ifinner\mth@err@hdsp\fi\fi% \expandafter\dsp@startouter% \fi% } % \end{macrocode} % % \end{macro} % % \begin{macro}{\dsp@tabpause} % % This sets up the correct pre- and postambles for the |\tabpause| macro in % maths displays. This is fairly simple stuff. % % \begin{macrocode} \def\dsp@tabpause{% \def\tab@startpause% {\penalty\postdisplaypenalty\vskip\belowdisplayskip}% \def\tab@endpause% {\penalty\predisplaypenalty\vskip\abovedisplayskip}% } % \end{macrocode} % % \end{macro} % % % \subsection{The \env{eqnarray} environment} % % We allow the user to play with the style if this is really wanted. I dunno % why, really. Maybe someone wants very small alignments. % % \begin{macrocode} \let\eqastyle\displaystyle % \end{macrocode} % % \subsubsection{The main environments} % % \begin{environment}{eqnarray} % \begin{environment}{eqnarray*} % % We define the toplevel commands here. They just add in default arguments % and then call |\@eqnarray| with a preamble string. We handle equation % numbers by setting up a default (|\eqa@defnumber|) which is put into % the final column. At the beginning of each row, we globally |\let| % |\eqa@number| equal to |\eqa@defnumber|. The |\eqnumber| macro just % changes |\eqa@number| as required. Since |\eqa@number| is changed globally % we must save it in this environment. % % First, we must sort out the optional arguments and things. This is really % easy. The only difference between the starred and non-starred environments % is the default definition of |\eqa@defnumber|. % % \begin{macrocode} \def\eqnarray{% \eqnarray@i\eqa@eqcount% } \@namedef{eqnarray*}{\eqnarray@i{}} \def\eqnarray@i#1{\@ifnextchar[{\eqnarray@ii{#1}}{\eqnarray@ii{#1}[rcl]}} % \end{macrocode} % % Right. Now for the real work. The first argument is the default numbering % tokens; the second is the preamble string. % % \begin{macrocode} \def\eqnarray@ii#1[#2]{% % \end{macrocode} % % Set up the equation counter and labels correctly. % % \medskip\par\noindent|\begin{rant}|\par % The hacking with |\@currentlabel| is here because (in the author's opinion) % \LaTeX's |\refstepcounter| macro is broken. It's currently defined as % \begin{listing} %\def\refstepcounter#1{% % \stepcounter{#1}% % \protected@edef\@currentlabel% % {\csname p@#1\endcsname\csname the#1\endcsname}% %} % \end{listing} % which means that the current label gets `frozen' as soon as you do the % counter step. By redefining the macro as % \begin{listing} %\def\refstepcounter#1{% % \stepcounter{#1}% % \edef\@currentlabel{% % \expandafter\noexpand\csname p@#1\endcsname% % \expandafter\noexpand\csname the#1\endcsname% % }% %} % \end{listing} % these sorts of problems would be avoided, without any loss of functionality % or compatibility that I can see. % \par\noindent|\end{rant}|\par % % \begin{macrocode} \stepcounter{equation}% \def\@currentlabel{\p@equation\theequation}% % \end{macrocode} % % The next step is to set up the numbering. I must save the old numbering % so I can restore it later (once in the alignment, I must assign these % things globally). % % \begin{macrocode} \let\eqa@oldnumber\eqa@number% \def\eqa@defnumber{#1}% \global\let\eqa@number\eqa@defnumber% % \end{macrocode} % % The |\if@eqalastfalse| switch is false everywhere except when we're in the % final column. % % \begin{macrocode} \@eqalastfalse% % \end{macrocode} % % Remove the |\mathsurround| kerning, since it will look very odd inside % the display. We have our own spacing parameters for configuring these % things, so |\mathsurround| is unnecessary. % % \begin{macrocode} \m@th% % \end{macrocode} % % Time to parse the preamble string now. I must choose the correct column % set, initialise the preamble parser and set up the various macros. The% % extra `|@{\tabskip\eqacloseskip}|' item sets up the tabskip glue to centre % the alignment properly. % % \begin{macrocode} \colset{eqnarray}% \tab@initread% \def\tab@tabtext{&\tabskip\z@skip}% \tab@preamble{\tabskip\z@skip}% \tab@readpreamble{#2@{\tabskip\eqacloseskip}}% \dsp@tabpause% % \end{macrocode} % % Now for some final setting up. The column separation is set from the % user's parameter, the |\everycr| tokens are cleared, and I set up the % newline command appropriately. % % \begin{macrocode} \col@sep.5\eqainskip% \everycr{}% \let\\\@eqncr% % \end{macrocode} % % Now start a maths display and do the alignment. Set up the left hand % tabskip glue to centre the alignment, and do the actual alignment. % The preamble used is mainly that generated from the user's string, although % the stuff at the end is how we set up the equation number -- it repeats % appropriately so we can always find it. % % \begin{macrocode} \dsp@start% \tabskip\eqaopenskip% \halign to\displaywidth\expandafter\bgroup% \the\tab@preamble% &&\eqa@lastcol\hb@xt@\z@{\hss##}\tabskip\z@\cr% } % \end{macrocode} % % Now for the end of the environment. This is really easy. Set the final % equation number, close the |\halign|, tidy up the equation counter (it's % been stepped once too many times) and close the display. % % \begin{macrocode} \def\endeqnarray{% \eqa@eqnum% \egroup% \dsp@end% \global\let\eqa@number\eqa@oldnumber% \global\@ignoretrue% \global\advance\c@equation\m@ne% } \expandafter\let\csname endeqnarray*\endcsname\endeqnarray % \end{macrocode} % % \end{environment} % \end{environment} % % Now we can define the column types. % % \begin{macrocode} \colpush{eqnarray} % \end{macrocode} % % Note the positioning of ord atoms in the stuff below. This will space out % relations and binops correctly when they occur at the edges of columns, and % won't affect ord atoms at the edges, because ords pack closely. % % First the easy onces. Just stick |\hfil| in the right places and % everything will be all right. % % \begin{macrocode} \coldef r{\tabcoltype{\hfil$\eqastyle}{{}$}} \coldef c{\tabcoltype{\hfil$\eqastyle{}}{{}$\hfil}} \coldef l{\tabcoltype{$\eqastyle{}}{$\hfil}} \coldef x{\tabcoltype{\if@fleqn\else\hfil\fi$\eqastyle}{$\hfil}} % \end{macrocode} % % Now for the textual ones. This is also fairly easy. % % \begin{macrocode} \collet T [tabular]T % \end{macrocode} % % Sort of split types of equations. I mustn't use |\rlap| here, or % everything goes wrong -- |\\| doesn't get noticed by \TeX\ in the same way % as |\cr| does. % % \begin{macrocode} \coldef L{\tabcoltype{\hb@xt@2em\bgroup$\eqastyle}{$\hss\egroup}} % \end{macrocode} % % The \lit{:} column type is fairly simple. % % \begin{macrocode} \coldef :{\tabspctype{\tabskip\eqacolskip}} \coldef q{\tabspctype{\quad}} % \end{macrocode} % % The other column types just insert given text in an appropriate way. % % \begin{macrocode} \collet > [tabular]> \collet < [tabular]< \collet * [tabular]* \collet @ [tabular]@ % \end{macrocode} % % Finally, the magical `|\magic|' column type, which sets the equation % number. We set up the |\tabskip| glue properly, tab on, and set the flag % which marks the final column. The |\eqa@lastcol| command is there to % raise an error if the user tabs over to this column. I'll temporarily % redefine it to |\@eqalasttrue| when I enter this column legitimately. % The extra magical bits here will make the final column repeat, so that we % can find it if necessary. Well is this column type named. % % That's it. We can return to normal now. % % \begin{macrocode} \colpop % \end{macrocode} % % \subsubsection{Newline codes} % % Newline sequences (|\\|) get turned into calls of |\@eqncr|. The job is % fairly simple, really. % % \begin{macrocode} \def\@eqncr{\tab@cr\eqacr@i\interdisplaylinepenalty\@M}% \def\eqacr@i#1#2{% \eqa@eqnum% \noalign{\penalty#2\vskip\jot\vskip#1}% } % \end{macrocode} % % \subsubsection{Setting equation numbers} % % \begin{macro}{\eqa@eqpos} % % Before we start, we need to generalise the flush-left number handling bits. % The macro |\eqa@eqpos| will put its argument in the right place. % % \begin{macrocode} \if@leqno \def\eqa@eqpos#1{% \hb@xt@.01\p@{}\rlap{\normalfont\normalcolor\hskip-\displaywidth#1}% } \else \def\eqa@eqpos#1{\normalfont\normalcolor#1} \fi % \end{macrocode} % % \end{macro} % % \begin{macro}{\eqa@eqnum} % % Here we typeset an equation number in roughly the right place. First I'll % redefine |\eqa@lastcol| so that it tells me I'm in the right place, and % start a loop to find that place. % % \begin{macrocode} \def\eqa@eqnum{% \global\let\eqa@lastcol\@eqalasttrue% \eqa@eqnum@i% } % \end{macrocode} % % Now for the loop. The |\relax| here is absolutely vital -- it starts the % table column, inserting useful tokens like `|\eqa@lastcol|' which tell % me where I am in the alignment. Then, if I've reached the end, I can % typeset the equation number; otherwise I go off into another macro and % step on to the next column. % % \begin{macrocode} \def\eqa@eqnum@i{% \relax% \if@eqalast% \expandafter\eqa@eqnum@ii% \else% \expandafter\eqa@eqnum@iii% \fi% } \def\eqa@eqnum@ii{% \eqa@eqpos\eqa@number% \global\let\eqa@number\eqa@defnumber% \global\let\eqa@lastcol\eqa@@lastcol% \cr% } \def\eqa@eqnum@iii{&\eqa@eqnum@i} % \end{macrocode} % % \end{macro} % % \begin{macro}{\eqa@lastcol} % % This is used as a marker for the final column in an \env{eqnarray} % environment. By default it informs the user that they've been very % silly and swallows the contents of the column. I'll redefine it to % something more useful at appropriate times, and then turn it back again. % % \begin{macrocode} \def\eqa@@lastcol{\mth@err@number\setbox\z@} \let\eqa@lastcol\eqa@@lastcol % \end{macrocode} % % \end{macro} % % \subsubsection{Numbering control} % % \begin{macro}{\eqnumber} % % The |\eqnumber| command sets the equation number on the current equation. % This is really easy, actually. % % \begin{macrocode} \newcommand\eqnumber[1][\eqa@eqcount]{\gdef\eqa@number{#1}} % \end{macrocode} % % \end{macro} % % \begin{macro}{\eqa@eqcount} % % This is how a standard equation number is set, stepping the counter and % all. It's really easy and obvious. % % \begin{macrocode} \def\eqa@eqcount{(\theequation)\global\advance\c@equation\@ne} % \end{macrocode} % % \end{macro} % % \begin{macro}{\nonumber} % % The \LaTeX\ |\nonumber| command could be defined by saying % \begin{listing} %\renewcommand{\nonumber}{\eqnumber[]} % \end{listing} % but I'll be slightly more efficient and redefine |\eqa@number| directly. % % \begin{macrocode} \def\nonumber{\global\let\eqa@number\@empty} % \end{macrocode} % % \end{macro} % % \subsubsection{The \env{eqnalign} environment} % % As a sort of companion to \env{eqnarray}, here's an environment which does % similar things inside a box, rather than taking up the whole display width. % It uses the same column types that we've already created, so there should % be no problems. % % \begin{environment}{eqnalign} % % First, sort out some simple things like optional arguments. % % \begin{macrocode} \def\eqnalign{\@ifnextchar[\eqnalign@i{\eqnalign@i[rcl]}} \def\eqnalign@i[#1]{% \@ifnextchar[{\eqnalign@ii{#1}}{\eqnalign@ii{#1}[c]}% } % \end{macrocode} % % Now we actually do the environment. This is fairly easy, actually. % % \begin{macrocode} \def\eqnalign@ii#1[#2]{% \let\\\eqn@cr% \colset{eqnarray}% \tab@initread% \def\tab@tabtext{&\tabskip\z@skip}% \tabskip\z@skip% \col@sep.5\eqainskip% \tab@readpreamble{#1}% \everycr{}% \if#2t\vtop\else% \if#2b\vbox\else% \vcenter% \fi% \fi% \bgroup% \halign\expandafter\bgroup\the\tab@preamble\cr% } % \end{macrocode} % % Finishing the environment is even simpler. % % \begin{macrocode} \def\endeqnalign{% \crcr% \egroup% \egroup% } % \end{macrocode} % % \end{environment} % % \begin{macro}{\eqn@cr} % % Newlines are really easy here. % % \begin{macrocode} \def\eqn@cr{\tab@cr\eqn@cr@i{}{}} \def\eqn@cr@i#1{\cr\noalign{\vskip\jot\vskip#1}\@gobble} % \end{macrocode} % % \end{macro} % % % \subsection{Simple multiline equations} % % As a sort of example and abbreviation, here's a multiline display % environment which just centres everything. % % \begin{environment}{eqlines} % % We just get |\eqnarray| to do everything for us. This is really easy. % % \begin{macrocode} \def\eqlines{\eqnarray[x]} \let\endeqlines\endeqnarray % \end{macrocode} % % \end{environment} % % \begin{environment}{eqlines*} % % There's a $*$ version which omits numbers. This is easy too. Lots of % hacking with expansion here to try and reduce the number of tokens being % used. Is it worth it? % % \begin{macrocode} \expandafter\edef\csname eqlines*\endcsname{% \expandafter\noexpand\csname eqnarray*\endcsname[x]% } \expandafter\let\csname endeqlines*\expandafter\endcsname \csname endeqnarray*\endcsname % \end{macrocode} % % \end{environment} % % % \subsection{Split equations} % % Based on an idea from \textit{The \TeX book}, we provide some simple % environments for doing split equations. These's plenty of scope for % improvement here, though. % % \begin{environment}{spliteqn} % \begin{environment}{spliteqn*} % % The only difference between these two is that the $*$-version doesn't put % in an equation number by default (although this behaviour can be % changed by |\eqnumber|). % % The fun here mainly concerns putting in the equation number at the right % place -- for |leqno| users, we need to put the number on the first line; % otherwise we put it on the last line. % % The way we handle this is to have two macros, |\\| (which clearly does % all the user line breaks) and |\seq@lastcr| which is used at the end of % the environment to wrap everything up. The |\seq@eqnocr| macro puts an % equation number on the current line and then does a normal |\\|. It also % resets |\\| and |\seq@lastcr| so that they don't try to put another % equation number in. This must be done globally, although anyone who tries % to nest maths displays will get what they deserve. % % For the non-$*$ environment, then, we need to step the equation counter, % and set |\\| to |\seq@cr| or |\seq@eqnocr| as appropriate for the setting % of the |leqno| flag -- |\seq@lastcr| always gets set to put an equation % number in (because it will be reset if the number actually gets done % earlier -- this catches stupid users trying to put a single row into % a split environment). % % \begin{macrocode} \def\spliteqn{% \let\eqa@oldnumber\eqa@number% \global\let\eqa@number\eqa@eqcount% \spliteqn@i% } % \end{macrocode} % % For the $*$ variant, we don't need to bother with equation numbering, so % this is really easy. % % \begin{macrocode} \@namedef{spliteqn*}{% \let\eqa@oldnumber\eqa@number% \gdef\eqa@number{}% \spliteqn@i% } % \end{macrocode} % % Ending the environments is easy. Most of the stuff here will be described % later. % % \begin{macrocode} \def\endspliteqn{% \hfilneg\seq@lastcr% \egroup% \dsp@end% \global\let\eqa@number\eqa@oldnumber% \global\advance\c@equation\m@ne% } \expandafter\let\csname endspliteqn*\endcsname\endspliteqn % \end{macrocode} % % \end{environment} % \end{environment} % % \begin{macro}{\spliteqn@i} % % Here we handle the full display splits. Start a maths display, and make % each row of the alignment take up the full display width. % % The macro |\seq@dosplit| does most of the real work for us -- setting up % the alignment and so forth. The template column is interesting. There % are two items glue on both sides of the actual text: % % \begin{itemize} % % \item Some glue which can shrink. This keeps the display from the edges % of the page unless we get a really wide item. % % \item An |\hfil| to do the alignment. By default, this centres the % equations. On the first line, however, we put a leading |\hfilneg| % which cancels the first |\hfil|, making the first row left aligned. % Similarly, at the end, we put an |\hfilneg| after the last equation % to right align the last line. % % \end{itemize} % % We pass this information on as an argument. It's easy really. % % \begin{macrocode} \def\spliteqn@i{% % \end{macrocode} % % First, set up equation numbering properly. See my rant about % |\refstepcounter| above. % % \begin{macrocode} \stepcounter{equation}% \def\@currentlabel{\p@equation\theequation}% % \end{macrocode} % % Right; now to sort out the numbering and newline handling. If the number's % meant to be on the first line (for \textsf{leqno} users), then it gets % typeset on the first like; otherwise we just do a normal newline on % all lines except the first. Once |\seq@eqnocr| has done its stuff, it % redefines all the newline handling not to insert another number. % % \begin{macrocode} \if@leqno% \global\let\seq@docr\seq@eqnocr% \else% \global\let\seq@docr\seq@cr% \fi% \global\let\seq@lastcr\seq@eqnocr% % \end{macrocode} % % For my next trick, I'll do some display handling -- start a (possibly % nested) maths display, set up the |\tabpause| macro appropriately, and % set the newline command to do the right thing. % % \begin{macrocode} \dsp@start% \dsp@tabpause% \def\\{\seq@docr}% % \end{macrocode} % % Finally, call another macro to do the remaining bits of setting up. % % \begin{macrocode} \seq@dosplit% {\hb@xt@\displaywidth{% \hskip\splitleft\hfil$\displaystyle##$% \hfil\hskip\splitright}}% {\hfilneg}% } % \end{macrocode} % % \end{macro} % % \begin{environment}{subsplit} % % For doing splits in the middle of equations, we provide a similar % environment. Here, we make |\\| just start a new line. We also use % a |\vcenter| rather than a full maths display. The glue items are also % a bit different: we use plain double-quads on each side of the item, and % we need to remove them by hand at the extremeties of the environment. % % \begin{macrocode} \def\subsplit{% \let\\\seq@cr% \vcenter\bgroup% \seq@dosplit{\hfil\qquad$##$\qquad\hfil}{\hfilneg\hskip-2em}% } % \end{macrocode} % % Ending the environment is fairly easy. We remove the final glue item, % and close the alignment and the vbox. % % \begin{macrocode} \def\endsubsplit{% \hfilneg\hskip-2em\cr% \egroup\egroup% } % \end{macrocode} % % \end{environment} % % \begin{macro}{\seq@dosplit} % % Here we do most of the real work. Actually, since the preamble is passed % in as an argument, most of the work is already done. The only thing to % really note is the template for subsequent columns. To stop users putting % in extra columns (which is where we put the equation number) we raise an % error and discard the input in a scratch box register. This template is % repeated infinitely so as to allow us to put the equation number in nicely. % However, the final negative glue item won't work properly, so the equation % will look awful. % % \begin{macrocode} \def\seq@dosplit#1#2{% \halign\bgroup% #1&&\mth@err@number\setbox\z@\hbox{##}\cr% #2\relax% } % \end{macrocode} % % \end{macro} % % \begin{macro}{\seq@eqnocr} % % Here's how we set equation numbers. Since the column provided raises % errors as soon as a token finds its way into it, we start with a |&\omit|. % Then we just put the equation number in a zero-width box. Finally, we % reset the newline commands to avoid putting in more than one equation % number, and do normal newline things. % % \begin{macrocode} \def\seq@eqnocr{% &\omit% \hb@xt@\z@{\hss\eqa@eqpos\eqa@number}% \global\let\seq@docr\seq@cr% \global\let\seq@lastcr\seq@cr% \seq@cr% } % \end{macrocode} % % \end{macro} % % \begin{macro}{\seq@cr} % % Newlines are very easy. We add a |\jot| of extra space, since this is % a nice thing to do. % % \begin{macrocode} \def\seq@cr{\tab@cr\seq@cr@i\interdisplaylinepenalty\@M} \def\seq@cr@i#1#2{\cr\noalign{\penalty#2\vskip\jot\vskip#1}} % \end{macrocode} % % \end{macro} % % % \subsection{Matrix handling} % % There's been a complete and total overhaul of the spacing calculations % for matrices here. The vertical spacing now bears an uncanny similarity % to the rules \TeX\ uses to space out |\atop|-like fractions, the difference % being that you can have more than one column in a matrix. This has the % interesting side-effect that we get an \package{amsmath}-style % sub/superscript environment almost free of charge with the matrix handling % (it just ends up being a script-size single-column matrix). % % What is rather gratifying is that our \env{matrix} environment looks % rather nicer than \package{amsmath}'s (which is based directly on % \env{array}, giving it nasty restrictions on the numbers of columns and % so on); in particular, the version here gives the `correct' result for % Knuth's exercise~18.42 (which states categorically that a |\smallskip| % should be placed between the rows of the big matrix). % % The reason the interrow space doesn't come out in the AMS version is % that \env{array} inserts extra vertical space by extending the depth of % the final row using a strut: the big matrix already extends deeper than % this, so the strut doesn't make any difference. If the space was added % by |\hlx{s[\smallskipamount]}| instead of the |\\| command, things would % be different. % % \begin{figure} % % ^^A This is essentially what amsmath (version 1.2b) does. The real % ^^A implementation requires a counter MaxMatrixCols, and has fewer braces: % ^^A that's all the difference. Oh, and I turn off \arrayextrasep here, % ^^A since amsmath doesn't expect it to be there (accurate emulation, see?) % ^^A and I've used \hspace instead of \hskip since everything else is % ^^A `proper' LaTeX stuff. % % \newenvironment{ams-pmatrix}{^^A % \setlength{\arrayextrasep}{0pt}^^A % \left(^^A % \hspace{-\arraycolsep}^^A % \begin{array}{*{10}{c}}^^A % }{^^A % \end{array}^^A % \hspace{-\arraycolsep}^^A % \right)^^A % } % % \begin{demo}{Exercise 18.42 from \emph{The \TeX book}} %\newcommand{\domatrix}[1]{ % \def\mat##1 % {\begin{#1}##1\end{#1}} % \[ \begin{#1} % \mat{a & b \\ c & d} & % \mat{e & f \\ g & h} % \\[\smallskipamount] % 0 & % \mat{i & j \\ k & l} % \end{#1} % \] %} %\domatrix{pmatrix} %\domatrix{ams-pmatrix} % \end{demo} % % \end{figure} % % \begin{environment}{genmatrix} % % The first job is to store my maths style and font away, because I'll be % needing it lots later. % % \begin{macrocode} \def\genmatrix#1#2#3#4#5{% \let\mat@style#1% \ifx#2\scriptstyle% \let\mat@font\scriptfont% \else\ifx#2\scriptscriptstyle% \let\mat@font\scriptscriptfont% \else% \let\mat@font\textfont% \fi\fi% % \end{macrocode} % % Now to cope with inserted text. This is easy. % % \begin{macrocode} \ifx\mat@style\scriptstyle% \let\mat@textsize\scriptsize% \else\ifx\mat@style\scriptscriptstyle% \let\mat@textsize\scriptscriptsize% \else% \let\mat@textsize\relax% \fi\fi% % \end{macrocode} % % Now for some fun. I'll remember how to start and end the matrix in a % couple of macros |\mat@left| and |\mat@right|. I haven't yet worked out % exactly what needs to be in |\mat@right| yet, though, so I'll build that % up in a scratch token list while I'm making my mind up. % % Initially, I want to open a group (to trap the style changes), set the % maths style (to get the right spacing), insert the left delimiter, insert % some spacing around the matrix, and start a centred box. The ending just % closes all the groups and delimiters I opened. % % \begin{macrocode} \def\mat@left{\bgroup\mat@style\left#4#3\vcenter\bgroup}% \toks@{\egroup#3\right#5\egroup}% % \end{macrocode} % % Now comes a slightly trickier bit. If the maths style is script or % scriptscript, then I need to raise the box by a little bit to make it look % really good. The right amount is somewhere around \smallf 3/4\,pt, I % think, so that's what I'll use. % % \begin{macrocode} \@tempswatrue% \ifx\mat@style\displaystyle\else\ifx\mat@style\textstyle\else% \@tempswafalse% \setbox\z@\hbox\bgroup$% \toks@\expandafter{\the\toks@$\m@th\egroup\raise.75\p@\box\z@}% \fi\fi% % \end{macrocode} % % If I'm not in maths mode right now, then I should enter maths mode, and % remember to leave it later. % % \begin{macrocode} \if@tempswa\ifmmode\else% $\m@th% \toks@\expandafter{\the\toks@$}% \fi\fi% % \end{macrocode} % % Now I've sorted out how to end the environment properly, so I can set up % the macro, using |\edef|. % % \begin{macrocode} \edef\mat@right{\the\toks@}% % \end{macrocode} % % Now see if there's an optional argument. If not, create lots of centred % columns. % % \begin{macrocode} \@ifnextchar[\genmatrix@i{\genmatrix@i[[c]}% } % \end{macrocode} % % Now to sort out everything else. % % \begin{macrocode} \def\genmatrix@i[#1]{% % \end{macrocode} % % Some initial setting up: choose the correct column set, and set up some % variables for reading the preamble. % % \begin{macrocode} \colset{matrix}% \tab@initread% % \end{macrocode} % % Now comes some of the tricky stuff. The space between columns should be % 12\,mu (by trial and error). We put the space in a box so we can measure % it in the correct mathstyle. % % \begin{macrocode} \setbox\z@\hbox{$\mat@style\mskip12mu$}% \edef\tab@tabtext{&\kern\the\wd\z@}% \tab@readpreamble{#1}% % \end{macrocode} % % Now we need to decide how to space out the rows. The code here is based % on the information in appendix~G of \emph{The \TeX book}: I think it'd be % nice if my matrices were spaced out in the same way as normal fractions % (particularly |\choose|y things). The standard |\baselineskip| and % |\lineskip| parameters come in really handy here. % % The parameters vary according to the size of the text, so I need to see % if we have scriptsize or less, or not. The tricky |\if| sorts this out. % % \begin{macrocode} \if1\ifx\mat@style\scriptstyle1\else% \ifx\mat@style\scriptscriptstyle1\else0\fi\fi% \baselineskip\fontdimen10\mat@font\tw@% \advance\baselineskip\fontdimen12\mat@font\tw@% \lineskip\thr@@\fontdimen8\mat@font\thr@@% \else% \baselineskip\fontdimen8\mat@font\tw@% \advance\baselineskip\fontdimen11\mat@font\tw@% \lineskip7\fontdimen8\mat@font\thr@@% \fi% \lineskiplimit\lineskip% % \end{macrocode} % % Now actually set up for the alignment. Assign |\\| to the correct value. % Set up the |\tabskip|. Do the appropriate |\mat@left| thing set up above. % And then start the alignment. % % \begin{macrocode} \let\\\mat@cr% \tabskip\z@skip% \col@sep\z@% \mat@left% \halign\expandafter\bgroup\the\tab@preamble\tabskip\z@skip\cr% % \end{macrocode} % % Now for a little hack to make the spacing consistent between matrices of % the same height. This comes directly from \PlainTeX. This appears to % make the spacing \emph{exactly} the same as the \TeX\ primites, oddly % enough. % % \begin{macrocode} \ifx\mat@font\textfont% \omit$\mat@style\mathstrut$\cr\noalign{\kern-\baselineskip}% \fi% } % \end{macrocode} % % Finishing the environment is really easy. We do the spacing hack again % at the bottom, close the alignment and then tidy whatever we started in % |\mat@left|. % % \begin{macrocode} \def\endgenmatrix{% \crcr% \ifx\mat@font\textfont% \omit$\mat@style\mathstrut$\cr\noalign{\kern-\baselineskip}% \fi% \egroup% \mat@right% } % \end{macrocode} % % \end{environment} % % \begin{macro}{\mat@cr} % % Newlines are really easy. The $*$-form means nothing here, so we ignore % it. % % \begin{macrocode} \def\mat@cr{\tab@cr\mat@cr@i{}{}} \def\mat@cr@i#1{\cr\noalign{\vskip#1}\@gobble} % \end{macrocode} % % \end{macro} % % \begin{macro}{\newmatrix} % % This is how we define new matrix environments. It's simple fun with % |\csname| and |\expandafter|. % % \begin{macrocode} \def\newmatrix#1#2{% \@namedef{#1}{\genmatrix#2}% \expandafter\let\csname end#1\endcsname\endgenmatrix% } % \end{macrocode} % % \end{macro} % % \begin{environment}{matrix} % \begin{environment}{pmatrix} % \begin{environment}{dmatrix} % \begin{environment}{smatrix} % \begin{environment}{spmatrix} % \begin{environment}{sdmatrix} % \begin{environment}{smatrix*} % \begin{environment}{spmatrix*} % \begin{environment}{sdmatrix*} % % Now we define all the other environments we promised. This is easy. % % \begin{macrocode} \newmatrix{matrix}{{\textstyle}{\textstyle}{\,}{.}{.}} \newmatrix{pmatrix}{{\textstyle}{\textstyle}{\,}{(}{)}} \newmatrix{dmatrix}{{\textstyle}{\textstyle}{\,}} \newmatrix{smatrix}{{\scriptstyle}{\scriptstyle}{}{.}{.}} \newmatrix{spmatrix}{{\scriptstyle}{\scriptstyle}{}{(}{)}} \newmatrix{sdmatrix}{{\scriptstyle}{\scriptstyle}{}} \newmatrix{smatrix*}{{\scriptstyle}{\textstyle}{}{.}{.}} \newmatrix{spmatrix*}{{\scriptstyle}{\textstyle}{}{(}{)}} \newmatrix{sdmatrix*}{{\scriptstyle}{\textstyle}{}} % \end{macrocode} % % \end{environment} % \end{environment} % \end{environment} % \end{environment} % \end{environment} % \end{environment} % \end{environment} % \end{environment} % \end{environment} % % \begin{environment}{script} % % Now for superscripts and subscripts. This is fairly easy, because I % took so much care over the matrix handling. % % \begin{macrocode} \def\script{% \let\mat@style\scriptstyle% \def\mat@left{\vcenter\bgroup}% \def\mat@right{\egroup}% \let\mat@font\scriptfont% \let\mat@textsize\scriptsize% \@ifnextchar[\genmatrix@i{\genmatrix@i[c]}% } \let\endscript\endgenmatrix % \end{macrocode} % % \end{environment} % % Now define the column types. % % \begin{macrocode} \colpush{matrix} \coldef l{\tabcoltype{\kern\z@$\mat@style}{\m@th$\hfil}} \coldef c{\tabcoltype{\hfil$\mat@style}{\m@th$\hfil}} \coldef r{\tabcoltype{\hfil$\mat@style}{\m@th$}} \coldef T#1{\tab@aligncol{#1}{\begingroup\mat@textsize}{\endgroup}} % \end{macrocode} % % The repeating type is more awkward. Things will go wrong if this is % given before the first column, so we must do a whole repeat by hand. We % can tell if we haven't contributed a column yet, since |\tab@column| will % be zero. Otherwise, we fiddle the parser state to start a new column, and % insert the |&| character to make \TeX\ repeat the preamble. % % \begin{macrocode} \coldef {[}{% \@firstoftwo{% \ifnum\tab@columns=\z@% \def\@tempa##1\q@delim{% \tab@mkpreamble##1[##1\q@delim% }% \expandafter\@tempa% \else% \tab@setstate\tab@prestate% \tab@append\tab@preamble{&}% \expandafter\tab@mkpreamble% \fi% }% } % \end{macrocode} % % We're done defining columns now. % % \begin{macrocode} \colpop % \end{macrocode} % % % \subsection{Dots\dots} % % Nothing whatsoever to do with alignments, although vertical and diagonal % dots in small matrices look really silly. The following hacky definitions % work rather better. % % \begin{macro}{\mdw@dots} % % First of all, here's some definitions common to both of the dots macros. % The macro takes as an argument the actual code to draw the dots, passing % it the scaled size of a point in the scratch register |\dimen@|; the % register |\box 0| is set to contain a dot of the appropriate size. % % \begin{macrocode} \def\mdw@dots#1{\ensuremath{\mathpalette\mdw@dots@i{#1}}} \def\mdw@dots@i#1#2{% \setbox\z@\hbox{$#1\mskip1.8mu$}% \dimen@\wd\z@% \setbox\z@\hbox{$#1.$}% #2% } % \end{macrocode} % % \end{macro} % % \begin{macro}{\vdots} % % I'll start with the easy one. This is a simple translation of the original % implementation. % % \begin{macrocode} \def\vdots{% \mdw@dots{\vbox{% \baselineskip4\dimen@% \lineskiplimit\z@% \kern6\dimen@% \copy\z@\copy\z@\box\z@% }}% } % \end{macrocode} % % \end{macro} % % \begin{macro}{\ddots} % % And I'll end with the other easy one\dots % % \begin{macrocode} \def\ddots{% \mdw@dots{\mathinner{% \mkern1mu% \raise7\dimen@\vbox{\kern7\dimen@\copy\z@}% \mkern2mu% \raise4\dimen@\copy\z@% \mkern2mu% \raise\dimen@\box\z@% \mkern1mu% }}% } % \end{macrocode} % % \end{macro} % % % \subsection{Lucky dip} % % Time to round off with some trivial environments, just to show how easy % this stuff is. % % \begin{environment}{cases} % \begin{environment}{smcases} % % These are totally and utterly trivial. % % \begin{macrocode} \def\cases{\left\{\,\array{@{}lTl@{}}} \def\endcases{\endarray\,\right.} \def\smcases{\left\{\smarray{@{}lTl@{}}} \def\endsmcases{\endsmarray\,\right.} % \end{macrocode} % % \end{environment} % \end{environment} % % \subsection{Error messages} % % Some token saving: % % \begin{macrocode} \def\mth@error{\PackageError{mathenv}} % \end{macrocode} % % Now for the error messages. % % \begin{macrocode} \def\mth@err@number{% \mth@error{Too many `&' characters found}{% You've put too many `&' characters in an alignment^^J% environment (like `eqnarray' or `spliteqn') and wandered^^J% into trouble. I've gobbled the contents of that column^^J% and hopefully I can recover fairly easily.% }% } % \end{macrocode} % % \begin{macrocode} \def\mth@err@mdsp{% \mth@error{Can't do displays in nondisplay maths mode}{% You're trying to start a display environment, but you're^^J% in nondisplay maths mode. The display will appear but^^J% don't blame me when it looks horrible.% }% } % \end{macrocode} % % \begin{macrocode} \def\mth@err@hdsp{% \mth@error{Can't do displays in LR mode}{% You're trying to start a display environment, but you're^^J% in LR (restricted horizontal) mode. Everything will go^^J% totally wrong, so your best bet is to type `X', fix the^^J% mistake and start again.% }% } % \end{macrocode} % % \vskip\parskip\vbox{ ^^A The best way I could find of keeping this lot % ^^A together, I'm afraid. % That's all there is. Byebye. % % \begin{macrocode} %</mathenv> % \end{macrocode} % \nopagebreak % % \hfill Mark Wooding, \today % } % % \Finale % \endinput