# Creating a table with FIXED length column widths

I'm trying to recreate a table from a RPG character sheet:

I don't care about the colouring particularly, black and white works perfectly fine, the sizing is the main issue. I am going to be making multiple copies of this table, and I want all of them to be uniform. So even if every data value was blank is would then just produce a blank table of the same measurements as a filled in version.

I'm wanting each box to have (roughly) the following widths (relative to the total table width):

• Name:70%, Class: 30%
• Range: 21%, RoF: 37%, Damage: 40%
• Pen: 21%, Clip 18%, Reload: 18%, Weight: 14%, Availability: 19%
• Special: 100%

Sketch with measurements in place of the data for the table (not drawn to scale):

These widths are to keep the table looking close to the original as possible in terms of layout. Being still relatively new to LaTeX and very new to doing more than a basic data table. The only way I could think of doing this would be something along the lines of making 100 columns, and using \multicolumn{% above} . However even with that lengthy method, since LaTeX likes to make everything as small as possible the far right columns wouldn't have their full width and it would create different sized tables depending on the data.

I've looked around as quite a number of questions about fixed length table columns but all seem to be fixing it to be smaller than the text within (causing multine) rather than wanting to keep the size the same and allow empty space to fill the gap.

For some context, the goal is to have a macro that I can input the data for each box, and then the macro creates a copy of the table, filled in with the entered data. The macro I have sorted, the issue is just the formatting/layout of the table.

Here is the macro code I have:

\documentclass[11pt]{article}

\usepackage{fancyhdr}

\oddsidemargin 0.2cm
\topmargin -1.0cm
\textheight 24.0cm
\textwidth 15.25cm
\parindent=0pt
\parskip 1ex
\pagestyle{fancy}

\newcommand{\printweptable}{
\begin{tabular}{p{\linewidth}}
Name: \name \hfill Cls: \class \\
Rng: \range    \hfill RoF: \rof \hfill Rmg: \dmg\\
Pen: \pen \hfill Clip: \clip \hfill Rld: \rld \hfill Wt: \wt \hfill Avl: \avl\\
Special: \special
\end{tabular}
}

