% \iffalse %<*driver> \documentclass{l3doc} \usepackage[override=global]{booktabstabular} \usepackage[T1]{fontenc} \EnableCrossrefs% \CodelineIndex% \RecordChanges% \newenvironment{publicmacro}[1]{\DescribeMacro{#1}}{} \begin{document} \DocInput{booktabstabular.dtx} \end{document} % % \fi % % \ProvidesFile{booktabstabular.dtx} % [2026/06/28 v1.1.2 tabular and tabular* wrappers with booktabs rules] % % \CheckSum{69} % % \changes{v1.0.0}{2026/03/27}{Initial release.} % \changes{v1.1.0}{2026/04/10}{Added the \texttt{override} package option.} % \changes{v1.1.1}{2026/06/26}{Delegate wrapper closing to the saved original tabular end code.} % \changes{v1.1.2}{2026/06/28}{Require \textsf{array} and use LaTeX row-state tracking before inserting the automatic bottom rule.} % % \GetFileInfo{booktabstabular.dtx} % % \title{Automatic booktabs rules for tabular/tabular*} % \author{Moritz R. Schäfer} % \date{\filedate\space \fileversion} % % \maketitle % % \tableofcontents % % \section{Introduction} % The \textsf{booktabstabular} package provides wrapper environments around % |tabular| and |tabular*| that insert \cs{toprule} at the beginning, % \cs{bottomrule} at the end, and locally remap the legacy |\hline| and |\cline| % commands to their booktabs equivalents \cs{midrule} and \cs{cmidrule}. % It also provides commands and options to temporarily override the standard % |tabular| and |tabular*| environments so that existing documents can opt into % this behaviour without rewriting each table. % % \subsection{Example} % A typical opt-in workflow is: % % \begin{verbatim} % \usepackage[override=global]{booktabstabular} % % \begin{tabular}{lll} % First & Second & Third \\ % \hline % mapped to \midrule % Alpha & Beta & Gamma % \end{tabular} % \end{verbatim} % % Alternatively, the wrapper environments can be used directly: % % \begin{verbatim} % \begin{booktabstabular}{lll} % First & Second & Third \\ % \hline % Alpha & Beta & Gamma % \end{booktabstabular} % \end{verbatim} % % Both should yield the following output: % \begin{tabular}{lll} % First & Second & Third \\ % \hline % Alpha & Beta & Gamma % \end{tabular} % As you can see, |\toprule| and |\bottomrule| are automatically inserted, and % |\hline| is remapped to |\midrule|, so the table is typeset according to % booktabs guidelines. The same applies to |tabular*| when using the % |booktabstabular*| environment or the overridden |tabular*| name. % \begin{verbatim} % \begin{booktabstabular*}{\textwidth}{l@{\extracolsep{\fill}}l} % First & Second \\ % \hline % Alpha & Beta % \end{booktabstabular*} % \end{verbatim} % which produces the slightly wider table % \begin{booktabstabular*}{.4\textwidth}{l@{\extracolsep{\fill}}l} % First & Second \\ % \hline % Alpha & Beta % \end{booktabstabular*}. % % \subsection{Notes} % This package deliberately does not insert any intermediate rule such as % \cs{midrule} automatically; that remains the author's responsibility. % % The end wrapper inserts \cs{bottomrule} at the legal alignment boundary, % so the final row may be written either with or without a trailing |\\|. % % The package loads the standard \textsf{array} package. This provides the % table row hooks used internally to decide whether the wrapper must insert a % final |\crcr| before \cs{bottomrule}. As a result, all |tabular| and % |tabular*| environments in the document use \textsf{array}'s extended table % implementation. This is intended to be compatible with normal document-level % table code, including packages such as \textsf{siunitx}; code that patches % low-level tabular internals may observe \textsf{array}'s definitions rather % than the kernel definitions. % % The package does not attempt to remap the legacy |\hline| and |\cline| outside % of the wrapper environments, so they will continue to work as normal in % documents that only use the wrappers for a subset of tables. % % The package affects only |tabular| and |tabular*|. It does not modify % |array|, |longtable|, or any other alignment environment. % % \StopEventually{\PrintChanges\PrintIndex} % % \section{Interface and Implementation} % Since the package is rather small, we will present the interface and % implementation together in the following subsections. % \begin{macrocode} %<*package> \RequirePackage{booktabs} \RequirePackage{array} \ProvidesExplPackage {booktabstabular} {2026-06-28} {1.1.2} {tabular/tabular* wrappers with booktabs rules} % \end{macrocode} % \subsection{Environments} % \begin{environment}{origtabular} % \begin{environment}{origtabular*} % The |origtabular| and |origtabular*| environments are copies of the original % |tabular| and |tabular*| environments. They are used internally but also made % available for users who want to call the original versions if they have % overridden the standard names. % \begin{macrocode} \NewEnvironmentCopy { origtabular } { tabular } \NewEnvironmentCopy { origtabular* } { tabular* } % \end{macrocode} % \end{environment} % \end{environment} % % \begin{environment}{booktabstabular} % The |booktabstabular| environment is a drop-in wrapper for |tabular|. It % accepts the same arguments as |tabular|: an optional position specifier % |[t]| or |[b]|, followed by the column specification. % % It calls the copied begin macro directly rather than nesting a separate % environment, which keeps LaTeX's environment stack balanced when the % custom end code is used. % \begin{macrocode} \NewDocumentEnvironment { booktabstabular } { o m } { \group_begin: \bool_set_true:N \l__booktabstabular_active_bool \cs_gset_eq:NN \__booktabstabular_maybe_crcr: \prg_do_nothing: \cs_set_eq:NN \hline \midrule \cs_set_eq:NN \cline \cmidrule \IfNoValueTF { #1 } { \origtabular { #2 } } { \origtabular [ #1 ] { #2 } } \toprule } { \__booktabstabular_maybe_crcr: \bottomrule \endorigtabular \cs_gset_eq:NN \__booktabstabular_maybe_crcr: \prg_do_nothing: \group_end: } % \end{macrocode} % \end{environment} % % \begin{environment}{booktabstabular*} % The |booktabstabular*| environment is a drop-in wrapper for |tabular*|. % It accepts the width, an optional position specifier, and the column % specification. % \begin{macrocode} \NewDocumentEnvironment { booktabstabular* } { m o m } { \group_begin: \bool_set_true:N \l__booktabstabular_active_bool \cs_gset_eq:NN \__booktabstabular_maybe_crcr: \prg_do_nothing: \cs_set_eq:NN \hline \midrule \cs_set_eq:NN \cline \cmidrule \IfNoValueTF { #2 } { \use:c { origtabular* } { #1 } { #3 } } { \use:c { origtabular* } { #1 } [ #2 ] { #3 } } \toprule } { \__booktabstabular_maybe_crcr: \bottomrule \use:c { endorigtabular* } \cs_gset_eq:NN \__booktabstabular_maybe_crcr: \prg_do_nothing: \group_end: } % \end{macrocode} % \end{environment} % % \subsection{Public commands} % \begin{publicmacro}{\overridetabular} % After calling \cs{overridetabular}, the public |tabular| and |tabular*| % environments are rebound to the wrapper environments provided by this % package. Note that many classes, including the standard classes, use a % tabular in unexpected places, \textit{i.e.}, to typeset the list of authors. % if you don't want that to be affected, you should use the |override| package % option. % % \begin{macrocode} \NewDocumentCommand \overridetabular {} { \RenewEnvironmentCopy { tabular } { booktabstabular } \RenewEnvironmentCopy { tabular* } { booktabstabular* } } % \end{macrocode} % \end{publicmacro} % % \begin{publicmacro}{\restoretabular} % The command \cs{restoretabular} restores the original definitions of % |tabular| and |tabular*|. % % \begin{macrocode} \NewDocumentCommand \restoretabular {} { \RenewEnvironmentCopy { tabular } { origtabular } \RenewEnvironmentCopy { tabular* } { origtabular* } } % \end{macrocode} % \end{publicmacro} % % \subsection{Options} % We offer an option to automatically override the standard |tabular| and |tabular*| % environments. % % \DescribeOption{override} % The |override| option selects how |tabular| and |tabular*| are rebound. % The full syntax is \texttt{\detokenize{override=none|table|global}}. % \begin{description} % \item[|none|] Do not override the standard environments. % \item[|global|] This has almost the same effect as calling % \cs{overridetabular} after loading the package. The difference is that % the package option automatically disables the override around % \cs{maketitle} to avoid affecting the title page, while the command does % not. % \item[|table|] Override |tabular| and |tabular*| only inside % |table| and |table*| environments. This is the safest and \textbf{recommended} % option. % \end{description} % \begin{macrocode} \str_new:N \l__booktabstabular_override_str \DeclareKeys [ booktabstabular ] { override .choices:nn = { none , table , global } { \str_set_eq:NN \l__booktabstabular_override_str \l_keys_choice_str } , override .initial:n = none , % \end{macrocode} % % \begin{publicmacro}{overridetabular} % The |overridetabular| option is a legacy alias for |override=global|, and is % provided for backwards compatibility. % \begin{macrocode} overridetabular .meta:n = { override = global } , } \ProcessKeyOptions [ booktabstabular ] % \end{macrocode} % \end{publicmacro} % % \section{Internal Helpers} % \begin{macro}{\__booktabstabular_maybe_crcr:} % The token at the start of the end code must have the meaning of |\crcr| when % the final row is still open, and expand to nothing when the last user token % was a row separator. Keep that state through the table hooks rather than % running a conditional at the alignment boundary. % \begin{macrocode} \bool_new:N \l__booktabstabular_active_bool \cs_new_eq:NN \__booktabstabular_maybe_crcr: \prg_do_nothing: \AddToHook { tbl/row/begin } [ booktabstabular ] { \bool_if:NT \l__booktabstabular_active_bool { \cs_gset_eq:NN \__booktabstabular_maybe_crcr: \crcr } } \AddToHook { tbl/row/init } [ booktabstabular ] { \bool_if:NT \l__booktabstabular_active_bool { \cs_gset_eq:NN \__booktabstabular_maybe_crcr: \prg_do_nothing: } } % \end{macrocode} % \end{macro} % \begin{macro}{\__booktabstabular_apply_override:} % Applies the override according to the value of the |override| option. % \begin{macrocode} \cs_new_protected:Nn \__booktabstabular_apply_override: { \str_case:Vn \l__booktabstabular_override_str { { none } { } { global } { \__booktabstabular_activate_global: } { table } { \__booktabstabular_activate_float: } } } % \end{macrocode} % \end{macro} % % \begin{macro}{\__booktabstabular_activate_global:} % The function called if the |override| option is set to |global|. % \begin{macrocode} \cs_new_protected:Nn \__booktabstabular_activate_global: { \hook_gput_code:nnn { cmd/maketitle/before } { . } { \restoretabular } \hook_gput_code:nnn { cmd/maketitle/after } { . } { \overridetabular } \overridetabular } % \end{macrocode} % \end{macro} % % \begin{macro}{\__booktabstabular_activate_float:} % The function called if the |override| option is set to |table|. % \begin{macrocode} \cs_new_protected:Nn \__booktabstabular_activate_float: { \hook_gput_code:nnn { env/table/begin } { . } { \overridetabular } \hook_gput_code:nnn { env/table*/begin } { . } { \overridetabular } } % \end{macrocode} % \end{macro} % % Finally, we call the function at the end of the package to apply the override % if requested. % \begin{macrocode} \__booktabstabular_apply_override: % % \end{macrocode} % \endinput