summaryrefslogtreecommitdiff
path: root/macros/luatex/generic/lutabulartools
diff options
context:
space:
mode:
Diffstat (limited to 'macros/luatex/generic/lutabulartools')
-rw-r--r--macros/luatex/generic/lutabulartools/README.md29
-rw-r--r--macros/luatex/generic/lutabulartools/lutabulartools.lua287
-rw-r--r--macros/luatex/generic/lutabulartools/lutabulartools.sty218
-rw-r--r--macros/luatex/generic/lutabulartools/lutabulartools.tex220
4 files changed, 754 insertions, 0 deletions
diff --git a/macros/luatex/generic/lutabulartools/README.md b/macros/luatex/generic/lutabulartools/README.md
new file mode 100644
index 0000000000..34da5ee1e5
--- /dev/null
+++ b/macros/luatex/generic/lutabulartools/README.md
@@ -0,0 +1,29 @@
+# lutabulartools
+
+This LuaLaTeX package offers some useful macros for tabular matter. The `\MC` command it provides allows one to specify a multi-row and or multi-column cell with minimal syntax. The package loads `booktabs` and adds a gray midrule, and allows a user to specify multiple `\cmidrules` with one command.
+
+
+
+# License
+
+Copyright (C) 2021 Kale Ewasiuk
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
+ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
+TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
+PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
+SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
+ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE
+OR OTHER DEALINGS IN THE SOFTWARE.
diff --git a/macros/luatex/generic/lutabulartools/lutabulartools.lua b/macros/luatex/generic/lutabulartools/lutabulartools.lua
new file mode 100644
index 0000000000..40f916e3bc
--- /dev/null
+++ b/macros/luatex/generic/lutabulartools/lutabulartools.lua
@@ -0,0 +1,287 @@
+--% Kale Ewasiuk (kalekje@gmail.com)
+--% 2021-10-13
+--%
+--% Copyright (C) 2021 Kale Ewasiuk
+--%
+--% Permission is hereby granted, free of charge, to any person obtaining a copy
+--% of this software and associated documentation files (the "Software"), to deal
+--% in the Software without restriction, including without limitation the rights
+--% to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+--% copies of the Software, and to permit persons to whom the Software is
+--% furnished to do so, subject to the following conditions:
+--%
+--% The above copyright notice and this permission notice shall be included in
+--% all copies or substantial portions of the Software.
+--%
+--% THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
+--% ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
+--% TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
+--% PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
+--% SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
+--% ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+--% ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+--% OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE
+--% OR OTHER DEALINGS IN THE SOFTWARE.
+
+local ltt = {}
+
+--todo clean up the code, make some stuff local
+--todo maybe add the penlight lua table to latex tabular?
+
+--integrate current cell
+--todo check if S[] column cells work!!!
+--todo might be smarter to just append this to tabular commands and read argument
+
+-- Initialize counter variable
+ltt.NumTabCols = 0
+ltt.NumTabColsAbv = 0 -- num of tab columns if above 1
+ltt.TabColSpec = ''
+ltt.TabColSpecAbv = '' -- tab column spec if above 1
+ltt.CurTabColAbv = '' -- current tab column spec if above 1
+ltt.TabColNum = 0
+ltt.NumBkts = 0
+ltt.NumTabColsMX = 0
+
+
+--todo re=enable
+local glue_t, unset_t, tabskip_st = node.id'glue', node.id'unset'
+local tabskip_st = table.swapped(node.subtypes'glue').tabskip
+assert(tabskip_st)
+
+
+function ltt.get_TabColNum()
+ local nest
+ for i = tex.nest.ptr, 1, -1 do
+ local tail = tex.nest[i].tail
+ if tail.id == glue_t and tail.subtype == tabskip_st then
+ nest = tex.nest[i]
+ break
+ end
+ end
+ if nest then
+ local col = 1
+ for _, sub in node.traverse_id(unset_t, nest.head) do
+ col = col + sub + 1
+ end
+ ltt.TabColNum = col
+ else
+ ltt.TabColNum = 1
+ end
+ ltt.CurTabColAbv = ltt.TabColSpecAbv:sub(ltt.TabColNum,ltt.TabColNum)
+ ltt.NumTabColsMX = math.max(ltt.NumTabColsMX, ltt.TabColNum) -- a different way to calculate total cols
+ return ltt.TabColNum
+end
+
+
+function ltt.get_TabRowNum()
+ return tex.count['c@RowNumCnt'] -- access latex counter
+end
+
+-- The next function computes the number of columns from
+-- contents of string 'zz'
+function ltt.calc_NumTabCols( zz )
+ --help_wrt(zz, 'col spec OG')
+ -- -- NOT NEEDED: zz = zz:gsub ( "^.-(%b{}).-$", "%1" ) -- retain the first "{....}" substring
+ zz = zz:gsub ( "%*%s-{(%d-)}%s-(%b{})" , -- expand expressions such as "*{5}{l}" to "lllll"
+ function(y, z ) z = z:sub (2 , -2) return string.rep (z, y) end ) --
+ zz = zz:gsub ( "%b{}" , "" ) -- omit all stuff in curly braces and square
+ zz = zz:gsub ( "%b[]" , "" )
+ zz = zz:gsub ( "[@!|><%s%*\']" , "" ) -- some more characters to ignore
+ -- todo: any columns defined that break into more than one column should be expanded here
+ --help_wrt(zz, 'col spec CLEAN')
+ ltt.TabColSpec = zz -- stripped column spec with no {} or <
+ ltt.NumTabCols = string.len(ltt.TabColSpec)
+ if ltt.NumTabCols > 1 then
+ ltt.TabColSpecAbv = ltt.TabColSpec
+ ltt.NumTabColsAbv = ltt.NumTabCols
+ end
+end
+
+--todo fix square brackets after letters, expand multiple
+--calc_NumTabCols('ss')
+--calc_NumTabCols('*{6}{s}')
+--calc_NumTabCols('l*{6}{l}')
+--calc_NumTabCols('lll')
+--calc_NumTabCols('ll[]')
+--calc_NumTabCols('ll[]*{6}{l}')
+--calc_NumTabCols('*{6}{l}')
+--calc_NumTabCols('y*{6}{sq}x')
+--print(_TabColSpec.. '<---Column spec')
+
+--http://ctan.mirror.rafal.ca/macros/latex/contrib/multirow/multirow.pdf
+--http://ctan.mirror.colo-serv.net/macros/latex/contrib/makecell/makecell.pdf
+-- todo CONSIDER THIS
+-- https://tex.stackexchange.com/questions/331716/newline-in-multirow-environment
+
+
+
+--todo
+-- if p{} column, and multirow is 1, use {=} instead of {*}
+-- but note, makecell will not work. So you may want to skip it.multicolumn
+-- this case should be considered in this code.
+-- for example: \multirow{2}{=}
+
+function ltt.MagicCell(s0,spec,mcspec,pre,content)
+ --
+ local STR = ''
+ reset_bkt_cnt()
+
+ local v, h, r, c, mrowsym, skipmakecell = ltt.parse_MagicCell_spec(spec) -- get v/h align, number rows/columns
+
+ local mcspec = mcspec or ''
+
+ h, mcspec, c = ltt.get_HColSpec(h, mcspec, c) -- infer horizontal alignment, num columns
+
+ --help_wrt(_CurTabColAbv,'current column')
+ if s0 == _xTrue or (pl.List({'S', 'Q', 'L', 'R'}):contains(ltt.CurTabColAbv) -- special columns for SI
+ and c == '') then -- multicolumn cannot have {} around it todo test with siunitx, num rows, num columns
+ STR = STR .. '{' -- multirow and makcell must have {} around it S column is used
+ add_bkt_cnt()
+ end
+
+ if c ~= '' then
+ STR = STR .. "\\multicolumn{"..c.."}{"..mcspec.."}{"
+ add_bkt_cnt()
+ end
+
+ if r ~= '' then
+ STR = STR .."\\multirow["..v.."]{"..r.."}{"..mrowsym.."}{" -- optional arg here
+ add_bkt_cnt()
+ end
+
+ if not skipmakecell then
+ if pre ~= '' then
+ STR = STR.."\\renewcommand{\\cellset}{"..pre.."}"
+ end
+
+ STR = STR.."\\makecell[{"..v.."}{"..h.."}]{"
+ add_bkt_cnt()
+ else
+ content = content:gsub('\\\\', '\\newline')
+ end
+
+ STR = STR..content..close_bkt_cnt()
+ --Troubleshooting
+ --help_wrt(STR..' <<< magic cell string')
+ tex.sprint(STR)--tex print the STR
+end
+
+
+function ltt.parse_MagicCell_spec(spec)
+ local mrowsym = '*'
+ local skipmakecell = false
+ if string.find(spec, '=') then
+ spec = spec:gsub('=', '')
+ mrowsym = '='
+ skipmakecell = true
+ end
+
+ spec = spec:lower():gsub('%s','') -- take lower case and remove
+ local vh, rc = spec:gextract('%a') -- extract characters
+ local v = vh:gfirst({'t', 'm', 'b'}) or 't'
+ local h = vh:gfirst({'l', 'c', 'r'}) or ''
+ v = v:gsub('m', 'c')
+
+ local rc_ = (rc):split(',')
+ local c = rc_[1] or '' --num columns, width
+ local r = rc_[2] or '' --num rows, height
+ if c == '0' or c == '1' then c = '' end
+ if r == '0' or r == '1' then r = '' end
+
+ return v, h, r, c, mrowsym, skipmakecell
+end
+
+
+ltt.TabColMapping = { -- horizontal cell alignment that multicolumn should use if () or [hori] not passed to func
+ l = 'l',
+ c = 'c',
+ r = 'r',
+ p = 'p',
+ P = 'c',
+ X = 'l',
+ Y = 'c',
+ Z = 'l',
+ N = 'c',
+ L = 'l',
+ R = 'r',
+ C = 'c',
+}
+
+function ltt.get_HColSpec(h, mcspec, c) -- take horizontal alignment
+ -- c is num columns, h is horizontal alginment,
+ --Assumes _TabColNum was calculated previosly
+ ltt.get_TabColNum()
+ if c == '+' then -- fill row to end
+ c = tostring(ltt.NumTabColsAbv - ltt.TabColNum + 1)
+ end
+ if h == '' then -- if horizontal not provided, use declared column
+ h = ltt.TabColMapping[ltt.CurTabColAbv] or 'l'
+ end
+ if c ~= '' then -- only make new mcspec if column nums > 0
+ if mcspec == '' then -- and if no mcspec was passed
+ mcspec = h
+ if ltt.TabColNum == 1 then -- if first column, auto detect padding
+ mcspec = '@{}'..mcspec
+ end
+ if (ltt.TabColNum + tonumber(c) - 1) == ltt.NumTabColsAbv then -- if end on last column
+ mcspec = mcspec..'@{}'
+ end
+ else -- if mcspec if given, extract the alignment
+ ltt.calc_NumTabCols(mcspec)
+ h = ltt.TabColSpec -- get 1 character column spec from mcspec and override h
+ end
+ end
+ return h, mcspec, c
+end
+
+
+-- midrule stuff
+
+function ltt.get_midrule_col(s)
+ if string.find(s, '+') then
+ s = s:gsub('+', '')
+ if (s == '') or (s == '0') then
+ s = 1
+ end
+ s = tostring(ltt.NumTabColsAbv - tonumber(s) + 1) -- use number of tabular columns above 0,
+ end
+ return s
+end
+
+--todo if comma present, create multiple according to spec. Also allow LR in []
+
+--- midrule stuff ---
+
+function ltt.make1cmidrule(s, r, c, cmd) -- s=square r=round c=curly
+ cmd = '\\'..cmd
+ if s ~= '' then
+ cmd = cmd..'['..s..']'
+ end
+ if r ~= '' then
+ cmd = cmd..'('..r..')'
+ end
+ t = string.split(c, '-')
+ if t[2] == '' then
+ t[2] = '+'
+ end
+ if t[2] == nil then
+ t[2] = t[1]
+ end
+ c = ltt.get_midrule_col(t[1])..'-'..ltt.get_midrule_col(t[2])
+ cmd = cmd..'{'..c..'}'
+ --help_wrt(cmd)
+ tex.print(cmd)
+end
+
+function ltt.makecmidrules(s, r, c, cmd)
+ for k, c1 in pairs(string.split(c, ',')) do
+ r1, c2 = c1:gextract('%a')
+ if r1 == '' then -- if nothing passed in with the column
+ r1 = r -- set to the global value passed in round brackets
+ end
+ ltt.make1cmidrule(s, r1:strip(), c2:strip(), cmd)
+ end
+end
+
+
+return ltt -- lutabulartools \ No newline at end of file
diff --git a/macros/luatex/generic/lutabulartools/lutabulartools.sty b/macros/luatex/generic/lutabulartools/lutabulartools.sty
new file mode 100644
index 0000000000..450a0d0cbe
--- /dev/null
+++ b/macros/luatex/generic/lutabulartools/lutabulartools.sty
@@ -0,0 +1,218 @@
+% Kale Ewasiuk (kalekje@gmail.com)
+% 2021-10-13
+%
+% Copyright (C) 2021 Kale Ewasiuk
+%
+% Permission is hereby granted, free of charge, to any person obtaining a copy
+% of this software and associated documentation files (the "Software"), to deal
+% in the Software without restriction, including without limitation the rights
+% to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+% copies of the Software, and to permit persons to whom the Software is
+% furnished to do so, subject to the following conditions:
+%
+% The above copyright notice and this permission notice shall be included in
+% all copies or substantial portions of the Software.
+%
+% THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
+% ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
+% TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
+% PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
+% SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
+% ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+% ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+% OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE
+% OR OTHER DEALINGS IN THE SOFTWARE.
+
+\NeedsTeXFormat{LaTeX2e}
+\ProvidesPackage{lutabulartools}[2021-10-13]
+
+\RequirePackage{booktabs}
+\RequirePackage{multirow}
+\RequirePackage{makecell}
+\RequirePackage{xparse}
+\RequirePackage{array}
+\RequirePackage{xcolor}
+\RequirePackage{colortbl}
+\RequirePackage{luacode}
+\RequirePackage[pl,extras]{penlight} % NEEDED
+
+\luadirect{lutabt = require('lutabulartools')}
+
+
+
+% DOES NOT WORK WITH colortbl package ???
+
+
+%% adapted from
+% https://tex.stackexchange.com/questions/182953/add-midrule-every-5-lines-automatically
+\def\midruleXstep{5}
+\def\midruleXrule{\gmidrule}
+
+\newcounter{midruleX}
+\newcommand*{\midruleX}{%
+ \aftergroup\aftergroup\aftergroup\midruleXaux
+}
+\newif\ifmidruleX
+\makeatletter
+\newcommand*{\midruleXaux}{%
+ \noalign{%
+ \stepcounter{midruleX}%
+ \ifnum\value{midruleX}=\midruleXstep %
+ \global\midruleXtrue
+ \resetmidruleX%
+ \else
+ \global\midruleXfalse
+ \fi
+ }
+ \ifmidruleX\midruleXrule\fi
+}
+\newcommand*{\resetmidruleX}{\setcounter{midruleX}{0}} %%% todo maybe put noalign in here
+\makeatother
+
+\resetmidruleX
+
+%% https://tex.stackexchange.com/questions/145084/what-is-the-function-of-noalign
+%\begin{tabular}{rclc@{\midruleX}} % inject midrule
+%\toprule
+%Number & Student Id & Name & Score\\\midrule\resetmidruleX % reset to 0
+%1 & 14-001 & Andy & 75 \\
+%2 & & & \\
+%3 & & & \\
+%4 & & & \\
+%5 & & & \\
+%6 & & & \\
+%7 & & & \\
+%8 & & & \\
+%9 & & & \\\resetmidruleX
+%10 & & & \\%\noalign{\resetmidruleX} % noalig required to not cause error with bottomrule, but that's oka because it should be used on row 9 anyway
+%\bottomrule
+%\end{tabular}
+
+
+%%%%
+
+%%% gray midrule and gray cmidrule
+
+\providecommand{\gmidrule}{\arrayrulecolor{lightgray}\specialrule{\lightrulewidth}{0.4\aboverulesep}{0.6\belowrulesep}\arrayrulecolor{black}}
+
+%%% use gcmidrule for a gray "c" midrule
+% Copy \cmidrule from
+% http://mirrors.ctan.org/macros/latex/contrib/booktabs/booktabs.dtx
+% and modify it for insertion of \arrayrulecolor{lightgray} at the start,
+% and \arrayrulecolor{black} at the end
+\makeatletter
+\def\oldgcmidrule{\arrayrulecolor{lightgray}% Switch colour to lightgray
+ \noalign{\ifnum0=`}\fi
+ \@ifnextchar[{\@gcmidrule}{\@gcmidrule[\cmidrulewidth]}}
+\def\@gcmidrule[#1]{\@ifnextchar({\@@gcmidrule[#1]}{\@@gcmidrule[#1]()}}
+\def\@@gcmidrule[#1](#2)#3{\@@@gcmidrule[#3]{#1}{#2}}
+\def\@@@gcmidrule[#1-#2]#3#4{\global\@cmidla#1\relax
+ \global\advance\@cmidla\m@ne
+ \ifnum\@cmidla>0\global\let\@gtempa\@cmidrulea\else
+ \global\let\@gtempa\@cmidruleb\fi
+ \global\@cmidlb#2\relax
+ \global\advance\@cmidlb-\@cmidla
+ \global\@thisrulewidth=#3
+ \@setrulekerning{#4}
+ \ifnum\@lastruleclass=\z@\vskip 0.4\aboverulesep\fi
+ \ifnum0=`{\fi}\@gtempa
+ \noalign{\ifnum0=`}\fi\futurenonspacelet\@tempa\@xgcmidrule}
+\def\@xgcmidrule{%
+ \ifx\@tempa\gcmidrule
+ \vskip-\@thisrulewidth
+ \global\@lastruleclass=\@ne
+ \else \ifx\@tempa\morecmidrules
+ \vskip \cmidrulesep
+ \global\@lastruleclass=\@ne\else
+ \vskip 0.6\belowrulesep
+ \global\@lastruleclass=\z@
+ \fi\fi
+ \ifnum0=`{\fi}
+ \arrayrulecolor{black}}% Switch colour back to black
+\makeatother
+
+%%%
+
+\let\oldcmidrule\cmidrule % improve cmidrule. Can reference column number from back with a +, eg, {3+-2+} means third to second last
+\RenewExpandableDocumentCommand{\cmidrule}{ O{} D(){} m }{%
+ \luadirect{lutabt.make1cmidrule( \luastringN{#1},
+ \luastring{#2},
+ \luastring{#3},
+ 'oldcmidrule')
+ }%
+}
+
+\NewExpandableDocumentCommand{\cmidrules}{ O{} D(){} m }{% supports comma in {}
+ \luadirect{lutabt.makecmidrules( \luastringN{#1},
+ \luastring{#2},
+ \luastring{#3},
+ 'cmidrule')
+ }%
+}
+
+\NewExpandableDocumentCommand{\gcmidrule}{ O{} D(){} m }{%
+ \luadirect{lutabt.make1cmidrule( \luastringN{#1},
+ \luastring{#2},
+ \luastring{#3},
+ 'oldgcmidrule')
+ }%
+}
+
+\NewExpandableDocumentCommand{\gcmidrules}{ O{} D(){} m }{% supports comma in {}, rl can be passed as well
+ \luadirect{lutabt.makecmidrules( \luastringN{#1},
+ \luastring{#2},
+ \luastring{#3},
+ 'gcmidrule')
+ }%
+}
+
+
+
+
+
+%%% tabular commands
+
+\newcommand{\TabColNum}{\luadirect{tex.print(lutabt.get_TabColNum())}} % prints the current column number of tabular
+\newcommand{\NumTabCols}{\luadirect{tex.print(lutabt.NumTabCols)}} % prints the total num columns of tabular
+\newcounter{RowNumCnt}
+\setcounter{RowNumCnt}{1}
+
+\let\oldtabular\tabular
+\RenewExpandableDocumentCommand{\tabular}{ O{c} m }{%
+ \setcounter{RowNumCnt}{1}%
+ \luadirect{lutabt.calc_NumTabCols(\luastringN{#2})}%
+ \luadirect{lutabt.NumTabColsMX = 0}% fall back if calc doesnt work for some reason
+ \oldtabular[#1]{@{\stepcounter{RowNumCnt}}#2@{\luadirect{lutabt.get_TabColNum()}}}%
+}
+
+\expandafter\let\expandafter\oldtabulars\csname tabular*\endcsname
+\expandafter\let\expandafter\endoldtabulars\csname endtabular*\endcsname
+\RenewDocumentEnvironment{tabular*}{ m O{c} m }{%
+ \setcounter{RowNumCnt}{1}%
+ \luadirect{lutabt.calc_NumTabCols(\luastringN{#3})}%
+ \luadirect{lutabt.NumTabColsMX = 0}% fall back if calc doesnt work for some reason
+ \begin{oldtabulars}{#1}[#2]{@{\stepcounter{RowNumCnt}}#3@{\luadirect{lutabt.get_TabColNum()}}}
+}{%
+ \end{oldtabulars}%
+}
+%%% NOTE tabularx uses tabular*, NO NEED TO CHANGE TABULARX!
+
+
+%s0,spec,mcspec,pre,content
+% Magic Cell
+\NewExpandableDocumentCommand{\MC}{ s O{} D(){} D<>{} m }{%
+% Magic cell *wraps with {} but automatically checks for SI column,
+ % [column spec] (mult-column spec override) <pre-cell stuff> {cell content}
+\luadirect{lutabt.MagicCell(
+ \luastring{#1},
+ \luastring{#2},
+ \luastringN{#3},
+ \luastringN{#4},
+ \luastringN{#5}
+)}}
+
+
+
+
+\ProcessOptions
+
diff --git a/macros/luatex/generic/lutabulartools/lutabulartools.tex b/macros/luatex/generic/lutabulartools/lutabulartools.tex
new file mode 100644
index 0000000000..cc0aa0ea8a
--- /dev/null
+++ b/macros/luatex/generic/lutabulartools/lutabulartools.tex
@@ -0,0 +1,220 @@
+% Kale Ewasiuk (kalekje@gmail.com)
+% 2021-10-13
+%
+% Copyright (C) 2021 Kale Ewasiuk
+%
+% Permission is hereby granted, free of charge, to any person obtaining a copy
+% of this software and associated documentation files (the "Software"), to deal
+% in the Software without restriction, including without limitation the rights
+% to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+% copies of the Software, and to permit persons to whom the Software is
+% furnished to do so, subject to the following conditions:
+%
+% The above copyright notice and this permission notice shall be included in
+% all copies or substantial portions of the Software.
+%
+% THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
+% ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
+% TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
+% PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
+% SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
+% ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+% ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+% OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE
+% OR OTHER DEALINGS IN THE SOFTWARE.
+
+\documentclass{article}
+\usepackage{lutabulartools}
+\usepackage{url}
+\setlength{\parindent}{0ex}
+\setlength{\parskip}{0.75em}
+\newcommand{\PARA}[1]{\leavevmode\llap{\texttt{#1}\ \ \ }}
+\newcommand{\CMD}[1]{\texttt{\textbackslash #1}}
+\newcommand{\tMC}{\CMD{MC}}
+\newcommand{\ttt}[1]{``\texttt{#1}''}
+
+\newcommand{\Example}[1]{
+ \texttt{\detokenize{#1}}\par
+will produce:\par
+#1
+}
+
+\begin{document}
+\vspace*{-4em}
+ {\noindent\huge\bfseries lutabulartools \large -- some useful tabular tools\\(LuaLaTeX-based)}\\
+ 2021-09-24, Kale Ewasiuk, \url{kalekje@gmail.com}
+
+lutabulartools is a package that contains a few useful macros to help with tables.
+Most functions require LuaLaTeX. The following packages are loaded:
+{\tt
+{booktabs},
+{multirow},
+{makecell},
+{xparse},
+{array},
+{xcolor},
+{colortbl},
+{luacode},
+{penlight},
+}
+
+\section{\texttt{\textbackslash MC} -- Magic Cell}
+\texttt{\textbackslash MC} (magic cell) combines the facilities of
+\CMD{multirow} and \CMD{multicolumn} from the \texttt{multirow} package, and \CMD{makcell} from the titular package.
+With the help of LuaLaTeX, it takes an easy-to-use cell specification and employs said commands as required.
+Here is the usage:
+
+\texttt{\textbackslash MC * [cell spec] <cell format> (override multicolumn col) \{contents\} }
+
+\PARA{*}This will wrap the entire command in \{\}. This is necessary for \texttt{siunitx} single-column width columns.
+However, the \tMC\ command attempts to detect this automatically.
+
+\PARA{[cell spec]}%
+Any letters placed in this argument are used for cell alignment.
+You can use one of three: \ttt{t}, \ttt{m}, \ttt{b} for top, middle, bottom (vertical alignment),
+or \ttt{l}, \ttt{c}, \ttt{r} for horizontal alignment.
+By default, \tMC\ will try to autodetect the horizontal alignment based on the current column.
+If it can't, it will be left-aligned.
+The default vertical alginment is top.
+
+This argument can also contain two integers, separated by a comma (if two are used).
+\ttt{C,R}, \ttt{C}, or \ttt{,R} are a valid inputs,
+where R=rows (int), and C=columns, (int).
+If you want a 1 column wide, multirow cell,
+you can pass \ttt{,R}. These numbers can be negative.
+If no spec is passed, (argument empty), \tMC\
+acts like a \texttt{makecell}.
+Additionally, you can pass \ttt{+} in place of C (number of columns wide),
+and it will make the cell width fill until the end of the current row.
+
+Examples:\\
+\ttt{\textbackslash MC[2,2]} means two columns wide, two rows tall.\\
+\ttt{\textbackslash MC[2,1]} or \ttt{\textbackslash MC[2]} or means two columns wide, one row tall.\\
+\ttt{\textbackslash MC[1,2]} or \ttt{\textbackslash MC[,2]} means one column wide, two rows tall.\\
+In any of these examples, you can place the alignment letters anywhere.
+
+
+\PARA{(override}%
+You may want to adjust the column specification of a multicolumn cell,\\
+\PARA{multicolumn)}for example, using
+\texttt{@\{\}c@\{\}} to remove padding between the cell.
+
+\PARA{<cell format>}%
+You can place formatting like \CMD{bfseries} here.
+
+Here's an example.
+
+\Example{
+\begin{tabular}{| c | c | c | c | c | c |}\toprule
+ \MC[2,2cm]<\tt>{2,2cm} & \MC[2r]<\tt>{2r} & 5 & \MC[,2b]<\tt>{,2b}\\
+ & & 3 & 4 & 5 & \\\midrule
+ 1 & 2 & \MC[2l](@{}l)<\tt>{2l (\@\{\}l)} & 5 & 6666\\\cmidrule{3-4}
+ 1 & \MC[+r]<\tt>{+r} \\
+ \\
+ 1 & 2 & 3 & 4 & 5 & \MC[,-2]<\tt>{,\\-2}\\
+\end{tabular}
+}
+
+%%% https://tex.stackexchange.com/questions/287346/width-of-column-after-multicolumn-header
+
+%\textbackslash MC[<spec>]{<contents}
+
+\subsection{Notes}
+This package redefines the {\tt tabular} and {\tt tabular*| environments.
+It uses Lua pattern matching to parse the column specification of the table to know how many columns there are,
+and what the current column type is. If you have defined a column that creates many, it will not work.
+This will be worked out in later package revisions.
+%But, to know how many columns there are you, you will have
+%either have to manually set todo the lua variable before the table, or,
+%in the last column of the first row, \texttt{get\_tab\_colnum ()}.
+%The above Lua function can be used to get the current column number.
+%You will also remove the ability for this package to know what the current column spec is
+
+
+
+\section{Some additional rules}
+This package also redefines the \texttt{booktabs} midrules.\\
+\PARA{\textbackslash gmidrule}is a full gray midrule.\\
+By taking advantage of knowing how many columns there are (if you chose to redefine \texttt{tabular}),
+you can specify individual column numbers (for a one column wide rule),
+or reference with respect to the last column (blank, \texttt{+1}, \texttt{+0}, or \texttt{+} means last column,
+\texttt{+2} means second last column, for example)
+or omit the last number.\\
+\PARA{\textbackslash cmidrule}is a single partial rule, with the above features\\
+\PARA{\textbackslash gcmidrule}is a single partial gray rule, with the above features\\
+You can add multiple \ttt{cmidrule}'s with the \texttt{(g)cmidrules} command. Separate with a comma.
+You can apply global trimming of the rules with the \ttt{()} optional argument, and then
+override it for a specific rule by placing \ttt{r} or \ttt{l} with the span specification.\\
+\PARA{\textbackslash gcmidrules}Can produce multiple, light gray partial rules\\
+\PARA{\textbackslash cmidrules}Can produce multiple black partial rules.\\
+Here's an example:
+
+\Example{
+\begin{tabular}{c c c c c c}\toprule
+ 1 & 2 & 3 & 4 & 5 & 6\\ \cmidrule{+1} % rule on last column
+ 1 & 2 & 3 & 4 & 5 & 6\\ \cmidrules{1,3-+3,+} % rule on first col, third to third last col, and last col
+ 1 & 2 & 3 & 4 & 5 & 6\\ \cmidrules{1,3-+3rl,+} % same as above, but trim middle
+ 1 & 2 & 3 & 4 & 5 & 6\\ \cmidrules(l){1,r3-+3,+1}% trim left for all, but only trim right for middle rule
+\end{tabular}
+}
+
+\subsection{Midrule every X\textsuperscript{th} row}
+
+\PARA{\textbackslash midruleX}%
+With this command, you can place a rule
+every X rows.
+You can change the step size and what kind of midrule you prefer.
+\begin{verbatim}
+\def\midruleXstep{5}
+\def\midruleXrule{\gmidrule}
+\end{verbatim}
+
+
+Usage: Insert midrulex at the end of each row in the column spec. Before you want counting to beg
+\begin{verbatim}
+
+\def\midruleXstep{4}
+\def\midruleXrule{\cmidrules{1,3-4}}
+\begin{tabular}{rclc@{\midruleX}} % inject midrule
+\toprule
+Num & . & . & . \\\midrule\resetmidruleX % reset to 0
+1 & & & \\
+2 & & & \\
+3 & & & \\
+4 & & & \\
+5 & & & \\
+6 & & & \\
+7 & & & \\
+8 & & & \\
+9 & & & \\
+10 & & & \\
+11 & & & \\\resetmidruleX % dont want a bottom rule
+12 & & & \\
+\bottomrule
+\end{tabular}
+\end{verbatim}
+
+ will produce
+
+\def\midruleXstep{4}
+\def\midruleXrule{\cmidrules{1,3-4}}
+\begin{tabular}{rclc@{\midruleX}} % inject midrule
+\toprule
+Num & . & . & . \\\midrule\resetmidruleX % reset to 0
+1 & & & \\
+2 & & & \\
+3 & & & \\
+4 & & & \\
+5 & & & \\
+6 & & & \\
+7 & & & \\
+8 & & & \\
+9 & & & \\
+10 & & & \\
+11 & & & \\\resetmidruleX % dont want a bottom rule
+12 & & & \\
+\bottomrule
+\end{tabular}
+
+\end{document}
+ \ No newline at end of file