%Weapon info
\newcommand{\setwepstats}[9]{%
\def\name{#1}%
\def\class{#2}%
\def\range{#3}%
\def\rof{#4}%
\def\dmg{#5}%
\def\pen{#6}%
\def\clip{#7}%
\def\rld{#8}%
\def\wt{#9}
\wepstatsrelay
}%

\def\wepstatsrelay#1#2{
\def\avl{#1}%
\def\special{#2}}%

\begin{document}

\printweptable

\setwepstats{}{}{}{}{}{}{}{}{}{}{}

\printweptable

\end{document}


I think I shall use the code provided by Werner, and scrap the macro I had (his seems cleaner to use and reuse.

I have now merged the codes provided by both Werner and cfr and have this working code:

\documentclass{article}

\usepackage{xkeyval}

\makeatletter
\define@cmdkey{rpg}[char@]{name}[]{}
\define@cmdkey{rpg}[char@]{class}[]{}
\define@cmdkey{rpg}[char@]{range}[]{}
\define@cmdkey{rpg}[char@]{rof}[]{}
\define@cmdkey{rpg}[char@]{damage}[]{}
\define@cmdkey{rpg}[char@]{pen}[]{}
\define@cmdkey{rpg}[char@]{clip}[]{}
\define@cmdkey{rpg}[char@]{weight}[]{}
\define@cmdkey{rpg}[char@]{availability}[]{}
\define@cmdkey{rpg}[char@]{special}[]{}
\define@cmdkey{rpg}[char@]{mods}[]{}
\define@cmdkey{rpg}[char@]{qualities}[]{}
\define@cmdkey{rpg}[char@]{width}[\linewidth]{}

\newcommand{\statlabel}{\textsc}

\usepackage{calc,xparse}
\newlength\mycardwidth
\setlength\mycardwidth{.75\textwidth}
\NewDocumentCommand \boxme { O{} }{%
\fbox{%
\parbox{\linewidth-2\fboxrule-2.5\fboxsep}{\strut #1}%
}%
}
\NewDocumentCommand \pageme { O{} O{} }{%
\begin{minipage}{#1\mycardwidth}
\centering\boxme[#2]
\end{minipage}%
}
\NewDocumentCommand \cardme { O{.75\textwidth} +m }{%
\setlength\mycardwidth{#1-2\fboxrule-2\fboxsep}%
\fbox{%
\begin{minipage}{\mycardwidth}
\centering
#2%
\end{minipage}%
}%
}

\newcommand{\setcharacterstats}[1]{%
\begingroup
\noindent
\centering
\cardme[\textwidth]{%
\pageme[.69][\statlabel{Name}: \char@name]%
\pageme[.31][\statlabel{Cls}: \char@class]

\pageme[.22][\statlabel{Rng}: \hfill \char@range \hfill m]%
\pageme[.38][\statlabel{RoF}: \char@rof]%
\pageme[.4][\statlabel{Dmg}: \char@damage]

\pageme[.22][\statlabel{Pen}: \char@pen]%
\pageme[.19][\statlabel{Clip}: \char@clip]%
\pageme[.18][\statlabel{Wt}: \hfill \char@weight \hfill kg]%
\pageme[.22][\statlabel{Avl}: \char@availability]

\pageme[][\statlabel{Special}: \char@special]%

\pageme[][\statlabel{Mods}: \char@mods]%

\pageme[][\statlabel{Qualities}: \char@qualities]%
}
}
\makeatletter

%-----------------------------------
\begin{document}

\setcharacterstats{
name = Long Las,
class = Basic,
range = 150,
rof = S/-/-,
damage = 1d10+3,
pen = 1,
clip = 40,
weight = 4.5,
availability = Scarce,
special = Accurate Felling (4) Reliable
}

\end{document}


With that code however, is there a way to center the variable data within the remaining space of the cell? E.g. If I had Carbine as the name, it would make the distance from the : and the C be equal to the distance from the e and the right side of the cell. (Not put it in the center of the entire cell).

I tried to edit the variable within the macro to replace \char@name with \center{\char@name} however this threw the variable onto an entirely new line (within the cell) and centered it based on the cell sides alone. If there's no simple way to do this, I'll stick with it being left aligned.

• Welcome! Please post the code you've got so far. Not necessarily the table, though it would help to have something we can copy-paste with the right number of rows and columns, but knowing how you have defined the macro would be helpful. Otherwise, very likely suggestions will conflict with what you've got.
– cfr
Nov 5, 2016 at 22:25
• Instead of a table, you probably could better use a LaTeX picture environment. Or use tikz, but that seems overkill for such a simple thing. Nov 5, 2016 at 22:27
• And how wide is the table? Will the standard column spacing work or do you need to shrink that? It looks as if you'd need 6 columns of type p{} if you create this as a table. Alternatively, you could just use a series of minipages or boxes, for example.
– cfr
Nov 5, 2016 at 22:28
• Shouldn't each line add up to 100?
– cfr
Nov 5, 2016 at 22:34
• To answer these in order cfr; I'll edit in the code I had to start with. The table I'm hoping to fit the width to half an A4 page, but it can take the full page if needs be. I was planning to use minipages to fit 2 of them side by side. Yes, it SHOULD add up to 100, but I forgot to account for the width of the separator lines when I measured the original. Nov 5, 2016 at 23:33

# Key-Value Version

Here's a version of my earlier answer which provides a key-value interface. I've used the LaTeX 3 code for handling the keys because I find this the most intuitive to use, although the syntax looks a bit alien at first. (That is, I've never understood how to use xkeyval etc. properly, so I can't do it in the difficult, non-alien-looking way.)

Also, since my original code used xparse, I didn't need to add any packages to use this approach. geometry is added just to get a wider page for the example.

\cardme[<list of key-value pairs>]


\cardme now takes one optional argument which is a list of key-value pairs.

Available keys are:

avl=, card width=, clip=, cls=, dmg=, name=, pen=, rld=, rng=, rof=, special=, wt=,

Apart from card width, these take text which is the value of the key in this case. In each case, the default is to typeset nothing as the value. The default for card width, which expects a dimension, is .75\textwidth, as in the original code.

\cardnames{<list of key-value pairs>}


can be used to customise the labels used for each key-value pair. The keys are as before, except that there is no card width. The difference is that this macro doesn't typeset anything. It just changes the text which will be used when creating the label in each box if, and when, \cardme is later used.

Similarly

\cardunits{<list of key-value pairs>}


can be used to assign a unit to each type of value. By default these are empty except for rng and wt which are set to km and kg respectively.

For example, suppose we create the following cards:

\noindent
\cardme


This will be an empty card with default width and labels.

\noindent
\cardme[%
card width=\textwidth,
name=This One,
cls=Aristocracy,
rng=450,
rof=RoF,
dmg=Extensive,
pen=Pen,
clip=Y,
rld=N,
wt=1,
avl=Y,
special=Extraordinary powers,
]


This card uses the default labels but, this time, the card is completed with a value in each box and the width is increased to allow for this.

\cardnames{
name=Enw,
cls=Dth,
rng=Pll,
dmg=Dfd,
clip=Cnf,
rld=AilLl,
wt=Pwys,
avl=ArGl,
special=Arbennig,
}


This doesn't typeset anything. Rather, it changes some of the labels, while leaving others untouched. Hence, when we typeset another empty card with

\noindent\cardme[card width=.85\textwidth]


we will get an empty card with a mixture of default and custom labels and width of .85\textwidth.

Complete code:

\documentclass{article}
\usepackage{calc,xparse,geometry}
\ExplSyntaxOn
\dim_new:N \l_Archimedes_card_width_dim
\dim_set:Nn \l_Archimedes_card_width_dim { .75\textwidth }
\keys_define:nn { Archimedes / card }
{
avl .tl_set:N = \l_Archimedes_avl_tl,
card~width .dim_set:N = \l_Archimedes_card_width_dim,
clip .tl_set:N = \l_Archimedes_clip_tl,
cls .tl_set:N = \l_Archimedes_cls_tl,
dmg .tl_set:N = \l_Archimedes_dmg_tl,
name .tl_set:N = \l_Archimedes_name_tl,
pen .tl_set:N = \l_Archimedes_pen_tl,
rld .tl_set:N = \l_Archimedes_rld_tl,
rng .tl_set:N = \l_Archimedes_rng_tl,
rof .tl_set:N = \l_Archimedes_rof_tl,
special .tl_set:N = \l_Archimedes_special_tl,
wt .tl_set:N = \l_Archimedes_wt_tl,
avl .initial:n = ,
clip .initial:n = ,
cls .initial:n = ,
dmg .initial:n = ,
name .initial:n = ,
pen .initial:n = ,
rld .initial:n = ,
rng .initial:n = ,
rof .initial:n = ,
special .initial:n = ,
wt .initial:n = ,
}
\keys_define:nn { Archimedes / card / names }
{
avl .tl_set:N = \l_Archimedes_avlname_tl,
clip .tl_set:N = \l_Archimedes_clipname_tl,
cls .tl_set:N = \l_Archimedes_clsname_tl,
dmg .tl_set:N = \l_Archimedes_dmgname_tl,
name .tl_set:N = \l_Archimedes_namename_tl,
pen .tl_set:N = \l_Archimedes_penname_tl,
rld .tl_set:N = \l_Archimedes_rldname_tl,
rng .tl_set:N = \l_Archimedes_rngname_tl,
rof .tl_set:N = \l_Archimedes_rofname_tl,
special .tl_set:N = \l_Archimedes_specialname_tl,
wt .tl_set:N = \l_Archimedes_wtname_tl,
avl .initial:n = Avl,
clip .initial:n = Clip,
cls .initial:n = Cls,
dmg .initial:n = Dmg,
name .initial:n = Name,
pen .initial:n = Pen,
rld .initial:n = Rld,
rng .initial:n = Rng,
rof .initial:n = RoF,
special .initial:n = Special,
wt .initial:n = Wt,
}
\keys_define:nn { Archimedes / card / units }
{
avl .tl_set:N = \l_Archimedes_avlunit_tl,
clip .tl_set:N = \l_Archimedes_clipunit_tl,
cls .tl_set:N = \l_Archimedes_clsunit_tl,
dmg .tl_set:N = \l_Archimedes_dmgunit_tl,
name .tl_set:N = \l_Archimedes_nameunit_tl,
pen .tl_set:N = \l_Archimedes_penunit_tl,
rld .tl_set:N = \l_Archimedes_rldunit_tl,
rng .tl_set:N = \l_Archimedes_rngunit_tl,
rof .tl_set:N = \l_Archimedes_rofunit_tl,
special .tl_set:N = \l_Archimedes_specialunit_tl,
wt .tl_set:N = \l_Archimedes_wtunit_tl,
avl .initial:n = ,
clip .initial:n = ,
cls .initial:n = ,
dmg .initial:n = ,
name .initial:n = ,
pen .initial:n = ,
rld .initial:n = ,
rng .initial:n = km,
rof .initial:n = ,
special .initial:n = ,
wt .initial:n = kg,
}
\cs_new_protected:Nn \Archimedes_pageme:nn
{
\begin{minipage}{#1\l_Archimedes_card_width_dim}
\centering
\fbox{
\parbox{\linewidth-2\fboxrule-2.5\fboxsep}
{
\strut
\textsc{ \use:c { l_Archimedes_#2name_tl } } : ~ \skip_horizontal:n {\fill} \use:c { l_Archimedes_#2_tl } \skip_horizontal:n {\fill}  \hbox:n { \use:c { l_Archimedes_#2unit_tl } }
}
}
\end{minipage}
}
\NewDocumentCommand \cardnames { m }{
\keys_set:nn { Archimedes / card / names } { #1 }
}
\NewDocumentCommand \cardunits { m }{
\keys_set:nn { Archimedes / card / units } { #1 }
}
\NewDocumentCommand \cardme { O {} }{%
\group_begin:
\keys_set:nn { Archimedes / card } { #1 }
\dim_sub:Nn \l_Archimedes_card_width_dim { 2\fboxrule + 2\fboxsep }
\fbox{
\begin{minipage}{\l_Archimedes_card_width_dim}
\centering
\Archimedes_pageme:nn {.69 } { name }
\Archimedes_pageme:nn {.31 } { cls }\par
\Archimedes_pageme:nn { .22 } { rng }
\Archimedes_pageme:nn { .38 } { rof }
\Archimedes_pageme:nn { .4 } { dmg }\par
\Archimedes_pageme:nn { .22 } { pen }
\Archimedes_pageme:nn { .19 } { clip }
\Archimedes_pageme:nn { .19 } { rld }
\Archimedes_pageme:nn { .18 } { wt }
\Archimedes_pageme:nn { .22 } { avl }\par
\Archimedes_pageme:nn {  } { special }
\end{minipage}
}
\group_end:
}
\ExplSyntaxOff
\begin{document}
\noindent
\cardme

\noindent
\cardme[%
card width=\textwidth,
name=This One,
cls=Aristocracy,
rng=450,
rof=RoF,
dmg=Extensive,
pen=Pen,
clip=Y,
rld=N,
wt=1,
avl=Y,
special=Extraordinary powers,
]

\cardnames{
name=Enw,
cls=Dth,
rng=Pll,
dmg=Dfd,
clip=Cnf,
rld=AilLl,
wt=Pwys,
avl=ArGl,
special=Arbennig,
}

\noindent\cardme[card width=.85\textwidth]
\end{document}


This uses \fbox and minipage to create the boxes-in-a-box look. I adjusted some of the percentages so they added to 100 on each line.

\cardme[<width>]{<content>}


sets up the main box. The optional parameter specifies the width. The required argument specifies the content. The default width is three-quarters of the width of the text block.

\pageme[<width>][<content>]


constructs an inner box. The two optional arguments are, first, the width and, second, the content. The default is to create an empty box the entire (inner) width of the card.

Then

\cardme[.8\textwidth]{%
\pageme[.69][Name]%
\pageme[.31][Class]

\pageme[.22][Range]%
\pageme[.38][RoF]%
\pageme[.4][Damage]

\pageme[.22][Pen]%
\pageme[.19][Clip]%
\pageme[.18][Weight]%
\pageme[.22][Availability]

\pageme[][Special]%
}


specifies the following card:

Complete code:

\documentclass{article}
\usepackage{calc,xparse}
\newlength\mycardwidth
\setlength\mycardwidth{.75\textwidth}
\NewDocumentCommand \boxme { O{} }{%
\fbox{%
\parbox{\linewidth-2\fboxrule-2.5\fboxsep}{\strut #1}%
}%
}
\NewDocumentCommand \pageme { O{} O{} }{%
\begin{minipage}{#1\mycardwidth}
\centering\boxme[#2]
\end{minipage}%
}
\NewDocumentCommand \cardme { O{.75\textwidth} +m }{%
\setlength\mycardwidth{#1-2\fboxrule-2\fboxsep}%
\fbox{%
\begin{minipage}{\mycardwidth}
\centering
#2%
\end{minipage}%
}%
}
\begin{document}
\cardme[.8\textwidth]{%
\pageme[.69][Name]%
\pageme[.31][Class]

\pageme[.22][Range]%
\pageme[.38][RoF]%
\pageme[.4][Damage]

\pageme[.22][Pen]%
\pageme[.19][Clip]%
\pageme[.18][Weight]%
\pageme[.22][Availability]

\pageme[][Special]%
}
\end{document}

• This does keep much more of the look of the original. Thank you. I'm going to see if I can put this into the macro that @Werner made so I get the better macro usability he gave (compared to what I was going to use originally) and the awesome look from yours. Nov 5, 2016 at 23:59
• If you get stuck, let me know. I was going to set up a macro with a key-value interface, but I didn't know how that would fit with what you'd already got. So I can convert it if need be. (So long as I don't have to use xkeyval, which I've never got on with :(.)
– cfr
Nov 6, 2016 at 0:05
• I just finished merging them. All seems to be working great, and I added the unit for the range and weight (I didn't even think about it when asking the orginal question), but thank you for the offer to convert it :) The only thing I have left that would be changed (providing it doesn't require excessive work to do) would be to center the variable data within the remaining space of the cell. Do you have any suggestions? \center{\char@name} pushes the variable to a new line and then center it. I managed to get the weight and range variables centered using \hfill. Nov 6, 2016 at 0:18
• Not sure if at this point it would be better to create a new thread and post the merged versions of the code I now have. I'm still new here xD Nov 6, 2016 at 0:19
• @Archimedes94 See above. It centres now. The usual trick in that case is to use \mbox{}. This won't itself take any space, unlike a white full-stop. I've done something similar above.
– cfr
Nov 6, 2016 at 2:03

This seems to beg for a key-value interface:

\documentclass{article}

\usepackage{xkeyval}

\makeatletter
\define@cmdkey{rpg}[char@]{name}[]{}
\define@cmdkey{rpg}[char@]{class}[]{}
\define@cmdkey{rpg}[char@]{range}[]{}
\define@cmdkey{rpg}[char@]{rof}[]{}
\define@cmdkey{rpg}[char@]{damage}[]{}
\define@cmdkey{rpg}[char@]{pen}[]{}
\define@cmdkey{rpg}[char@]{clip}[]{}
\define@cmdkey{rpg}[char@]{weight}[]{}
\define@cmdkey{rpg}[char@]{availability}[]{}
\define@cmdkey{rpg}[char@]{special}[]{}
\define@cmdkey{rpg}[char@]{width}[\linewidth]{}

\newcommand{\setcharacterstats}[1]{%
\begingroup
\noindent
\begin{tabular}{ @{} c @{} }
\begin{tabular}{ | p{\dimexpr.69\char@width-2\tabcolsep} | p{\dimexpr.31\char@width-2\tabcolsep} | }
\hline
\statlabel{Name}: \char@name & \statlabel{Cls}: \char@class
\end{tabular} \\
\begin{tabular}{ | p{\dimexpr.23\char@width-2\tabcolsep} | p{\dimexpr.37\char@width-2\tabcolsep} | p{\dimexpr.4\char@width-2\tabcolsep} | }
\hline
\statlabel{Rng}: \char@range & \statlabel{RoF}: \char@rof & \statlabel{Dmg}: \char@damage
\end{tabular} \\
\begin{tabular}{ | p{\dimexpr.23\char@width-2\tabcolsep} | p{\dimexpr.20\char@width-2\tabcolsep} | p{\dimexpr.20\char@width-2\tabcolsep} |
p{\dimexpr.16\char@width-2\tabcolsep} | p{\dimexpr.21\char@width-2\tabcolsep} | }
\hline
\statlabel{Pen}: \char@pen & \statlabel{Clip}: \char@clip & \statlabel{Rld}: \char@reload & \statlabel{Wt}: \char@weight & \statlabel{Avl}: \char@availability
\end{tabular} \\
\begin{tabular}{ | p{\dimexpr\char@width-2\tabcolsep} | }
\hline
\statlabel{Special}: \char@special \\
\hline
\end{tabular}
\end{tabular}\par
\endgroup
}
\makeatletter

\newcommand{\statlabel}{\textsc}

\begin{document}

\setcharacterstats{
name = The HuLK,
class = Huge,
range = Arm,
rof = 5m,
damage = 400,
pen = 12,
clip = 12.2,
weight = 7t,
availability = 7,
special = Green and very angry
}

\end{document}


All the keys should be self-explanatory, including the possibility that you can adjust the width for each table. The default it \linewidth, and all the columns are specified in percentages of this.

• I think I misunderstood the question now I see yours :(.
– cfr
Nov 5, 2016 at 23:22
• This is brilliant thank you. Even makes the macro part easier to reuse. Alignment of some of the boxes was off and then I remembered, that I forgot to adjust for the (notably thick) lines in the original table. I fiddled and got that to line up the Wt and Dmg left line. Nov 5, 2016 at 23:28
• @Archimedes94: Yes, the default document class width is perhaps too small for what you want. You can adjust the text block width using geometry. And, all the percentage widths within the character sheet construction should be evident (if you wish to change it).
– Werner
Nov 5, 2016 at 23:30
• Annoyingly, you cannot accept multiple answers :/ Both methods, by yourself and by cfr do what I was wanting. I'm now in a deciding process of which I'd prefer aesthetically xD Nov 5, 2016 at 23:47
• @Archimedes94: Try using \hfill\char@name\hfill\mbox{}.
– Werner
Nov 6, 2016 at 2:45