From 11a1186b093ff2ae9478c20979b6eec30f63f32c Mon Sep 17 00:00:00 2001 From: Norbert Preining Date: Fri, 14 May 2021 03:01:09 +0000 Subject: CTAN sync 202105140301 --- macros/latex/contrib/tabularray/README | 4 + macros/latex/contrib/tabularray/tabularray.pdf | Bin 0 -> 379316 bytes macros/latex/contrib/tabularray/tabularray.sty | 3897 ++++++++++++++++++++++++ macros/latex/contrib/tabularray/tabularray.tex | 751 +++++ 4 files changed, 4652 insertions(+) create mode 100644 macros/latex/contrib/tabularray/README create mode 100644 macros/latex/contrib/tabularray/tabularray.pdf create mode 100644 macros/latex/contrib/tabularray/tabularray.sty create mode 100644 macros/latex/contrib/tabularray/tabularray.tex (limited to 'macros/latex/contrib/tabularray') diff --git a/macros/latex/contrib/tabularray/README b/macros/latex/contrib/tabularray/README new file mode 100644 index 0000000000..b4714bd0a7 --- /dev/null +++ b/macros/latex/contrib/tabularray/README @@ -0,0 +1,4 @@ +Package: Typeset tabulars and arrays with LaTeX3 +Author: Jianrui Lyu +Repository: https://github.com/lvjr/tabularray +License: The LaTeX Project Public License 1.3 diff --git a/macros/latex/contrib/tabularray/tabularray.pdf b/macros/latex/contrib/tabularray/tabularray.pdf new file mode 100644 index 0000000000..f367583b1b Binary files /dev/null and b/macros/latex/contrib/tabularray/tabularray.pdf differ diff --git a/macros/latex/contrib/tabularray/tabularray.sty b/macros/latex/contrib/tabularray/tabularray.sty new file mode 100644 index 0000000000..dbd07ddc8e --- /dev/null +++ b/macros/latex/contrib/tabularray/tabularray.sty @@ -0,0 +1,3897 @@ +%%% % -*- coding: utf-8 -*- +%%% ---------------------------------------------------------------------------- +%%% Tabularray: Typeset tabulars and arrays with LaTeX3 +%%% Author : Jianrui Lyu +%%% Repository: https://github.com/lvjr/tabularray +%%% License : The LaTeX Project Public License 1.3 +%%% ---------------------------------------------------------------------------- + +%%% -------------------------------------------------------- +%% \section{Scratch Variables and Data Structures} +%%% -------------------------------------------------------- + +\NeedsTeXFormat{LaTeX2e} +\RequirePackage{expl3} +\ProvidesExplPackage{tabularray}{2021-05-13}{2021H} + {Typeset tabulars and arrays with LaTeX3} + +%\RequirePackage{xparse} +\AtBeginDocument{\@ifpackageloaded{xcolor}{\RequirePackage{ninecolors}}{}} + +\ExplSyntaxOn + +\cs_if_exist:NF \vbox_set_top_to_ht:Nnn + { + \cs_new_protected:Npn \vbox_set_top_to_ht:Nnn #1 #2 #3 + { + \tex_setbox:D #1 \tex_vtop:D to \dim_eval:n { #2 } + { \color_group_begin: #3 \par \color_group_end: } + } + } + +\cs_generate_variant:Nn \msg_error:nnnn { nnVn } +\cs_generate_variant:Nn \prop_item:Nn { Ne, NV } +\cs_generate_variant:Nn \prop_put:Nnn { Nxn, Nxx, NxV } +\cs_generate_variant:Nn \regex_replace_all:NnN { NVN } +\cs_generate_variant:Nn \seq_map_indexed_inline:Nn { cn } +\cs_generate_variant:Nn \tl_gput_right:Nn { Nf } +\prg_generate_conditional_variant:Nnn \clist_if_in:Nn { Nx } { TF } +\prg_generate_conditional_variant:Nnn \prop_if_in:Nn { c } { T } +\prg_generate_conditional_variant:Nnn \str_if_eq:nn { xn } { TF } +\prg_generate_conditional_variant:Nnn \tl_if_eq:nn { en } { T, TF } +\prg_generate_conditional_variant:Nnn \tl_if_head_eq_meaning:nN { VN } { T, TF } + +\tl_new:N \l__tblr_a_tl +\tl_new:N \l__tblr_b_tl +\tl_new:N \l__tblr_c_tl +\tl_new:N \l__tblr_d_tl +\tl_new:N \l__tblr_e_tl +\tl_new:N \l__tblr_f_tl +\tl_new:N \l__tblr_h_tl +\tl_new:N \l__tblr_i_tl % for row index +\tl_new:N \l__tblr_j_tl % for column index +\tl_new:N \l__tblr_k_tl +\tl_new:N \l__tblr_n_tl +\tl_new:N \l__tblr_o_tl +\tl_new:N \l__tblr_r_tl +\tl_new:N \l__tblr_s_tl +\tl_new:N \l__tblr_t_tl +\tl_new:N \l__tblr_u_tl +\tl_new:N \l__tblr_v_tl +\tl_new:N \l__tblr_w_tl +\tl_new:N \l__tblr_x_tl +\tl_new:N \l__tblr_y_tl +\int_new:N \l__tblr_a_int +\int_new:N \l__tblr_c_int % for column number +\int_new:N \l__tblr_r_int % for row number +\dim_new:N \l__tblr_d_dim % for depth +\dim_new:N \l__tblr_h_dim % for height +\dim_new:N \l__tblr_o_dim +\dim_new:N \l__tblr_p_dim +\dim_new:N \l__tblr_q_dim +\dim_new:N \l__tblr_r_dim +\dim_new:N \l__tblr_s_dim +\dim_new:N \l__tblr_t_dim +\dim_new:N \l__tblr_v_dim +\dim_new:N \l__tblr_w_dim % for width +\box_new:N \l__tblr_a_box +\box_new:N \l__tblr_b_box +\box_new:N \l__tblr_c_box % for cell box +\box_new:N \l__tblr_d_box + +\int_new:N \g_tblr_level_int % store table nesting level + +\cs_new_protected:Npn \__tblr_prop_gput:nnn #1 #2 #3 + { + \prop_gput:cnn + { g_tblr_#1_ \int_use:N \g_tblr_level_int _prop } { #2 } { #3 } + } +\cs_generate_variant:Nn \__tblr_prop_gput:nnn { nnx, nnV, nxn, nxx, nxV } + +\cs_new:Npn \__tblr_prop_item:nn #1 #2 + { + \prop_item:cn { g_tblr_#1_ \int_use:N \g_tblr_level_int _prop } { #2 } + } +\cs_generate_variant:Nn \__tblr_prop_item:nn { ne } + +\cs_new_protected:Npn \__tblr_prop_if_in:nnT #1 + { + \prop_if_in:cnT { g_tblr_#1_ \int_use:N \g_tblr_level_int _prop } + } +\cs_new_protected:Npn \__tblr_prop_if_in:nnF #1 + { + \prop_if_in:cnF { g_tblr_#1_ \int_use:N \g_tblr_level_int _prop } + } +\cs_new_protected:Npn \__tblr_prop_if_in:nnTF #1 + { + \prop_if_in:cnTF { g_tblr_#1_ \int_use:N \g_tblr_level_int _prop } + } +\prg_generate_conditional_variant:Nnn \__tblr_prop_if_in:nn { nx } { T, F, TF } + +\cs_new_protected:Npn \__tblr_prop_log:n #1 + { + \prop_log:c { g_tblr_#1_ \int_use:N \g_tblr_level_int _prop } + } + +\cs_new_protected:Npn \__tblr_prop_map_inline:nn #1 #2 + { + \prop_map_inline:cn { g_tblr_#1_ \int_use:N \g_tblr_level_int _prop } {#2} + } + +\cs_new_protected:Npn \__tblr_prop_gput_if_larger:nnn #1 #2 #3 + { + \__tblr_gput_if_larger:cnn + { g_tblr_#1_ \int_use:N \g_tblr_level_int _prop } { #2 } { #3 } + } +\cs_generate_variant:Nn \__tblr_prop_gput_if_larger:nnn { nnx, nnV, nxn, nxx, nxV } + +\cs_new_protected:Npn \__tblr_prop_gadd_dimen_value:nnn #1 #2 #3 + { + \__tblr_gadd_dimen_value:cnn + { g_tblr_#1_ \int_use:N \g_tblr_level_int _prop } { #2 } { #3 } + } +\cs_generate_variant:Nn \__tblr_prop_gadd_dimen_value:nnn { nnx, nnV, nxn, nxx } + +%% Put the dimension to the prop list only if it's larger than the old one + +\tl_new:N \l__tblr_put_if_larger_tl + +\cs_new_protected:Npn \__tblr_put_if_larger:Nnn #1 #2 #3 + { + \tl_set:Nx \l__tblr_put_if_larger_tl { \prop_item:Nn #1 { #2 } } + \bool_lazy_or:nnT + { \tl_if_empty_p:N \l__tblr_put_if_larger_tl } + { \dim_compare_p:nNn { #3 } > { \l__tblr_put_if_larger_tl } } + { \prop_put:Nnn #1 { #2 } { #3 } } + } +\cs_generate_variant:Nn \__tblr_put_if_larger:Nnn { Nnx, Nxn, Nxx } + +\cs_new_protected:Npn \__tblr_gput_if_larger:Nnn #1 #2 #3 + { + \tl_set:Nx \l__tblr_put_if_larger_tl { \prop_item:Nn #1 { #2 } } + \bool_lazy_or:nnT + { \tl_if_empty_p:N \l__tblr_put_if_larger_tl } + { \dim_compare_p:nNn { #3 } > { \l__tblr_put_if_larger_tl } } + { \prop_gput:Nnn #1 { #2 } { #3 } } + } +\cs_generate_variant:Nn \__tblr_gput_if_larger:Nnn { Nnx, Nxn, Nxx, cnn } + +%% Add the dimension to some key value of the prop list +%% #1: the prop list, #2: the key, #3: the dimen to add + +\cs_new_protected:Npn \__tblr_add_dimen_value:Nnn #1 #2 #3 + { + \prop_put:Nnx #1 { #2 } { \dim_eval:n { \prop_item:Nn #1 { #2 } + #3 } } + } +\cs_generate_variant:Nn \__tblr_add_dimen_value:Nnn { cnn } + +\cs_new_protected:Npn \__tblr_gadd_dimen_value:Nnn #1 #2 #3 + { + \prop_gput:Nnx #1 { #2 } { \dim_eval:n { \prop_item:Nn #1 { #2 } + #3 } } + } +\cs_generate_variant:Nn \__tblr_gadd_dimen_value:Nnn { cnn } + +%% Some counters for row and column numbering +\newcounter{rownum} +\newcounter{colnum} +\newcounter{rowcount} +\newcounter{colcount} + +%%% -------------------------------------------------------- +%% \section{Child Selectors} +%%% -------------------------------------------------------- + +\clist_new:N \g_tblr_used_child_selectors_clist + +\tl_new:N \l__tblr_childs_arg_spec_tl + +\msg_new:nnn { tabularray } { used-child-selector } + { Child ~ selector ~ name ~ "#1" ~ has ~ been ~ used! } + +\NewDocumentCommand \NewChildSelector { m O{0} o m } + { + \__tblr_new_child_selector_aux:xnnn { \tl_trim_spaces:n {#1} } {#2} {#3} {#4} + } + +\cs_new_protected:Npn \__tblr_new_child_selector_aux:nnnn #1 #2 #3 #4 + { + \clist_if_in:NnTF \g_tblr_used_child_selectors_clist { #1 } + { + \msg_error:nnn { tabularray } { used-child-selector } { #1 } + \clist_log:N \g_tblr_used_child_selectors_clist + } + { + \__tblr_make_xparse_arg_spec:nnN { #2 } { #3 } \l__tblr_childs_arg_spec_tl + \exp_args:NcV \NewDocumentCommand + { __tblr_child_selector_ #1 :w } \l__tblr_childs_arg_spec_tl { #4 } + \clist_gput_right:Nn \g_tblr_used_child_selectors_clist { #1 } + } + } +\cs_generate_variant:Nn \__tblr_new_child_selector_aux:nnnn { xnnn } + +%% #1: argument number, #2: optional argument default, #3: result tl +\cs_new_protected:Npn \__tblr_make_xparse_arg_spec:nnN #1 #2 #3 + { + \tl_clear:N #3 + \int_compare:nNnT { #1 } > { 0 } + { + \IfValueTF { #2 } + { \tl_set:Nn #3 { O{#2} } } + { \tl_set:Nn #3 { m } } + \tl_put_right:Nx #3 { \prg_replicate:nn { #1 - 1 } { m } } + } + } + +\clist_new:N \l_tblr_childs_clist +\tl_new:N \l_tblr_childs_total_tl + +\NewChildSelector { odd } + { + \int_step_inline:nnnn {1} {2} { \l_tblr_childs_total_tl } + { \clist_put_right:Nn \l_tblr_childs_clist {##1} } + } + +\NewChildSelector { even } + { + \int_step_inline:nnnn {2} {2} { \l_tblr_childs_total_tl } + { \clist_put_right:Nn \l_tblr_childs_clist {##1} } + } + +\regex_const:Nn \c__tblr_split_selector_name_regex { ^ ( [A-Za-z] {2,} ) ( . * ) } +\seq_new:N \l__tblr_childs_split_seq +\seq_new:N \l__tblr_childs_regex_seq +\tl_new:N \l__tblr_childs_end_tl +\tl_new:N \l__tblr_childs_selector_tl + +%% #1, child specifications; #2, total number. +%% The result will be put into \l_tblr_childs_clist +\cs_new_protected:Npn \__tblr_get_childs:nn #1 #2 + { + \clist_clear:N \l_tblr_childs_clist + \tl_set:Nx \l_tblr_childs_total_tl {#2} + \regex_extract_once:NnNTF \c__tblr_split_selector_name_regex {#1} + \l__tblr_childs_regex_seq + { + \tl_set:No \l__tblr_childs_selector_tl + { + \cs:w + __tblr_child_selector_ \seq_item:Nn \l__tblr_childs_regex_seq {2} :w + \cs_end: + } + \exp_args:Nx \l__tblr_childs_selector_tl + { \seq_item:Nn \l__tblr_childs_regex_seq{3} } + } + { + \tl_if_eq:nnTF {#1} {-} + { \__tblr_get_childs_normal:nn {1-#2} {#2} } + { \__tblr_get_childs_normal:nn {#1} {#2} } + } + %\clist_log:N \l_tblr_childs_clist + } +\cs_generate_variant:Nn \__tblr_get_childs:nn { nx } + +\cs_new_protected:Npn \__tblr_get_childs_normal:nn #1 #2 + { + \seq_set_split:Nnn \l__tblr_childs_split_seq {,} {#1} + \seq_map_inline:Nn \l__tblr_childs_split_seq + { \__tblr_get_childs_normal_aux:w ##1 - s \scan_stop } + } + +\cs_new_protected_nopar:Npn \__tblr_get_childs_normal_aux:w #1 - #2 #3 \scan_stop + { + \tl_if_eq:nnTF {#2} {s} + { \tl_set:Nn \l__tblr_childs_end_tl {#1} } + { \tl_set:Nn \l__tblr_childs_end_tl {#2} } + \int_step_inline:nnn {#1} { \l__tblr_childs_end_tl } + { \clist_put_right:Nn \l_tblr_childs_clist {##1} } + } + +%%% -------------------------------------------------------- +%% \section{New Table Commands} +%%% -------------------------------------------------------- + +%% We need some commands to modify table/row/column/cell specifications. +%% These commands must be defined with \NewTableCommand command, +%% so that we could extract them, execute them once, then disable them. + +\clist_new:N \g__tblr_table_commands_clist + +\msg_new:nnn { tabularray } { defined-table-command } + { Table ~ commnad ~ #1 has ~ been ~ defined! } + +\NewDocumentCommand \NewTableCommand { m O{0} o m } + { + \clist_if_in:NnTF \g__tblr_table_commands_clist { #1 } + { + \msg_error:nnn { tabularray } { defined-table-command } { #1 } + \clist_log:N \g__tblr_table_commands_clist + } + { + \__tblr_make_xparse_arg_spec:nnN { #2 } { #3 } \l__tblr_a_tl + \exp_args:NcV \NewDocumentCommand + { __tblr_table_command_ \cs_to_str:N #1 :w } \l__tblr_a_tl { #4 } + \exp_args:NcV \NewDocumentCommand + { __tblr_table_command_ \cs_to_str:N #1 _gobble :w } \l__tblr_a_tl { } + \IfValueTF { #3 } + { + \tl_gset:cn { g__tblr_table_cmd_ \cs_to_str:N #1 _arg_numb_tl } {-#2} + } + { + \tl_gset:cn { g__tblr_table_cmd_ \cs_to_str:N #1 _arg_numb_tl } {#2} + } + \clist_gput_right:Nn \g__tblr_table_commands_clist { #1 } + } + } + +\cs_new_protected:Npn \__tblr_enable_table_commands: + { + \clist_map_inline:Nn \g__tblr_table_commands_clist + { \cs_set_eq:Nc ##1 { __tblr_table_command_ \cs_to_str:N ##1 :w } } + } + +\cs_new_protected:Npn \__tblr_disable_table_commands: + { + \clist_map_inline:Nn \g__tblr_table_commands_clist + { \cs_set_eq:Nc ##1 { __tblr_table_command_ \cs_to_str:N ##1 _gobble:w } } + } + +\cs_new_protected:Npn \__tblr_execute_table_commands: + { + \__tblr_prop_map_inline:nn { command } + { + \__tblr_set_row_col_from_key_name:w ##1 + ##2 + } + \LogTblrTracing { cell } + } + +\cs_new_protected:Npn \__tblr_set_row_col_from_key_name:w [#1][#2] + { + \int_set:Nn \c@rownum {#1} + \int_set:Nn \c@colnum {#2} + } + +%%% -------------------------------------------------------- +%% \section{New Dash Styles} +%%% -------------------------------------------------------- + +%% \NewDashStyle commands + +\dim_zero_new:N \rulewidth +\dim_set:Nn \rulewidth {0.4pt} + +\prop_gset_from_keyval:Nn \g__tblr_defined_hdash_styles_prop + { solid = \hrule height \rulewidth } +\prop_gset_from_keyval:Nn \g__tblr_defined_vdash_styles_prop + { solid = \vrule width \rulewidth } + +\NewDocumentCommand \NewDashStyle { m m } + { + \seq_set_split:Nnn \l_tmpa_seq { ~ } {#2} + \tl_set:Nx \l__tblr_a_tl { \seq_item:Nn \l_tmpa_seq {1} } + \tl_set:Nx \l__tblr_b_tl { \seq_item:Nn \l_tmpa_seq {2} } + \tl_set:Nx \l__tblr_c_tl { \seq_item:Nn \l_tmpa_seq {3} } + \tl_set:Nx \l__tblr_d_tl { \seq_item:Nn \l_tmpa_seq {4} } + \tl_if_eq:NnT \l__tblr_a_tl { on } + { + \tl_if_eq:NnT \l__tblr_c_tl { off } + { + \__tblr_dash_style_make_boxes:nxx {#1} + { \dim_eval:n {\l__tblr_b_tl} } { \dim_eval:n {\l__tblr_d_tl} } + } + } + } + +\cs_new_protected:Npn \__tblr_dash_style_make_boxes:nnn #1 #2 #3 + { + \dim_set:Nn \l_tmpa_dim { #2 + #3 } + \tl_set:Nn \l__tblr_h_tl { \hbox_to_wd:nn } + \tl_put_right:Nx \l__tblr_h_tl { { \dim_use:N \l_tmpa_dim } } + \tl_put_right:Nn \l__tblr_h_tl + { + { \hss \vbox:n { \hbox_to_wd:nn {#2} {} \hrule height \rulewidth } \hss } + } + \prop_gput:NnV \g__tblr_defined_hdash_styles_prop {#1} \l__tblr_h_tl + %\prop_log:N \g__tblr_defined_hdash_styles_prop + \tl_set:Nn \l__tblr_v_tl { \vbox_to_ht:nn } + \tl_put_right:Nx \l__tblr_v_tl { { \dim_use:N \l_tmpa_dim } } + \tl_put_right:Nn \l__tblr_v_tl + { + { \vss \hbox:n { \vbox_to_ht:nn {#2} {} \vrule width \rulewidth } \vss } + } + \prop_gput:NnV \g__tblr_defined_vdash_styles_prop {#1} \l__tblr_v_tl + %\prop_log:N \g__tblr_defined_vdash_styles_prop + } +\cs_generate_variant:Nn \__tblr_dash_style_make_boxes:nnn { nxx } + +\cs_new_protected:Npn \__tblr_get_hline_dash_style:N #1 + { + \tl_set:Nx \l_tmpa_tl + { \prop_item:NV \g__tblr_defined_hdash_styles_prop #1 } + \tl_if_empty:NF \l_tmpa_tl { \tl_set_eq:NN #1 \l_tmpa_tl } + } + +\cs_new_protected:Npn \__tblr_get_vline_dash_style:N #1 + { + \tl_set:Nx \l_tmpa_tl + { \prop_item:NV \g__tblr_defined_vdash_styles_prop #1 } + \tl_if_empty:NF \l_tmpa_tl { \tl_set_eq:NN #1 \l_tmpa_tl } + } + +\NewDashStyle {dashed} {on ~ 2pt ~ off ~ 2pt} +\NewDashStyle {dotted} {on ~ 0.4pt ~ off ~ 1pt} + +%%% -------------------------------------------------------- +%% \section{Set Hlines and Vlines} +%%% -------------------------------------------------------- + +\tl_set:Nn \@tblr@dash { dash } +\tl_set:Nn \@tblr@text { text } + +\regex_const:Nn \c__tblr_is_color_key_regex { ^[A-Za-z] } + +%% \SetHlines command for setting every hline in the table +\NewTableCommand \SetHlines [3] [+] + { + \tblr_set_every_hline:nnn {#1} {#2} {#3} + } + +%% We put all code inside a group to avoid affecting other table commands +\cs_new_protected:Npn \tblr_set_every_hline:nnn #1 #2 #3 + { + \group_begin: + \int_step_inline:nn { \int_eval:n { \c@rowcount + 1 } } + { + \int_set:Nn \c@rownum {##1} + \tblr_set_hline:nnn {#1} {#2} {#3} + } + \group_end: + } + +%% Check the number of arguments and call \tblr_set_every_hline in different ways +%% This function is called when parsing table specifications +\cs_new_protected:Npn \__tblr_set_every_hline_aux:n #1 + { + \tl_if_head_is_group:nTF {#1} + { + \int_compare:nNnTF { \tl_count:n {#1} } = {3} + { \tblr_set_every_hline:nnn #1 } + { \tblr_set_every_hline:nnn {1} #1 } + } + { \tblr_set_every_hline:nnn {1} {-} {#1} } + } + +%% Add \SetHline, \hline and \cline commands + +\tl_new:N \l__tblr_hline_count_tl % the count of all hlines +\tl_new:N \l__tblr_hline_num_tl % the index of the hline +\tl_new:N \l__tblr_hline_cols_tl % the columns of the hline +\tl_new:N \l__tblr_hline_dash_tl % dash style +\tl_new:N \l__tblr_hline_fg_tl % dash foreground +\tl_new:N \l__tblr_hline_wd_tl % dash width + +\NewTableCommand \cline [2] [] { \SetHline [=] {#2} {#1} } + +\NewTableCommand \hline [1] [] { \SetHline [+] {-} {#1} } + +%% #1: the index of the hline (may be + or =) +%% #2: which columns of the hline, separate by commas +%% #3: key=value pairs +\NewTableCommand \SetHline [3] [+] + { + \tblr_set_hline:nnn {#1} {#2} {#3} + } + +%% We need to check "text" key first +%% If it does exist and has empty value, then do nothing +\cs_new_protected:Npn \tblr_set_hline:nnn #1 #2 #3 + { + \group_begin: + \keys_set_groups:nnn { tblr-hline } { text } {#3} + \tl_if_eq:NnF \l__tblr_hline_dash_tl { \exp_not:N \@tblr@text } + { + \__tblr_set_hline_num:n {#1} + \tl_clear:N \l__tblr_hline_dash_tl + \keys_set:nn { tblr-hline } { dash = solid, #3 } + \__tblr_set_hline_cmd:n {#2} + } + \group_end: + } + +\cs_new_protected:Npn \tblr_set_hline:nnnn #1 #2 #3 #4 + { + \group_begin: + \__tblr_get_childs:nx {#1} { \int_eval:n { \c@rowcount + 1 } } + \clist_map_inline:Nn \l_tblr_childs_clist + { + \int_set:Nn \c@rownum {##1} + \tblr_set_hline:nnn {#2} {#3} {#4} + } + \group_end: + } + +%% Check the number of arguments and call \tblr_set_hline in different ways +%% Note that #1 always includes an outer pair of braces +%% This function is called when parsing table specifications +\cs_new_protected:Npn \__tblr_set_hline_aux:nn #1 #2 + { + \tl_if_head_is_group:nTF {#2} + { + \int_compare:nNnTF { \tl_count:n {#2} } = {3} + { \tblr_set_hline:nnnn #1 #2 } + { \tblr_set_hline:nnnn #1 {1} #2 } + } + { \tblr_set_hline:nnnn #1 {1} {-} {#2} } + } +\cs_generate_variant:Nn \__tblr_set_hline_aux:nn { Vn } + +%% #1: the index of hline to set (may be + or =) +\cs_new_protected:Npn \__tblr_set_hline_num:n #1 + { + \tl_clear:N \l__tblr_hline_num_tl + \tl_set:Nx \l__tblr_hline_count_tl + { \__tblr_prop_item:ne { hline } { [\int_use:N \c@rownum] / @hline-count } } + \tl_if_empty:NTF \l__tblr_hline_count_tl + { + \tl_set:Nx \l__tblr_hline_num_tl { 1 } + \__tblr_prop_gput:nxx { hline } + { [\int_use:N \c@rownum] / @hline-count } { 1 } + } + { + \tl_if_eq:nnTF {#1} {+} + { \__tblr_set_hline_num_incr: } + { + \tl_if_eq:nnTF {#1} {=} + { \tl_set_eq:NN \l__tblr_hline_num_tl \l__tblr_hline_count_tl } + { + \int_compare:nNnTF {#1} > { \l__tblr_hline_count_tl } + { \__tblr_set_hline_num_incr: } + { \tl_set:Nn \l__tblr_hline_num_tl {#1} } + } + } + } + } + +\cs_new_protected:Npn \__tblr_set_hline_num_incr: + { + \tl_set:Nx \l__tblr_hline_count_tl + { \int_eval:n { \l__tblr_hline_count_tl + 1 } } + \__tblr_prop_gput:nxx { hline } + { [\int_use:N \c@rownum] / @hline-count } { \l__tblr_hline_count_tl } + \tl_set_eq:NN \l__tblr_hline_num_tl \l__tblr_hline_count_tl + } + +\keys_define:nn { tblr-hline } + { + dash .code:n = \tl_set:Nn \l__tblr_hline_dash_tl { \exp_not:N \@tblr@dash #1 }, + text .code:n = \tl_set:Nn \l__tblr_hline_dash_tl { \exp_not:N \@tblr@text #1 }, + text .groups:n = { text }, + wd .code:n = \tl_set:Nn \l__tblr_hline_wd_tl { \dim_eval:n {#1} }, + fg .code:n = \tl_set:Nn \l__tblr_hline_fg_tl {#1}, + baseline .code:n = \__tblr_hline_set_baseline:n {#1}, + unknown .code:n = \__tblr_hline_unknown_key:V \l_keys_key_str, + } + +\cs_new_protected:Npn \__tblr_hline_unknown_key:n #1 + { + \prop_if_in:NnTF \g__tblr_defined_hdash_styles_prop {#1} + { \tl_set:Nn \l__tblr_hline_dash_tl { \exp_not:N \@tblr@dash #1 } } + { + \regex_match:NnTF \c__tblr_is_color_key_regex {#1} + { \tl_set:Nn \l__tblr_hline_fg_tl {#1} } + { + \tl_set_rescan:Nnn \l__tblr_v_tl {} {#1} + \tl_set:Nn \l__tblr_hline_wd_tl { \dim_eval:n {\l__tblr_v_tl} } + } + } + } +\cs_generate_variant:Nn \__tblr_hline_unknown_key:n { V } + +\cs_new_protected_nopar:Npn \__tblr_set_hline_cmd:n #1 + { + \__tblr_get_childs:nx {#1} { \int_use:N \c@colcount } + \clist_map_inline:Nn \l_tblr_childs_clist + { + \__tblr_prop_gput:nxx { hline } + { [\int_use:N \c@rownum][##1](\l__tblr_hline_num_tl) / @dash } + { \l__tblr_hline_dash_tl } + \tl_if_empty:NF \l__tblr_hline_wd_tl + { + \__tblr_prop_gput:nxx { hline } + { [\int_use:N \c@rownum][##1](\l__tblr_hline_num_tl) / wd } + { \l__tblr_hline_wd_tl } + } + \tl_if_empty:NF \l__tblr_hline_fg_tl + { + \__tblr_prop_gput:nxx { hline } + { [\int_use:N \c@rownum][##1](\l__tblr_hline_num_tl) / fg } + { \l__tblr_hline_fg_tl } + } + } + } + +\NewTableCommand \firsthline [1] [] { \SetHline [+] {-} { #1, baseline=below } } +\NewTableCommand \lasthline [1] [] { \SetHline [+] {-} { #1, baseline=above } } + +\cs_new_protected:Npn \__tblr_hline_set_baseline:n #1 + { + \tl_if_eq:nnTF {#1} {above} + { + \__tblr_prop_gput:nnx { table } + { baseline } { \int_eval:n { \c@rownum - 1 } } + } + { + \tl_if_eq:nnT {#1} {below} + { + \__tblr_prop_gput:nnx { table } { baseline } { \int_use:N \c@rownum } + } + } + } + +%% \SetVlines command for setting every vline in the table +\NewTableCommand \SetVlines [3] [+] + { + \tblr_set_every_vline:nnn {#1} {#2} {#3} + } + +%% We put all code inside a group to avoid affecting other table commands +\cs_new_protected:Npn \tblr_set_every_vline:nnn #1 #2 #3 + { + \group_begin: + \int_step_inline:nn { \int_eval:n { \c@colcount + 1 } } + { + \int_set:Nn \c@colnum {##1} + \tblr_set_vline:nnn {#1} {#2} {#3} + } + \group_end: + } + +%% Check the number of arguments and call \tblr_set_every_vline in different ways +%% This function is called when parsing table specifications +\cs_new_protected:Npn \__tblr_set_every_vline_aux:n #1 + { + \tl_if_head_is_group:nTF {#1} + { + \int_compare:nNnTF { \tl_count:n {#1} } = {3} + { \tblr_set_every_vline:nnn #1 } + { \tblr_set_every_vline:nnn {1} #1 } + } + { \tblr_set_every_vline:nnn {1} {-} {#1} } + } + +%% Add \SetVline, \vline and \rline commands + +\tl_new:N \l__tblr_vline_count_tl % the count of all vlines +\tl_new:N \l__tblr_vline_num_tl % the index of the vline +\tl_new:N \l__tblr_vline_rows_tl % the rows of the vline +\tl_new:N \l__tblr_vline_dash_tl % dash style +\tl_new:N \l__tblr_vline_fg_tl % dash foreground +\tl_new:N \l__tblr_vline_wd_tl % dash width + +\NewTableCommand \rline [2] [] { \SetVline [=] {#2} {#1} } + +\NewTableCommand \vline [1] [] { \SetVline [+] {-} {#1} } + +%% #1: the index of the vline (may be + or =) +%% #2: which rows of the vline, separate by commas +%% #3: key=value pairs +\NewTableCommand \SetVline [3] [+] + { + \tblr_set_vline:nnn {#1} {#2} {#3} + } + +%% We need to check "text" key first +%% If it does exist and has empty value, then do nothing +\cs_new_protected:Npn \tblr_set_vline:nnn #1 #2 #3 + { + \group_begin: + \keys_set_groups:nnn { tblr-vline } { text } {#3} + \tl_if_eq:NnF \l__tblr_vline_dash_tl { \exp_not:N \@tblr@text } + { + \__tblr_set_vline_num:n {#1} + \tl_clear:N \l__tblr_vline_dash_tl + \keys_set:nn { tblr-vline } { dash = solid, #3 } + \__tblr_set_vline_cmd:n {#2} + } + \group_end: + } + +\cs_new_protected:Npn \tblr_set_vline:nnnn #1 #2 #3 #4 + { + \group_begin: + \__tblr_get_childs:nx {#1} { \int_eval:n { \c@colcount + 1} } + \clist_map_inline:Nn \l_tblr_childs_clist + { + \int_set:Nn \c@colnum {##1} + \tblr_set_vline:nnn {#2} {#3} {#4} + } + \group_end: + } + +%% Check the number of arguments and call \tblr_set_vline in different ways +%% Note that #1 always includes an outer pair of braces +%% This function is called when parsing table specifications +\cs_new_protected:Npn \__tblr_set_vline_aux:nn #1 #2 + { + \tl_if_head_is_group:nTF {#2} + { + \int_compare:nNnTF { \tl_count:n {#2} } = {3} + { \tblr_set_vline:nnnn #1 #2 } + { \tblr_set_vline:nnnn #1 {1} #2 } + } + { \tblr_set_vline:nnnn #1 {1} {-} {#2} } + } +\cs_generate_variant:Nn \__tblr_set_vline_aux:nn { Vn } + +%% #1: the index of vline to set (may be + or =) +\cs_new_protected:Npn \__tblr_set_vline_num:n #1 + { + \tl_clear:N \l__tblr_vline_num_tl + \tl_set:Nx \l__tblr_vline_count_tl + { \__tblr_prop_item:ne { vline } { [\int_use:N \c@colnum] / @vline-count } } + \tl_if_empty:NTF \l__tblr_vline_count_tl + { + \tl_set:Nx \l__tblr_vline_num_tl { 1 } + \__tblr_prop_gput:nxx { vline } + { [\int_use:N \c@colnum] / @vline-count } { 1 } + } + { + \tl_if_eq:nnTF {#1} {+} + { \__tblr_set_vline_num_incr: } + { + \tl_if_eq:nnTF {#1} {=} + { \tl_set_eq:NN \l__tblr_vline_num_tl \l__tblr_vline_count_tl } + { + \int_compare:nNnTF {#1} > { \l__tblr_vline_count_tl } + { \__tblr_set_vline_num_incr: } + { \tl_set:Nn \l__tblr_vline_num_tl {#1} } + } + } + } + } + +\cs_new_protected:Npn \__tblr_set_vline_num_incr: + { + \tl_set:Nx \l__tblr_vline_count_tl + { \int_eval:n { \l__tblr_vline_count_tl + 1 } } + \__tblr_prop_gput:nxx { vline } + { [\int_use:N \c@colnum] / @vline-count } { \l__tblr_vline_count_tl } + \tl_set_eq:NN \l__tblr_vline_num_tl \l__tblr_vline_count_tl + } + +\keys_define:nn { tblr-vline } + { + dash .code:n = \tl_set:Nn \l__tblr_vline_dash_tl { \exp_not:N \@tblr@dash #1 }, + text .code:n = \tl_set:Nn \l__tblr_vline_dash_tl { \exp_not:N \@tblr@text #1 }, + text .groups:n = { text }, + wd .code:n = \tl_set:Nn \l__tblr_vline_wd_tl { \dim_eval:n {#1} }, + fg .code:n = \tl_set:Nn \l__tblr_vline_fg_tl {#1}, + unknown .code:n = \__tblr_vline_unknown_key:V \l_keys_key_str, + } + +\cs_new_protected:Npn \__tblr_vline_unknown_key:n #1 + { + \prop_if_in:NnTF \g__tblr_defined_vdash_styles_prop {#1} + { \tl_set:Nn \l__tblr_vline_dash_tl { \exp_not:N \@tblr@dash #1 } } + { + \regex_match:NnTF \c__tblr_is_color_key_regex {#1} + { \tl_set:Nn \l__tblr_vline_fg_tl {#1} } + { + \tl_set_rescan:Nnn \l__tblr_v_tl {} {#1} + \tl_set:Nn \l__tblr_vline_wd_tl { \dim_eval:n {\l__tblr_v_tl} } + } + } + } +\cs_generate_variant:Nn \__tblr_vline_unknown_key:n { V } + +\cs_new_protected_nopar:Npn \__tblr_set_vline_cmd:n #1 + { + \__tblr_get_childs:nx {#1} { \int_use:N \c@rowcount } + \clist_map_inline:Nn \l_tblr_childs_clist + { + \__tblr_prop_gput:nxx { vline } + { [##1][\int_use:N \c@colnum](\l__tblr_vline_num_tl) / @dash } + { \l__tblr_vline_dash_tl } + \tl_if_empty:NF \l__tblr_vline_wd_tl + { + \__tblr_prop_gput:nxx { vline } + { [##1][\int_use:N \c@colnum](\l__tblr_vline_num_tl) / wd } + { \l__tblr_vline_wd_tl } + } + \tl_if_empty:NF \l__tblr_vline_fg_tl + { + \__tblr_prop_gput:nxx { vline } + { [##1][\int_use:N \c@colnum](\l__tblr_vline_num_tl) / fg } + { \l__tblr_vline_fg_tl } + } + } + } + +%%% -------------------------------------------------------- +%% \section{Set Cells} +%%% -------------------------------------------------------- + +%% \SetCells command for setting every cell in the table +\NewTableCommand \SetCells [2] [] + { + \tblr_set_every_cell:nn {#1} {#2} + } + +%% We put all code inside a group to avoid affecting other table commands +\cs_new_protected:Npn \tblr_set_every_cell:nn #1 #2 + { + \group_begin: + \int_step_inline:nn { \c@rowcount } + { + \int_set:Nn \c@rownum {##1} + \int_step_inline:nn { \c@colcount } + { + \int_set:Nn \c@colnum {####1} + \tblr_set_cell:nn {#1} {#2} + } + } + \group_end: + } + +%% Check the number of arguments and call \tblr_set_every_cell in different ways +%% This function is called when parsing table specifications +\cs_new_protected:Npn \__tblr_set_every_cell_aux:n #1 + { + \tl_if_head_is_group:nTF {#1} + { \tblr_set_every_cell:nn #1 } + { \tblr_set_every_cell:nn {} {#1} } + } + +%% \SetCell command for multirow and/or multicolumn cells + +\NewTableCommand \SetCell [2] [] + { + \tblr_set_cell:nn { #1 } { #2 } + } + +\tl_new:N \l__tblr_row_span_num_tl +\tl_new:N \l__tblr_col_span_num_tl + +\cs_new_protected:Npn \tblr_set_cell:nn #1 #2 + { + \tl_set:Nn \l__tblr_row_span_num_tl { 1 } + \tl_set:Nn \l__tblr_col_span_num_tl { 1 } + \keys_set:nn { tblr-cell-span } { #1 } + \keys_set:nn { tblr-cell-spec } { #2 } + \__tblr_set_span_spec:VV \l__tblr_row_span_num_tl \l__tblr_col_span_num_tl + } +\cs_generate_variant:Nn \tblr_set_cell:nn { nV } + +\cs_new_protected:Npn \tblr_set_cell:nnnn #1 #2 #3 #4 + { + \group_begin: + \__tblr_get_childs:nx {#1} { \int_use:N \c@rowcount } + \clist_set_eq:NN \l_tmpa_clist \l_tblr_childs_clist + \__tblr_get_childs:nx {#2} { \int_use:N \c@colcount } + \clist_set_eq:NN \l_tmpb_clist \l_tblr_childs_clist + \clist_map_inline:Nn \l_tmpa_clist + { + \int_set:Nn \c@rownum {##1} + \clist_map_inline:Nn \l_tmpb_clist + { + \int_set:Nn \c@colnum {####1} + \tblr_set_cell:nn {#3} {#4} + } + } + \group_end: + } + +%% Check the number of arguments and call \tblr_set_cell in different ways +%% Note that #1 is always of the type {}{} +%% This function is called when parsing table specifications +\cs_new_protected:Npn \__tblr_set_cell_aux:nn #1 #2 + { + \tl_if_head_is_group:nTF {#2} + { \tblr_set_cell:nnnn #1 #2 } + { \tblr_set_cell:nnnn #1 {} {#2} } + } +\cs_generate_variant:Nn \__tblr_set_cell_aux:nn { Vn } + +\keys_define:nn { tblr-cell-span } + { + r .tl_set:N = \l__tblr_row_span_num_tl, + c .tl_set:N = \l__tblr_col_span_num_tl, + } + +\keys_define:nn { tblr-cell-spec } + { + l .code:n = \__tblr_prop_gput:nxx {cell} + {[\int_use:N \c@rownum][\int_use:N \c@colnum] / halign} {l}, + c .code:n = \__tblr_prop_gput:nxx {cell} + {[\int_use:N \c@rownum][\int_use:N \c@colnum] / halign} {c}, + r .code:n = \__tblr_prop_gput:nxx {cell} + {[\int_use:N \c@rownum][\int_use:N \c@colnum] / halign} {r}, + t .code:n = \__tblr_prop_gput:nxx {cell} + {[\int_use:N \c@rownum][\int_use:N \c@colnum] / valign} {t}, + p .code:n = \__tblr_prop_gput:nxx {cell} + {[\int_use:N \c@rownum][\int_use:N \c@colnum] / valign} {t}, + m .code:n = \__tblr_prop_gput:nxx {cell} + {[\int_use:N \c@rownum][\int_use:N \c@colnum] / valign} {m}, + b .code:n = \__tblr_prop_gput:nxx {cell} + {[\int_use:N \c@rownum][\int_use:N \c@colnum] / valign} {b}, + h .code:n = \__tblr_prop_gput:nxx {cell} + {[\int_use:N \c@rownum][\int_use:N \c@colnum] / valign} {h}, + f .code:n = \__tblr_prop_gput:nxx {cell} + {[\int_use:N \c@rownum][\int_use:N \c@colnum] / valign} {f}, + wd .code:n = \__tblr_prop_gput:nxx {cell} + {[\int_use:N \c@rownum][\int_use:N \c@colnum] / width} {#1}, + bg .code:n = \__tblr_prop_gput:nxx {cell} + {[\int_use:N \c@rownum][\int_use:N \c@colnum] / background} {#1}, + unknown .code:n = \__tblr_cell_unknown_key:V \l_keys_key_str, + } + +\cs_new_protected:Npn \__tblr_cell_unknown_key:n #1 + { + \regex_match:NnTF \c__tblr_is_color_key_regex {#1} + { + \__tblr_prop_gput:nxx {cell} + {[\int_use:N \c@rownum][\int_use:N \c@colnum] / background} {#1} + } + { + \tl_set_rescan:Nnn \l__tblr_v_tl {} {#1} + \__tblr_prop_gput:nxx {cell} + { [\int_use:N \c@rownum][\int_use:N \c@colnum] / width } + { \dim_eval:n { \l__tblr_v_tl } } + } + } +\cs_generate_variant:Nn \__tblr_cell_unknown_key:n { V } + +\cs_new_protected:Npn \__tblr_set_span_spec:nn #1 #2 + { + \int_compare:nNnT { #1 } > { 1 } + { + \__tblr_prop_gput:nnn {table} {rowspan} {true} + \__tblr_prop_gput:nxn {cell} + { [\int_use:N \c@rownum][\int_use:N \c@colnum] / rowspan } { #1 } + } + \int_compare:nNnT { #2 } > { 1 } + { + \__tblr_prop_gput:nnn {table} {colspan} {true} + \__tblr_prop_gput:nxn {cell} + { [\int_use:N \c@rownum][\int_use:N \c@colnum] / colspan } { #2 } + } + \int_step_variable:nnNn + { \int_use:N \c@rownum } { \int_eval:n { \c@rownum + #1 - 1 } } \l__tblr_i_tl + { + \int_step_variable:nnNn + { \int_use:N \c@colnum } { \int_eval:n { \c@colnum + #2 - 1 } } + \l__tblr_j_tl + { + \bool_lazy_and:nnF + { \int_compare_p:nNn { \l__tblr_i_tl } = { \c@rownum } } + { \int_compare_p:nNn { \l__tblr_j_tl } = { \c@colnum } } + { + \__tblr_prop_gput:nxx {cell} + { [\l__tblr_i_tl][\l__tblr_j_tl] / omit } {true} + } + \int_compare:nNnF { \l__tblr_i_tl } = { \c@rownum } + { + \__tblr_prop_gput:nxx {hline} + { [\l__tblr_i_tl][\l__tblr_j_tl] / omit } {true} + } + \int_compare:nNnF { \l__tblr_j_tl } = { \c@colnum } + { + \__tblr_prop_gput:nxx {vline} + { [\l__tblr_i_tl][\l__tblr_j_tl] / omit } {true} + } + } + } + } +\cs_generate_variant:Nn \__tblr_set_span_spec:nn { VV } + +%% Legacy \multicolumn and \multirow commands +%% Both of them could be replaced with \SetCell command +%% Note that they don't have cell text as the last arguments + +%% If \multicolumn is followed by \multirow, +%% We need to call \tblr_set_cell together +%% in order to omit all hlines inside the span cell. +\tl_new:N \g__tblr_multicolumn_num_tl +\tl_new:N \g__tblr_multicolumn_spec_tl + +%% There maybe p{2em} inside #2 of \multicolumn command +\NewTableCommand \multicolumn [2] + { + \tl_gclear:N \g__tblr_multicolumn_num_tl + \tl_gclear:N \g__tblr_multicolumn_spec_tl + \tl_map_inline:nn {#2} + { + \bool_lazy_and:nnF + { \tl_if_single_token_p:n {##1} } + { \token_if_eq_charcode_p:NN ##1 | } + { \tl_put_right:Nn \g__tblr_multicolumn_spec_tl {,##1} } + } + \peek_meaning:NTF \multirow + { \tl_gset:Nn \g__tblr_multicolumn_num_tl {#1} } + { \tblr_set_cell:nV { c = #1 } \g__tblr_multicolumn_spec_tl } + } + +\NewTableCommand \multirow [3] [m] + { + \tl_if_eq:nnTF {#1} {c} + { \tl_set:Nn \l_tmpa_tl {, m} } + { + \tl_if_eq:nnTF {#1} {t} + { \tl_set:Nn \l_tmpa_tl {, h} } + { \tl_if_eq:nnTF {#1} {b} + { \tl_set:Nn \l_tmpa_tl {, f} } + { \tl_set:Nn \l_tmpa_tl {, #1} } + } + } + \tl_if_eq:nnF {#3} {*} + { \tl_if_eq:nnF {#3} {=} { \tl_put_right:Nn \l_tmpa_tl {, wd=#3} } } + \tl_if_empty:NTF \g__tblr_multicolumn_num_tl + { \tblr_set_cell:nV { r = #2 } \l_tmpa_tl } + { + \tl_put_left:NV \l_tmpa_tl \g__tblr_multicolumn_spec_tl + \exp_args:Nx \tblr_set_cell:nV + { c = \g__tblr_multicolumn_num_tl, r = #2 } \l_tmpa_tl + \tl_gclear:N \g__tblr_multicolumn_num_tl + } + } + +%%% -------------------------------------------------------- +%% \section{Set Columns and Rows} +%%% -------------------------------------------------------- + +%% \SetColumns command for setting every column in the table +\NewTableCommand \SetColumns [2] [] + { + \tblr_set_every_column:nn {#1} {#2} + } + +%% We put all code inside a group to avoid affecting other table commands +\cs_new_protected:Npn \tblr_set_every_column:nn #1 #2 + { + \group_begin: + \int_step_inline:nn { \c@colcount } + { + \int_set:Nn \c@colnum {##1} + \tblr_set_column:nn {#1} {#2} + } + \group_end: + } + +%% Check the number of arguments and call \tblr_set_every_column in different ways +%% This function is called when parsing table specifications +\cs_new_protected:Npn \__tblr_set_every_column_aux:n #1 + { + \tl_if_head_is_group:nTF {#1} + { \tblr_set_every_column:nn #1 } + { \tblr_set_every_column:nn {} {#1} } + } + +%% \SetColumn command for current column or each cells in the column + +\NewTableCommand \SetColumn [2] [] + { + \tblr_set_column:nn {#1} {#2} + } + +\cs_new_protected:Npn \tblr_set_column:nn #1 #2 + { + \keys_set:nn { tblr-column } {#2} + } + +\cs_new_protected:Npn \tblr_set_column:nnn #1 #2 #3 + { + \group_begin: + \__tblr_get_childs:nx {#1} { \int_use:N \c@colcount } + \clist_map_inline:Nn \l_tblr_childs_clist + { + \int_set:Nn \c@colnum {##1} + \tblr_set_column:nn {#2} {#3} + } + \group_end: + } + +%% Check the number of arguments and call \tblr_set_column in different ways +%% Note that #1 always includes an outer pair of braces +%% This function is called when parsing table specifications +\cs_new_protected:Npn \__tblr_set_column_aux:nn #1 #2 + { + \tl_if_head_is_group:nTF {#2} + { \tblr_set_column:nnn #1 #2 } + { \tblr_set_column:nnn #1 {} {#2} } + } +\cs_generate_variant:Nn \__tblr_set_column_aux:nn { Vn } + +\keys_define:nn { tblr-column } + { + l .code:n = \__tblr_set_key_for_every_column_cell:nnn + { \int_use:N \c@colnum } { halign } {l}, + c .code:n = \__tblr_set_key_for_every_column_cell:nnn + { \int_use:N \c@colnum } { halign } {c}, + r .code:n = \__tblr_set_key_for_every_column_cell:nnn + { \int_use:N \c@colnum } { halign } {r}, + t .code:n = \__tblr_set_key_for_every_column_cell:nnn + { \int_use:N \c@colnum } { valign } {t}, + p .code:n = \__tblr_set_key_for_every_column_cell:nnn + { \int_use:N \c@colnum } { valign } {t}, + m .code:n = \__tblr_set_key_for_every_column_cell:nnn + { \int_use:N \c@colnum } { valign } {m}, + b .code:n = \__tblr_set_key_for_every_column_cell:nnn + { \int_use:N \c@colnum } { valign } {b}, + h .code:n = \__tblr_set_key_for_every_column_cell:nnn + { \int_use:N \c@colnum } { valign } {h}, + f .code:n = \__tblr_set_key_for_every_column_cell:nnn + { \int_use:N \c@colnum } { valign } {f}, + bg .code:n = \__tblr_set_key_for_every_column_cell:nnn + { \int_use:N \c@colnum } { background } {#1}, + wd .code:n = \__tblr_prop_gput:nxx { column } + { [\int_use:N \c@colnum] / width } { \dim_eval:n {#1} }, + co .code:n = \__tblr_prop_gput:nxx { column } + { [\int_use:N \c@colnum] / coefficient } {#1}, + leftsep .code:n = \__tblr_prop_gput:nxx { column } + { [\int_use:N \c@colnum] / leftsep } { \dim_eval:n {#1} }, + rightsep .code:n = \__tblr_prop_gput:nxx { column } + { [\int_use:N \c@colnum] / rightsep } { \dim_eval:n {#1} }, + colsep .meta:n = { leftsep = #1, rightsep = #1}, + leftsep+ .code:n = \__tblr_prop_gadd_dimen_value:nxx { column } + { [\int_use:N \c@colnum] / leftsep } { \dim_eval:n {#1} }, + rightsep+ .code:n = \__tblr_prop_gadd_dimen_value:nxx { column } + { [\int_use:N \c@colnum] / rightsep } { \dim_eval:n {#1} }, + colsep+ .meta:n = { leftsep+ = #1, rightsep+ = #1}, + unknown .code:n = \__tblr_column_unknown_key:V \l_keys_key_str, + } + +%% #1: column number; #2: key; #3: value +\cs_new_protected:Npn \__tblr_set_key_for_every_column_cell:nnn #1 #2 #3 + { + \int_step_inline:nn { \c@rowcount } + { + \__tblr_prop_gput:nxn {cell} { [##1][#1] / #2 } {#3} + } + } + +\regex_const:Nn \c__tblr_is_number_key_regex { ^[\+\-]? (\d+|\d*\.\d+)$ } + +\cs_new_protected:Npn \__tblr_column_unknown_key:n #1 + { + \regex_match:NnTF \c__tblr_is_number_key_regex {#1} + { + \__tblr_prop_gput:nxx { column } + { [\int_use:N \c@colnum] / coefficient } {#1} + } + { + \regex_match:NnTF \c__tblr_is_color_key_regex {#1} + { + \__tblr_set_key_for_every_column_cell:nnn + { \int_use:N \c@colnum } { background } {#1} + } + { + \tl_set_rescan:Nnn \l__tblr_v_tl {} {#1} + \__tblr_prop_gput:nxx { column } + { [\int_use:N \c@colnum] / width } { \dim_eval:n { \l__tblr_v_tl } } + } + } + } +\cs_generate_variant:Nn \__tblr_column_unknown_key:n { V } + +%% \SetRows command for setting every row in the table +\NewTableCommand \SetRows [2] [] + { + \tblr_set_every_row:nn {#1} {#2} + } + +%% We put all code inside a group to avoid affecting other table commands +\cs_new_protected:Npn \tblr_set_every_row:nn #1 #2 + { + \group_begin: + \int_step_inline:nn { \c@rowcount } + { + \int_set:Nn \c@rownum {##1} + \tblr_set_row:nn {#1} {#2} + } + \group_end: + } + +%% Check the number of arguments and call \tblr_set_every_row in different ways +%% This function is called when parsing table specifications +\cs_new_protected:Npn \__tblr_set_every_row_aux:n #1 + { + \tl_if_head_is_group:nTF {#1} + { \tblr_set_every_row:nn #1 } + { \tblr_set_every_row:nn {} {#1} } + } + +%% \SetRow command for current row or each cells in the row + +\NewTableCommand \SetRow [2] [] + { + \tblr_set_row:nn {#1} {#2} + } + +\cs_new_protected:Npn \tblr_set_row:nn #1 #2 + { + \keys_set:nn { tblr-row } {#2} + } + +\cs_new_protected:Npn \tblr_set_row:nnn #1 #2 #3 + { + \group_begin: + \__tblr_get_childs:nx {#1} { \int_use:N \c@rowcount } + \clist_map_inline:Nn \l_tblr_childs_clist + { + \int_set:Nn \c@rownum {##1} + \tblr_set_row:nn {#2} {#3} + } + \group_end: + } + +%% Check the number of arguments and call \tblr_set_row in different ways +%% Note that #1 always includes an outer pair of braces +%% This function is called when parsing table specifications +\cs_new_protected:Npn \__tblr_set_row_aux:nn #1 #2 + { + \tl_if_head_is_group:nTF {#2} + { \tblr_set_row:nnn #1 #2 } + { \tblr_set_row:nnn #1 {} {#2} } + } +\cs_generate_variant:Nn \__tblr_set_row_aux:nn { Vn } + +\keys_define:nn { tblr-row } + { + l .code:n = \__tblr_set_key_for_every_row_cell:nnn + { \int_use:N \c@rownum } { halign } {l}, + c .code:n = \__tblr_set_key_for_every_row_cell:nnn + { \int_use:N \c@rownum } { halign } {c}, + r .code:n = \__tblr_set_key_for_every_row_cell:nnn + { \int_use:N \c@rownum } { halign } {r}, + t .code:n = \__tblr_set_key_for_every_row_cell:nnn + { \int_use:N \c@rownum } { valign } {t}, + p .code:n = \__tblr_set_key_for_every_row_cell:nnn + { \int_use:N \c@rownum } { valign } {t}, + m .code:n = \__tblr_set_key_for_every_row_cell:nnn + { \int_use:N \c@rownum } { valign } {m}, + b .code:n = \__tblr_set_key_for_every_row_cell:nnn + { \int_use:N \c@rownum } { valign } {b}, + h .code:n = \__tblr_set_key_for_every_row_cell:nnn + { \int_use:N \c@rownum } { valign } {h}, + f .code:n = \__tblr_set_key_for_every_row_cell:nnn + { \int_use:N \c@rownum } { valign } {f}, + bg .code:n = \__tblr_set_key_for_every_row_cell:nnn + { \int_use:N \c@rownum } { background } {#1}, + ht .code:n = \__tblr_prop_gput:nxx { row } + { [\int_use:N \c@rownum] / height } { \dim_eval:n {#1} }, + co .code:n = \__tblr_prop_gput:nxx { row } + { [\int_use:N \c@rownum] / coefficient } {#1}, + abovesep .code:n = \__tblr_prop_gput:nxx { row } + { [\int_use:N \c@rownum] / abovesep } { \dim_eval:n {#1} }, + belowsep .code:n = \__tblr_prop_gput:nxx { row } + { [\int_use:N \c@rownum] / belowsep } { \dim_eval:n {#1} }, + rowsep .meta:n = { abovesep = #1, belowsep = #1}, + abovesep+ .code:n = \__tblr_prop_gadd_dimen_value:nxx { row } + { [\int_use:N \c@rownum] / abovesep } { \dim_eval:n {#1} }, + belowsep+ .code:n = \__tblr_prop_gadd_dimen_value:nxx { row } + { [\int_use:N \c@rownum] / belowsep } { \dim_eval:n {#1} }, + rowsep+ .meta:n = { abovesep+ = #1, belowsep+ = #1}, + nobreak .code:n = \__tblr_prop_gput:nxx { row } + { [\int_eval:n {\c@rownum - 1}] / nobreak } { true }, + unknown .code:n = \__tblr_row_unknown_key:V \l_keys_key_str, + } + +%% #1: row number; #2: key; #3: value +\cs_new_protected:Npn \__tblr_set_key_for_every_row_cell:nnn #1 #2 #3 + { + \int_step_inline:nn { \c@colcount } + { + \__tblr_prop_gput:nxn {cell} { [#1][##1] / #2 } {#3} + } + } + +\cs_new_protected:Npn \__tblr_row_unknown_key:n #1 + { + \regex_match:NnTF \c__tblr_is_number_key_regex {#1} + { + \__tblr_prop_gput:nxx { row } + { [\int_use:N \c@rownum] / coefficient } {#1} + } + { + \regex_match:NnTF \c__tblr_is_color_key_regex {#1} + { + \__tblr_set_key_for_every_row_cell:nnn + { \int_use:N \c@rownum } { background } {#1} + } + { + \tl_set_rescan:Nnn \l__tblr_v_tl {} {#1} + \__tblr_prop_gput:nxx { row } + { [\int_use:N \c@rownum] / height } { \dim_eval:n { \l__tblr_v_tl } } + } + } + } +\cs_generate_variant:Nn \__tblr_row_unknown_key:n { V } + +%%% -------------------------------------------------------- +%% \section{Column Types and Row Types} +%%% -------------------------------------------------------- + +%% Some primitive column/row types + +\str_const:Nn \c_tblr_primitive_colrow_types_str { Q | < > } +\tl_new:N \g__tblr_expanded_colrow_spec_tl + +\exp_args:Nc \NewDocumentCommand { tblr_primitive_column_type_ Q } { O{} } + { + \keys_set:nn { tblr-column } { #1 } + \int_incr:N \c@colnum + \__tblr_execute_colrow_spec_next:N + } +\exp_args:Nc \NewDocumentCommand { tblr_column_type_ Q } { O{} } + { + \tl_gput_right:Nn \g__tblr_expanded_colrow_spec_tl { Q[#1] } + \__tblr_expand_colrow_spec_next:N + } + +\exp_args:Nc \NewDocumentCommand { tblr_primitive_row_type_ Q } { O{} } + { + \keys_set:nn { tblr-row } { #1 } + \int_incr:N \c@rownum + \__tblr_execute_colrow_spec_next:N + } +\exp_args:Nc \NewDocumentCommand { tblr_row_type_ Q } { O{} } + { + \tl_gput_right:Nn \g__tblr_expanded_colrow_spec_tl { Q[#1] } + \__tblr_expand_colrow_spec_next:N + } + +\exp_args:Nc \NewDocumentCommand { tblr_primitive_column_type_ | } { O{} } + { + \vline [#1] + \__tblr_execute_colrow_spec_next:N + } +\exp_args:Nc \NewDocumentCommand { tblr_column_type_ | } { O{} } + { + \tl_gput_right:Nn \g__tblr_expanded_colrow_spec_tl { |[#1] } + \__tblr_expand_colrow_spec_next:N + } + +\exp_args:Nc \NewDocumentCommand { tblr_primitive_row_type_ | } { O{} } + { + \hline [#1] + \__tblr_execute_colrow_spec_next:N + } +\exp_args:Nc \NewDocumentCommand { tblr_row_type_ | } { O{} } + { + \tl_gput_right:Nn \g__tblr_expanded_colrow_spec_tl { |[#1] } + \__tblr_expand_colrow_spec_next:N + } + +\exp_args:Nc \NewDocumentCommand { tblr_primitive_column_type_ > } { O{} m } + { + \tl_if_blank:nF { #1 } + { + \__tblr_prop_gput:nxx + { column } + { [\int_use:N \c@colnum] / leftsep} + { \dim_eval:n { #1 } } + } + \tl_if_blank:nF { #2 } + { + \int_step_variable:nNn { \c@rowcount } \l__tblr_i_tl + { + \tl_set:Nx \l_tmpa_tl + { + \__tblr_prop_item:ne {text} + { [\l__tblr_i_tl][\int_use:N \c@colnum] } + } + \tl_put_left:Nn \l_tmpa_tl { #2 } + \__tblr_prop_gput:nxV {text} + { [\l__tblr_i_tl][\int_use:N \c@colnum] } \l_tmpa_tl + } + } + \__tblr_execute_colrow_spec_next:N + } +\exp_args:Nc \NewDocumentCommand { tblr_column_type_ > } { O{} m } + { + \tl_gput_right:Nn \g__tblr_expanded_colrow_spec_tl { >[#1]{#2} } + \__tblr_expand_colrow_spec_next:N + } + +\exp_args:Nc \NewDocumentCommand { tblr_primitive_row_type_ > } { O{} m } + { + \tl_if_blank:nF { #1 } + { + \__tblr_prop_gput:nxx + { row } + { [\int_use:N \c@rownum] / abovesep } + { \dim_eval:n { #1 } } + } + \tl_if_blank:nF { #2 } + { + \int_step_variable:nNn { \c@colcount } \l__tblr_j_tl + { + \tl_set:Nx \l_tmpa_tl + { + \__tblr_prop_item:ne {text} + { [\int_use:N \c@rownum][\l__tblr_j_tl] } + } + \tl_put_left:Nn \l_tmpa_tl { #2 } + \__tblr_prop_gput:nxV {text} + { [\int_use:N \c@rownum][\l__tblr_j_tl] } \l_tmpa_tl + } + } + \__tblr_execute_colrow_spec_next:N + } +\exp_args:Nc \NewDocumentCommand { tblr_row_type_ > } { O{} m } + { + \tl_gput_right:Nn \g__tblr_expanded_colrow_spec_tl { >[#1]{#2} } + \__tblr_expand_colrow_spec_next:N + } + +\exp_args:Nc \NewDocumentCommand { tblr_primitive_column_type_ < } { O{} m } + { + \tl_if_blank:nF { #1 } + { + \__tblr_prop_gput:nxx + { column } + { [\int_eval:n {\c@colnum - 1}] / rightsep } + { \dim_eval:n { #1 } } + } + \tl_if_blank:nF { #2 } + { + \int_step_variable:nNn { \c@rowcount } \l__tblr_i_tl + { + \tl_set:Nx \l_tmpa_tl + { + \__tblr_prop_item:ne {text} + { [\l__tblr_i_tl][\int_eval:n {\c@colnum - 1}] } + } + \tl_put_right:Nn \l_tmpa_tl { #2 } + \__tblr_prop_gput:nxV {text} + { [\l__tblr_i_tl][\int_eval:n {\c@colnum - 1}] } \l_tmpa_tl + } + } + \__tblr_execute_colrow_spec_next:N + } +\exp_args:Nc \NewDocumentCommand { tblr_column_type_ < } { O{} m } + { + \tl_gput_right:Nn \g__tblr_expanded_colrow_spec_tl { <[#1]{#2} } + \__tblr_expand_colrow_spec_next:N + } + +\exp_args:Nc \NewDocumentCommand { tblr_primitive_row_type_ < } { O{} m } + { + \tl_if_blank:nF { #1 } + { + \__tblr_prop_gput:nxx + { row } + { [\int_eval:n {\c@rownum - 1}] / belowsep } + { \dim_eval:n { #1 } } + } + \tl_if_blank:nF { #2 } + { + \int_step_variable:nNn { \c@colcount } \l__tblr_j_tl + { + \tl_set:Nx \l_tmpa_tl + { + \__tblr_prop_item:ne {text} + { [\int_eval:n {\c@rownum - 1}][\l__tblr_j_tl] } + } + \tl_put_right:Nn \l_tmpa_tl { #2 } + \__tblr_prop_gput:nxV {text} + { [\int_eval:n {\c@rownum - 1}][\l__tblr_j_tl] } \l_tmpa_tl + } + } + \__tblr_execute_colrow_spec_next:N + } +\exp_args:Nc \NewDocumentCommand { tblr_row_type_ < } { O{} m } + { + \tl_gput_right:Nn \g__tblr_expanded_colrow_spec_tl { <[#1]{#2} } + \__tblr_expand_colrow_spec_next:N + } + +%% \NewColumnType/\NewRowType command and predefined column/row types + +\str_new:N \g_tblr_used_column_types_str +\str_gset_eq:NN \g_tblr_used_column_types_str \c_tblr_primitive_colrow_types_str + +\str_new:N \g_tblr_used_row_types_str +\str_gset_eq:NN \g_tblr_used_row_types_str \c_tblr_primitive_colrow_types_str + +\bool_new:N \g__tblr_colrow_spec_expand_stop_bool +\tl_new:N \g__tblr_column_or_row_tl + +\msg_new:nnn { tabularray } { used-colrow-type } + { #1 ~ type ~ name ~ #2 ~ has ~ been ~ used! } + +\NewDocumentCommand \NewColumnType { m O{0} o m } + { + \tl_set:Nn \g__tblr_column_or_row_tl { column } + \__tblr_new_column_or_row_type:nnnn {#1} {#2} {#3} {#4} + } + +\NewDocumentCommand \NewRowType { m O{0} o m } + { + \tl_set:Nn \g__tblr_column_or_row_tl { row } + \__tblr_new_column_or_row_type:nnnn {#1} {#2} {#3} {#4} + } + +\NewDocumentCommand \NewColumnRowType { m O{0} o m } + { + \tl_set:Nn \g__tblr_column_or_row_tl { column } + \__tblr_new_column_or_row_type:nnnn {#1} {#2} {#3} {#4} + \tl_set:Nn \g__tblr_column_or_row_tl { row } + \__tblr_new_column_or_row_type:nnnn {#1} {#2} {#3} {#4} + } + +\cs_new_protected:Npn \__tblr_new_column_or_row_type:nnnn #1 #2 #3 #4 + { + \str_if_in:cnTF { g_tblr_used_ \g__tblr_column_or_row_tl _types_str } {#1} + { + \tl_if_eq:NnTF \g__tblr_column_or_row_tl { row } + { \msg_warning:nnnn { tabularray } { used-colrow-type } { Row } {#1} } + { \msg_warning:nnnn { tabularray } { used-colrow-type } { Column } {#1} } + \str_log:c { g_tblr_used_ \g__tblr_column_or_row_tl _types_str } + } + { + \__tblr_make_xparse_arg_spec:nnN {#2} {#3} \l__tblr_a_tl + \exp_args:NcV \NewDocumentCommand + { tblr_ \g__tblr_column_or_row_tl _type_ #1 } \l__tblr_a_tl + { + \bool_gset_false:N \g__tblr_colrow_spec_expand_stop_bool + \tl_gput_right:Nf \g__tblr_expanded_colrow_spec_tl {#4} + \__tblr_expand_colrow_spec_next:N + } + \str_gput_right:cn + { g_tblr_used_ \g__tblr_column_or_row_tl _types_str } {#1} + } + } + +\NewColumnRowType { l } { Q[l] } +\NewColumnRowType { c } { Q[c] } +\NewColumnRowType { r } { Q[r] } + +\NewColumnType { t } [1] { Q[t,wd=#1] } +\NewColumnType { p } [1] { Q[p,wd=#1] } +\NewColumnType { m } [1] { Q[m,wd=#1] } +\NewColumnType { b } [1] { Q[b,wd=#1] } +\NewColumnType { h } [1] { Q[h,wd=#1] } +\NewColumnType { f } [1] { Q[f,wd=#1] } + +\NewRowType { t } [1] { Q[t,ht=#1] } +\NewRowType { p } [1] { Q[p,ht=#1] } +\NewRowType { m } [1] { Q[m,ht=#1] } +\NewRowType { b } [1] { Q[b,ht=#1] } +\NewRowType { h } [1] { Q[h,ht=#1] } +\NewRowType { f } [1] { Q[f,ht=#1] } + +\NewColumnRowType { X } [1][] { Q[co=1,#1] } + +\NewColumnRowType { ! } [1] { |[text={#1}] } +\NewColumnRowType { @ } [1] { <[0pt]{} |[text={#1}] >[0pt]{} } +\NewColumnRowType { * } [2] { \prg_replicate:nn {#1} {#2} } + +\cs_new_protected:Npn \__tblr_parse_colrow_spec:nn #1 #2 + { + \tl_gset:Nn \g__tblr_column_or_row_tl {#1} + \tl_gset:Nn \g__tblr_expanded_colrow_spec_tl {#2} + \__tblr_expand_colrow_spec:N \g__tblr_expanded_colrow_spec_tl + \__tblr_execute_colrow_spec:N \g__tblr_expanded_colrow_spec_tl + } + +%% Expand defined column/row types + +\cs_new_protected:Npn \__tblr_expand_colrow_spec:N #1 + { + \bool_do_until:Nn \g__tblr_colrow_spec_expand_stop_bool + { + \LogTblrTracing { colspec, rowspec } + \bool_gset_true:N \g__tblr_colrow_spec_expand_stop_bool + \tl_set_eq:NN \l_tmpa_tl #1 + \tl_gclear:N #1 + \exp_last_unbraced:NV + \__tblr_expand_colrow_spec_next:N \l_tmpa_tl \scan_stop: + } + } + +\msg_new:nnn { tabularray } { unexpandable-colrow-type } + { Unexpandable ~ command ~ #2 inside ~ #1 ~ type! } + +\msg_new:nnn { tabularray } { unknown-colrow-type } + { Unknown ~ #1 ~ type ~ #2! } + +\cs_new_protected:Npn \__tblr_expand_colrow_spec_next:N #1 + { + \token_if_eq_catcode:NNTF #1 \scan_stop: + { + \token_if_eq_meaning:NNF #1 \scan_stop: + { + \msg_error:nnVn { tabularray } { unexpandable-colrow-type } + \g__tblr_column_or_row_tl {#1} + } + } + { + \str_if_in:cnTF { g_tblr_used_ \g__tblr_column_or_row_tl _types_str } {#1} + { \cs:w tblr_ \g__tblr_column_or_row_tl _type_ #1 \cs_end: } + { + \msg_error:nnVn { tabularray } { unknown-colrow-type } + \g__tblr_column_or_row_tl {#1} + \str_log:c { g_tblr_used_ \g__tblr_column_or_row_tl _types_str } + } + } + } + +%% Execute primitive column/row types + +\cs_new_protected:Npn \__tblr_execute_colrow_spec:N #1 + { + \tl_if_eq:NnTF \g__tblr_column_or_row_tl { row } + { \int_set:Nn \c@rownum {1} } + { \int_set:Nn \c@colnum {1} } + \exp_last_unbraced:NV \__tblr_execute_colrow_spec_next:N #1 \scan_stop: + } + +\cs_new_protected:Npn \__tblr_execute_colrow_spec_next:N #1 + { + \token_if_eq_meaning:NNF #1 \scan_stop: + { \cs:w tblr_primitive_ \g__tblr_column_or_row_tl _type_ #1 \cs_end: } + } + +%%% -------------------------------------------------------- +%% \section{Tabularray Environments} +%%% -------------------------------------------------------- + +\tl_new:N \l__tblr_env_name_tl +\bool_new:N \l__tblr_math_mode_bool + +\NewDocumentEnvironment { tblr } { O{c} m +b } + { + \tl_set:Nn \l__tblr_env_name_tl { tblr } + \mode_if_math:TF + { \bool_set_true:N \l__tblr_math_mode_bool } + { \bool_set_false:N \l__tblr_math_mode_bool } + \buildtblr {#1} {#2} {#3} + } { } + +%% Read, split and build the table + +\cs_new_protected:Npn \buildtblr #1 #2 #3 + { + \mode_leave_vertical: + \int_gincr:N \g_tblr_level_int + \__tblr_clear_prop_lists: + \__tblr_enable_table_commands: + \__tblr_split_table:n { #3 } + \LogTblrTracing { command } + \__tblr_initial_table_spec: + \LogTblrTracing { table } + \__tblr_parse_table_spec:n { #2 } + \__tblr_execute_table_commands: + \__tblr_disable_table_commands: + \__tblr_calc_cell_and_line_sizes: + \__tblr_build_whole:n { #1 } + \int_gdecr:N \g_tblr_level_int + } + +\cs_new_protected:Npn \__tblr_clear_prop_lists: + { + \prop_gclear_new:c { g_tblr_text_ \int_use:N \g_tblr_level_int _prop } + \prop_gclear_new:c { g_tblr_command_ \int_use:N \g_tblr_level_int _prop } + \prop_gclear_new:c { g_tblr_table_ \int_use:N \g_tblr_level_int _prop } + \prop_gclear_new:c { g_tblr_row_ \int_use:N \g_tblr_level_int _prop } + \prop_gclear_new:c { g_tblr_column_ \int_use:N \g_tblr_level_int _prop } + \prop_gclear_new:c { g_tblr_cell_ \int_use:N \g_tblr_level_int _prop } + \prop_gclear_new:c { g_tblr_hline_ \int_use:N \g_tblr_level_int _prop } + \prop_gclear_new:c { g_tblr_vline_ \int_use:N \g_tblr_level_int _prop } + } + +%% Insert and remove braces for nesting environments inside cells +%% These make line split and cell split workable +%% We need to replace N times for N level nestings +\regex_const:Nn \c__tblr_insert_braces_regex + { + \c{begin} \cB\{ (\c[^BE].*) \cE\} (.*?) \c{end} \cB\{ (\c[^BE].*) \cE\} + } +\tl_const:Nn \c__tblr_insert_braces_tl + { + \c{begin} \cB\{ \cB\{ \1 \cE\} \2 \c{end} \cE\} \cB\{ \3 \cE\} + } +\regex_const:Nn \c__tblr_remove_braces_regex + { + \c{begin} \cB\{ \cB\{ (.*?) \c{end} \cE\} + } +\tl_const:Nn \c__tblr_remove_braces_tl + { + \c{begin} \cB\{ \1 \c{end} + } +\cs_new_protected:Npn \__tblr_insert_braces:N #1 + { + \regex_replace_all:NVN \c__tblr_insert_braces_regex \c__tblr_insert_braces_tl #1 + \regex_replace_all:NVN \c__tblr_insert_braces_regex \c__tblr_insert_braces_tl #1 + } +\cs_new_protected:Npn \__tblr_remove_braces:N #1 + { + \regex_replace_all:NVN \c__tblr_remove_braces_regex \c__tblr_remove_braces_tl #1 + \regex_replace_all:NVN \c__tblr_remove_braces_regex \c__tblr_remove_braces_tl #1 + } + +%% Split table content to cells and store them +%% #1: table content + +\seq_new:N \l_tblr_lines_seq + +\cs_new_protected:Npn \__tblr_split_table:n #1 + { + \int_zero:N \c@rowcount + \int_zero:N \c@colcount + \__tblr_split_table_to_lines:nN { #1 } \l_tblr_lines_seq + \__tblr_split_lines_to_cells:N \l_tblr_lines_seq + } + +%% Split table content to a sequence of lines +%% #1: table content, #2: resulting sequence of lines +\cs_new_protected:Npn \__tblr_split_table_to_lines:nN #1 #2 + { + \tl_set:Nn \l_tmpa_tl { #1 } + \__tblr_insert_braces:N \l_tmpa_tl + \seq_set_split:NnV \l_tmpa_seq { \\ } \l_tmpa_tl + \seq_clear:N #2 + \seq_map_inline:Nn \l_tmpa_seq + { + \tl_if_head_eq_meaning:nNTF {##1} * + { + \tl_set:Nn \l__tblr_b_tl { \SetRow{nobreak} } + \tl_set:Nx \l__tblr_c_tl { \tl_tail:n {##1} } + \tl_trim_spaces:N \l__tblr_c_tl %% Ignore spaces between * and [dimen] + \tl_log:N \l__tblr_c_tl + \tl_if_head_eq_meaning:VNT \l__tblr_c_tl [ + { + \tl_put_right:Nn \l__tblr_b_tl { \RowBefore@AddBelowSep } + } + \tl_put_right:NV \l__tblr_b_tl \l__tblr_c_tl + \seq_put_right:NV #2 \l__tblr_b_tl + } + { + \tl_if_head_eq_meaning:nNTF { ##1 } [ + { \seq_put_right:Nn #2 { \RowBefore@AddBelowSep ##1 } } + { \seq_put_right:Nn #2 { ##1 } } + } + } + \int_set:Nn \c@rowcount { \seq_count:N #2 } + } + +%% Treat \\[dimen] command +\NewTableCommand \RowBefore@AddBelowSep [1] [] + { + \IfValueT { #1 } + { + \__tblr_prop_gadd_dimen_value:nxx { row } + { [\int_eval:n {\c@rownum - 1}] / belowsep } { #1 } + } + } + +%% Split table lines to cells and store them +%% #1: sequence of lines +\cs_new_protected:Npn \__tblr_split_lines_to_cells:N #1 + { + \seq_map_indexed_function:NN #1 \__tblr_split_one_line:nn + \LogTblrTracing { text } + } + +%% Split one line into cells and store them +%% #1: row number, #2 the line text +\cs_new_protected:Npn \__tblr_split_one_line:nn #1 #2 + { + \seq_set_split:Nnn \l_tmpa_seq { & } { #2 } + \int_set:Nn \c@rownum {#1} + \int_zero:N \c@colnum + \seq_map_inline:Nn \l_tmpa_seq + { + \tl_set:Nn \l_tmpa_tl { ##1 } + \__tblr_remove_braces:N \l_tmpa_tl + \int_incr:N \c@colnum + \__tblr_extract_table_commands:N \l_tmpa_tl + \__tblr_prop_gput:nxV {text} { [#1][\int_use:N \c@colnum] } \l_tmpa_tl + \__tblr_add_multicolumn_empty_cell: + } + %% Decrease row count by 1 if the last row has only one empty cell text + %% We need to do it here since the > or < column type may add text to cells + \bool_lazy_and:nnTF + { \int_compare_p:nNn {\c@colnum} = {1} } + { \tl_if_empty_p:N \l_tmpa_tl } + { \int_decr:N \c@rowcount } + { + \__tblr_prop_gput:nnx + {row} { [#1] / cell-number } { \int_use:N \c@colnum } + \int_compare:nT { \c@colnum > \c@colcount } + { + \int_set_eq:NN \c@colcount \c@colnum + } + } + } + +%% Add empty cells after the \multicolumn span cell +\cs_new_protected:Npn \__tblr_add_multicolumn_empty_cell: + { + \int_step_inline:nn { \l__multicolumn_cell_number_int - 1 } + { + \int_incr:N \c@colnum + \__tblr_prop_gput:nxn {text} + { [\int_use:N \c@rownum][\int_use:N \c@colnum] } { } + } + } + +%%% -------------------------------------------------------- +%% \section{Extract Table Commands from Cell Text} +%%% -------------------------------------------------------- + +%% Extract table commands defined with \NewTableCommand from cell text + +\clist_gset:Nn \g__tblr_table_commands_unbrace_next_clist {\multirow, \multicolumn} +\bool_new:N \l__tblr_table_command_unbrace_next_bool +\int_new:N \l__multicolumn_cell_number_int +\tl_new:N \l__tblr_saved_table_commands_before_cell_text_tl +\tl_new:N \l__tblr_saved_cell_text_after_table_commands_tl + +\cs_new_protected:Npn \__tblr_extract_table_commands:N #1 + { + \tl_clear:N \l__tblr_saved_table_commands_before_cell_text_tl + \tl_clear:N \l__tblr_saved_cell_text_after_table_commands_tl + \int_set:Nn \l__multicolumn_cell_number_int {1} + \exp_last_unbraced:NV \__tblr_extract_table_commands_next:w #1 \scan_stop: + \tl_if_empty:NF \l__tblr_saved_table_commands_before_cell_text_tl + { + \__tblr_prop_gput:nxV { command } + {[\int_use:N \c@rownum][\int_use:N \c@colnum]} + \l__tblr_saved_table_commands_before_cell_text_tl + } + \tl_set_eq:NN #1 \l__tblr_saved_cell_text_after_table_commands_tl + } + +%% #1 maybe a single token or multiple tokens given in braces +\cs_new_protected:Npn \__tblr_extract_table_commands_next:w #1 + { + \clist_if_in:NnTF \g__tblr_table_commands_clist { #1 } + { + \clist_if_in:NnTF \g__tblr_table_commands_unbrace_next_clist { #1 } + { \bool_set_true:N \l__tblr_table_command_unbrace_next_bool } + { \bool_set_false:N \l__tblr_table_command_unbrace_next_bool } + \token_if_eq_meaning:NNTF #1 \multicolumn + { \__tblr_extract_multicolumn_command:Nn #1 } + { \__tblr_extract_one_table_command:N #1 } + } + { + \tl_if_single_token:nTF {#1} + { + \token_if_eq_meaning:NNF #1 \scan_stop: + { \__tblr_save_real_cell_text:w #1 } + } + { \__tblr_save_real_cell_text:w {#1} } + } + } + +\cs_new_protected:Npn \__tblr_extract_multicolumn_command:Nn #1 #2 + { + \int_set:Nn \l__multicolumn_cell_number_int {#2} + \__tblr_extract_one_table_command:N #1 {#2} + } + +\cs_new_protected:Npn \__tblr_extract_one_table_command:N #1 + { + \int_set:Nn \l__tblr_a_int + { \cs:w g__tblr_table_cmd_ \cs_to_str:N #1 _arg_numb_tl \cs_end: } + \tl_put_right:Nn \l__tblr_saved_table_commands_before_cell_text_tl {#1} + \int_compare:nNnTF {\l__tblr_a_int} < {0} + { + \int_set:Nn \l__tblr_a_int { \int_abs:n {\l__tblr_a_int} - 1 } + \peek_charcode:NTF [ + { \__tblr_extract_table_command_arg_o:w } + { \__tblr_extract_table_command_arg_next: } + } + { \__tblr_extract_table_command_arg_next: } + } + +\cs_new_protected:Npn \__tblr_extract_table_command_arg_o:w [#1] + { + \tl_put_right:Nn \l__tblr_saved_table_commands_before_cell_text_tl { [#1] } + \__tblr_extract_table_command_arg_next: + } + +\cs_new_protected:Npn \__tblr_extract_table_command_arg_m:n #1 + { + \tl_put_right:Nn \l__tblr_saved_table_commands_before_cell_text_tl { {#1} } + \__tblr_extract_table_command_arg_next: + } + +\cs_new_protected:Npn \__tblr_extract_table_command_arg_next: + { + \int_compare:nNnTF {\l__tblr_a_int} > {0} + { + \int_decr:N \l__tblr_a_int + \__tblr_extract_table_command_arg_m:n + } + { + \bool_if:NTF \l__tblr_table_command_unbrace_next_bool + { \__tblr_last_unbraced:Nn \__tblr_extract_table_commands_next:w } + { \__tblr_extract_table_commands_next:w } + } + } + +\cs_new_protected:Npn \__tblr_last_unbraced:Nn #1 #2 { #1 #2 } + +%% The outermost set of braces of cell text #1 will be removed +\cs_new_protected:Npn \__tblr_save_real_cell_text:w #1 \scan_stop: + { + \tl_set:Nn \l__tblr_saved_cell_text_after_table_commands_tl {#1} + } + +%%% -------------------------------------------------------- +%% \section{Initial Table Specifications} +%%% -------------------------------------------------------- + +\prop_gset_from_keyval:Nn \g__tblr_default_tblr_table_prop + { + stretch = 1, + } + +\prop_gset_from_keyval:Nn \g__tblr_default_tblr_rows_prop + { + abovesep = 2pt, + belowsep = 2pt, + @row-height = 0pt, + @row-head = 0pt, + @row-foot = 0pt, + @row-upper = 0pt, + @row-lower = 0pt, + } + +\prop_gset_from_keyval:Nn \g__tblr_default_tblr_columns_prop + { + leftsep = 6pt, + rightsep = 6pt, + @col-width = 0pt, + } + +\prop_gset_from_keyval:Nn \g__tblr_default_tblr_cells_prop + { + halign = l, + valign = t, + } + +\prop_gset_from_keyval:Nn \g__tblr_default_tblr_hlines_prop + { + rulesep = 2pt, + } + +\prop_gset_from_keyval:Nn \g__tblr_default_tblr_vlines_prop + { + rulesep = 2pt, + } + +\cs_new_protected:Npn \__tblr_initial_table_spec: + { + \prop_map_inline:cn { g__tblr_default_ \l__tblr_env_name_tl _table_prop } + { + \__tblr_prop_gput:nxn { table } { ##1 } {##2} + } + \int_step_variable:nNn { \c@rowcount } \l__tblr_i_tl + { + \prop_map_inline:cn { g__tblr_default_ \l__tblr_env_name_tl _rows_prop } + { + \__tblr_prop_gput:nxn { row } { [\l__tblr_i_tl] / ##1 } {##2} + } + \prop_map_inline:cn { g__tblr_default_ \l__tblr_env_name_tl _hlines_prop } + { + \__tblr_prop_gput:nxn { hline } { [\l__tblr_i_tl] / ##1 } {##2} + } + \int_step_variable:nNn { \c@colcount } \l__tblr_j_tl + { + \prop_map_inline:cn + { g__tblr_default_ \l__tblr_env_name_tl _cells_prop } + { + \__tblr_prop_gput:nxn { cell } + { [\l__tblr_i_tl][\l__tblr_j_tl] / ##1 } {##2} + } + } + } + \prop_map_inline:cn { g__tblr_default_ \l__tblr_env_name_tl _hlines_prop } + { + \__tblr_prop_gput:nxn { hline } + { [\int_eval:n { \c@rowcount + 1}] / ##1 } {##2} + } + \int_step_variable:nNn { \c@colcount } \l__tblr_j_tl + { + \prop_map_inline:cn { g__tblr_default_ \l__tblr_env_name_tl _columns_prop } + { + \__tblr_prop_gput:nxn { column } { [\l__tblr_j_tl] / ##1 } {##2} + } + \prop_map_inline:cn { g__tblr_default_ \l__tblr_env_name_tl _vlines_prop } + { + \__tblr_prop_gput:nxn { vline } { [\l__tblr_j_tl] / ##1 } {##2} + } + } + \prop_map_inline:cn { g__tblr_default_ \l__tblr_env_name_tl _vlines_prop } + { + \__tblr_prop_gput:nxn { vline } + { [\int_eval:n { \c@colcount + 1}] / ##1 } {##2} + } + \keys_set:nv { tblr } { l__tblr_default_ \l__tblr_env_name_tl _tl } + } + +\tl_new:N \l__tblr_default_tblr_tl + +%% #1: env name; #2: options +\NewDocumentCommand \SetTabularrayDefault { O{tblr} m } + { + \tl_put_right:cn { l__tblr_default_ #1 _tl } { , #2 } + } +\cs_new_eq:NN \SetTblrDefault \SetTabularrayDefault + +%%% -------------------------------------------------------- +%% \section{Parse Table Specifications} +%%% -------------------------------------------------------- + +\clist_new:N \g__tblr_table_known_keys_clist +\clist_gset:Nn \g__tblr_table_known_keys_clist + { + colspec, rowspec, width, hspan, stretch, + column, row, cell, vline, hline, columns, rows, cells, vlines, hlines, + leftsep, rightsep, colsep, abovesep, belowsep, rowsep, + } + +\keys_define:nn { tblr } + { + colspec .code:n = \__tblr_parse_colrow_spec:nn { column } {#1}, + rowspec .code:n = \__tblr_parse_colrow_spec:nn { row } {#1}, + width .code:n = \__tblr_keys_gput:nx { width } { \dim_eval:n {#1} }, + hspan .code:n = \__tblr_keys_gput:nn { hspan } {#1}, + stretch .code:n = \__tblr_keys_gput:nn { stretch } {#1}, + columns .code:n = \__tblr_set_every_column_aux:n {#1}, + rows .code:n = \__tblr_set_every_row_aux:n {#1}, + cells .code:n = \__tblr_set_every_cell_aux:n {#1}, + hlines .code:n = \__tblr_set_every_hline_aux:n {#1}, + vlines .code:n = \__tblr_set_every_vline_aux:n {#1}, + leftsep .code:n = \tblr_set_every_column:nn { } { leftsep = #1 }, + rightsep .code:n = \tblr_set_every_column:nn { } { rightsep = #1 }, + colsep .meta:n = { leftsep = #1, rightsep = #1 }, + abovesep .code:n = \tblr_set_every_row:nn { } { abovesep = #1 }, + belowsep .code:n = \tblr_set_every_row:nn { } { belowsep = #1 }, + rowsep .meta:n = { abovesep = #1, belowsep = #1 }, + unknown .code:n = \__tblr_table_special_key:Vn \l_keys_key_str {#1}, + } + +\regex_const:Nn \c__tblr_split_key_name_regex { ^ ( [a-z] + ) ( . * ) } + +\cs_new_protected:Npn \__tblr_table_special_key:nn #1 #2 + { + \regex_extract_once:NnNT \c__tblr_split_key_name_regex {#1} \l_tmpa_seq + { + \tl_set:Nx \l__tblr_a_tl { \seq_item:Nn \l_tmpa_seq {2} } + \tl_set_rescan:Nnx \l__tblr_b_tl {} { \seq_item:Nn \l_tmpa_seq {3} } + \cs:w __tblr_set_ \l__tblr_a_tl _aux:Vn \cs_end: \l__tblr_b_tl {#2} + } + } +\cs_generate_variant:Nn \__tblr_table_special_key:nn { Vn } + +%% If the first key name is known, treat #1 is the table spec; +%% otherwise, treat #1 as colspec. + +\regex_const:Nn \c__tblr_first_key_name_regex { ^ \s * ( [A-Za-z\-] + ) } + +\cs_new_protected:Npn \__tblr_parse_table_spec:n #1 + { + \regex_extract_once:NnNTF \c__tblr_first_key_name_regex {#1} \l_tmpa_seq + { + \clist_if_in:NxTF \g__tblr_table_known_keys_clist + { \seq_item:Nn \l_tmpa_seq {2} } + { \keys_set:nn { tblr } {#1} } + { \__tblr_parse_colrow_spec:nn { column } {#1} } + } + { \__tblr_parse_colrow_spec:nn { column } {#1} } + } + +\cs_new_protected:Npn \__tblr_keys_gput:nn #1 #2 + { + \__tblr_prop_gput:nnn { table } {#1} {#2} + } +\cs_generate_variant:Nn \__tblr_keys_gput:nn { nx } + +%%% -------------------------------------------------------- +%% \section{Typeset and Calculate Sizes} +%%% -------------------------------------------------------- + +%% Calculate the width and height for every cell and border + +\cs_new_protected:Npn \__tblr_calc_cell_and_line_sizes: + { + \__tblr_make_strut_box: + \__tblr_calculate_line_sizes: + \__tblr_calculate_cell_sizes: + \LogTblrTracing { cell, row, column, hline, vline } + \__tblr_compute_extendable_column_width: + \__tblr_adjust_sizes_for_span_cells: + } + +%% make strut box from stretch option of the table + +\box_new:N \l__tblr_strut_ht_box +\box_new:N \l__tblr_strut_dp_box + +\cs_new_protected:Npn \__tblr_make_strut_box: + { + \tl_set:Nx \l__tblr_s_tl { \__tblr_prop_item:ne { table } { stretch } } + \hbox_set:Nn \l__tblr_strut_ht_box + { \vrule height \l__tblr_s_tl \box_ht:N \strutbox width ~ 0pt } + \hbox_set:Nn \l__tblr_strut_dp_box + { \vrule depth \l__tblr_s_tl \box_dp:N \strutbox width ~ 0pt } + } + +%% Calculate the thickness for every hline and vline +\cs_new_protected:Npn \__tblr_calculate_line_sizes: + { + %% We need these two counters in executing hline and vline commands + \int_zero:N \c@rownum + \int_zero:N \c@colnum + \int_step_inline:nn { \c@rowcount + 1 } + { + \int_incr:N \c@rownum + \int_zero:N \c@colnum + \int_step_inline:nn { \c@colcount + 1 } + { + \int_incr:N \c@colnum + \int_compare:nNnT { ##1 } < { \c@rowcount + 1 } + { + \__tblr_measure_and_update_vline_size:nn { ##1 } { ####1 } + } + \int_compare:nNnT { ####1 } < { \c@colcount + 1 } + { + \__tblr_measure_and_update_hline_size:nn { ##1 } { ####1 } + } + } + } + } + +%% Measure and update thickness of the vline +%% #1: row number, #2 column number +\cs_new_protected:Npn \__tblr_measure_and_update_vline_size:nn #1 #2 + { + \dim_zero:N \l__tblr_w_dim + \tl_set:Nx \l__tblr_n_tl + { \__tblr_prop_item:ne { vline } { [#2] / @vline-count } } + \tl_if_empty:NF \l__tblr_n_tl + { + \tl_set:Nx \l__tblr_s_tl + { \__tblr_prop_item:ne { vline } { [#2] / rulesep } } + \int_step_inline:nn { \l__tblr_n_tl } + { + \vbox_set_to_ht:Nnn \l__tblr_b_box {1pt} + { + \__tblr_get_vline_segment_child:nnnnn + {#1} {#2} {##1} {1pt} {1pt} + } + \tl_set:Nx \l__tblr_w_tl { \dim_eval:n { \box_wd:N \l__tblr_b_box } } + \__tblr_prop_gput_if_larger:nxx { vline } + { [#2](##1) / @vline-width } { \l__tblr_w_tl } + \dim_add:Nn \l__tblr_w_dim { \l__tblr_w_tl } + \dim_add:Nn \l__tblr_w_dim { \l__tblr_s_tl } + } + \dim_add:Nn \l__tblr_w_dim { - \l__tblr_s_tl } + } + \__tblr_prop_gput_if_larger:nxx { vline } + { [#2]/ @vline-width } { \dim_use:N \l__tblr_w_dim } + } + +%% Get text of a vline segment +%% #1: row number, #2: column number; #3: index number; #4: height; #5: depth +%% We put all code inside a group to avoid conflicts of local variables +\cs_new_protected:Npn \__tblr_get_vline_segment_child:nnnnn #1 #2 #3 #4 #5 + { + \group_begin: + \tl_set:Nx \l__tblr_w_tl + { \__tblr_prop_item:ne { vline } { [#1][#2](#3) / wd } } + \tl_if_empty:NF \l__tblr_w_tl { \dim_set:Nn \rulewidth { \l__tblr_w_tl } } + \tl_set:Nx \l__tblr_d_tl + { \__tblr_prop_item:ne { vline } { [#1][#2](#3) / @dash } } + \tl_set:Nx \l__tblr_a_tl { \tl_head:N \l__tblr_d_tl } + \tl_set:Nx \l__tblr_b_tl { \tl_tail:N \l__tblr_d_tl } + \exp_args:NV \tl_if_eq:NNTF \l__tblr_a_tl \@tblr@dash + { + \__tblr_get_vline_dash_style:N \l__tblr_b_tl + \xleaders \l__tblr_b_tl \vfil + } + { + \hbox_set:Nn \l__tblr_d_box { \l__tblr_b_tl } + \box_set_ht:Nn \l__tblr_d_box {#4} + \box_set_dp:Nn \l__tblr_d_box {#5} + \box_use:N \l__tblr_d_box + } + \group_end: + } +\cs_generate_variant:Nn \__tblr_get_vline_segment_child:nnnnn { nnnxx } + +%% Measure and update thickness of the hline +%% #1: row number, #2 column number +\cs_new_protected:Npn \__tblr_measure_and_update_hline_size:nn #1 #2 + { + \dim_zero:N \l__tblr_h_dim + \tl_set:Nx \l__tblr_n_tl + { \__tblr_prop_item:ne { hline } { [#1] / @hline-count } } + \tl_if_empty:NF \l__tblr_n_tl + { + \tl_set:Nx \l__tblr_s_tl + { \__tblr_prop_item:ne { hline } { [#1] / rulesep } } + \int_step_inline:nn { \l__tblr_n_tl } + { + \hbox_set_to_wd:Nnn \l__tblr_b_box {1pt} + { \__tblr_get_hline_segment_child:nnn {#1} {#2} {##1} } + \tl_set:Nx \l__tblr_h_tl + { + \dim_eval:n + { \box_ht:N \l__tblr_b_box + \box_dp:N \l__tblr_b_box } + } + \__tblr_prop_gput_if_larger:nxx { hline } + { [#1](##1) / @hline-height } { \l__tblr_h_tl } + \dim_add:Nn \l__tblr_h_dim { \l__tblr_h_tl } + \dim_add:Nn \l__tblr_h_dim { \l__tblr_s_tl } + } + \dim_add:Nn \l__tblr_h_dim { - \l__tblr_s_tl } + } + \__tblr_prop_gput_if_larger:nxx { hline } + { [#1] / @hline-height } { \dim_use:N \l__tblr_h_dim } + } + +%% Get text of a hline segment +%% #1: row number, #2: column number; #3: index number +\cs_new_protected:Npn \__tblr_get_hline_segment_child:nnn #1 #2 #3 + { + \group_begin: + \tl_set:Nx \l__tblr_w_tl + { \__tblr_prop_item:ne { hline } { [#1][#2](#3) / wd } } + \tl_if_empty:NF \l__tblr_w_tl { \dim_set:Nn \rulewidth { \l__tblr_w_tl } } + \tl_set:Nx \l__tblr_d_tl + { \__tblr_prop_item:ne { hline } { [#1][#2](#3) / @dash } } + \tl_set:Nx \l__tblr_a_tl { \tl_head:N \l__tblr_d_tl } + \tl_set:Nx \l__tblr_b_tl { \tl_tail:N \l__tblr_d_tl } + \exp_args:NV \tl_if_eq:NNTF \l__tblr_a_tl \@tblr@dash + { + \__tblr_get_hline_dash_style:N \l__tblr_b_tl + \xleaders \l__tblr_b_tl \hfil + } + { \l__tblr_b_tl \hfil } + \group_end: + } + +%% current cell alignments +\tl_new:N \g__tblr_cell_halign_tl +\tl_new:N \g__tblr_cell_valign_tl +\tl_new:N \g__tblr_cell_middle_tl + +\tl_const:Nn \c__tblr_valign_h_tl { h } +\tl_const:Nn \c__tblr_valign_m_tl { m } +\tl_const:Nn \c__tblr_valign_f_tl { f } +\tl_const:Nn \c__tblr_valign_t_tl { t } +\tl_const:Nn \c__tblr_valign_b_tl { b } + +\tl_const:Nn \c__tblr_middle_t_tl { t } +\tl_const:Nn \c__tblr_middle_m_tl { m } +\tl_const:Nn \c__tblr_middle_b_tl { b } + +%% #1: row number; #2: column number +\cs_new_protected:Npn \__tblr_get_cell_alignments:nn #1 #2 + { + \group_begin: + \tl_gset:Nx \g__tblr_cell_halign_tl + { \__tblr_prop_item:ne { cell } { [#1][#2] / halign } } + \tl_set:Nx \l__tblr_v_tl + { \__tblr_prop_item:ne { cell } { [#1][#2] / valign } } + \tl_case:NnF \l__tblr_v_tl + { + \c__tblr_valign_t_tl + { + \tl_gset:Nn \g__tblr_cell_valign_tl {m} + \tl_gset:Nn \g__tblr_cell_middle_tl {t} + } + \c__tblr_valign_m_tl + { + \tl_gset:Nn \g__tblr_cell_valign_tl {m} + \tl_gset:Nn \g__tblr_cell_middle_tl {m} + } + \c__tblr_valign_b_tl + { + \tl_gset:Nn \g__tblr_cell_valign_tl {m} + \tl_gset:Nn \g__tblr_cell_middle_tl {b} + } + } + { + \tl_gset_eq:NN \g__tblr_cell_valign_tl \l__tblr_v_tl + \tl_gclear:N \g__tblr_cell_middle_tl + } + \group_end: + } + +%% current cell dimensions +\dim_new:N \g__tblr_cell_wd_dim +\dim_new:N \g__tblr_cell_ht_dim +\dim_new:N \g__tblr_cell_head_dim +\dim_new:N \g__tblr_cell_foot_dim + +%% Calculate the width and height for every cell +\cs_new_protected:Npn \__tblr_calculate_cell_sizes: + { + %% You can use these two counters in cell text + \int_zero:N \c@rownum + \int_zero:N \c@colnum + \int_step_inline:nn { \c@rowcount } + { + \int_incr:N \c@rownum + \int_zero:N \c@colnum + \tl_set:Nx \l__tblr_h_tl + { \__tblr_prop_item:ne { row } { [\int_use:N \c@rownum] / height } } + \tl_if_empty:NF \l__tblr_h_tl + { + \__tblr_prop_gput:nxV { row } + { [\int_use:N \c@rownum] / @row-height } \l__tblr_h_tl + } + \int_step_inline:nn { \c@colcount } + { + \int_incr:N \c@colnum + \__tblr_measure_cell_update_sizes:nnNNNN + { \int_use:N \c@rownum } + { \int_use:N \c@colnum } + \g__tblr_cell_wd_dim + \g__tblr_cell_ht_dim + \g__tblr_cell_head_dim + \g__tblr_cell_foot_dim + } + } + } + +%% Measure and update natural dimensions of the row/column/cell +%% #1: row number; #2 column number; #3: width dimension; +%% #4: total height dimension; #5: head dimension; #6: foot dimension +\cs_new_protected:Npn \__tblr_measure_cell_update_sizes:nnNNNN #1 #2 #3 #4 #5 #6 + { + \__tblr_get_cell_alignments:nn {#1} {#2} + \hbox_set:Nn \l_tmpa_box { \__tblr_get_cell_text:nn {#1} {#2} } + \__tblr_update_cell_size:nnNNNN {#1} {#2} #3 #4 #5 #6 + \__tblr_update_row_size:nnNNN {#1} {#2} #4 #5 #6 + \__tblr_update_col_size:nN {#2} #3 + } + +%% #1: row number, #2: column number +\cs_new_protected:Npn \__tblr_get_cell_text:nn #1 #2 + { + \__tblr_prop_if_in:nxTF {cell} { [#1][#2] / omit } + { + \dim_gzero:N \g__tblr_cell_wd_dim + \dim_gzero:N \g__tblr_cell_ht_dim + \dim_gzero:N \g__tblr_cell_head_dim + \dim_gzero:N \g__tblr_cell_foot_dim + } + { \__tblr_get_cell_text_real:nn { #1 } { #2 } } + } + +%% Get cell text, #1: row number, #2: column number +%% If the width of the cell is not set, split it with \\ and compute the width +%% Therefore we always get a vbox for any cell +\cs_new_protected:Npn \__tblr_get_cell_text_real:nn #1 #2 + { + \group_begin: + \tl_set:Nx \l__tblr_c_tl { \__tblr_prop_item:ne {text} {[#1][#2]} } + \tl_set:Nx \l__tblr_w_tl + { \__tblr_prop_item:ne { cell } { [#1][#2] / width } } + \tl_if_empty:NT \l__tblr_w_tl + { + \__tblr_prop_if_in:nxF { cell } { [#1][#2] / colspan } + { + \tl_set:Nx \l__tblr_w_tl + { \__tblr_prop_item:ne { column } { [#2] / width } } + } + } + \tl_if_empty:NT \l__tblr_w_tl + { + \bool_if:NTF \l__tblr_math_mode_bool + { + \hbox_set:Nn \l_tmpa_box { $\l__tblr_c_tl$ } + \tl_set:Nx \l__tblr_w_tl { \box_wd:N \l_tmpa_box } + } + { + \tl_set_eq:NN \l_tmpb_tl \l__tblr_c_tl + \__tblr_insert_braces:N \l_tmpb_tl + \seq_set_split:NnV \l_tmpa_seq { \\ } \l_tmpb_tl + \tl_set:Nn \l__tblr_w_tl { 0pt } + \seq_map_variable:NNn \l_tmpa_seq \l_tmpa_tl + { + \__tblr_remove_braces:N \l_tmpa_tl + \hbox_set:Nn \l_tmpa_box { \l_tmpa_tl } + \tl_set:Nx \l__tblr_w_tl + { \dim_max:nn { \l__tblr_w_tl } { \box_wd:N \l_tmpa_box } } + } + } + } + \__tblr_get_vcell_and_sizes:NN \l__tblr_c_tl \l__tblr_w_tl + \group_end: + } + +%% #1: cell text; #2: box width +\cs_new_protected:Npn \__tblr_get_vcell_and_sizes:NN #1 #2 + { + \group_begin: + \vbox_set_top:Nn \l_tmpa_box { \__tblr_make_vcell_text:nN #1 #2 } + \vbox_set:Nn \l_tmpb_box { \__tblr_make_vcell_text:nN #1 #2 } + \dim_gset:Nn \g__tblr_cell_wd_dim { \box_wd:N \l_tmpb_box } + \dim_gset:Nn \g__tblr_cell_ht_dim + { \box_ht:N \l_tmpb_box + \box_dp:N \l_tmpb_box } + \dim_gset:Nn \g__tblr_cell_head_dim { \box_ht:N \l_tmpa_box } + \dim_gset:Nn \g__tblr_cell_foot_dim { \box_dp:N \l_tmpb_box } + \tl_case:Nn \g__tblr_cell_valign_tl + { + \c__tblr_valign_h_tl + { \box_use:N \l_tmpa_box } + \c__tblr_valign_m_tl + { + \tl_case:Nn \g__tblr_cell_middle_tl + { + \c__tblr_middle_t_tl + { \box_use:N \l_tmpa_box } + \c__tblr_middle_m_tl + { + \tl_set:Nx \l__tblr_b_tl + { + \dim_eval:n + { + ( \g__tblr_cell_ht_dim - \g__tblr_cell_head_dim + - \g__tblr_cell_foot_dim ) / 2 + } + } + \box_set_ht:Nn \l_tmpb_box + { \g__tblr_cell_head_dim + \l__tblr_b_tl } + \box_set_dp:Nn \l_tmpb_box + { \g__tblr_cell_foot_dim + \l__tblr_b_tl } + \box_use:N \l_tmpb_box + } + \c__tblr_middle_b_tl + { \box_use:N \l_tmpb_box } + } + } + \c__tblr_valign_f_tl + { \box_use:N \l_tmpb_box } + } + \group_end: + } + +\cs_new_eq:NN \__tlbr_halign_l: \raggedright +\cs_new_eq:NN \__tlbr_halign_c: \centering +\cs_new_eq:NN \__tlbr_halign_r: \raggedleft + +%% #1: cell text; #2: box width +\cs_new_protected:Npn \__tblr_make_vcell_text:nN #1 #2 + { + \dim_set:Nn \tex_hsize:D { #2 } + \@arrayparboxrestore + \cs:w __tlbr_halign_ \g__tblr_cell_halign_tl : \cs_end: + \mode_leave_vertical: + \box_use:N \l__tblr_strut_ht_box + \bool_if:NTF \l__tblr_math_mode_bool { $#1$ } { #1 } + \box_use:N \l__tblr_strut_dp_box + } + +%% #1: total height dimension; #2: head dimension; #3: foot dimension; +%% #4: tl for resulting upper size; #5: tl for resulting lower size + +\tl_new:N \l__tblr_middle_body_tl + +\cs_new_protected:Npn \__tblr_get_middle_cell_upper_lower:NNNNN #1 #2 #3 #4 #5 + { + \tl_case:Nn \g__tblr_cell_middle_tl + { + \c__tblr_middle_t_tl + { + \tl_set:Nx #4 { \dim_use:N #2 } + \tl_set:Nx #5 { \dim_eval:n { #1 - #2 } } + } + \c__tblr_middle_m_tl + { + \tl_set:Nx \l__tblr_middle_body_tl { \dim_eval:n { #1 - #2 - #3 } } + \tl_set:Nx #4 { \dim_eval:n { #2 + \l__tblr_middle_body_tl / 2 } } + \tl_set:Nx #5 { \dim_eval:n { #3 + \l__tblr_middle_body_tl / 2 } } + } + \c__tblr_middle_b_tl + { + \tl_set:Nx #4 { \dim_eval:n { #1 - #3 } } + \tl_set:Nx #5 { \dim_use:N #3 } + } + } + } + +%% Update natural dimensions of the cell +%% #1: row number; #2 column number; #3: width dimension; +%% #4: total height dimension; #5: head dimension; #6: foot dimension +\cs_new_protected:Npn \__tblr_update_cell_size:nnNNNN #1 #2 #3 #4 #5 #6 + { + \group_begin: + \tl_set:Nx \l__tblr_c_tl + { \__tblr_prop_item:ne {cell} { [#1][#2] / colspan } } + \tl_if_empty:NF \l__tblr_c_tl + { + \__tblr_prop_gput:nxx {cell} { [#1][#2] / @cell-width } { \dim_use:N #3 } + \dim_gzero:N #3 % don't affect column width + } + \tl_set:Nx \l__tblr_r_tl + { \__tblr_prop_item:ne {cell} { [#1][#2] / rowspan } } + \tl_if_empty:NF \l__tblr_r_tl + { + \tl_case:Nn \g__tblr_cell_valign_tl + { + \c__tblr_valign_h_tl + { + \tl_set:Nx \l__tblr_u_tl { \dim_use:N #5 } + \tl_set:Nx \l__tblr_v_tl { \dim_eval:n { #4 - #5 } } + %% Update the head size of the first span row here + \__tblr_prop_gput_if_larger:nxx { row } + { [#1] / @row-head } { \dim_use:N #5 } + } + \c__tblr_valign_f_tl + { + \tl_set:Nx \l__tblr_u_tl { \dim_eval:n { #4 - #6 } } + \tl_set:Nx \l__tblr_v_tl { \dim_use:N #6 } + %% Update the foot size of the last span row here + \__tblr_prop_gput_if_larger:nxx { row } + { [\int_eval:n { #1 + \l__tblr_r_tl - 1 }] / @row-foot } + { \dim_use:N #6 } + } + \c__tblr_valign_m_tl + { + \__tblr_get_middle_cell_upper_lower:NNNNN + #4 #5 #6 \l__tblr_u_tl \l__tblr_v_tl + } + } + \__tblr_prop_gput:nxV {cell} { [#1][#2] / @cell-height } \l__tblr_u_tl + \__tblr_prop_gput:nxV {cell} { [#1][#2] / @cell-depth } \l__tblr_v_tl + %% Don't affect row sizes + \dim_gzero:N #4 + \dim_gzero:N #5 + \dim_gzero:N #6 + } + \group_end: + } + + +%% Update size of the row. #1: row number; #2: column number; +%% #3: total height dimension; #4: head dimension; #5: foot dimension +\cs_new_protected:Npn \__tblr_update_row_size:nnNNN #1 #2 #3 #4 #5 + { + \group_begin: + %% Note that \l__tblr_h_tl may be empty + \tl_set:Nx \l__tblr_h_tl + { \__tblr_prop_item:ne { row } { [#1] / @row-height } } + \tl_if_eq:NNTF \g__tblr_cell_valign_tl \c__tblr_valign_m_tl + { + \tl_set:Nx \l__tblr_a_tl + { \__tblr_prop_item:ne { row } { [#1] / @row-upper } } + \tl_set:Nx \l__tblr_b_tl + { \__tblr_prop_item:ne { row } { [#1] / @row-lower } } + \__tblr_get_middle_cell_upper_lower:NNNNN + #3 #4 #5 \l__tblr_u_tl \l__tblr_v_tl + \dim_compare:nNnT { \l__tblr_u_tl } > { \l__tblr_a_tl } + { + \tl_set_eq:NN \l__tblr_a_tl \l__tblr_u_tl + \__tblr_prop_gput:nxV { row } + { [#1] / @row-upper } \l__tblr_a_tl + } + \dim_compare:nNnT { \l__tblr_v_tl } > { \l__tblr_b_tl } + { + \tl_set_eq:NN \l__tblr_b_tl \l__tblr_v_tl + \__tblr_prop_gput:nxV { row } + { [#1] / @row-lower } \l__tblr_b_tl + } + \dim_compare:nNnT + { \l__tblr_a_tl + \l__tblr_b_tl } > { \l__tblr_h_tl + 0pt } + { + \__tblr_prop_gput:nxx { row } { [#1] / @row-height } + { \dim_eval:n { \l__tblr_a_tl + \l__tblr_b_tl } } + } + } + { + \tl_set:Nx \l__tblr_e_tl + { \__tblr_prop_item:ne { row } { [#1] / @row-head } } + \tl_set:Nx \l__tblr_f_tl + { \__tblr_prop_item:ne { row } { [#1] / @row-foot } } + \dim_compare:nNnT {#4} > {\l__tblr_e_tl} + { + \__tblr_prop_gput:nxx { row } { [#1] / @row-head } { \dim_use:N #4 } + } + \dim_compare:nNnT {#5} > {\l__tblr_f_tl} + { + \__tblr_prop_gput:nxx { row } { [#1] / @row-foot } { \dim_use:N #5 } + } + \tl_set:Nx \l__tblr_x_tl { \dim_max:nn {#4} { \l__tblr_e_tl } } + \tl_set:Nx \l__tblr_y_tl { \dim_max:nn {#5} { \l__tblr_f_tl } } + \dim_compare:nNnT + { #3 - #4 - #5 } > { \l__tblr_h_tl - \l__tblr_x_tl - \l__tblr_y_tl } + { + \__tblr_prop_gput:nxx { row } { [#1] / @row-height } + { + \dim_eval:n + { + \l__tblr_x_tl + + \dim_use:N #3 - \dim_use:N #4 - \dim_use:N #5 + + \l__tblr_y_tl + } + } + } + } + \group_end: + } + + +%% Update size of the column. #1: column number; #2: width dimension + +\cs_new_protected:Npn \__tblr_update_col_size:nN #1 #2 + { + \tl_set:Nx \l_tmpb_tl + { \__tblr_prop_item:ne {column} { [#1] / @col-width } } + \bool_lazy_or:nnT + { \tl_if_empty_p:N \l_tmpb_tl } + { \dim_compare_p:nNn { \dim_use:N #2 } > { \l_tmpb_tl } } + { + \__tblr_prop_gput:nxx {column} { [#1] / @col-width } { \dim_use:N #2 } + } + } + +%%% -------------------------------------------------------- +%% \section{Calculate and Adjust Extendable Columns} +%%% -------------------------------------------------------- + +%% Compute column widths when there are some extendable columns + +\dim_new:N \l__column_target_dim +\prop_new:N \l__column_coefficient_prop +\prop_new:N \l__column_natural_width_prop +\prop_new:N \l__column_computed_width_prop + +\msg_new:nnn { tabularray } { table-width-too-small } + { Table ~ width ~ is ~ too ~ small, need ~ #1 ~ more! } + +\cs_new_protected:Npn \__tblr_compute_extendable_column_width: + { + \__tblr_collect_extendable_column_width: + \dim_compare:nNnTF { \l__column_target_dim } > { 0pt } + { + \prop_if_empty:NF \l__column_coefficient_prop + { \__tblr_adjust_extendable_column_width: } + } + { + \msg_warning:nnx { tabularray } { table-width-too-small } + { \dim_abs:n { \l__column_target_dim } } + } + } + +\cs_new_protected:Npn \__tblr_collect_extendable_column_width: + { + \tl_set:Nx \l_tmpa_tl { \__tblr_prop_item:nn {table} {width} } + \tl_if_empty:NTF \l_tmpa_tl + { \dim_set_eq:NN \l__column_target_dim \linewidth } + { \dim_set:Nn \l__column_target_dim { \l_tmpa_tl } } + \prop_clear:N \l__column_coefficient_prop + \prop_clear:N \l__column_natural_width_prop + \prop_clear:N \l__column_computed_width_prop + \int_step_variable:nNn { \c@colcount } \l__tblr_j_tl + { + \tl_set:Nx \l__tblr_a_tl + { \__tblr_prop_item:ne {column} { [\l__tblr_j_tl] / width } } + \tl_set:Nx \l__tblr_b_tl + { \__tblr_prop_item:ne {column} { [\l__tblr_j_tl] / coefficient } } + \tl_set:Nx \l__tblr_c_tl + { \__tblr_prop_item:ne {column} { [\l__tblr_j_tl] / @col-width } } + \tl_if_empty:NTF \l__tblr_a_tl + { + \tl_if_empty:NTF \l__tblr_b_tl + { \dim_sub:Nn \l__column_target_dim { \l__tblr_c_tl } } + { + \prop_put:Nxx \l__column_coefficient_prop + { \l__tblr_j_tl } { \l__tblr_b_tl } + \prop_put:Nxn \l__column_computed_width_prop + { \l__tblr_j_tl } { 0pt } + \dim_compare:nNnF { \l__tblr_b_tl pt } > { 0pt } + { + \prop_put:Nxx \l__column_natural_width_prop + { \l__tblr_j_tl } { \l__tblr_c_tl } + } + } + } + { \dim_sub:Nn \l__column_target_dim { \l__tblr_a_tl } } + \tl_set:Nx \l__tblr_a_tl + { \__tblr_prop_item:ne {vline} { [\l__tblr_j_tl] / @vline-width } } + \tl_set:Nx \l__tblr_b_tl + { \__tblr_prop_item:ne {column} { [\l__tblr_j_tl] / leftsep} } + \tl_set:Nx \l__tblr_c_tl + { \__tblr_prop_item:ne {column} { [\l__tblr_j_tl] / rightsep } } + \dim_set:Nn \l__column_target_dim + { \l__column_target_dim - \l__tblr_a_tl - \l__tblr_b_tl - \l__tblr_c_tl } + } + \tl_set:Nx \l__tblr_a_tl + { + \__tblr_prop_item:ne {vline} + { [\int_eval:n {\c@colcount + 1}] / @vline-width } + } + \tl_if_empty:NF \l__tblr_a_tl + { \dim_sub:Nn \l__column_target_dim { \l__tblr_a_tl } } + \LogTblrTracing { target } + } + +%% If all columns have negative coefficients and small natural widths, +%% \l__column_coefficient_prop will be empty after one or more rounds +\cs_new_protected:Npn \__tblr_adjust_extendable_column_width: + { + \bool_while_do:nn + { \dim_compare_p:nNn { \l__column_target_dim } > { \hfuzz } } + { + \prop_if_empty:NTF \l__column_coefficient_prop + { \__tblr_adjust_extendable_column_width_negative: } + { \__tblr_adjust_extendable_column_width_once: } + } + \prop_map_inline:Nn \l__column_computed_width_prop + { + \__tblr_prop_gput:nnx {column} { [##1] / width } { ##2 } + \__tblr_prop_gput:nnn {column} { [##1] / @col-width } { 0pt } + } + \__tblr_calculate_cell_sizes: + } + +%% We use dimen register, since the coefficient may be a decimal number +\cs_new_protected:Npn \__tblr_adjust_extendable_column_width_once: + { + \dim_zero:N \l_tmpa_dim + \prop_map_inline:Nn \l__column_coefficient_prop + { + \dim_add:Nn \l_tmpa_dim { \dim_abs:n { ##2 pt } } + } + \tl_set:Nx \l__tblr_w_tl + { \dim_ratio:nn { \l__column_target_dim } { \l_tmpa_dim } } + \dim_zero:N \l__column_target_dim + \prop_map_inline:Nn \l__column_coefficient_prop + { + \tl_set:Nx \l__tblr_a_tl + { \dim_eval:n { \dim_abs:n { ##2 pt } * \l__tblr_w_tl } } + \dim_compare:nNnTF { ##2 pt } > { 0pt } + { + \__tblr_add_dimen_value:Nnn + \l__column_computed_width_prop { ##1 } { \l__tblr_a_tl } + } + { + \tl_set:Nx \l__tblr_b_tl + { \prop_item:Nn \l__column_natural_width_prop { ##1 } } + \tl_set:Nx \l__tblr_c_tl + { \prop_item:Nn \l__column_computed_width_prop { ##1 } } + \dim_compare:nNnTF { \l__tblr_a_tl + \l__tblr_c_tl } > { \l__tblr_b_tl } + { + \prop_put:Nnx \l__column_computed_width_prop + { ##1 } { \l__tblr_b_tl } + \dim_add:Nn \l__column_target_dim + { \l__tblr_a_tl + \l__tblr_c_tl - \l__tblr_b_tl } + \prop_remove:Nn \l__column_coefficient_prop { ##1 } + } + { + \__tblr_add_dimen_value:Nnn + \l__column_computed_width_prop { ##1 } { \l__tblr_a_tl } + } + } + } + \LogTblrTracing { target } + } + +\cs_new_protected:Npn \__tblr_adjust_extendable_column_width_negative: + { + \dim_zero:N \l_tmpa_dim + \prop_map_inline:Nn \l__column_natural_width_prop + { \dim_add:Nn \l_tmpa_dim { ##2 } } + \tl_set:Nx \l_tmpa_tl + { \dim_ratio:nn { \l__column_target_dim } { \l_tmpa_dim } } + \dim_zero:N \l__column_target_dim + \prop_map_inline:Nn \l__column_natural_width_prop + { + \tl_set:Nx \l_tmpb_tl { \dim_eval:n { ##2 * \l_tmpa_tl } } + \__tblr_add_dimen_value:Nnn + \l__column_computed_width_prop { ##1 } { \l_tmpb_tl } + } + \LogTblrTracing { target } + } + +%%% -------------------------------------------------------- +%% \section{Calculate and Adjust Multispan Cells} +%%% -------------------------------------------------------- + +%% Compute and adjust widths when there are some span cells. +%% By default, we will compute column widths from span widths; +%% but if we set table option "hspan = minimal", +%% we will compute span widths from column widths. + +\cs_new_protected:Npn \__tblr_adjust_sizes_for_span_cells: + { + \__tblr_prop_if_in:nnT {table} {colspan} + { + \__tblr_collect_column_widths_skips: + \str_if_eq:xnTF + { \__tblr_prop_item:ne {table} {hspan} } {minimal} + { + \__tblr_set_span_widths_from_column_widths: + } + { + \__tblr_collect_span_widths: + \__tblr_set_column_widths_from_span_widths: + } + \LogTblrTracing {column} + \__tblr_calculate_cell_sizes: + } + \__tblr_prop_if_in:nnT {table} {rowspan} + { + \__tblr_collect_row_heights_skips: + \__tblr_collect_span_heights: + \__tblr_set_row_heights_from_span_heights: + \LogTblrTracing {row} + } + } + +\prop_new:N \l__tblr_col_item_skip_size_prop +\prop_new:N \l__tblr_col_span_size_prop +\prop_new:N \l__tblr_row_item_skip_size_prop +\prop_new:N \l__tblr_row_span_size_prop + +\cs_new_protected:Npn \__tblr_collect_column_widths_skips: + { + \prop_clear:N \l__tblr_col_item_skip_size_prop + \int_step_variable:nNn { \c@colcount } \l__tblr_j_tl + { + \int_compare:nNnTF { \l__tblr_j_tl } > { 1 } + { + \prop_put:Nxx \l__tblr_col_item_skip_size_prop { skip[\l__tblr_j_tl] } + { + \dim_eval:n + { + \__tblr_prop_item:ne {column} + { [\int_eval:n { \l__tblr_j_tl - 1 }] / rightsep } + + + \__tblr_prop_item:ne {vline} + { [\l__tblr_j_tl] / @vline-width } + + + \__tblr_prop_item:ne {column} + { [\l__tblr_j_tl] / leftsep} + } + } + } + { + \prop_put:Nxn \l__tblr_col_item_skip_size_prop { skip[\l__tblr_j_tl] } + { 0pt } + } + \prop_put:Nxx \l__tblr_col_item_skip_size_prop { item[\l__tblr_j_tl] } + { \__tblr_prop_item:ne {column} { [\l__tblr_j_tl] / @col-width } } + } + \__tblr_do_if_tracing:nn { cellspan } + { \prop_log:N \l__tblr_col_item_skip_size_prop } + } + +\cs_new_protected:Npn \__tblr_collect_row_heights_skips: + { + \prop_clear:N \l__tblr_row_item_skip_size_prop + \int_step_variable:nNn { \c@rowcount } \l__tblr_i_tl + { + \int_compare:nNnTF { \l__tblr_i_tl } > { 1 } + { + \prop_put:Nxx \l__tblr_row_item_skip_size_prop { skip[\l__tblr_i_tl] } + { + \dim_eval:n + { + \__tblr_prop_item:ne {row} + { [\int_eval:n {\l__tblr_i_tl - 1}] / belowsep } + + + \__tblr_prop_item:ne {hline} + { [\l__tblr_i_tl] / @hline-height } + + + \__tblr_prop_item:ne {row} + { [\l__tblr_i_tl] / abovesep } + } + } + } + { + \prop_put:Nxn \l__tblr_row_item_skip_size_prop { skip[\l__tblr_i_tl] } + { 0pt } + } + \__tblr_collect_one_row_height:NN \l__tblr_i_tl \l__tblr_h_tl + \prop_put:Nxx \l__tblr_row_item_skip_size_prop + { item[\l__tblr_i_tl] } { \l__tblr_h_tl } + } + \__tblr_do_if_tracing:nn { cellspan } + { \prop_log:N \l__tblr_row_item_skip_size_prop } + } + +%% #1: row number; #2: tl with result +\cs_new_protected:Npn \__tblr_collect_one_row_height:NN #1 #2 + { + \tl_set:Nx #2 { \__tblr_prop_item:ne { row } { [#1] / @row-height } } + } + +\cs_new_protected:Npn \__tblr_collect_span_widths: + { + \prop_clear:N \l__tblr_col_span_size_prop + \int_step_variable:nNn { \c@colcount } \l__tblr_j_tl + { + \int_step_variable:nNn { \c@rowcount } \l__tblr_i_tl + { + \tl_set:Nx \l__tblr_a_tl + { + \__tblr_prop_item:ne {cell} + { [\l__tblr_i_tl][\l__tblr_j_tl] / colspan } + } + \tl_if_empty:NF \l__tblr_a_tl + { + \__tblr_put_if_larger:Nxx \l__tblr_col_span_size_prop + { + ( \l__tblr_j_tl - + \int_eval:n {\l__tblr_j_tl + \l__tblr_a_tl - 1} ) + } + { + \__tblr_prop_item:ne {cell} + { [\l__tblr_i_tl][\l__tblr_j_tl] / @cell-width } + } + } + } + } + \__tblr_do_if_tracing:nn { cellspan } + { \prop_log:N \l__tblr_col_span_size_prop } + } + +\prop_new:N \l__tblr_row_span_to_row_prop + +\cs_new_protected:Npn \__tblr_collect_span_heights: + { + \prop_clear:N \l__tblr_row_span_to_row_prop + \prop_clear:N \l__tblr_row_span_size_prop + \int_step_variable:nNn { \c@rowcount } \l__tblr_i_tl + { + \int_step_variable:nNn { \c@colcount } \l__tblr_j_tl + { + \tl_set:Nx \l__tblr_a_tl + { + \__tblr_prop_item:ne {cell} + { [\l__tblr_i_tl][\l__tblr_j_tl] / rowspan } + } + \tl_if_empty:NF \l__tblr_a_tl + { + \tl_set:Nx \l__tblr_v_tl + { + \__tblr_prop_item:ne {cell} + { [\l__tblr_i_tl][\l__tblr_j_tl] / valign } + } + \tl_if_eq:NnT \l__tblr_v_tl { h } + { + \tl_set:Nx \l__tblr_h_tl + { + \__tblr_prop_item:ne {row} + { [\l__tblr_i_tl] / @row-head } + } + \__tblr_prop_gput:nxV {cell} + { [\l__tblr_i_tl][\l__tblr_j_tl] / @cell-height } + \l__tblr_h_tl + } + \tl_if_eq:NnT \l__tblr_v_tl { f } + { + \tl_set:Nx \l__tblr_d_tl + { + \__tblr_prop_item:ne {row} + { + [\int_eval:n {\l__tblr_i_tl + \l__tblr_a_tl - 1}] + / @row-foot + } + } + \__tblr_prop_gput:nxV {cell} + { [\l__tblr_i_tl][\l__tblr_j_tl] / @cell-depth } + \l__tblr_d_tl + } + \__tblr_put_if_larger:Nxx \l__tblr_row_span_size_prop + { + ( \l__tblr_i_tl - + \int_eval:n {\l__tblr_i_tl + \l__tblr_a_tl - 1} ) + } + { + \dim_eval:n + { + \__tblr_prop_item:ne {cell} + { [\l__tblr_i_tl][\l__tblr_j_tl] / @cell-height } + + + \__tblr_prop_item:ne {cell} + { [\l__tblr_i_tl][\l__tblr_j_tl] / @cell-depth } + } + } + \prop_put:Nxx \l__tblr_row_span_to_row_prop + { [\l__tblr_i_tl][\l__tblr_j_tl] } + { \int_eval:n {\l__tblr_i_tl + \l__tblr_a_tl - 1} } + } + } + } + \__tblr_do_if_tracing:nn { cellspan } + { + \prop_log:N \l__tblr_row_span_to_row_prop + \prop_log:N \l__tblr_row_span_size_prop + } + } + +%% Compute and set column widths from span widths +\cs_new_protected:Npn \__tblr_set_column_widths_from_span_widths: + { + \__tblr_calc_item_sizes_from_span_sizes:xNN + { \int_use:N \c@colcount } + \l__tblr_col_item_skip_size_prop + \l__tblr_col_span_size_prop + \__tblr_set_all_column_widths: + } + +%% Compute and set row heights from span heights +\cs_new_protected:Npn \__tblr_set_row_heights_from_span_heights: + { + \__tblr_calc_item_sizes_from_span_sizes:xNN + { \int_use:N \c@rowcount } + \l__tblr_row_item_skip_size_prop + \l__tblr_row_span_size_prop + \__tblr_set_all_row_heights: + } + +%% See page 245 in Chapter 22 of TeXbook +%% #1: total number of items +%% #2: prop list with item sizes and skip sizes; #3: prop list with span sizes +\cs_new_protected:Npn \__tblr_calc_item_sizes_from_span_sizes:nNN #1 #2 #3 + { + \int_step_variable:nNn { #1 } \l__tblr_j_tl + { + \dim_set:Nn \l__tblr_w_dim + { + \prop_item:Ne #2 { item[\l__tblr_j_tl] } + } + \int_step_variable:nNn { \l__tblr_j_tl - 1 } \l__tblr_i_tl + { + \tl_set:Nx \l__tblr_a_tl + { \prop_item:Ne #3 { (\l__tblr_i_tl-\l__tblr_j_tl) } } + \tl_if_empty:NF \l__tblr_a_tl + { + \int_step_variable:nnNn + { \l__tblr_i_tl } { \l__tblr_j_tl - 1 } \l__tblr_k_tl + { + \__tblr_do_if_tracing:nn { cellspan } + { + \tl_log:x + { \l__tblr_j_tl : \l__tblr_i_tl -> \l__tblr_k_tl } + } + \tl_set:Nx \l_tmpa_tl + { + \prop_item:Ne #2 { itemskip[\l__tblr_k_tl] } + } + \tl_set:Nx \l__tblr_a_tl + { \dim_eval:n { \l__tblr_a_tl - \l_tmpa_tl } } + } + \dim_compare:nNnT { \l__tblr_a_tl } > { \l__tblr_w_dim } + { + \dim_set:Nn \l__tblr_w_dim { \l__tblr_a_tl } + } + } + } + \prop_put:Nxx #2 + { item[\l__tblr_j_tl] } { \dim_use:N \l__tblr_w_dim } + \int_compare:nNnT { \l__tblr_j_tl } < { #1 } + { + \tl_set:Nx \l_tmpb_tl + { + \prop_item:Ne #2 + { skip[\int_eval:n { \l__tblr_j_tl + 1} ] } + } + \dim_add:Nn \l__tblr_w_dim { \l_tmpb_tl } + \prop_put:Nxx #2 + { itemskip[\l__tblr_j_tl] } { \dim_use:N \l__tblr_w_dim } + } + } + \__tblr_do_if_tracing:nn { cellspan } { \prop_log:N #2 } + } +\cs_generate_variant:Nn \__tblr_calc_item_sizes_from_span_sizes:nNN { x } + +\cs_new_protected:Npn \__tblr_set_all_column_widths: + { + \int_step_variable:nNn { \c@colcount } \l__tblr_j_tl + { + \__tblr_prop_gput:nxx {column} + { [\l__tblr_j_tl] / @col-width } + { \prop_item:Ne \l__tblr_col_item_skip_size_prop { item[\l__tblr_j_tl] } } + } + } + +\cs_new_protected:Npn \__tblr_set_all_row_heights: + { + \int_step_variable:nNn { \c@rowcount } \l__tblr_i_tl + { + \tl_set:Nx \l__tblr_h_tl + { + \__tblr_prop_item:ne {row} { [\l__tblr_i_tl] / @row-head } + } + \tl_set:Nx \l__tblr_d_tl + { + \__tblr_prop_item:ne {row} { [\l__tblr_i_tl] / @row-foot } + } + \tl_set:Nx \l__tblr_a_tl + { + \prop_item:Ne \l__tblr_row_item_skip_size_prop { item[\l__tblr_i_tl] } + } + \__tblr_collect_one_row_height:NN \l__tblr_i_tl \l__tblr_t_tl + \__tblr_prop_gput:nxx {row} + { [\l__tblr_i_tl] / @row-height } { \l__tblr_a_tl } + } + } + +\cs_new_protected:Npn \__tblr_get_span_key_row_col:w [#1][#2] + { + \tl_set:Nn \l__tblr_i_tl {#1} + \tl_set:Nn \l__tblr_j_tl {#2} + } + +%% Compute and set span widths from column widths +\cs_new_protected:Npn \__tblr_set_span_widths_from_column_widths: + { + \int_step_variable:nNn { \c@colcount } \l__tblr_j_tl + { + \int_step_variable:nNn { \c@rowcount } \l__tblr_i_tl + { + \tl_set:Nx \l__tblr_a_tl + { + \__tblr_prop_item:ne {cell} + { [\l__tblr_i_tl][\l__tblr_j_tl] / colspan } + } + \tl_if_empty:NF \l__tblr_a_tl + { + \__tblr_calc_span_widths:xxN + { \l__tblr_j_tl } + { \int_eval:n { \l__tblr_j_tl + \l__tblr_a_tl - 1 } } + \l__tblr_w_dim + \__tblr_prop_gput:nxx {cell} + { [\l__tblr_i_tl][\l__tblr_j_tl] / width } + { \dim_use:N \l__tblr_w_dim } + } + } + } + } + +%% Cell is spanned from col #1 to col #2, #3 is the return dim +\cs_new_protected:Npn \__tblr_calc_span_widths:nnN #1 #2 #3 + { + \dim_zero:N #3 + \int_step_inline:nnn { #1 } { #2 } + { + \tl_set:Nx \l_tmpa_tl + { \prop_item:Ne \l__tblr_col_item_skip_size_prop { skip[##1] } } + \tl_set:Nx \l_tmpb_tl + { \prop_item:Ne \l__tblr_col_item_skip_size_prop { item[##1] } } + \dim_add:Nn #3 { \dim_eval:n { \l_tmpa_tl + \l_tmpb_tl } } + } + } +\cs_generate_variant:Nn \__tblr_calc_span_widths:nnN { xxN } + +%%% -------------------------------------------------------- +%% \section{Build the Whole Table} +%%% -------------------------------------------------------- + +\tl_new:N \__tlbr_vbox_align_tl +\tl_const:Nn \__tlbr_vbox_t_tl {t} +\tl_const:Nn \__tlbr_vbox_m_tl {m} +\tl_const:Nn \__tlbr_vbox_c_tl {c} +\tl_const:Nn \__tlbr_vbox_b_tl {b} + +\box_new:N \l__tblr_table_box + +\cs_new_protected:Npn \__tblr_build_whole:n #1 + { + \vbox_set:Nn \l__tblr_table_box + { + \int_step_variable:nNn { \c@rowcount } \l__tblr_i_tl + { + \hbox:n { \__tblr_build_hline:V \l__tblr_i_tl } + \hrule height ~ 0pt % remove lineskip between hlines and rows + \hbox:n { \__tblr_build_row:N \l__tblr_i_tl } + \hrule height ~ 0pt + } + \hbox:n { \__tblr_build_hline:n { \int_eval:n {\c@rowcount + 1} } } + } + \__tblr_valign_whole:Nn \l__tblr_table_box #1 + } + +\cs_new_protected:Npn \__tblr_valign_whole:Nn #1 #2 + { + \group_begin: + \tl_set:Nn \__tlbr_vbox_align_tl {#2} + \dim_set:Nn \l__tblr_t_dim { \box_ht:N #1 + \box_dp:N #1 } + \tl_case:NnF \__tlbr_vbox_align_tl + { + \__tlbr_vbox_m_tl + { \__tblr_valign_whole_middle:N #1 } + \__tlbr_vbox_c_tl + { \__tblr_valign_whole_middle:N #1 } + \__tlbr_vbox_t_tl + { \__tblr_valign_whole_top:N #1 } + \__tlbr_vbox_b_tl + { \__tblr_valign_whole_bottom:N #1 } + } + { \__tblr_valign_whole_middle:N #1 } + \group_end: + } + +\cs_new_protected:Npn \__tblr_valign_whole_middle:N #1 + { + \hbox:n { $ \m@th \tex_vcenter:D { \vbox_unpack_drop:N #1 } $ } + } + +\cs_new_protected:Npn \__tblr_valign_whole_top:N #1 + { + \tl_set:Nx \l__tblr_a_tl + { \__tblr_prop_item:ne { hline } { [1] / @hline-height } } + %% Note that \l__tblr_b_tl may be empty + \tl_set:Nx \l__tblr_b_tl + { \__tblr_prop_item:ne { table } { baseline } } + \bool_lazy_or:nnTF + { \dim_compare_p:nNn { \l__tblr_a_tl } = { 0pt } } + { \int_compare_p:nNn { \l__tblr_b_tl + 0 } = { 1 } } + { + \dim_set:Nn \l__tblr_h_dim + { + \__tblr_prop_item:ne { row } { [1] / abovesep } + + + ( \__tblr_prop_item:ne { row } { [1] / @row-height } + + + \__tblr_prop_item:ne { row } { [1] / @row-upper } + - + \__tblr_prop_item:ne { row } { [1] / @row-lower } + ) / 2 + } + \dim_set:Nn \l__tblr_d_dim { \l__tblr_t_dim - \l__tblr_h_dim } + } + { + \dim_set:Nn \l__tblr_h_dim { 0pt } + \dim_set_eq:NN \l__tblr_d_dim \l__tblr_t_dim + } + \box_set_ht:Nn #1 { \l__tblr_h_dim } + \box_set_dp:Nn #1 { \l__tblr_d_dim } + \box_use_drop:N #1 + } + +\cs_new_protected:Npn \__tblr_valign_whole_bottom:N #1 + { + \tl_set:Nx \l__tblr_a_tl + { + \__tblr_prop_item:ne { hline } + { [\int_eval:n {\c@rowcount + 1}] / @hline-height } + } + %% Note that \l__tblr_b_tl may be empty + \tl_set:Nx \l__tblr_b_tl + { \__tblr_prop_item:ne { table } { baseline } } + \bool_lazy_or:nnTF + { \dim_compare_p:nNn { \l__tblr_a_tl } = { 0pt } } + { \int_compare_p:nNn { \l__tblr_b_tl + 0 } = { \c@rowcount } } + { + \dim_set:Nn \l__tblr_d_dim + { + ( \__tblr_prop_item:ne { row } + { [\int_use:N \c@rowcount] / @row-height } + - + \__tblr_prop_item:ne { row } + { [\int_use:N \c@rowcount] / @row-upper } + + + \__tblr_prop_item:ne { row } + { [\int_use:N \c@rowcount] / @row-lower } + ) / 2 + + + \__tblr_prop_item:ne { row } { [1] / belowsep } + } + \dim_set:Nn \l__tblr_h_dim { \l__tblr_t_dim - \l__tblr_d_dim } + } + { + \dim_set:Nn \l__tblr_d_dim { 0pt } + \dim_set_eq:NN \l__tblr_h_dim \l__tblr_t_dim + } + \box_set_ht:Nn #1 { \l__tblr_h_dim } + \box_set_dp:Nn #1 { \l__tblr_d_dim } + \box_use_drop:N #1 + } + +\dim_new:N \l__tblr_col_o_wd_dim +\dim_new:N \l__tblr_col_b_wd_dim + +%% Build hline. #1: row number +\cs_new_protected:Npn \__tblr_build_hline:n #1 + { + \int_step_inline:nn { \c@colcount } + { \__tblr_build_hline_segment:nn { #1 } { ##1 } } + } +\cs_generate_variant:Nn \__tblr_build_hline:n { x, V } + +%% #1: row number, #2: column number +\cs_new_protected:Npn \__tblr_build_hline_segment:nn #1 #2 + { + \tl_set:Nx \l__tblr_n_tl + { \__tblr_prop_item:ne { hline } { [#1] / @hline-count } } + \tl_set:Nx \l__tblr_o_tl + { \__tblr_prop_item:ne { hline } { [#1][#2] / omit } } + \__tblr_get_col_outer_width_border_width:nNN {#2} + \l__tblr_col_o_wd_dim \l__tblr_col_b_wd_dim + \tl_if_empty:NTF \l__tblr_o_tl + { + \tl_if_empty:NF \l__tblr_n_tl + { \__tblr_build_hline_segment_real:nn {#1} {#2} } + } + { \__tblr_build_hline_segment_omit:nn {#1} {#2} } + } + +%% #1: row number, #2: column number +\cs_new_protected:Npn \__tblr_build_hline_segment_omit:nn #1 #2 + { + \skip_horizontal:n { \l__tblr_col_o_wd_dim - \l__tblr_col_b_wd_dim } + } + +%% #1: row number, #2: column number +\cs_new_protected:Npn \__tblr_build_hline_segment_real:nn #1 #2 + { + \tl_set:Nx \l__tblr_s_tl + { \__tblr_prop_item:ne { hline } { [#1] / rulesep } } + \vbox_set:Nn \l__tblr_c_box + { + %% add an empty hbox to support vbox width + \tex_hbox:D to \l__tblr_col_o_wd_dim {} + \int_step_inline:nn { \l__tblr_n_tl } + { + \tl_set:Nx \l__tblr_h_tl + { \__tblr_prop_item:ne { hline } { [#1](##1) / @hline-height } } + \hrule height ~ 0pt % remove lineskip + \hbox_set_to_wd:Nnn \l__tblr_b_box { \l__tblr_col_o_wd_dim } + { + \tl_set:Nx \l__tblr_f_tl + { \__tblr_prop_item:ne { hline } { [#1][#2](##1) / fg } } + \tl_if_empty:NF \l__tblr_f_tl { \color{\l__tblr_f_tl} } + \__tblr_get_hline_segment_child:nnn {#1} {#2} {##1} + } + \box_set_ht:Nn \l__tblr_b_box { \l__tblr_h_tl } + \box_set_dp:Nn \l__tblr_b_box { 0pt } + \box_use:N \l__tblr_b_box + \skip_vertical:n { \l__tblr_s_tl } + } + \skip_vertical:n { - \l__tblr_s_tl } + } + \box_use:N \l__tblr_c_box + \skip_horizontal:n { - \l__tblr_col_b_wd_dim } + } + +%% Read from table specifications and calculate the widths of row and border +%% column outer width = content width + colsep width + border width +%% #1: the column number, #2: outer width, #3: border width +\cs_new_protected:Npn \__tblr_get_col_outer_width_border_width:nNN #1 #2 #3 + { + \dim_set:Nn #3 + { \__tblr_prop_item:ne {vline} { [\int_eval:n {#1 + 1}] / @vline-width } } + \dim_set:Nn #2 + { + \__tblr_prop_item:ne {vline} { [#1] / @vline-width } + + + \__tblr_prop_item:ne {column} { [#1] / leftsep } + + + \__tblr_prop_item:ne {column} { [#1] / @col-width } + + + \__tblr_prop_item:ne {column} { [#1] / rightsep } + + + #3 + } + } + +\dim_new:N \l__tblr_row_ht_dim +\dim_new:N \l__tblr_row_dp_dim +\dim_new:N \l__tblr_row_abovesep_dim +\dim_new:N \l__tblr_row_belowsep_dim + +%% Build current row, #1: row number +\cs_new_protected:Npn \__tblr_build_row:N #1 + { + \__tblr_get_row_inner_height_depth:VNNNN #1 + \l__tblr_row_ht_dim \l__tblr_row_dp_dim + \l__tblr_row_abovesep_dim \l__tblr_row_belowsep_dim + \vrule width ~ 0pt ~ height ~ \l__tblr_row_ht_dim ~ depth ~ \l__tblr_row_dp_dim + \int_step_variable:nNn { \c@colcount } \l__tblr_j_tl + { + \__tblr_build_vline_segment:nn {#1} { \l__tblr_j_tl } + \__tblr_build_cell:NN #1 \l__tblr_j_tl + } + \__tblr_build_vline_segment:nn {#1} { \int_eval:n {\c@colcount + 1} } + } + +%% Read from table specifications and calculate inner height/depth of the row +%% inner height = abovesep + above vspace + row upper +%% inner depth = row lower + below vspace + belowsep +%% #1: the row number; #2: resulting inner height; #3: resulting inner depth; +%% #4: restulting abovesep; #5: restulting belowsep. + +\dim_new:N \l__row_upper_dim +\dim_new:N \l__row_lower_dim +\dim_new:N \l__row_vpace_dim + +\cs_new_protected:Npn \__tblr_get_row_inner_height_depth:nNNNN #1 #2 #3 #4 #5 + { + \dim_set:Nn #4 + { \__tblr_prop_item:ne { row } { [#1] / abovesep } } + \dim_set:Nn #5 + { \__tblr_prop_item:ne { row } { [#1] / belowsep } } + \dim_set:Nn \l__row_upper_dim + { \__tblr_prop_item:ne { row } { [#1] / @row-upper } } + \dim_set:Nn \l__row_lower_dim + { \__tblr_prop_item:ne { row } { [#1] / @row-lower } } + \dim_set:Nn \l__row_vpace_dim + { + ( \__tblr_prop_item:ne { row } { [#1] / @row-height } + - \l__row_upper_dim - \l__row_lower_dim ) / 2 + } + \dim_set:Nn #2 { #4 + \l__row_vpace_dim + \l__row_upper_dim } + \dim_set:Nn #3 { \l__row_lower_dim + \l__row_vpace_dim + #5 } + } +\cs_generate_variant:Nn \__tblr_get_row_inner_height_depth:nNNNN { V } + +%% #1: row number, #2: column number +\cs_new_protected:Npn \__tblr_build_vline_segment:nn #1 #2 + { + \tl_set:Nx \l__tblr_n_tl + { \__tblr_prop_item:ne { vline } { [#2] / @vline-count } } + \tl_set:Nx \l__tblr_o_tl + { \__tblr_prop_item:ne { vline } { [#1][#2] / omit } } + \tl_if_empty:NTF \l__tblr_o_tl + { + \tl_if_empty:NF \l__tblr_n_tl + { \__tblr_build_vline_segment_real:nn {#1} {#2} } + } + { \__tblr_build_vline_segment_omit:nn {#1} {#2} } + } + +%% #1: row number, #2: column number +\cs_new_protected:Npn \__tblr_build_vline_segment_omit:nn #1 #2 + { + \tl_set:Nx \l__tblr_w_tl + { \__tblr_prop_item:ne { vline } { [#2] / @vline-width } } + \skip_horizontal:N \l__tblr_w_tl + } + +%% #1: row number, #2: column number +%% We make every vline segment intersect with first hline below +%% to remove gaps in vlines around multirow cells +\cs_new_protected:Npn \__tblr_build_vline_segment_real:nn #1 #2 + { + \tl_set:Nx \l__tblr_s_tl + { \__tblr_prop_item:ne { vline } { [#2] / rulesep } } + \tl_set:Nx \l__tblr_b_tl + { + \__tblr_prop_item:ne { hline } + { [\int_eval:n{#1 + 1}](1) / @hline-height } + } + \tl_if_empty:NT \l__tblr_b_tl { \tl_set:Nn \l__tblr_b_tl { 0pt } } + \hbox_set:Nn \l__tblr_a_box + { + \int_step_inline:nn { \l__tblr_n_tl } + { + \tl_set:Nx \l__tblr_w_tl + { \__tblr_prop_item:ne { vline } { [#2](##1) / @vline-width } } + \vbox_set_to_ht:Nnn \l__tblr_b_box + { \dim_eval:n { \l__tblr_row_ht_dim + \l__tblr_row_dp_dim } } + { + \tl_set:Nx \l__tblr_f_tl + { \__tblr_prop_item:ne { vline } { [#1][#2](##1) / fg } } + \tl_if_empty:NF \l__tblr_f_tl { \color{\l__tblr_f_tl} } + \__tblr_get_vline_segment_child:nnnxx {#1} {#2} {##1} + { \dim_eval:n { \l__tblr_row_ht_dim } } + { \dim_eval:n { \l__tblr_row_dp_dim + \l__tblr_b_tl } } + \skip_vertical:n { - \l__tblr_b_tl } + } + \box_set_wd:Nn \l__tblr_b_box { \l__tblr_w_tl } + \box_use:N \l__tblr_b_box + \skip_horizontal:n { \l__tblr_s_tl } + } + \skip_horizontal:n { - \l__tblr_s_tl } + } + \vbox_set:Nn \l__tblr_c_box { \box_use:N \l__tblr_a_box } + \box_set_ht:Nn \l__tblr_c_box { \dim_use:N \l__tblr_row_ht_dim } + \box_set_dp:Nn \l__tblr_c_box { \dim_use:N \l__tblr_row_dp_dim } + \box_use:N \l__tblr_c_box + } + +\tl_new:N \l__tblr_cell_rowspan_tl +\tl_new:N \l__tblr_cell_colspan_tl +\dim_new:N \l__tblr_cell_wd_dim +\dim_new:N \l__tblr_cell_ht_dim + +\cs_new_protected:Npn \__tblr_build_cell:NN #1 #2 + { + \int_set:Nn \c@rownum {#1} + \int_set:Nn \c@colnum {#2} + \group_begin: + \tl_set:Nx \l__tblr_w_tl + { \__tblr_prop_item:ne { column } { [#2] / @col-width } } + \tl_set:Nx \l__tblr_h_tl + { \__tblr_prop_item:ne { row } { [#1] / @row-height } } + \tl_set:Nx \l__tblr_x_tl + { \__tblr_prop_item:ne { column } { [#2] / leftsep} } + \tl_set:Nx \l__tblr_y_tl + { \__tblr_prop_item:ne { column } { [#2] / rightsep } } + \tl_set:Nx \l__tblr_cell_colspan_tl + { \__tblr_prop_item:ne { cell } { [#1][#2] / colspan } } + \tl_if_empty:NTF \l__tblr_cell_colspan_tl + { \dim_set:Nn \l__tblr_cell_wd_dim { \l__tblr_w_tl } } + { + \__tblr_get_span_horizontal_sizes:NNNNN #1 #2 + \l__tblr_o_dim \l__tblr_cell_wd_dim \l__tblr_q_dim + } + \tl_set:Nx \l__tblr_cell_rowspan_tl + { \__tblr_prop_item:ne { cell } { [#1][#2] / rowspan } } + \tl_if_empty:NTF \l__tblr_cell_rowspan_tl + { \dim_set:Nn \l__tblr_cell_ht_dim { \l__tblr_h_tl } } + { + \__tblr_get_span_vertical_sizes:NNNNN #1 #2 + \l__tblr_r_dim \l__tblr_cell_ht_dim \l__tblr_t_dim + } + \__tblr_get_cell_alignments:nn {#1} {#2} + \__tblr_build_cell_background:NN #1 #2 + \__tblr_build_cell_content:NN #1 #2 + \group_end: + } + +\cs_new_protected:Npn \__tblr_build_cell_content:NN #1 #2 + { + \hbox_set_to_wd:Nnn \l__tblr_a_box { \l__tblr_cell_wd_dim } + { + \tl_if_eq:NnF \g__tblr_cell_halign_tl {l} { \hfil } + \__tblr_get_cell_text:nn {#1} {#2} + \tl_if_eq:NnF \g__tblr_cell_halign_tl {r} { \hfil } + } + \vbox_set_to_ht:Nnn \l__tblr_b_box { \l__tblr_cell_ht_dim } + { + \tl_case:Nn \g__tblr_cell_valign_tl + { + \c__tblr_valign_m_tl + { + \vfil + \tl_if_empty:NT \l__tblr_cell_rowspan_tl + { + \box_set_ht:Nn \l__tblr_a_box + { \__tblr_prop_item:ne { row } { [#1] / @row-upper } } + \box_set_dp:Nn \l__tblr_a_box + { \__tblr_prop_item:ne { row } { [#1] / @row-lower } } + } + \box_use:N \l__tblr_a_box + \vfil + } + \c__tblr_valign_h_tl + { + \box_set_ht:Nn \l__tblr_a_box + { \__tblr_prop_item:ne { row } { [#1] / @row-head } } + \box_use:N \l__tblr_a_box + \vfil + } + \c__tblr_valign_f_tl + { + \vfil + \tl_if_empty:NTF \l__tblr_cell_rowspan_tl + { + \box_set_dp:Nn \l__tblr_a_box + { \__tblr_prop_item:ne { row } { [#1] / @row-foot } } + } + { + \box_set_dp:Nn \l__tblr_a_box + { + \__tblr_prop_item:ne { row } + { + [\int_eval:n {#1 + \l__tblr_cell_rowspan_tl - 1}] + / @row-foot + } + } + } + \box_use:N \l__tblr_a_box + } + } + \hrule height ~ 0pt %% zero depth + } + \vbox_set_to_ht:Nnn \l__tblr_c_box + { \l__tblr_row_ht_dim - \l__tblr_row_abovesep_dim } + { + \box_use:N \l__tblr_b_box + \vss + } + \skip_horizontal:n { \l__tblr_x_tl } + \box_use:N \l__tblr_c_box + \skip_horizontal:n { \l__tblr_y_tl - \l__tblr_cell_wd_dim + \l__tblr_w_tl } + } + +\cs_new_protected:Npn \__tblr_build_cell_background:NN #1 #2 + { + \__tblr_prop_if_in:nxF {cell} { [#1][#2] / omit } + { + \group_begin: + \tl_set:Nx \l__tblr_b_tl + { \__tblr_prop_item:ne { cell } { [#1][#2] / background } } + \tl_if_empty:NF \l__tblr_b_tl + { + \__tblr_get_cell_background_width:NNN #1 #2 \l_tmpa_dim + \__tblr_get_cell_background_depth:NNN #1 #2 \l_tmpb_dim + \__tblr_build_cell_background:nnnn + { \dim_use:N \l_tmpa_dim } + { \l__tblr_row_ht_dim } + { \dim_use:N \l_tmpb_dim } + { \l__tblr_b_tl } + } + \group_end: + } + } + +%% #1: row number; #2: column number; #3 resulting dimension +\cs_new_protected:Npn \__tblr_get_cell_background_width:NNN #1 #2 #3 + { + \tl_if_empty:NTF \l__tblr_cell_colspan_tl + { \dim_set:Nn #3 { \l__tblr_x_tl + \l__tblr_w_tl + \l__tblr_y_tl } } + { + \dim_set:Nn #3 { \l__tblr_o_dim + \l__tblr_cell_wd_dim + \l__tblr_q_dim } + } + } + +%% #1: row number; #2: column number; #3 resulting dimension +\cs_new_protected:Npn \__tblr_get_cell_background_depth:NNN #1 #2 #3 + { + \tl_if_empty:NTF \l__tblr_cell_rowspan_tl + { \dim_set_eq:NN #3 \l__tblr_row_dp_dim } + { + \dim_set:Nn #3 + { + \l__tblr_r_dim + \l__tblr_cell_ht_dim + + \l__tblr_t_dim - \l__tblr_row_ht_dim + } + } + } + +%% #1: width, #2: height, #3: depth, #4: color +\cs_new_protected:Npn \__tblr_build_cell_background:nnnn #1 #2 #3 #4 + { + \hbox_set:Nn \l__tblr_a_box + { + \color {#4} + \vrule width ~ #1 ~ height ~ #2 ~ depth ~ #3 + } + \box_set_dp:Nn \l__tblr_a_box { 0pt } + \box_use:N \l__tblr_a_box + \skip_horizontal:n { - #1 } + } + +%% #1: row number; #2: column number; #3: dimen register for rowsep above. +%% #4: dimen register for total height; #5: dimen register for rowsep below. +%% We can use \l__tblr_row_item_skip_size_prop which was made before +\cs_new_protected:Npn \__tblr_get_span_vertical_sizes:NNNNN #1 #2 #3 #4 #5 + { + \dim_set:Nn #3 + { \__tblr_prop_item:ne { row } { [#1] / abovesep } } + \dim_zero:N #4 + \int_step_inline:nnn { #1 } { #1 + \l__tblr_cell_rowspan_tl - 2 } + { + \dim_add:Nn #4 + { \prop_item:Ne \l__tblr_row_item_skip_size_prop { itemskip[##1] } } + } + \dim_add:Nn #4 + { + \prop_item:Ne \l__tblr_row_item_skip_size_prop + { item[\int_eval:n { #1 + \l__tblr_cell_rowspan_tl - 1 }] } + } + \dim_set:Nn #5 + { + \__tblr_prop_item:ne { row } + { [\int_eval:n {#1 + \l__tblr_cell_rowspan_tl - 1}] / belowsep } + } + %\tl_log:x { cell[#1][#2] ~:~ \dim_use:N #3, \dim_use:N #4, \dim_use:N #5 } + } + +%% #1: row number; #2: column number; #3: dimen register for colsep left. +%% #4: dimen register for total width; #5: dimen register for colsep right. +%% We can use \l__tblr_col_item_skip_size_prop which was made before +%% But when hspan=minimal, there are no itemskip in the prop list. +%% Therefore we need to calculate them from the sizes of items and skips +\cs_new_protected:Npn \__tblr_get_span_horizontal_sizes:NNNNN #1 #2 #3 #4 #5 + { + \dim_set:Nn #3 + { \__tblr_prop_item:ne { column } { [#2] / leftsep} } + \dim_zero:N #4 + \int_step_inline:nnn { #2 } { #2 + \l__tblr_cell_colspan_tl - 2 } + { + \dim_add:Nn #4 + { \prop_item:Ne \l__tblr_col_item_skip_size_prop { item[##1] } } + \dim_add:Nn #4 + { + \prop_item:Ne \l__tblr_col_item_skip_size_prop + { skip[\int_eval:n { ##1 + 1 }] } + } + } + \dim_add:Nn #4 + { + \prop_item:Ne \l__tblr_col_item_skip_size_prop + { item[\int_eval:n { #2 + \l__tblr_cell_colspan_tl - 1 }] } + } + \dim_set:Nn #5 + { + \__tblr_prop_item:ne { column } + { [\int_eval:n {#2 + \l__tblr_cell_colspan_tl - 1}] / rightsep } + } + %\tl_log:x { cell[#1][#2] ~:~ \dim_use:N #3, \dim_use:N #4, \dim_use:N #5 } + } + +%%% -------------------------------------------------------- +%% \section{Tracing Tabularray} +%%% -------------------------------------------------------- + +\NewDocumentCommand \SetTabularrayTracing { m } + { + \keys_set:nn { tblr-set-tracing } {#1} + } +\cs_new_eq:NN \SetTblrTracing \SetTabularrayTracing + +\bool_new:N \g__tblr_tracing_text_bool +\bool_new:N \g__tblr_tracing_command_bool +\bool_new:N \g__tblr_tracing_table_bool +\bool_new:N \g__tblr_tracing_column_bool +\bool_new:N \g__tblr_tracing_row_bool +\bool_new:N \g__tblr_tracing_cell_bool +\bool_new:N \g__tblr_tracing_vline_bool +\bool_new:N \g__tblr_tracing_hline_bool +\bool_new:N \g__tblr_tracing_colspec_bool +\bool_new:N \g__tblr_tracing_rowspec_bool +\bool_new:N \g__tblr_tracing_target_bool +\bool_new:N \g__tblr_tracing_cellspan_bool + +\keys_define:nn { tblr-set-tracing } + { + +text .code:n = \bool_gset_true:N \g__tblr_tracing_text_bool, + -text .code:n = \bool_gset_false:N \g__tblr_tracing_text_bool, + +command .code:n = \bool_gset_true:N \g__tblr_tracing_command_bool, + -command .code:n = \bool_gset_false:N \g__tblr_tracing_command_bool, + +table .code:n = \bool_gset_true:N \g__tblr_tracing_table_bool, + -table .code:n = \bool_gset_false:N \g__tblr_tracing_table_bool, + +column .code:n = \bool_gset_true:N \g__tblr_tracing_column_bool, + -column .code:n = \bool_gset_false:N \g__tblr_tracing_column_bool, + +row .code:n = \bool_gset_true:N \g__tblr_tracing_row_bool, + -row .code:n = \bool_gset_false:N \g__tblr_tracing_row_bool, + +cell .code:n = \bool_gset_true:N \g__tblr_tracing_cell_bool, + -cell .code:n = \bool_gset_false:N \g__tblr_tracing_cell_bool, + +vline .code:n = \bool_gset_true:N \g__tblr_tracing_vline_bool, + -vline .code:n = \bool_gset_false:N \g__tblr_tracing_vline_bool, + +hline .code:n = \bool_gset_true:N \g__tblr_tracing_hline_bool, + -hline .code:n = \bool_gset_false:N \g__tblr_tracing_hline_bool, + +colspec .code:n = \bool_gset_true:N \g__tblr_tracing_colspec_bool, + -colspec .code:n = \bool_gset_false:N \g__tblr_tracing_colspec_bool, + +rowspec .code:n = \bool_gset_true:N \g__tblr_tracing_rowspec_bool, + -rowspec .code:n = \bool_gset_false:N \g__tblr_tracing_rowspec_bool, + +target .code:n = \bool_gset_true:N \g__tblr_tracing_target_bool, + -target .code:n = \bool_gset_false:N \g__tblr_tracing_target_bool, + +cellspan .code:n = \bool_gset_true:N \g__tblr_tracing_cellspan_bool, + -cellspan .code:n = \bool_gset_false:N \g__tblr_tracing_cellspan_bool, + all .code:n = \__tblr_enable_all_tracings:, + none .code:n = \__tblr_disable_all_tracings:, + } + +\cs_new_protected_nopar:Npn \__tblr_enable_all_tracings: + { + \bool_gset_true:N \g__tblr_tracing_text_bool + \bool_gset_true:N \g__tblr_tracing_command_bool + \bool_gset_true:N \g__tblr_tracing_table_bool + \bool_gset_true:N \g__tblr_tracing_column_bool + \bool_gset_true:N \g__tblr_tracing_row_bool + \bool_gset_true:N \g__tblr_tracing_cell_bool + \bool_gset_true:N \g__tblr_tracing_vline_bool + \bool_gset_true:N \g__tblr_tracing_hline_bool + \bool_gset_true:N \g__tblr_tracing_colspec_bool + \bool_gset_true:N \g__tblr_tracing_rowspec_bool + \bool_gset_true:N \g__tblr_tracing_target_bool + \bool_gset_true:N \g__tblr_tracing_cellspan_bool + } + +\cs_new_protected_nopar:Npn \__tblr_disable_all_tracings: + { + \bool_gset_false:N \g__tblr_tracing_text_bool + \bool_gset_false:N \g__tblr_tracing_command_bool + \bool_gset_false:N \g__tblr_tracing_table_bool + \bool_gset_false:N \g__tblr_tracing_column_bool + \bool_gset_false:N \g__tblr_tracing_row_bool + \bool_gset_false:N \g__tblr_tracing_cell_bool + \bool_gset_false:N \g__tblr_tracing_vline_bool + \bool_gset_false:N \g__tblr_tracing_hline_bool + \bool_gset_false:N \g__tblr_tracing_colspec_bool + \bool_gset_false:N \g__tblr_tracing_rowspec_bool + \bool_gset_false:N \g__tblr_tracing_target_bool + \bool_gset_false:N \g__tblr_tracing_cellspan_bool + } + +\NewDocumentCommand \LogTabularrayTracing { m } + { + \keys_set:nn { tblr-log-tracing } {#1} + } +\cs_new_eq:NN \LogTblrTracing \LogTabularrayTracing + +\keys_define:nn { tblr-log-tracing } + { + unknown .code:n = \__tblr_log_tracing:N \l_keys_key_str + } + +\cs_new_protected:Npn \__tblr_log_tracing:N #1 + { + \bool_if:cT { g__tblr_tracing_ #1 _bool } + { \cs:w __tblr_log_tracing _ #1 : \cs_end: } + } + +\cs_new_protected:Npn \__tblr_log_tracing_text: + { + \__tblr_prop_log:n { text } + } + +\cs_new_protected:Npn \__tblr_log_tracing_command: + { + \__tblr_prop_log:n { command } + } + +\cs_new_protected:Npn \__tblr_log_tracing_table: + { + \__tblr_prop_log:n { table } + } + +\cs_new_protected:Npn \__tblr_log_tracing_column: + { + \__tblr_prop_log:n { column } + } + +\cs_new_protected:Npn \__tblr_log_tracing_row: + { + \__tblr_prop_log:n { row } + } + +\cs_new_protected:Npn \__tblr_log_tracing_cell: + { + \__tblr_prop_log:n { cell } + } + +\cs_new_protected:Npn \__tblr_log_tracing_vline: + { + \__tblr_prop_log:n { vline } + } + +\cs_new_protected:Npn \__tblr_log_tracing_hline: + { + \__tblr_prop_log:n { hline } + } + +\cs_new_protected:Npn \__tblr_log_tracing_colspec: + { + \tl_if_eq:NnT \g__tblr_column_or_row_tl { column } + { \tl_log:N \g__tblr_expanded_colrow_spec_tl } + } + +\cs_new_protected:Npn \__tblr_log_tracing_rowspec: + { + \tl_if_eq:NnT \g__tblr_column_or_row_tl { row } + { \tl_log:N \g__tblr_expanded_colrow_spec_tl } + } + +\cs_new_protected:Npn \__tblr_log_tracing_target: + { + \dim_log:N \l__column_target_dim + \prop_log:N \l__column_coefficient_prop + \prop_log:N \l__column_natural_width_prop + \prop_log:N \l__column_computed_width_prop + } + +\cs_new_protected:Npn \__tblr_log_tracing_cellspan: + { + \prop_log:N \l__tblr_col_item_skip_size_prop + \prop_log:N \l__tblr_col_span_size_prop + \prop_log:N \l__tblr_row_item_skip_size_prop + \prop_log:N \l__tblr_row_span_size_prop + \prop_log:N \l__tblr_row_span_to_row_prop + } + +\cs_new_protected:Npn \__tblr_do_if_tracing:nn #1 #2 + { + \bool_if:cT { g__tblr_tracing_ #1 _bool } {#2} + } + +\ExplSyntaxOff + diff --git a/macros/latex/contrib/tabularray/tabularray.tex b/macros/latex/contrib/tabularray/tabularray.tex new file mode 100644 index 0000000000..41aa2ab9f6 --- /dev/null +++ b/macros/latex/contrib/tabularray/tabularray.tex @@ -0,0 +1,751 @@ +% -*- coding: utf-8 -*- +\documentclass[oneside]{book} +\usepackage[a4paper,margin=2.5cm]{geometry} + +\usepackage{codehigh} % https://github.com/lvjr/codehigh +%\usepackage{arev} +\usepackage{tabularray} +\usepackage{array,multirow,amsmath} + +\usepackage{hyperref} +\hypersetup{ + colorlinks=true, + urlcolor=blue3, + linkcolor=red3, +} +\renewcommand*{\thefootnote}{*} + +\newcommand*{\myversion}{2021H} +\newcommand*{\mydate}{Version \myversion\ (\the\year-\mylpad\month-\mylpad\day)\\\myrepo} +\newcommand*{\myrepo}{\url{https://github.com/lvjr/tabularray}} +\newcommand*{\mylpad}[1]{\ifnum#1<10 0\the#1\else\the#1\fi} + +\colorlet{highback}{\ifcase\month\or + red9\or brown9\or yellow9\or cyan9\or azure9\or blue9\or + red9\or brown9\or yellow9\or cyan9\or azure9\or blue9\fi +} +\CodeHigh{language=latex/table,style/main=highback,style/code=highback} +\NewCodeHighEnv{code}{style/main=gray9,style/code=gray9} +\NewCodeHighEnv{demo}{style/main=gray9,style/code=gray9,demo} + +%\CodeHigh{lite} + +\begin{document} + +\title{\sffamily\color{red3}Tabularray: Typeset Tabulars and Arrays with \LaTeX3} +\author{Jianrui Lyu (tolvjr@163.com)} +\date{\mydate} +\maketitle + +\tableofcontents + +\chapter{From Old to New} + +\section{Vertical Space} + +After loading \verb!tabularrray! package in the preamble, +we can use \verb!tblr! environments to typeset tabulars and arrays. +The name \verb!tblr! is short for \verb!tabularray! or \verb!top-bottom-left-right!. +The following is our first example: + +\begin{demo} +\begin{tabular}{lccr} +\hline + Alpha & Beta & Gamma & Delta \\ +\hline + Epsilon & Zeta & Eta & Theta \\ +\hline + Iota & Kappa & Lambda & Mu \\ +\hline +\end{tabular} +\end{demo} + +\begin{demohigh} +\begin{tblr}{lccr} +\hline + Alpha & Beta & Gamma & Delta \\ +\hline + Epsilon & Zeta & Eta & Theta \\ +\hline + Iota & Kappa & Lambda & Mu \\ +\hline +\end{tblr} +\end{demohigh} + +You may notice that there is extra space above and below the table rows with \verb!tblr! envirenment. +This space makes the table look better. +If you don't like it, you could use \verb!\SetTblrDefault! command: + +\begin{demohigh} +\SetTblrDefault{rowsep=0pt} +\begin{tblr}{lccr} +\hline + Alpha & Beta & Gamma & Delta \\ +\hline + Epsilon & Zeta & Eta & Theta \\ +\hline + Iota & Kappa & Lambda & Mu \\ +\hline +\end{tblr} +\end{demohigh} + +But in many cases, this rowsep is useful: + +\begin{demo} +$\begin{array}{rrr} +\hline + \dfrac{2}{3} & \dfrac{2}{3} & \dfrac{1}{3} \\ + \dfrac{2}{3} & -\dfrac{1}{3} & -\dfrac{2}{3} \\ + \dfrac{1}{3} & -\dfrac{2}{3} & \dfrac{2}{3} \\ +\hline +\end{array}$ +\end{demo} + +\begin{demohigh} +$\begin{tblr}{rrr} +\hline + \dfrac{2}{3} & \dfrac{2}{3} & \dfrac{1}{3} \\ + \dfrac{2}{3} & -\dfrac{1}{3} & -\dfrac{2}{3} \\ + \dfrac{1}{3} & -\dfrac{2}{3} & \dfrac{2}{3} \\ +\hline +\end{tblr}$ +\end{demohigh} + +Note that you can use \verb!tblr! in both text and math modes. + +\section{Multiline Cells} + +It's quite easy to write multiline cells without fixing the column width in \verb!tblr! environments: +just enclose the cell text with braces and use \verb!\\! to break lines: + +\begin{demohigh} +\begin{tblr}{|l|c|r|} +\hline + Left & {Center \\ Cent \\ C} & {Right \\ R} \\ +\hline + {L \\ Left} & {C \\ Cent \\ Center} & R \\ +\hline +\end{tblr} +\end{demohigh} + +\section{Cell Alignment} + +From time to time, +you may want to specify the horizontal and vertical alignment of cells at the same time. +\verb!Tabularray! package provides a \verb!Q! column for this +(In fact, \verb!Q! column is the only primitive column, +other columns are defined as \verb!Q! columns with some options): + +\begin{demohigh} +\begin{tblr}{|Q[l,t]|Q[c,m]|Q[r,b]|} +\hline + {Top Baseline \\ Left Left} & Middle Center & {Right Right \\ Bottom Baseline} \\ +\hline +\end{tblr} +\end{demohigh} + +Note that you can use more meaningful \verb!t! instead of \verb!p! for top baseline alignment. +For some users who are familiar with word processors, +these \verb!t! and \verb!b! columns are counter-intuitive. +In \verb!tabularray! package, there are another two column types \verb!h! and \verb!f!, +which will align cell text at row head and row foot, respectively: + +\begin{demohigh} +\begin{tblr}{Q[h,4em]Q[t,4em]Q[m,4em]Q[b,4em]Q[f,4em]} +\hline + {row\\head} & {top\\line} & {middle} & {line\\bottom} & {row\\foot} \\ +\hline + {row\\head} & {top\\line} & {11\\22\\mid\\44\\55} & {line\\bottom} & {row\\foot} \\ +\hline +\end{tblr} +\end{demohigh} + +\section{Multirow Cells} + +The above \verb!h! and \verb!f! columns are necessary for multirow cells. +In \verb!tabularray!, the \verb!t! and \verb!b! in the optional argument of \verb!\multirow! commands +will be treated as \verb!h! and \verb!f!, respectively: + +\begin{demo} +\begin{tabular}{|l|l|l|l|} +\hline + \multirow[t]{4}{1.5cm}{Multirow Cell One} & Alpha & + \multirow[b]{4}{1.5cm}{Multirow Cell Two} & Alpha \\ + & Beta & & Beta \\ + & Gamma & & Gamma \\ + & Delta & & Delta \\ +\hline +\end{tabular} +\end{demo} + +\begin{demohigh} +\begin{tblr}{|l|l|l|l|} +\hline + \multirow[t]{4}{1.5cm}{Multirow Cell One} & Alpha & + \multirow[b]{4}{1.5cm}{Multirow Cell Two} & Alpha \\ + & Beta & & Beta \\ + & Gamma & & Gamma \\ + & Delta & & Delta \\ +\hline +\end{tblr} +\end{demohigh} + +Note that you don't need to load \verb!multirow! package first, +since \verb!tabularrray! doesn't depend on it. +Furthermore, \verb!tabularray! will always typeset descent multirow cells. +First, it will set correct vertical \verb!c! alignment, +even though some rows have large height: + +\begin{demo} +\begin{tabular}{|l|m{4em}|} +\hline + \multirow[c]{4}{1.5cm}{Multirow} & Alpha \\ + & Beta \\ + & Gamma \\ + & Delta Delta Delta \\ +\hline +\end{tabular} +\end{demo} + +\begin{demohigh} +\begin{tblr}{|l|m{4em}|} +\hline + \multirow[c]{4}{1.5cm}{Multirow} & Alpha \\ + & Beta \\ + & Gamma \\ + & Delta Delta Delta \\ +\hline +\end{tblr} +\end{demohigh} + +Second, it will enlarge row heights if the multirow cells have large height, +therefore it always avoids vertical overflow: + +\begin{demo} +\begin{tabular}{|l|m{4em}|} +\hline + \multirow[c]{2}{1cm}{Line \\ Line \\ Line \\ Line} & Alpha \\ +\cline{2-2} + & Beta \\ +\hline +\end{tabular} +\end{demo} + +\begin{demohigh} +\begin{tblr}{|l|m{4em}|} +\hline + \multirow[c]{2}{1cm}{Line \\ Line \\ Line \\ Line} & Alpha \\ +\cline{2} + & Beta \\ +\hline +\end{tblr} +\end{demohigh} + +\section{Multi Rows and Columns} + +It was a hard job to typeset cells with multiple rows and multiple columns. For example: + +\begin{demo} +\begin{tabular}{|c|c|c|c|c|} +\hline + \multirow{2}{*}{2 Rows} + & \multicolumn{2}{c|}{2 Columns} + & \multicolumn{2}{c|}{\multirow{2}{*}{2 Rows 2 Columns}} \\ +\cline{2-3} +  & 2-2 & 2-3 & \multicolumn{2}{c|}{} \\ +\hline + 3-1 & 3-2 & 3-3 & 3-4 & 3-5 \\ +\hline +\end{tabular} +\end{demo} + +With \verb!tabularray! package, you can set spanned cells with \verb!\SetCell! command: +within the optional argument of \verb!\SetCell! command, +option \verb!r! is for rowspan number, and \verb!c! for colspan number; +within the mandatory argument of it, horizontal and vertical alignment options are accepted. +Therefore it's much simpler to typeset spanned cells: + +\begin{demohigh} +\begin{tblr}{|c|c|c|c|c|} +\hline + \SetCell[r=2]{c} 2 Rows + & \SetCell[c=2]{c} 2 Columns + & & \SetCell[r=2,c=2]{c} 2 Rows 2 Columns & \\ +\hline +  & 2-2 & 2-3 & & \\ +\hline + 3-1 & 3-2 & 3-3 & 3-4 & 3-5 \\ +\hline +\end{tblr} +\end{demohigh} + +Using \verb!\multicolumn! command, the omitted cells \textcolor{red3}{must} be removed. +On the contrary, +using \verb!\multirow! command, the omitted cells \textcolor{red3}{must not} be removed. +\verb!\SetCell! command behaves the same as \verb!\multirow! command in this aspect. + +With \verb!tblr! environment, any \verb!\hline! segments inside a spanned cell will be ignored, +therefore we're free to use \verb!\hline! in the above example. +Also, any omitted cell will definitely be ignored when typesetting, +no matter it's empty or not. +With this feature, we could put row and column numbers into the omitted cells, +which will help us to locate cells when the tables are rather complex: + +\begin{demohigh} +\begin{tblr}{|ll|c|rr|} +\hline + \SetCell[r=3,c=2]{h} r=3 c=2 & 1-2 & \SetCell[r=2,c=3]{r} r=2 c=3 & 1-4 & 1-5 \\ + 2-1 & 2-2 & 2-3 & 2-4 & 2-5 \\ +\hline + 3-1 & 3-2 & MIDDLE & \SetCell[r=3,c=2]{f} r=3 c=2 & 3-5 \\ +\hline + \SetCell[r=2,c=3]{l} r=2 c=3 & 4-2 & 4-3 & 4-4 & 4-5 \\ + 5-1 & 5-2 & 5-3 & 5-4 & 5-5 \\ +\hline +\end{tblr} +\end{demohigh} + +\section{Column Types} + +\verb!Tabularray! package supports all normal column types, as well as +the extendable \verb!X! column type, +which first occured in \verb!tabularx! package and was largely improved by \verb!tabu! package: + +\begin{demohigh} +\begin{tblr}{|X[2,l]|X[3,l]|X[1,r]|X[r]|} +\hline + Alpha & Beta & Gamma & Delta \\ +\hline +\end{tblr} +\end{demohigh} + +Also, \verb!X! columns with negative coefficients are possible: + +\begin{demohigh} +\begin{tblr}{|X[2,l]|X[3,l]|X[-1,r]|X[r]|} +\hline + Alpha & Beta & Gamma & Delta \\ +\hline +\end{tblr} +\end{demohigh} + +We need the width to typeset a table with \verb!X! columns. +If unset, the default is \verb!\linewidth!. +To change the width, we have to first put all column specifications into \verb!colspec={...}!: + +\begin{demohigh} +\begin{tblr}{width=0.8\linewidth,colspec={|X[2,l]|X[3,l]|X[-1,r]|X[r]|}} +\hline + Alpha & Beta & Gamma & Delta \\ +\hline +\end{tblr} +\end{demohigh} + +You can define new column types with \verb!\NewColumnType! command. +For example, in \verb!tabularray! package, +\verb!b! and \verb!X! columns are defined as special \verb!Q! columns: +\begin{codehigh} +\NewColumnType{b}[1]{Q[b,wd=#1]} +\NewColumnType{X}[1][]{Q[co=1,#1]} +\end{codehigh} + +\section{Row Types} + +Now that we have column types and \verb!colspec! option, +you may ask for row types and \verb!rowspec! option. +Yes, they are here: + +\begin{demohigh} +\begin{tblr}{colspec={Q[l]Q[c]Q[r]},rowspec={|Q[t]|Q[m]|Q[b]|}} + {Alpha \\ Alpha} & Beta & Gamma \\ + Delta & Epsilon & {Zeta \\ Zeta} \\ + Eta & {Theta \\ Theta} & Iota \\ +\end{tblr} +\end{demohigh} + +Same as column types, \verb!Q! is the only primitive row type, +and other row types are defined as \verb!Q! types with different options. +It's better to specify horizontal alignment in \verb!colspec!, +and vertical alignment in \verb!rowspec!, respectively. + +Inside \verb!rowspec!, \verb!|! is the hline type. +Therefore we need not to write \verb!\hline! commnad, which makes table code cleaner. + +\section{Hlines and Vlines} + +Hlines and vlines have been improved too. You can specify the widths and styles of them: + +\begin{demohigh} +\begin{tblr}{|l|[dotted]|[2pt]c|r|[solid]|[dashed]|} +\hline +One & Two & Three \\ +\hline\hline[dotted]\hline +Four & Five & Six \\ +\hline[dashed]\hline[1pt] +Seven & Eight & Nine \\ +\hline +\end{tblr} +\end{demohigh} + +\section{Colorful Tables} + +To add colors to your tables, you need to load \verb!xcolor! package first. +\verb!Tabularray! package will also load \verb!ninecolors! package for proper color contrast. +First you can specify background option for \verb!Q! rows/columns inside \verb!rowspec!/\verb!colspec!: + +\begin{demohigh} +\begin{tblr}{colspec={lcr},rowspec={|Q[cyan7]|Q[azure7]|Q[blue7]|}} + Alpha & Beta & Gamma \\ + Epsilon & Zeta & Eta \\ + Iota & Kappa & Lambda \\ +\end{tblr} +\end{demohigh} + +\begin{demohigh} +\begin{tblr}{colspec={Q[l,brown7]Q[c,yellow7]Q[r,olive7]},rowspec={|Q|Q|Q|}} + Alpha & Beta & Gamma \\ + Epsilon & Zeta & Eta \\ + Iota & Kappa & Lambda \\ +\end{tblr} +\end{demohigh} + +Also you can use \verb!\SetRow! or \verb!\SetColumn! command to specify row or column colors: + +\begin{demohigh} +\begin{tblr}{colspec={lcr},rowspec={|Q|Q|Q|}} + \SetRow{cyan7} Alpha & Beta & Gamma \\ + \SetRow{azure7} Epsilon & Zeta & Eta \\ + \SetRow{blue7} Iota & Kappa & Lambda \\ +\end{tblr} +\end{demohigh} + +\begin{demohigh} +\begin{tblr}{colspec={lcr},rowspec={|Q|Q|Q|}} + \SetColumn{brown7} + Alpha & \SetColumn{yellow7} + Beta & \SetColumn{olive7} + Gamma \\ + Epsilon & Zeta & Eta \\ + Iota & Kappa & Lambda \\ +\end{tblr} +\end{demohigh} + +Hlines and vlines can also have colors: + +\begin{demohigh} +\begin{tblr}{colspec={lcr},rowspec={|[2pt,green7]Q|[teal7]Q|[green7]Q|[3pt,teal7]}} + Alpha & Beta & Gamma \\ + Epsilon & Zeta & Eta \\ + Iota & Kappa & Lambda \\ +\end{tblr} +\end{demohigh} + +\begin{demohigh} +\begin{tblr}{colspec={|[2pt,violet5]l|[2pt,magenta5]c|[2pt,purple5]r|[2pt,red5]}} + Alpha & Beta & Gamma \\ + Epsilon & Zeta & Eta \\ + Iota & Kappa & Lambda \\ +\end{tblr} +\end{demohigh} + +\section{New Table Commands} + +All commands which change the specifications of tables \textcolor{red3}{must} be defined with \verb!\NewTableCommand!. +The following example demonstrates how to define similar rules as in \verb!booktabs! package: + +\begin{demohigh} +\NewTableCommand\toprule{\hline[0.08em]} +\NewTableCommand\midrule{\hline[0.05em]} +\NewTableCommand\bottomrule{\hline[0.08em]} +\begin{tblr}{llll} +\toprule + Alpha & Beta & Gamma & Delta \\ +\midrule + Epsilon & Zeta & Eta & Theta \\ + Iota & Kappa & Lambda & Mu \\ + Nu & Xi & Omicron & Pi \\ +\bottomrule +\end{tblr} +\end{demohigh} + +\chapter{New Interface} + +With \verb!tabularray! package, you can separate style and content totally in tables. + +\section{Hlines and Vlines} + +Options \verb!hlines! and \verb!vlines! are for setting all hlines and vlines, respectively. +With empty value, all hlines/vlines will be solid. + +\begin{demohigh} +\begin{tblr}{hlines,vlines} + Alpha & Beta & Gamma & Delta \\ + Epsilon & Zeta & Eta & Theta \\ + Iota & Kappa & Lambda & Mu \\ + Nu & Xi & Omicron & Pi \\ + Rho & Sigma & Tau & Upsilon \\ + Phi & Chi & Psi & Omega \\ +\end{tblr} +\end{demohigh} + +With values inside one pair of braces, all hlines/vlines will be styled. + +\begin{demohigh} +\begin{tblr}{ + hlines = {1pt,solid}, + vlines = {red3,dashed}, +} + Alpha & Beta & Gamma & Delta \\ + Epsilon & Zeta & Eta & Theta \\ + Iota & Kappa & Lambda & Mu \\ + Nu & Xi & Omicron & Pi \\ + Rho & Sigma & Tau & Upsilon \\ + Phi & Chi & Psi & Omega \\ +\end{tblr} +\end{demohigh} + +Another pair of braces before will select segments in all hlines/vlines. + +\begin{demohigh} +\begin{tblr}{ + vlines = {1,3,5}{dashed}, + vlines = {2,4,6}{solid}, +} + Alpha & Beta & Gamma & Delta \\ + Epsilon & Zeta & Eta & Theta \\ + Iota & Kappa & Lambda & Mu \\ + Nu & Xi & Omicron & Pi \\ + Rho & Sigma & Tau & Upsilon \\ + Phi & Chi & Psi & Omega \\ +\end{tblr} +\end{demohigh} + +The above example can be simplified with \verb!odd! and \verb!even! values. +(More child selectors can be defined with \verb!\NewChildSelector! command. +Advanced users could read the source code for this.) + +\begin{demohigh} +\begin{tblr}{ + vlines = {odd}{dashed}, + vlines = {even}{solid}, +} + Alpha & Beta & Gamma & Delta \\ + Epsilon & Zeta & Eta & Theta \\ + Iota & Kappa & Lambda & Mu \\ + Nu & Xi & Omicron & Pi \\ + Rho & Sigma & Tau & Upsilon \\ + Phi & Chi & Psi & Omega \\ +\end{tblr} +\end{demohigh} + +Another pair of braces before will draw more hlines/vlines (in which \verb!-! stands for all line segments). + +\begin{demohigh} +\begin{tblr}{ + hlines = {1}{-}{dashed}, + hlines = {2}{-}{solid}, +} + Alpha & Beta & Gamma & Delta \\ + Epsilon & Zeta & Eta & Theta \\ + Iota & Kappa & Lambda & Mu \\ + Nu & Xi & Omicron & Pi \\ + Rho & Sigma & Tau & Upsilon \\ + Phi & Chi & Psi & Omega \\ +\end{tblr} +\end{demohigh} + +Options \verb!hline{i}! and \verb!vline{j}! are for setting some hlines and vlines, respectively. + +\begin{demohigh} +\begin{tblr}{ + hline{1,7} = {1pt,solid}, + hline{3-5} = {blue3,dashed}, + vline{1,5} = {3-4}{dotted}, +} + Alpha & Beta & Gamma & Delta \\ + Epsilon & Zeta & Eta & Theta \\ + Iota & Kappa & Lambda & Mu \\ + Nu & Xi & Omicron & Pi \\ + Rho & Sigma & Tau & Upsilon \\ + Phi & Chi & Psi & Omega \\ +\end{tblr} +\end{demohigh} + +\section{Cells and Spancells} + +Option \verb!cells! is for setting all cells. + +\begin{demohigh} +\begin{tblr}{hlines={white},cells={c,blue7}} + Alpha & Beta & Gamma & Delta \\ + Epsilon & Zeta & Eta & Theta \\ + Iota & Kappa & Lambda & Mu \\ + Nu & Xi & Omicron & Pi \\ + Rho & Sigma & Tau & Upsilon \\ + Phi & Chi & Psi & Omega \\ +\end{tblr} +\end{demohigh} + +Option \verb!cell{i}{j}! is for setting some cells. + +\begin{demohigh} +\begin{tblr}{ + hlines = {white}, + vlines = {white}, + cell{1,6}{odd} = {teal7}, + cell{1,6}{even} = {green7}, + cell{2,4}{1,4} = {red7}, + cell{3,5}{1,4} = {purple7}, + cell{2}{2} = {r=4,c=2}{c,azure7}, +} + Alpha & Beta & Gamma & Delta \\ + Epsilon & Zeta & Eta & Theta \\ + Iota & Kappa & Lambda & Mu \\ + Nu & Xi & Omicron & Pi \\ + Rho & Sigma & Tau & Upsilon \\ + Phi & Chi & Psi & Omega \\ +\end{tblr} +\end{demohigh} + +\section{Rows and Columns} + +Options \verb!rows! and \verb!columns! are for setting all rows and columns, respectively. + +\begin{demohigh} +\begin{tblr}{ + hlines, + vlines, + rows = {7mm}, + columns = {15mm,c}, +} + Alpha & Beta & Gamma & Delta \\ + Epsilon & Zeta & Eta & Theta \\ + Iota & Kappa & Lambda & Mu \\ +\end{tblr} +\end{demohigh} + +Options \verb!row{i}! and \verb!column{j}! are for setting some rows and columns, respectively. + +\begin{demohigh} +\begin{tblr}{ + hlines = {1pt,white}, + row{odd} = {blue7}, + row{even} = {azure7}, + column{1} = {purple7,c}, +} + Alpha & Beta & Gamma & Delta \\ + Epsilon & Zeta & Eta & Theta \\ + Iota & Kappa & Lambda & Mu \\ + Nu & Xi & Omicron & Pi \\ + Rho & Sigma & Tau & Upsilon \\ + Phi & Chi & Psi & Omega \\ +\end{tblr} +\end{demohigh} + +\section{Space in Tables} + +Options \verb!rowsep! and \verb!colsep! are for setting padding for rows and columns, respectively. + +\begin{demohigh} +\SetTblrDefault{rowsep=2pt,colsep=2pt} +\begin{tblr}{hlines,vlines} + Alpha & Beta & Gamma & Delta \\ + Epsilon & Zeta & Eta & Theta \\ + Iota & Kappa & Lambda & Mu \\ +\end{tblr} +\end{demohigh} + +Also \verb!abovesep!, \verb!belowsep!, \verb!leftsep!, \verb!rightsep! options are available: + +\begin{demohigh} +\begin{tblr}{ + hlines, + vlines, + rows = {abovesep=1pt,belowsep=5pt}, + columns = {leftsep=1pt,rightsep=5pt}, +} + Alpha & Beta & Gamma & Delta \\ + Epsilon & Zeta & Eta & Theta \\ + Iota & Kappa & Lambda & Mu \\ +\end{tblr} +\end{demohigh} + +And \verb!\\[dimen]! can be replaced by \verb!belowsep+! option: + +\begin{demohigh} +\begin{tblr}{ + hlines, row{2} = {belowsep+=5pt}, +} + Alpha & Beta & Gamma & Delta \\ + Epsilon & Zeta & Eta & Theta \\ + Iota & Kappa & Lambda & Mu \\ +\end{tblr} +\end{demohigh} + +Also \verb!\arraystretch! parameter can be replaced by \verb!stretch! option: + +\begin{demohigh} +\begin{tblr}{hlines,stretch=1.5} + Alpha & Beta & Gamma & Delta \\ + Epsilon & Zeta & Eta & Theta \\ + Iota & Kappa & Lambda & Mu \\ +\end{tblr} +\end{demohigh} + +\section{Counters in Tables} + +Counters \verb!rownum!, \verb!colnum!, \verb!rowcount!, \verb!colcount! can be used in cell text: + +\begin{demohigh} +\begin{tblr}{hlines} + Cell[\arabic{rownum}][\arabic{colnum}] & Cell[\arabic{rownum}][\arabic{colnum}] & + Cell[\arabic{rownum}][\arabic{colnum}] & Cell[\arabic{rownum}][\arabic{colnum}] \\ + Cell[\arabic{rownum}][\arabic{colnum}] & Cell[\arabic{rownum}][\arabic{colnum}] & + Cell[\arabic{rownum}][\arabic{colnum}] & Cell[\arabic{rownum}][\arabic{colnum}] \\ + Row=\arabic{rowcount}, Col=\arabic{colcount} & + Row=\arabic{rowcount}, Col=\arabic{colcount} & + Row=\arabic{rowcount}, Col=\arabic{colcount} & + Row=\arabic{rowcount}, Col=\arabic{colcount} \\ + Cell[\arabic{rownum}][\arabic{colnum}] & Cell[\arabic{rownum}][\arabic{colnum}] & + Cell[\arabic{rownum}][\arabic{colnum}] & Cell[\arabic{rownum}][\arabic{colnum}] \\ + Cell[\arabic{rownum}][\arabic{colnum}] & Cell[\arabic{rownum}][\arabic{colnum}] & + Cell[\arabic{rownum}][\arabic{colnum}] & Cell[\arabic{rownum}][\arabic{colnum}] \\ +\end{tblr} +\end{demohigh} + +\section{Experimental Interface} + +Everything described in this section is in \underline{\textcolor{red3}{\textbf{experimental}}} status. +Don’t use it in important documents, unless you have time +to update them for the newer versions of \verb!tabularray! package in the future. + +By default \verb!tabularray! package will compute column widths from span widths. +By setting option \verb!hspan=minimal!, it will compute span widths from column widths. +The following two examples show this difference: + +\begin{demohigh} +\SetTblrDefault{hlines,vlines} +\begin{tblr}{cell{2}{1}={c=2}{l},cell{3}{1}={c=3}{l},cell{4}{2}={c=2}{l}} + 111 111 & 222 222 & 333 333 \\ + 12 Multi Columns Multi Columns 12 & & 333 \\ + 13 Multi Columns Multi Columns Multi Columns 13 & & \\ + 111 & 23 Multi Columns Multi Columns 23 & \\ +\end{tblr} +\end{demohigh} + +\begin{demohigh} +\SetTblrDefault{hlines,vlines,hspan=minimal} +\begin{tblr}{cell{2}{1}={c=2}{l},cell{3}{1}={c=3}{l},cell{4}{2}={c=2}{l}} + 111 111 & 222 222 & 333 333 \\ + 12 Multi Columns Multi Columns 12 & & 333 \\ + 13 Multi Columns Multi Columns Multi Columns 13 & & \\ + 111 & 23 Multi Columns Multi Columns 23 & \\ +\end{tblr} +\end{demohigh} + +\chapter{Source Code} + +%\CodeHigh{lite} +\dochighinput[language=latex/latex3]{tabularray.sty} + +\end{document} -- cgit v1.2.3