tbl(1)
๐ tbl(1) โ prepare tables for groff documents ๐
okay bestie, welcome to the General Commands Manual entry for tbl, and honestly? she's a baddie. let's get into it โจ
โจ SYNOPSIS โจ
tbl [-C] [file ...]
tbl --help
tbl -v
tbl --version
๐ธ DESCRIPTION ๐ธ
so the GNU implementation of tbl is part of the groff(1) document formatting system ๐ซถ. tbl is a troff(1) preprocessor, and what she does is: she takes table descriptions that are embedded in roff(7) input and translates them into the language that troff actually understands. iconic behavior honestly.
here's the vibe: she copies each file's contents to the standard output stream, and along the way she transforms every table region โ those are the chunks between lines starting with .TS and .TE โ into instructions for the GNU troff formatter. ๐
โ ๏ธ ngl this matters: while GNUtbl's input syntax is highly compatible with AT&Ttbl, the output GNUtblproduces cannot be processed by AT&T troff. you must use GNU troff (or some troff implementing any GNU extensions that got used). no cap, that's a real constraint.
normally tbl is not executed directly by the user โ she gets invoked by specifying the -t option to groff(1). and if there are no file operands present, or if file is -, then tbl reads the standard input stream. simple as that ๐
๐ฆ Overview ๐ฆ
tbl expects to find table descriptions between input lines that begin with .TS (table start) and .TE (table end). each such table region encloses one or more table descriptions. within a table region, table descriptions beyond the first must each be preceded by an input line beginning with .T&. ๐
and listen โ this mechanism does not start a new table region. all table descriptions are treated as part of their .TS/.TE enclosure, even if they're boxed or have column headings that repeat on subsequent pages (see below). so don't get it twisted, bestie.
(experienced roff users, this part's for you ๐: observe that tbl is not a roff language interpreter. the default control character must be used, and no spaces or tabs are permitted between the control character and the macro name. these tbl input tokens remain as-is in the output, where they become ordinary macro calls. macro packages often define TS, T&, and TE macros to handle issues of table placement on the page. tbl produces troff requests to define these macros as empty if their definitions don't exist when the formatter encounters a table region.)
so every table region may begin with region options, and must contain one or more table definitions. each table definition contains a format specification followed by one or more input lines (rows) of entries. and those entries? they comprise the table data. ๐
โจ Region options โจ
the line immediately following the .TS token may specify region options โ these are keywords that influence the interpretation or rendering of the region as a whole, or of all table entries within it, indiscriminately. separate multiple region options with commas, spaces, or tabs. ๐
options that require a parenthesized argument do permit spaces and tabs between the option's name and the opening parenthesis. options accumulate, and they cannot be unset within a region once declared. if an option that takes a parameter gets repeated, the last occurrence controls. and if present, the set of region options must be terminated with a semicolon (;). periodt.
๐ก heads up: any of theallbox,box,doublebox,frame, anddoubleframeregion options makes a table "boxed" for the purpose of later discussion.
allboxโ enclose each table entry in a box; impliesbox.boxโ enclose the entire table region in a box. GNUtblrecognizesframeas a synonym.centerโ center the table region with respect to the line length, reducing the indentation if necessary (with a warning at formatting time) to make the table region fit; the default is to left-align it. GNUtblrecognizescentreas a synonym.decimalpoint(c)โ recognize character c as the decimal separator in columns using theN(numeric) classifier (see subsection "Column classifiers" below). this is a GNU extension. ๐delim(xy)โ recognize characters x and y as start and end delimiters, respectively, foreqn(1)input, and ignore input between them. x and y need not be distinct.doubleboxโ enclose the entire table region in a double box; impliesbox. GNUtblrecognizesdoubleframeas a synonym.expandโ spread the table horizontally to fill the available space (line length minus indentation) by increasing column separation. ordinarily, a table is made only as wide as necessary to accommodate the widths of its entries and its column separations (whether specified or default). whenexpandapplies to a table that exceeds the available horizontal space,tblreduces column separation as far as necessary (even to zero).tblproduces troff input that issues a diagnostic if such compression occurs. the column modifierx(see below) overrides this option.linesize(n)โ draw lines or rules (e.g., frombox) with a thickness of n points. the default is the current type size when the region begins. this option has no effect on terminal devices. ๐nokeepโ don't use roff diversions to manage page breaks. normally,tblemploys them to avoid breaking a page within a table row. this usage can sometimes interact badly with macro packages' own use of diversions โ when footnotes, for example, are employed. this is a GNU extension.nospacesโ ignore leading and trailing spaces in table entries. this is a GNU extension.nowarnโ suppress diagnostic messages produced at document formatting time when the line length and indentation or page length is inadequate to contain a table row. this is a GNU extension.tab(c)โ use the character c instead of a tab to separate entries in a row of table data.
๐ Table format specification ๐
the table format specification is mandatory โ she determines the number of columns in the table AND directs how the entries within it are to be typeset. she's the main character here, fr fr. ๐
the format specification is a series of column descriptors. each descriptor encodes a classifier followed by zero or more modifiers. classifiers are letters (recognized case-insensitively) or punctuation symbols; modifiers consist of or begin with letters or numerals. spaces, tabs, newlines, and commas separate descriptors. ๐
newlines and commas apply the descriptors following them to a subsequent row of the table (which is how you get column headings centered or emboldened while the table entries for the data are not, for instance). we term the resulting group of column descriptors a row definition. within a row definition, separation between column descriptors by spaces or tabs is often optional; only some modifiers, described below, make separation necessary.
the classifier selects from one of several arrangements ๐ฆ:
- some determine the positioning of table entries within a rectangular cell: centered, left-aligned, numeric (aligned to a configurable decimal separator), and so on.
- others perform special operations like drawing lines or spanning entries from adjacent cells in the table.
except for |, any classifier can be followed by one or more modifiers; some of these accept an argument, which in GNU tbl can be parenthesized. modifiers select fonts, set the type size, and perform other tasks described below.
the format specification can occupy multiple input lines, but must conclude with a dot . followed by a newline. each row definition applies in turn to one row of the table. tbl applies the last row definition to rows of table data in excess of the row definitions. โญ
for clarity in this document's examples, we write classifiers in UPPERCASE and modifiers in lowercase. so with that in mind:
CbCb,LR.
defines two rows of two columns! ๐ญ the first row's entries are centered and boldfaced; the second and any further rows' first and second columns are left- and right-aligned, respectively. any rows of entries appended to the table data reuse the row definition LR. slay.
and here's the flex: the row definition with the most column descriptors determines the number of columns in the table. any row definition with fewer โ GNU tbl implicitly extends it on the right-hand side with L descriptors as many times as necessary to make the table rectangular. ๐
๐ Column classifiers ๐
the L, R, and C classifiers are the easiest to understand and use. starting simple, bestie:
A,aโ center the longest entry in this column, left-align the remaining entries in the column with respect to the centered entry, then indent all entries by one en. such "alphabetic" entries (hence the name of the classifier) can be used in the same column asL-classified entries, as inLL,AR.. theAentries are often termed "sub-columns" due to their indentation.C,cโ center entry within the column.L,lโ left-align entry within the column.N,nโ numerically align entry in the column.tblaligns columns of decimal numbers vertically at the units place. if multiple decimal separators are adjacent to a digit, it uses the rightmost one for vertical alignment. if there is no decimal separator,tbluses the rightmost digit for vertical alignment; if no digits are present,tblcenters the entry within the column. the roff dummy character\&in an entry marks the glyph preceding it (if any) as the units place; if multiple instances occur in the data,tbluses the leftmost for alignment.
๐ also: ifN-classified entries share a column withLorRentries,tblcenters the widestNentry with respect to the widestLorRentry, preserving the alignment ofNentries with respect to each other.
๐ซถ and one more thing: decimal separators ineqnequations withinN-classified columns can conflict withtbl's use of them for alignment. specify thedelimregion option to maketblignore the data withineqndelimiters.
R,rโ right-align entry within the column.S,sโ span previous entry on the left into this column.^โ span entry in the same column from the previous row into this row._,-โ replace table entry with a horizontal rule.tblexpects an empty table entry to correspond to this classifier; if data are found there, it issues a diagnostic message. if the entire row definition consists of these classifiers (only one is necessary), it is treated as a_occupying a row of table entries, and no corresponding data are expected.=โ swaps that table entry out for a double horizontal rule.tblis lowkey expecting an empty entry to match this classifier, so if it finds actual data chilling there, it's gonna hit you with a diagnostic message. And if the whole row definition is nothing but these classifiers (literally one is enough, bestie), it gets treated as a=sitting in a row of table entries โ and no data are expected for it.|โ drops a vertical rule (a line, a slay) on the matching row of the table. Two of these next to each other? Double vertical rule, iconic. This classifier does not count toward the column count and zero table entries correspond to it. A|to the left of the first column descriptor or to the right of the last one gives you a vertical rule at the edge of the table โ and in boxed tables those are redundant (and ignored, fr).
To change up the table format inside a tbl region, use the .T& token at the start of a line. Then follow it with a format specification and table data โ but not region options. Heads up: the number of columns in a format introduced this way cannot increase relative to the previous format. If you need more, you gotta end the table region and start a new one. And if that won't work because your region uses box options or the columns line up in a way you simply cannot tolerate, then you have to design the initial table format specification to include the maximum quantity of columns you'll ever need, and use the S horizontal spanning classifier where necessary to get the columnar alignment you're after. Plan ahead, queen.
Spanning horizontally in the first column, or vertically on the first row, is an error. tbl does not support non-rectangular span areas. Periodt. ๐
โจ๐ Column modifiers ๐โจ
Any number of modifiers can follow a column classifier. Modifier arguments, where accepted, are case-sensitive (yes, really). If you slap a given modifier onto a classifier more than once, or you apply conflicting modifiers, only the last occurrence has any effect โ the rest is giving nothing.
The modifier x is mutually exclusive with e and w, but e is not mutually exclusive with w. If you use these in combination, x unsets both e and w, while either e or w overrides x.
b,Bโ typesets the entry in boldface, aka it's just out here abbreviatingf(B).d,Dโ aligns a vertically spanned table entry to the bottom ("down") of its range instead of the center. This one's a GNU extension โจe,Eโ equalizes the widths of columns bearing this modifier to the width of the widest one. This modifier also sets the default line length used in a text block.f,Fโ selects the typeface for the table entry. What follows must be a font or style name (one or two characters not starting with a digit), a font mounting position (a single digit), or a name or mounting position of any length in parentheses. That last form is a GNU extension. (The parameter lines up with the one accepted by the troffftrequest.) โ ๏ธ A one-character argument not in parentheses has to end the row description, or be separated from whatever follows by one or more spaces or tabs.i,Iโ typesets the entry in an oblique or italic face, abbreviatingf(I).m,Mโ calls a groff macro before typesetting a text block (peep the "Text blocks" subsection below). GNU extension, bestie. What follows must be a macro name of one or two characters, or a name of any length in parentheses. A one-character macro name not in parentheses must be separated from what follows by one or more spaces or tabs. The named macro has to be defined before the table region containing this column modifier is even encountered. Keep it simple in there โ the macro should contain only simple groff requests to change text formatting, like adjustment or hyphenation. It gets called after the column modifiersb,f,i,p, andvtake effect, which means it can override other column modifiers. Sneaky but valid. ๐p,Pโ sets the type size. An integer n with an optional leading sign must follow. Unsigned? Type size is set to n points. Otherwise the type size is incremented or decremented per the sign by n points. Using a signed multi-digit number is a GNU extension. (The parameter corresponds to the one accepted by the troffpsrequest.) โ ๏ธ If a type size modifier is followed by a column separation modifier (see below), they must be separated by at least one space or tab.t,Tโ aligns a vertically spanned table entry to the top of its range instead of the center.u,Uโ moves the column up one half-line, "staggering" the rows. This is a Documenter's Workbench (DWB) 1.0 and Research Tenth Edition Unix extension.v,Vโ sets the vertical spacing of a text block. An integer n with an optional leading sign must follow. Unsigned? Vertical spacing is set to n points. Otherwise it's incremented or decremented per the sign by n points. Signed multi-digit number = GNU extension. (This parameter corresponds to the one accepted by the troffvsrequest.) โ ๏ธ If a vertical spacing modifier is followed by a column separation modifier (see below), they must be separated by at least one space or tab.w,Wโ sets the column's minimum width. A number must follow: either a unitless integer, or a roff horizontal measurement in parentheses. Parentheses are required if the width is going to be followed immediately by an explicit column separation (or, alternatively, just follow the width with one or more spaces or tabs, do your thing). No unit specified? Ens are assumed. This modifier sets the default line length used in a text block.x,Xโ expands the column. After computing the column widths, any remaining line length gets distributed evenly over all columns bearing this modifier. This is a Documenter's Workbench (DWB) 3.3 extension. Applying thexmodifier to more than one column is a GNU extension. This modifier sets the default line length used in a text block.z,Zโ ignores the table entries corresponding to this column for width calculation purposes. Basically: compute the column's width using only the info in its descriptor. This is a Documenter's Workbench (DWB) 1.0 and Research Tenth Edition Unix extension.nโ a numeric suffix on a column descriptor sets the separation distance (in ens) from the succeeding column. The default separation is3n. This separation gets proportionally multiplied if theexpandregion option is in effect โ and for tables wider than the output line length, this separation might literally be zero. ๐ญ A negative separation cannot be specified. And a separation amount after the last column in a row is nonsensical and will provoke a diagnostic fromtbl.
๐ธ Table data ๐ธ
Put your table data on the lines after the format specification. Each text line corresponds to a table row โ except a backslash at the end of a line of table data continues an entry onto the next input line. (Text blocks, discussed below, also spread table entries across multiple input lines, so you're not alone.) Table entries within a row are separated in the input by a tab character by default โ check the tab region option above.
Excess entries in a row of table data (the ones with no corresponding column descriptor, not even an implicit one from rectangularization of the table)? tbl discards them, with a diagnostic message. ๐๏ธ
tbl passes roff control lines through unaltered to its output. If you want to visibly mark an empty table entry in the document source, populate it with the \& roff dummy character. The table data are interrupted by a line consisting of the .T& input token, and conclude with the line .TE.
Ordinarily, a table entry is typeset rigidly. It is not filled, broken, hyphenated, adjusted, or populated with supplemental inter-sentence space. Not one of those things. tbl instructs the formatter to measure each table entry as it occurs in the input, updating the width required by its corresponding column. If the z modifier applies to the entry, that measurement is ignored; if w applies and its argument is larger than this width, that argument is used instead. And in contrast to conventional roff input (within a paragraph, say), changes to text formatting โ like font selection or vertical spacing โ do not persist between entries. Every entry starts fresh, main character energy only. โจ
tbl interprets several forms of table entry specially. ๐
- If a table row contains only an underscore or equals sign (
_or=),tbldraws a single or double horizontal rule (line), respectively, across the table at that point. - If a table entry contains only
_or=on an otherwise populated row,tblpopulates its cell with a single or double horizontal rule, respectively, that joins its neighbors. - If a table entry contains only
\_or\=on an otherwise populated row,tblpopulates its cell with a single or double horizontal rule, respectively, that does not (quite) join its neighbors. - If a table entry contains only
\Rx, where x is any roff ordinary, special, or indexed character,tblpopulates its cell with enough repetitions of the glyph corresponding to x to fill the column without joining its neighbors. - On any row but the first, a table entry of
\^causes the entry above it to span down into the current one. ๐ฒ
On occasion, these special tokens may be required as literal table data. Here's how, bestie:
- To use either
_or=literally and alone in an entry, prefix or suffix it with the roff dummy character\&. - To express
\_,\=, or\R, use a roff escape sequence to interpolate the backslash:\eor\[rs]. - A reliable way to emplace the
\^glyph sequence within a table entry is to use a pair of groff special character escape sequences:\[rs]\[ha].
Rows of table entries can be interleaved with groff control lines โ and nope, those don't count as table data, they're just passing through. โ ๏ธ On such lines the default control character (.) must be used (and must not be changed); the no-break control character is not recognized. To start the first table entry in a row with a dot, precede it with the roff dummy character \&.
๐ฆ Text blocks ๐ฆ
An ordinary table entry's contents can make a column โ and therefore the whole table โ too wide. And then the table exceeds the line length of the page, and it becomes ugly, or it's exposed to truncation by the output device. Not the vibe. ๐ญ When a table entry requires more conventional typesetting, breaking across more than one output line (and thereby increasing the height of its row), it can be placed within a text block.
T{sitting at the end of an input line is not a normal entry, bestie โ it's a token that starts a text block. And aT}at the start of an input line is the token that ends one. These tokens can chill on the same line as other table data (stuff before theT{, stuff after theT}). Everything between the tokens gets formatted inside a diversion bytroff. ๐
Text blocks cannot be nested. No nesting. Zero. Don't even try it. ๐ซ
Multiple text blocks in a single table row? Totally fine, iconic even. โจ
A text block gets formatted the same way the text before the table was, just tweaked by whatever column descriptors apply. Specifically: the classifiers A, C, L, N, R, and S decide a text block's alignment inside its cell โ but not its adjustment. So if you wanna change the adjustment of a text block separately from the rest of the document, drop na or ad requests at the beginning of the text block. And just like any other table entry, when the text block ends, poof ๐จ โ any formatting parameter changes are discarded. They do not leak into subsequent table entries, not even into other text blocks. Boundaries, queen. ๐
Now, if w or x modifiers aren't specified for all columns of a text block's span, the default length of the text block (more precisely: the line length used to process the text block's diversion) gets computed as:
LรC/(N+1)
where L is the current line length, C is the number of columns the text block spans, and N is the number of columns in the table. If you need to, you can also control a text block's width by putting an ll (line length) request inside it, before any text you want formatted. And because tbl formats text blocks via a diversion, its height and width end up available in the troff registers dn and dl respectively. Free real estate. ๐
๐ ๏ธ roff interface โ bestie, meet the registers
The register TW holds the width of the table region in basic units. It can't be used within the region itself, but it's defined before the .TE token gets output, so a troff macro named TE can make use of it. Meanwhile T. is a Boolean-valued register telling you whether the bottom of the table is currently being processed. There's also a #T register used internally. Avoid using these names for anything else โ they're taken, no cap.
tbl also defines a macro T# that produces the bottom and side lines of a boxed table. tbl itself arranges for the output to include a call of this macro at the end of such a table, but macro packages can also use it to build boxes for multi-page tables โ call it from a page footer macro that is itself called by a trap planted near the bottom of the page. (Scroll to the โLimitationsโ section below for more on multi-page tables. ๐ซถ)
Oh and heads up: GNU tbl internally uses register, string, macro, and diversion names starting with the numeral 3. So if your document is getting preprocessed with GNU tbl, do not use any such identifiers. That namespace is spoken for. ๐
๐ Interaction with eqn
Process your document with tbl before eqn(1). (groff(1) handles putting preprocessors in the right order automatically, so you don't have to stress about it. ๐
)
Don't call the EQ and EN macros inside tables โ just don't. Instead, set up delimiters in your eqn input and use the delim region option so tbl will recognize them. That's the move. โจ
๐ GNU tbl enhancements โ the glow-up
On top of the extensions we already talked about, GNU tbl removes the constraints that AT&T tbl users just had to live with. RIP to those struggles. ๐๏ธ
- Region options can be written in any lettercase. uppercase, lowercase, whatever feels right.
- There's no limit on the number of columns in a table, no matter how they're classified โ and no limit on the number of text blocks either. Go off. ๐
- GNU
tbllooks at all table rows when computing column widths, not just the ones in the first 200 input lines of a region. Same energy for table continuation tokens (.T&) โ it recognizes them outside a region's first 200 input lines too. - Numeric and alphabetic entries can share a column. Coexist, besties. ๐ซถ
- Numeric and alphabetic entries can also span horizontally.
๐ช Using GNU tbl within macros (genuinely possible, kinda tricky)
You can embed a table region inside a macro definition. But since tbl writes its own macro definitions at the beginning of every table region, you've gotta call end macros instead of closing macro definitions with ... Also: the escape character must be disabled. Yeah. Both of those. ๐ญ
Not every tbl feature works from inside these macros, because tbl is a roff preprocessor โ it sees the input before troff does. Example: vertically aligning decimal separators fails if the numbers containing them show up as macro or string parameters. The alignment is done by tbl itself, and all it sees is \$1, \$2, and so on โ so it can't recognize a decimal separator that only appears later, when troff interpolates a macro or string definition. It's giving "you can't align what you can't see". ๐
And using tbl macros inside conditional input (anything contingent on an if, ie, el, or while request) can give you misleading line numbers in later diagnostics. Why? tbl unconditionally injects its output into the source document, but the conditional branch containing it might not be taken โ and if it isn't, the lf requests that tbl injects to restore the source line number can't take effect. Consider copying the input line counter register c. and restoring its value at a convenient spot after the applicable arithmetic. ๐งฎ
โจ Options โจ
--helpโ prints a usage message. Then dips. โ๏ธ-v,--versionโ show version info. Then also dips. โ๏ธ
(For real, --help, -v, and --version all exit afterward. No lingering.)
-Cโ Enable AT&T compatibility mode: recognize.TSand.TEeven when they're followed by a character other than a space or newline, and interpret the copy-mode leader escape sequence\aas a leader character. Vintage mode, but make it faithful. ๐
๐ฆ Exit status ๐ฆ
0โ successful operation. Slay.2โ the program can't interpret its command-line arguments. ๐ฌ1โ it encountered an error during operation. ๐ญ
๐ Limitations ๐
Okay bestie, this is the part where we're real with each other.
Inside a tbl region (not in text blocks, not on roff control lines), avoid escape sequences that read the rest of the input line โ like \" and \! do. Just don't.
Multi-page tables need support at the time the document is formatted if they're boxed and/or you want their column headings repeated after page breaks. A convention for that support has arisen in macro packages like ms, mm, and me. To use it: follow the .TS token with a space and then H โ the formatter reads that as a TS macro call with an H argument. Then, inside the table data, call the TH macro, which tells the macro package where the headings end. If your table has no such heading rows, or you just don't want them repeated, call TH immediately after the table format specification. Got it? ๐ซก
If a multi-page table is boxed or has repeating column headings, do not enclose it with keep/release macros, and don't divert it in any other way.
Also: the bp request will not cause a page break in a "TS H" table. So define a macro to wrap bp โ invoke it normally if there's no current diversion. Otherwise, pass the macro call to the enclosing diversion using the transparent line escape sequence \!; this will "bubble up" the page break to the output device. There's a demo down in the "Examples" section. โญ
grotty(1) does not support double horizontal rules โ it uses single rules instead. It also ignores half-line motions, so the u column modifier does absolutely nothing there. On terminal devices ("nroff mode"), horizontal rules and box borders take up a full vee of space, and doublebox doubles that for borders. So tables using these features need more vertical space in nroff mode than in troff mode โ write your ne requests accordingly, okay? In nroff mode, vertical rules between columns get drawn in the space between columns; using double vertical rules and/or shrinking the column separation below the default can make them ugly or even overstrike them with table data. Not the vibe. ๐ญ
A text block inside a table must fit on one page. That's just the rule.
Using \a to put leaders in table entries does not work in GNU tbl, except in compatibility mode. And honestly? This is correct behavior โ \a is an uninterpreted leader. You can still use the roff leader character (Ctrl+A), or define a string to use \a the way it was designed: interpreted only in copy mode. Like this:
.ds a \a
.TS
box center tab(;);
Lw(2i)0 L.
Population\*a;6,327,119
.TE
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ Population..........6,327,119 โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
A leading or trailing | in a format specification โ like in |LCR|. โ produces an en space between the rules and the content of adjacent columns. If you don't want that space (the rule should abut the content, thank you very much), you can introduce "dummy" columns with zero separation and empty corresponding table entries before and/or after. Like so:
.TS
center tab(#);
R0|L C R0|L.
_
#levulose#glucose#dextrose#
_
.TE
Those dummy columns have zero width, so they're invisible. ๐ซฅ Unfortunately, they usually don't work as intended on terminal devices. That's the tea. ๐
๐ EXAMPLES ๐
ngl bestie, tbl is one of those things where the formal description kinda eats but the examples are what actually make it click โจ like fr, at first it is so much easier to just absorb the language by looking at it. So let's go ๐
.TS
box center tab(#);
Cb Cb
L L.
Ability#Application
Strength#crushes a tomato
Dexterity#dodges a thrown tomato
Constitution#eats a month-old tomato without becoming ill
Intelligence#knows that a tomato is a fruit
Wisdom#chooses \f[I]not\f[] to put tomato in a fruit salad
Charisma#sells tomato-based fruit salads to hypercarnivores
.TE
and that renders as:
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ Ability Application โ
โ Strength crushes a tomato โ
โ Dexterity dodges a thrown tomato โ
โ Constitution eats a month-old tomato without becoming ill โ
โ Intelligence knows that a tomato is a fruit โ
โ Wisdom chooses not to put tomato in a fruit salad โ
โ Charisma sells tomato-based fruit salads to hypercarnivores โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
okay the tomato stat block is iconic, I don't make the rules ๐ ๐
The A and N column classifiers? lowkey way easier to see than to have me explain them in words, no cap. watch this:
.TS
center tab(;);
CbS,LN,AN.
Daily energy intake (in MJ)
Macronutrients
.\" assume 3 significant figures of precision
Carbohydrates;4.5
Fats;2.25
Protein;3
.T&
LN,AN.
Mineral
Pu-239;14.6
_
.T&
LN.
Total;\[ti]24.4
.TE
which gives you:
Daily energy intake (in MJ)
Macronutrients
Carbohydrates 4.5
Fats 2.25
Protein 3
Mineral
Pu-239 14.6
โโโโโโโโโโโโโโโโโโโโโโโโโโโโ
Total ~24.4
see how it just works?? the alignment is giving main character energy fr ๐
next up, bestie, we're gonna lightly adapt a compact presentation of spanning, vertical alignment, and zero-width column modifiers from the mandoc reference for its tbl interpreter. literally a glow-up of an example. it rewards close study โ that's a direct quote from the assignment, and honestly? it's not wrong. put your phone down for this one ๐ซถ
.TS
box center tab(:);
Lz S | Rt
Ld| Cb| ^
^ | Rz S.
left:r
l:center:
:right
.TE
and boom:
โโโโโโโโโโโโโโฌโโโโ
โ left โ r โ
โ โ center โ โ
โ l โ right โ
โโโโโดโโโโโโโโโโโโโ
look at those little ^ doing their thing. obsessed honestly โจ
now, row staggering? not visually achievable on terminal devices ๐ญ like the terminal just cannot render it, periodt. BUT a table that uses it can still remain comprehensible nonetheless. so it's not a total loss:
.TS
center tab(|);
Cf(BI) Cf(BI) Cf(B), C C Cu.
n|n\f[B]\[tmu]\f[]n|difference
1|1
2|4|3
3|9|5
4|16|7
5|25|9
6|36|11
.TE
which lands like:
n nรn difference
1 1
2 4 3
3 9 5
4 16 7
5 25 9
6 36 11
๐ some tbl features just cannot be illustrated in the limited environment of a portable man page. it is what it is, bestie.
so here's a workaround that is giving resourceful ๐ we can define a macro outside of a tbl region that we can then call from within it, to cause a page break inside a multi-page boxed table. you can pick whatever name you want โ I'm just out here choosing one. just be sure to change both occurrences of โBPโ if you rename it, or it will simply not slay:
.de BP
. ie '\\n(.z'' .bp \\$1
. el \!.BP \\$1
..
โจ SEE ALSO โจ
- "TblโA Program to Format Tables", by M. E. Lesk, 1976 (revised 16 January 1979), AT&T Bell Laboratories Computing Science Technical Report No. 49. the origin story, the blueprint, the mother of us all ๐ธ
- the spanning example above was taken from mandoc's man page for its tbl implementation ๐ซก
groff(1),troff(1)
and that's a wrap, bestie ๐ this whole thing was brought to you by groff 1.24.1, dated 2026-04-22, and it lives in tbl(1). go forth and make tables that eat ๐
โจ๐
๐ฆ where this page comes from
- ubuntu stonking: groff-base 1.24.1-1
- Copyright 1989-2020 Free Software Foundation, Inc
- ๐ girlified by deepseek-ai/DeepSeek-V4.1-Flash on : a language model's rewrite of the original, under its terms