%%% % -*- coding: utf-8 -*- %%% ---------------------------------------------------------------------------- %%% Tabularray: Typeset tabulars and arrays with LaTeX3 %%% Copyright : 2021-2023 (c) Jianrui Lyu %%% Repository: https://github.com/lvjr/tabularray %%% License : The LaTeX Project Public License 1.3c %%% ---------------------------------------------------------------------------- %%% -------------------------------------------------------- %%> \section{Scratch Variables and Function Variants} %%% -------------------------------------------------------- %% \DeclareRelease and \DeclareCurrentRelease are added in LaTeX 2018-04-01 \NeedsTeXFormat{LaTeX2e}[2018-04-01] \providecommand\DeclareRelease[3]{} \providecommand\DeclareCurrentRelease[2]{} \DeclareRelease{v2021}{2021-01-01}{tabularray-2021.sty} \DeclareCurrentRelease{}{2022-01-01} \RequirePackage{expl3} \ProvidesExplPackage{tabularray}{2024-02-16}{2024A} {Typeset tabulars and arrays with LaTeX3} %% \IfFormatAtLeastTF, xparse and lthooks are added in LaTeX 2020-10-01 %% Note that \@ifl@t@r or \@ifpackagelater means 'this date or later' \msg_new:nnn { tabularray } { latex-too-old } { Your ~ LaTeX ~ release ~ is ~ too ~ old. \\ Please ~ update ~ it ~ to ~ 2020-10-01 ~ first. } \@ifl@t@r\fmtversion{2020-10-01}{}{ %% Support TeX Live 2020 on Overleaf \msg_warning:nn { tabularray } { latex-too-old } \usepackage{xparse} } \AtBeginDocument{ \@ifpackageloaded{xcolor}{\RequirePackage{ninecolors}}{} \@ifpackageloaded{hyperref}{ \newenvironment{tblrNoHyper}{\NoHyper}{\endNoHyper} }{ \newenvironment{tblrNoHyper}{}{} } } \NewDocumentCommand \TblrParboxRestore { } { \@parboxrestore } \NewDocumentCommand \TblrAlignBoth { } { \let \\ = \@normalcr \leftskip = \z@skip \@rightskip = \z@skip \rightskip = \@rightskip \parfillskip = \@flushglue } \NewDocumentCommand \TblrAlignLeft { } { \raggedright } \NewDocumentCommand \TblrAlignCenter { } { \centering } \NewDocumentCommand \TblrAlignRight { } { \raggedleft } \cs_set_eq:NN \TblrNewPage \newpage %% Note that \cs_if_exist:NTF doesn't treat \relax as an existing command. %% Therefore we define our \__tblr_cs_if_defined:NTF here. \prg_set_conditional:Npnn \__tblr_cs_if_defined:N #1 { p, T, F, TF } { %% \if_cs_exist:N = \ifdefined in eTeX \if_cs_exist:N #1 \prg_return_true: \else: \prg_return_false: \fi: } \prg_set_conditional:Npnn \__tblr_cs_if_defined:c #1 { p, T, F, TF } { %% \if_cs_exist:w = \ifcsname in eTeX \if_cs_exist:w #1 \cs_end: \prg_return_true: \else: \prg_return_false: \fi: } \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_const:Nn { ce } \cs_generate_variant:Nn \tl_log:n { x } \cs_generate_variant:Nn \tl_gput_right:Nn { Nf } \cs_generate_variant:Nn \tl_put_left:Nn { Nv } \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 \regex_match:Nn { NV } { TF } \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_catcode:nN { VN } { 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 %% Total number of tblr tables \int_new:N \g__tblr_table_count_int %% Some commands for horizontal alignment \cs_new_eq:NN \__tblr_halign_command_j: \TblrAlignBoth \cs_new_eq:NN \__tblr_halign_command_l: \TblrAlignLeft \cs_new_eq:NN \__tblr_halign_command_c: \TblrAlignCenter \cs_new_eq:NN \__tblr_halign_command_r: \TblrAlignRight %% Some counters for row and column numbering. %% We may need to restore all LaTeX counters in measuring and building cells, %% so we must not define these counters with \newcounter command. \int_zero_new:N \c@rownum \int_zero_new:N \c@colnum \int_zero_new:N \c@rowcount \int_zero_new:N \c@colcount %% Add missing \therownum, \thecolnum, \therowcount, \thecolcount (issue #129) \ProvideExpandableDocumentCommand \therownum {} { \@arabic \c@rownum } \ProvideExpandableDocumentCommand \thecolnum {} { \@arabic \c@colnum } \ProvideExpandableDocumentCommand \therowcount {} { \@arabic \c@rowcount } \ProvideExpandableDocumentCommand \thecolcount {} { \@arabic \c@colcount } %% Some dimensions for row and column spacing \dim_new:N \abovesep \dim_new:N \belowsep \dim_new:N \leftsep \dim_new:N \rightsep %% Some functions for lwarp to remove rules and boxes \cs_new:Npn \tblr_hrule_ht:n #1 { \hrule height ~ #1 \scan_stop: } \cs_new:Npn \tblr_vrule_wd_ht_dp:nnn #1 #2 #3 { \vrule width ~ #1 ~ height ~ #2 ~ depth ~ #3 \scan_stop: } \cs_new_protected:Npn \tblr_box_use:N #1 { \box_use:N #1 } \cs_new_protected:Npn \tblr_vbox_set:Nn #1 #2 { \vbox_set:Nn #1 {#2} } %%% -------------------------------------------------------- %%> \section{Data Structures Based on Property Lists} %%% -------------------------------------------------------- \int_new:N \g_tblr_level_int % store table nesting level \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_inner_ \int_use:N \g_tblr_level_int _prop } \prop_gclear_new:c { g__tblr_note_ \int_use:N \g_tblr_level_int _prop } \prop_gclear_new:c { g__tblr_remark_ \int_use:N \g_tblr_level_int _prop } \prop_gclear_new:c { g__tblr_more_ \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 } } \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, NnV } \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 } %%% -------------------------------------------------------- %%> \section{Data Structures Based on Token Lists} %%% -------------------------------------------------------- \cs_new_protected:Npn \__tblr_clear_spec_lists: { %\__tblr_clear_one_spec_lists:n { row } %\__tblr_clear_one_spec_lists:n { column } %\__tblr_clear_one_spec_lists:n { cell } \__tblr_clear_one_spec_lists:n { text } \__tblr_clear_one_spec_lists:n { hline } \__tblr_clear_one_spec_lists:n { vline } \__tblr_clear_one_spec_lists:n { outer } } \cs_new_protected:Npn \__tblr_clear_one_spec_lists:n #1 { \clist_if_exist:cTF { g__tblr_#1_ \int_use:N \g_tblr_level_int _clist } { \clist_map_inline:cn { g__tblr_#1_ \int_use:N \g_tblr_level_int _clist } { \tl_gclear:c { g__tblr_spec_ \int_use:N \g_tblr_level_int _#1_##1_tl } } } { \clist_new:c { g__tblr_#1_ \int_use:N \g_tblr_level_int _clist } } } \cs_new_protected:Npn \__tblr_spec_gput:nnn #1 #2 #3 { \tl_gset:cn { g__tblr_spec_ \int_use:N \g_tblr_level_int _#1_#2_tl } {#3} \clist_gput_right:cx { g__tblr_#1_ \int_use:N \g_tblr_level_int _clist } {#2} } \cs_generate_variant:Nn \__tblr_spec_gput:nnn { nne, nnV, nen, nee, neV } \cs_new:Npn \__tblr_spec_item:nn #1 #2 { \tl_if_exist:cT { g__tblr_spec_ \int_use:N \g_tblr_level_int _#1_#2_tl } { \exp_args:Nv \exp_not:n { g__tblr_spec_ \int_use:N \g_tblr_level_int _#1_#2_tl } } } \cs_generate_variant:Nn \__tblr_spec_item:nn { ne } \cs_new_protected:Npn \__tblr_spec_gput_if_larger:nnn #1 #2 #3 { \tl_set:Nx \l__tblr_put_if_larger_tl { \__tblr_spec_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 } } { \__tblr_spec_gput:nnn {#1} {#2} {#3} } } \cs_generate_variant:Nn \__tblr_spec_gput_if_larger:nnn { nne, nnV, nen, nee, neV } \cs_new_protected:Npn \__tblr_spec_gadd_dimen_value:nnn #1 #2 #3 { \__tblr_spec_gput:nne {#1} {#2} { \dim_eval:n { \__tblr_spec_item:ne {#1} {#2} + #3 } } } \cs_generate_variant:Nn \__tblr_spec_gadd_dimen_value:nnn { nne, nnV, nen, nee } \cs_new_protected:Npn \__tblr_spec_log:n #1 { \clist_gremove_duplicates:c { g__tblr_#1_ \int_use:N \g_tblr_level_int _clist } \tl_log:x { The ~ spec ~ list ~ #1 _ \int_use:N \g_tblr_level_int \space contains ~ the ~ pairs: } \clist_map_inline:cn { g__tblr_#1_ \int_use:N \g_tblr_level_int _clist } { \tl_log:x { \space { ##1 } ~\space=>~\space { \__tblr_spec_item:nn {#1} {##1} } } } } %%% -------------------------------------------------------- %%> \section{Data Structures Based on Integer Arrays} %%% -------------------------------------------------------- \msg_new:nnn { tabularray } { intarray-beyond-bound } { Position ~ #2 ~ is ~ beyond ~ the ~ bound ~ of ~ intarray ~ #1.} \cs_new_protected:Npn \__tblr_intarray_gset:Nnn #1 #2 #3 { \bool_lazy_or:nnTF { \int_compare_p:nNn {#2} < {0} } { \int_compare_p:nNn {#2} > {\intarray_count:N #1} } { \bool_if:NT \g__tblr_tracing_intarray_bool { \msg_warning:nnnn { tabularray } { intarray-beyond-bound } {#1} {#2} } } { \intarray_gset:Nnn #1 {#2} {#3} } } \cs_generate_variant:Nn \__tblr_intarray_gset:Nnn { cnn } %% #1: data name; #2: key name; #3: value type \cs_new_protected:Npn \__tblr_data_new_key:nnn #1 #2 #3 { \int_gincr:c { g__tblr_data_#1_key_count_int } \tl_const:ce { c__tblr_data_#1_key_name_ \int_use:c { g__tblr_data_#1_key_count_int } _tl } { #2 } \tl_const:ce { c__tblr_data_#1_key_number_#2_tl } { \int_use:c { g__tblr_data_#1_key_count_int } } \tl_const:cn { c__tblr_data_#1_key_type_#2_tl } {#3} } \int_new:N \g__tblr_data_row_key_count_int \__tblr_data_new_key:nnn { row } { height } { dim } \__tblr_data_new_key:nnn { row } { coefficient } { dec } \__tblr_data_new_key:nnn { row } { abovesep } { dim } \__tblr_data_new_key:nnn { row } { belowsep } { dim } \__tblr_data_new_key:nnn { row } { @row-height } { dim } \__tblr_data_new_key:nnn { row } { @row-head } { dim } \__tblr_data_new_key:nnn { row } { @row-foot } { dim } \__tblr_data_new_key:nnn { row } { @row-upper } { dim } \__tblr_data_new_key:nnn { row } { @row-lower } { dim } \int_new:N \g__tblr_data_column_key_count_int \__tblr_data_new_key:nnn { column } { width } { dim } \__tblr_data_new_key:nnn { column } { coefficient } { dec } \__tblr_data_new_key:nnn { column } { leftsep } { dim } \__tblr_data_new_key:nnn { column } { rightsep } { dim } \__tblr_data_new_key:nnn { column } { @col-width } { dim } \int_new:N \g__tblr_data_cell_key_count_int \__tblr_data_new_key:nnn { cell } { width } { dim } \__tblr_data_new_key:nnn { cell } { rowspan } { int } \__tblr_data_new_key:nnn { cell } { colspan } { int } \__tblr_data_new_key:nnn { cell } { halign } { str } \__tblr_data_new_key:nnn { cell } { valign } { str } \__tblr_data_new_key:nnn { cell } { background } { str } \__tblr_data_new_key:nnn { cell } { foreground } { str } \__tblr_data_new_key:nnn { cell } { font } { str } \__tblr_data_new_key:nnn { cell } { mode } { str } \__tblr_data_new_key:nnn { cell } { cmd } { str } \__tblr_data_new_key:nnn { cell } { omit } { int } \__tblr_data_new_key:nnn { cell } { @cell-width } { dim } \__tblr_data_new_key:nnn { cell } { @cell-height } { dim } \__tblr_data_new_key:nnn { cell } { @cell-depth } { dim } \clist_const:Nn \c__tblr_data_clist { row, column, cell } \tl_const:Nn \c__tblr_data_row_count_tl { \c@rowcount } \tl_const:Nn \c__tblr_data_column_count_tl { \c@colcount } \tl_const:Nn \c__tblr_data_cell_count_tl { \c@rowcount * \c@colcount } \tl_const:Nn \c__tblr_data_row_index_number_tl {1} \tl_const:Nn \c__tblr_data_column_index_number_tl {1} \tl_const:Nn \c__tblr_data_cell_index_number_tl {2} \int_new:N \g__tblr_array_int \cs_new_protected:Npn \__tblr_init_table_data: { \clist_map_function:NN \c__tblr_data_clist \__tblr_init_one_data:n } \cs_new_protected:Npn \__tblr_init_one_data:n #1 { \int_gincr:N \g__tblr_array_int \intarray_new:cn { g__tblr_#1_ \int_use:N \g__tblr_array_int _intarray } { \int_use:c { g__tblr_data_#1_key_count_int } * \tl_use:c { c__tblr_data_#1_count_tl } } \cs_set_eq:cc { g__tblr_#1_ \int_use:N \g_tblr_level_int _intarray } { g__tblr_#1_ \int_use:N \g__tblr_array_int _intarray } %\intarray_log:c { g__tblr_#1_ \int_use:N \g_tblr_level_int _intarray } } %% #1: data name; #2: data index; #3: key name \cs_new:Npn \__tblr_data_key_to_int:nnn #1 #2 #3 { ( #2 - 1 ) * \int_use:c { g__tblr_data_#1_key_count_int } + \tl_use:c { c__tblr_data_#1_key_number_#3_tl } } %% #1: data name; #2: data index 1; #3: data index 2; #4: key name \cs_new:Npn \__tblr_data_key_to_int:nnnn #1 #2 #3 #4 { ( #2 - 1 ) * \c@colcount * \int_use:c { g__tblr_data_#1_key_count_int } + ( #3 - 1 ) * \int_use:c { g__tblr_data_#1_key_count_int } + \tl_use:c { c__tblr_data_#1_key_number_#4_tl } } \int_new:N \l__tblr_key_count_int \int_new:N \l__tblr_key_quotient_int \int_new:N \l__tblr_key_quotient_two_int \int_new:N \l__tblr_key_remainder_int %% #1: data name; #2: array position; %% #3: returning tl with index; #4: returning tl with key name \cs_new:Npn \__tblr_data_int_to_key:nnNN #1 #2 #3 #4 { \int_set_eq:Nc \l__tblr_key_count_int { g__tblr_data_#1_key_count_int } \int_set:Nn \l__tblr_key_quotient_int { \int_div_truncate:nn { #2 + \l__tblr_key_count_int - 1 } { \l__tblr_key_count_int } } \int_set:Nn \l__tblr_key_remainder_int { #2 + \l__tblr_key_count_int - \l__tblr_key_quotient_int * \l__tblr_key_count_int } \int_compare:nNnT { \l__tblr_key_remainder_int } = { 0 } { \int_set_eq:NN \l__tblr_key_remainder_int \l__tblr_key_count_int } \tl_set:Nx #3 { \int_use:N \l__tblr_key_quotient_int } \tl_set_eq:Nc #4 { c__tblr_data_#1_key_name_ \int_use:N \l__tblr_key_remainder_int _tl } } %% #1: data name; #2: array position; %% #3: returning tl with index 1; #4: returning tl with index 2; %% #5: returning tl with key name \cs_new:Npn \__tblr_data_int_to_key:nnNNN #1 #2 #3 #4 #5 { \int_set_eq:Nc \l__tblr_key_count_int { g__tblr_data_#1_key_count_int } \int_set:Nn \l__tblr_key_quotient_int { \int_div_truncate:nn { #2 + \l__tblr_key_count_int - 1 } { \l__tblr_key_count_int } } \int_set:Nn \l__tblr_key_remainder_int { #2 + \l__tblr_key_count_int - \l__tblr_key_quotient_int * \l__tblr_key_count_int } \int_compare:nNnT { \l__tblr_key_remainder_int } = { 0 } { \int_set_eq:NN \l__tblr_key_remainder_int \l__tblr_key_count_int } \tl_set_eq:Nc #5 { c__tblr_data_#1_key_name_ \int_use:N \l__tblr_key_remainder_int _tl } \int_set:Nn \l__tblr_key_quotient_two_int { \int_div_truncate:nn { \l__tblr_key_quotient_int + \c@colcount - 1 } { \c@colcount } } \int_set:Nn \l__tblr_key_remainder_int { \l__tblr_key_quotient_int + \c@colcount - \l__tblr_key_quotient_two_int * \c@colcount } \int_compare:nNnT { \l__tblr_key_remainder_int } = { 0 } { \int_set_eq:NN \l__tblr_key_remainder_int \c@colcount } \tl_set:Nx #4 { \int_use:N \l__tblr_key_remainder_int } \tl_set:Nx #3 { \int_use:N \l__tblr_key_quotient_two_int } } \tl_new:N \g__tblr_data_int_from_value_tl %% #1: data name; #2: key name; #3: value %% The result will be stored in \g__tblr_data_int_from_value_tl \cs_new_protected:Npn \__tblr_data_int_from_value:nnn #1 #2 #3 { \cs:w __tblr_data_int_from_ \tl_use:c { c__tblr_data_#1_key_type_#2_tl } :n \cs_end: {#3} } %% #1: data name; #2: key name; #3: int \cs_new:Npn \__tblr_data_int_to_value:nnn #1 #2 #3 { \cs:w __tblr_data_int_to_ \tl_use:c { c__tblr_data_#1_key_type_#2_tl } :n \cs_end: {#3} } \cs_generate_variant:Nn \__tblr_data_int_to_value:nnn { nne, nVe } \cs_new_protected:Npn \__tblr_data_int_from_int:n #1 { \tl_gset:Nn \g__tblr_data_int_from_value_tl {#1} } \cs_new:Npn \__tblr_data_int_to_int:n #1 { #1 } \cs_new_protected:Npn \__tblr_data_int_from_dim:n #1 { \tl_gset:Nx \g__tblr_data_int_from_value_tl { \dim_to_decimal_in_sp:n {#1} } } %% Return a dimension in pt so that it's easier to understand in tracing messages \cs_new:Npn \__tblr_data_int_to_dim:n #1 { %#1 sp %\dim_eval:n { #1 sp } \dim_to_decimal:n { #1 sp } pt } \cs_new_protected:Npn \__tblr_data_int_from_dec:n #1 { \tl_gset:Nx \g__tblr_data_int_from_value_tl { \dim_to_decimal_in_sp:n {#1 pt} } } \cs_new:Npn \__tblr_data_int_to_dec:n #1 { \dim_to_decimal:n {#1 sp} } \int_new:N \g__tblr_data_str_value_count_int \tl_gclear_new:c { g__tblr_data_0_to_str_tl } \cs_new_protected:Npn \__tblr_data_int_from_str:n #1 { \tl_if_exist:cTF { g__tblr_data_ \tl_to_str:n {#1} _to_int_tl } { \tl_gset_eq:Nc \g__tblr_data_int_from_value_tl { g__tblr_data_ \tl_to_str:n {#1} _to_int_tl } } { \int_gincr:N \g__tblr_data_str_value_count_int \tl_gset:cx { g__tblr_data_ \tl_to_str:n {#1} _to_int_tl } { \int_use:N \g__tblr_data_str_value_count_int } \tl_gset:cn { g__tblr_data_ \int_use:N \g__tblr_data_str_value_count_int _to_str_tl } { \exp_not:n {#1} } \tl_gset:Nx \g__tblr_data_int_from_value_tl { \int_use:N \g__tblr_data_str_value_count_int } } } \cs_new:Npn \__tblr_data_int_to_str:n #1 { \tl_use:c { g__tblr_data_#1_to_str_tl } } %% #1: data name; #2: data index; #3: key; #4: value \cs_new_protected:Npn \__tblr_data_gput:nnnn #1 #2 #3 #4 { \__tblr_data_int_from_value:nnn {#1} {#3} {#4} \__tblr_intarray_gset:cnn { g__tblr_#1_ \int_use:N \g_tblr_level_int _intarray } { \__tblr_data_key_to_int:nnn {#1} {#2} {#3} } { \g__tblr_data_int_from_value_tl } } \cs_generate_variant:Nn \__tblr_data_gput:nnnn { nnne, nnnV, nenn, nene, nenV, nVnn } %% #1: data name; #2: data index 1; #3: data index 2; #4: key; #5: value \cs_new_protected:Npn \__tblr_data_gput:nnnnn #1 #2 #3 #4 #5 { \__tblr_data_int_from_value:nnn {#1} {#4} {#5} \__tblr_intarray_gset:cnn { g__tblr_#1_ \int_use:N \g_tblr_level_int _intarray } { \__tblr_data_key_to_int:nnnn {#1} {#2} {#3} {#4} } { \g__tblr_data_int_from_value_tl } } \cs_generate_variant:Nn \__tblr_data_gput:nnnnn { nnnne, nnnnV, neenn, neene, neenV, neeen, nVVnn } %% #1: data name; #2: data index; #3: key \cs_new:Npn \__tblr_data_item:nnn #1 #2 #3 { \__tblr_data_int_to_value:nne {#1} {#3} { \intarray_item:cn { g__tblr_#1_ \int_use:N \g_tblr_level_int _intarray } { \__tblr_data_key_to_int:nnn {#1} {#2} {#3} } } } \cs_generate_variant:Nn \__tblr_data_item:nnn { nen } %% #1: data name; #2: data index 1; #3: data index 2; #4: key \cs_new:Npn \__tblr_data_item:nnnn #1 #2 #3 #4 { \__tblr_data_int_to_value:nne {#1} {#4} { \intarray_item:cn { g__tblr_#1_ \int_use:N \g_tblr_level_int _intarray } { \__tblr_data_key_to_int:nnnn {#1} {#2} {#3} {#4} } } } \cs_generate_variant:Nn \__tblr_data_item:nnnn { neen } \tl_new:N \l__tblr_data_key_tl \tl_new:N \l__tblr_data_index_tl \tl_new:N \l__tblr_data_index_two_tl \cs_new_protected:Npn \__tblr_data_log:n #1 { \use:c { __tblr_data_log_ \use:c { c__tblr_data_#1_index_number_tl } :n } {#1} \__tblr_prop_log:n {#1} } \cs_new_protected:cpn { __tblr_data_log_1:n } #1 { %\intarray_log:c { g__tblr_#1_ \int_use:N \g_tblr_level_int _intarray } \tl_set:Nx \l_tmpa_tl { g__tblr_#1_ \int_use:N \g_tblr_level_int _intarray } \tl_log:n { ----------~----------~----------~----------~---------- } \int_step_inline:nn { \intarray_count:c { \l_tmpa_tl } } { \__tblr_data_int_to_key:nnNN {#1} {##1} \l__tblr_data_index_tl \l__tblr_data_key_tl \tl_log:x { \space { #1 [\l__tblr_data_index_tl] / \l__tblr_data_key_tl } ~\space => ~\space { \__tblr_data_int_to_value:nVe {#1} \l__tblr_data_key_tl { \intarray_item:cn { \l_tmpa_tl } {##1} } } } } } \cs_new_protected:cpn { __tblr_data_log_2:n } #1 { %\intarray_log:c { g__tblr_#1_ \int_use:N \g_tblr_level_int _intarray } \tl_set:Nx \l_tmpa_tl { g__tblr_#1_ \int_use:N \g_tblr_level_int _intarray } \tl_log:n { ----------~----------~----------~----------~---------- } \int_step_inline:nn { \intarray_count:c { \l_tmpa_tl } } { \__tblr_data_int_to_key:nnNNN {#1} {##1} \l__tblr_data_index_tl \l__tblr_data_index_two_tl \l__tblr_data_key_tl \tl_log:x { \space { #1 [\l__tblr_data_index_tl][\l__tblr_data_index_two_tl] / \l__tblr_data_key_tl } ~\space => ~\space { \__tblr_data_int_to_value:nVe {#1} \l__tblr_data_key_tl { \intarray_item:cn { \l_tmpa_tl } {##1} } } } } } %% #1: data name; #2: row index; #3: key; #4: value \cs_new_protected:Npn \__tblr_data_gput_if_larger:nnnn #1 #2 #3 #4 { \__tblr_data_int_from_value:nnn {#1} {#3} {#4} \__tblr_array_gput_if_larger:cnn { g__tblr_#1_ \int_use:N \g_tblr_level_int _intarray } { \__tblr_data_key_to_int:nnn {#1} {#2} {#3} } { \g__tblr_data_int_from_value_tl } } \cs_generate_variant:Nn \__tblr_data_gput_if_larger:nnnn { nnne, nnnV, nene, nenV } \cs_new_protected:Npn \__tblr_array_gput_if_larger:Nnn #1 #2 #3 { \int_compare:nNnT {#3} > { \intarray_item:Nn #1 {#2} } { \__tblr_intarray_gset:Nnn #1 {#2} {#3} } } \cs_generate_variant:Nn \__tblr_array_gput_if_larger:Nnn { cnn } %% #1: data name; #2: data index; #3: key; #4: value \cs_new_protected:Npn \__tblr_data_gadd_dimen_value:nnnn #1 #2 #3 #4 { \__tblr_data_int_from_value:nnn {#1} {#3} {#4} \__tblr_array_gadd_value:cnn { g__tblr_#1_ \int_use:N \g_tblr_level_int _intarray } { \__tblr_data_key_to_int:nnn {#1} {#2} {#3} } { \g__tblr_data_int_from_value_tl } } \cs_generate_variant:Nn \__tblr_data_gadd_dimen_value:nnnn { nnne, nnnV, nenn, nene } \cs_new_protected:Npn \__tblr_array_gadd_value:Nnn #1 #2 #3 { \__tblr_intarray_gset:Nnn #1 {#2} { \intarray_item:Nn #1 {#2} + #3 } } \cs_generate_variant:Nn \__tblr_array_gadd_value:Nnn { cnn } \bool_new:N \g__tblr_use_intarray_bool \bool_gset_true:N \g__tblr_use_intarray_bool \AtBeginDocument { \bool_if:NF \g__tblr_use_intarray_bool { \cs_set_protected:Npn \__tblr_data_gput:nnnn #1 #2 #3 #4 { \__tblr_spec_gput:nnn {#1} { [#2] / #3 } {#4} } \cs_set_protected:Npn \__tblr_data_gput:nnnnn #1 #2 #3 #4 #5 { \__tblr_spec_gput:nnn {#1} { [#2][#3] / #4 } {#5} } \cs_set:Npn \__tblr_data_item:nnn #1 #2 #3 { \__tblr_spec_item:nn {#1} { [#2] / #3 } } \cs_set:Npn \__tblr_data_item:nnnn #1 #2 #3 #4 { \__tblr_spec_item:nn {#1} { [#2][#3] / #4 } } \cs_set_protected:Npn \__tblr_data_log:n #1 { \__tblr_spec_log:n {#1} } \cs_set_protected:Npn \__tblr_data_gput_if_larger:nnnn #1 #2 #3 #4 { \__tblr_spec_gput_if_larger:nnn {#1} { [#2] / #3 } {#4} } \cs_set_protected:Npn \__tblr_data_gput_if_larger:nnnnn #1 #2 #3 #4 #5 { \__tblr_spec_gput_if_larger:nnn {#1} { [#2][#3] / #4 } {#5} } \cs_set_protected:Npn \__tblr_data_gadd_dimen_value:nnnn #1 #2 #3 #4 { \__tblr_spec_gadd_dimen_value:nnn {#1} { [#2] / #3 } {#4} } \cs_set_protected:Npn \__tblr_data_gadd_dimen_value:nnnnn #1 #2 #3 #4 #5 { \__tblr_spec_gadd_dimen_value:nnn {#1} { [#2][#3] / #4 } {#5} } } } %%% -------------------------------------------------------- %%> \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 } [1] [] { \tl_if_blank:nTF {#1} { \int_step_inline:nnnn {1} {2} { \l_tblr_childs_total_tl } { \clist_put_right:Nn \l_tblr_childs_clist {##1} } } { \__tblr_child_selector_odd_or_even:nn { odd } {#1} } } \NewChildSelector { even } [1] [] { \tl_if_blank:nTF {#1} { \int_step_inline:nnnn {2} {2} { \l_tblr_childs_total_tl } { \clist_put_right:Nn \l_tblr_childs_clist {##1} } } { \__tblr_child_selector_odd_or_even:nn { even } {#1} } } \tl_new:N \l__tblr_child_from_tl \tl_new:N \l__tblr_child_to_tl %% #1: odd or even; #2: selector option \cs_new_protected:Npn \__tblr_child_selector_odd_or_even:nn #1 #2 { \seq_set_split:Nnn \l_tmpa_seq {-} { #2 - Z } \tl_set:Nx \l__tblr_child_from_tl { \seq_item:Nn \l_tmpa_seq {1} } \tl_set:Nx \l__tblr_child_to_tl { \seq_item:Nn \l_tmpa_seq {2} } \tl_use:c { int_if_ #1 :nF } { \l__tblr_child_from_tl } { \tl_set:Nx \l__tblr_child_from_tl { \int_eval:n { \l__tblr_child_from_tl + 1 } } } \__tblr_child_name_to_index:VN \l__tblr_child_to_tl \l__tblr_child_to_tl \int_step_inline:nnnn { \l__tblr_child_from_tl } {2} { \l__tblr_child_to_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_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_last_unbraced: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 { \tl_if_in:nnTF {##1} {-} { \__tblr_get_childs_normal_aux:w ##1 \scan_stop } { \__tblr_get_childs_normal_aux:w ##1 - ##1 \scan_stop } } } \cs_new_protected_nopar:Npn \__tblr_get_childs_normal_aux:w #1 - #2 \scan_stop { \__tblr_child_name_to_index:nN {#1} \l__tblr_child_from_tl \__tblr_child_name_to_index:nN {#2} \l__tblr_child_to_tl \int_step_inline:nnn { \l__tblr_child_from_tl } { \l__tblr_child_to_tl } { \clist_put_right:Nn \l_tblr_childs_clist {##1} } } \regex_const:Nn \c__tblr_child_name_regex { ^ [U-Z] $ } %% Convert U, V, W, X, Y, Z to the indexes of the last six childs, respectively \cs_new_protected_nopar:Npn \__tblr_child_name_to_index:nN #1 #2 { \regex_match:NnTF \c__tblr_child_name_regex {#1} { \tl_set:Nx #2 { \int_eval:n { \l_tblr_childs_total_tl + \int_from_alph:n {#1} - 26 } } } { \tl_set:Nx #2 { #1 } } } \cs_generate_variant:Nn \__tblr_child_name_to_index:nN { VN } %%% -------------------------------------------------------- %%> \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 ~ command ~ #1 already ~ 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 } %% we can not use \cs_if_exist:NTF here (see issue #328) \__tblr_cs_if_defined:NTF #1 { \cs_set_eq:cN { __tblr_table_command_ \cs_to_str:N #1 _saved:w } #1 } { \exp_args:NcV \NewDocumentCommand { __tblr_table_command_ \cs_to_str:N #1 _saved: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 _saved: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} } %% Add \empty as a table command so that users can write \\\empty\hline (see #328) \NewTableCommand\empty{} %% Table commands are defined only inside tblr environments, %% but some packages such as csvsimple need to use them outside tblr environments, %% therefore we define some of them first here. \ProvideDocumentCommand \SetHlines { o m m } {} \ProvideDocumentCommand \SetHline { o m m } {} \ProvideDocumentCommand \SetVlines { o m m } {} \ProvideDocumentCommand \SetVline { o m m } {} \ProvideDocumentCommand \SetCells { o m } {} \ProvideDocumentCommand \SetCell { o m } {} \ProvideDocumentCommand \SetRows { o m } {} \ProvideDocumentCommand \SetRow { o m } {} \ProvideDocumentCommand \SetColumns { o m } {} \ProvideDocumentCommand \SetColumn { o m } {} %%% -------------------------------------------------------- %%> \section{New Content Commands} %%% -------------------------------------------------------- %% We need to emulate or fix some commands such as \diagbox in other packages %% These commands must be defined with \NewContentCommand command %% We only enable them inside tblr environment to avoid potential conflict \clist_new:N \g__tblr_content_commands_clist \msg_new:nnn { tabularray } { defined-content-command } { Content ~ command ~ #1 already ~ defined! } \NewDocumentCommand \NewContentCommand { m O{0} o m } { \clist_if_in:NnTF \g__tblr_content_commands_clist { #1 } { \msg_error:nnn { tabularray } { defined-content-command } { #1 } \clist_log:N \g__tblr_content_commands_clist } { \__tblr_make_xparse_arg_spec:nnN { #2 } { #3 } \l__tblr_a_tl \exp_args:NcV \NewDocumentCommand { __tblr_content_command_ \cs_to_str:N #1 :w } \l__tblr_a_tl { #4 } \clist_gput_right:Nn \g__tblr_content_commands_clist { #1 } } } \cs_new_protected:Npn \__tblr_enable_content_commands: { \clist_map_inline:Nn \g__tblr_content_commands_clist { \cs_set_eq:Nc ##1 { __tblr_content_command_ \cs_to_str:N ##1 :w } } } %%% -------------------------------------------------------- %%> \section{New Dash Styles} %%% -------------------------------------------------------- %% \NewDashStyle commands \dim_zero_new:N \rulewidth \dim_set:Nn \rulewidth {0.4pt} \prop_new:N \g__tblr_defined_hdash_styles_prop \prop_new:N \g__tblr_defined_vdash_styles_prop \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_const:Nn \@tblr@dash { dash } \tl_const: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 \tl_new:N \l__tblr_hline_leftpos_tl % left position \tl_new:N \l__tblr_hline_rightpos_tl % right position \bool_new:N \l__tblr_hline_endpos_bool % whether set positions only for both ends \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_spec_item:ne { hline } { [\int_use:N \c@rownum] / @hline-count } } %% \l__tblr_hline_count_tl may be empty when rowspec has extra |'s \int_compare:nNnTF { \l__tblr_hline_count_tl + 0 } = {0} { \tl_set:Nx \l__tblr_hline_num_tl { 1 } \__tblr_spec_gput:nen { 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_spec_gput:nee { 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}, leftpos .code:n = \tl_set:Nx \l__tblr_hline_leftpos_tl {#1}, rightpos .code:n = \tl_set:Nx \l__tblr_hline_rightpos_tl {#1}, l .meta:n = { leftpos = #1 }, l .default:n = { -0.8 }, r .meta:n = { rightpos = #1 }, r .default:n = { -0.8 }, lr .meta:n = { leftpos = #1, rightpos = #1 }, lr .default:n = { -0.8 }, endpos .bool_set:N = \l__tblr_hline_endpos_bool, 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_set_hline_option:nnn { ##1 } { @dash } { \l__tblr_hline_dash_tl } \tl_if_empty:NF \l__tblr_hline_wd_tl { \__tblr_set_hline_option:nnn { ##1 } { wd } { \l__tblr_hline_wd_tl } } \tl_if_empty:NF \l__tblr_hline_fg_tl { \__tblr_set_hline_option:nnn { ##1 } { fg } { \l__tblr_hline_fg_tl } } } \tl_if_empty:NF \l__tblr_hline_leftpos_tl { \bool_if:NTF \l__tblr_hline_endpos_bool { \__tblr_set_hline_option:nnn { \clist_item:Nn \l_tblr_childs_clist {1} } { leftpos } { \l__tblr_hline_leftpos_tl } } { \clist_map_inline:Nn \l_tblr_childs_clist { \__tblr_set_hline_option:nnn { ##1 } { leftpos } { \l__tblr_hline_leftpos_tl } } } } \tl_if_empty:NF \l__tblr_hline_rightpos_tl { \bool_if:NTF \l__tblr_hline_endpos_bool { \__tblr_set_hline_option:nnn { \clist_item:Nn \l_tblr_childs_clist {-1} } { rightpos } { \l__tblr_hline_rightpos_tl } } { \clist_map_inline:Nn \l_tblr_childs_clist { \__tblr_set_hline_option:nnn { ##1 } { rightpos } { \l__tblr_hline_rightpos_tl } } } } } %% #1: column; #2: key; #3: value \cs_new_protected_nopar:Npn \__tblr_set_hline_option:nnn #1 #2 #3 { \__tblr_spec_gput:nee { hline } { [\int_use:N \c@rownum][#1](\l__tblr_hline_num_tl) / #2 } { #3 } } \msg_new:nnn { tabularray } { obsolete-firsthline } { \firsthline ~ is ~ obsolete; ~ use ~ 'baseline=T' ~ instead. } \msg_new:nnn { tabularray } { obsolete-lasthline } { \lasthline ~ is ~ obsolete; ~ use ~ 'baseline=B' ~ instead. } \NewTableCommand \firsthline [1] [] { \msg_error:nn { tabularray } { obsolete-firsthline } } \NewTableCommand \lasthline [1] [] { \msg_error:nn { tabularray } { obsolete-lasthline } } %% \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 \tl_new:N \l__tblr_vline_abovepos_tl % above position \tl_new:N \l__tblr_vline_belowpos_tl % below position \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_spec_item:ne { vline } { [\int_use:N \c@colnum] / @vline-count } } %% \l__tblr_vline_count_tl may be empty when colspec has extra |'s \int_compare:nNnTF { \l__tblr_vline_count_tl + 0 } = {0} { \tl_set:Nx \l__tblr_vline_num_tl { 1 } \__tblr_spec_gput:nen { 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_spec_gput:nee { 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}, abovepos .code:n = \tl_set:Nx \l__tblr_vline_abovepos_tl {#1}, belowpos .code:n = \tl_set:Nx \l__tblr_vline_belowpos_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_spec_gput:nee { 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_spec_gput:nee { 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_spec_gput:nee { vline } { [##1][\int_use:N \c@colnum](\l__tblr_vline_num_tl) / fg } { \l__tblr_vline_fg_tl } } \tl_if_empty:NF \l__tblr_vline_abovepos_tl { \__tblr_spec_gput:nee { vline } { [##1][\int_use:N \c@colnum](\l__tblr_vline_num_tl) / abovepos } { \l__tblr_vline_abovepos_tl } } \tl_if_empty:NF \l__tblr_vline_belowpos_tl { \__tblr_spec_gput:nee { vline } { [##1][\int_use:N \c@colnum](\l__tblr_vline_num_tl) / belowpos } { \l__tblr_vline_belowpos_tl } } } } %%% -------------------------------------------------------- %%> \section{Set Hborders and Vborders} %%% -------------------------------------------------------- %% Hborder holds keys not related to a specified hline \NewTableCommand \hborder [1] { \tblr_set_hborder:n {#1} } \cs_new_protected:Npn \tblr_set_hborder:n #1 { \keys_set:nn { tblr-hborder } {#1} } \cs_new_protected:Npn \tblr_set_hborder:nn #1 #2 { \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_hborder:n {#2} } \group_end: } %% This function is called when parsing table specifications %% Note that #1 always includes an outer pair of braces \cs_new_protected:Npn \__tblr_set_hborder_aux:nn #1 #2 { \tblr_set_hborder:nn #1 {#2} } \cs_generate_variant:Nn \__tblr_set_hborder_aux:nn { Vn } \keys_define:nn { tblr-hborder } { abovespace .code:n = \__tblr_row_gput_above:ne { belowsep } { \dim_eval:n {#1} }, belowspace .code:n = \__tblr_row_gput:ne { abovesep } { \dim_eval:n {#1} }, abovespace+ .code:n = \__tblr_row_gadd_dimen_above:ne { belowsep } { \dim_eval:n {#1} }, belowspace+ .code:n = \__tblr_row_gadd_dimen:ne { abovesep } { \dim_eval:n {#1} }, pagebreak .code:n = \__tblr_hborder_gput_pagebreak:n {#1}, pagebreak .default:n = yes, baseline .code:n = \__tblr_outer_gput_spec:ne { baseline } { - \int_use:N \c@rownum }, } \tl_const:Nn \c__tblr_pagebreak_yes_tl { 1 } \tl_const:Nn \c__tblr_pagebreak_auto_tl { 0 } \tl_const:Nn \c__tblr_pagebreak_no_tl { -1 } \cs_new_protected:Npn \__tblr_hborder_gput_pagebreak:n #1 { \tl_if_exist:cT { c__tblr_pagebreak_ #1 _tl } { \__tblr_spec_gput:nee { hline } { [\int_use:N \c@rownum] / @pagebreak } { \tl_use:c { c__tblr_pagebreak_ #1 _tl } } } } %% Vborder holds keys not related to a specified vline \NewTableCommand \vborder [1] { \tblr_set_vborder:n {#1} } \cs_new_protected:Npn \tblr_set_vborder:n #1 { \keys_set:nn { tblr-vborder } {#1} } \cs_new_protected:Npn \tblr_set_vborder:nn #1 #2 { \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_vborder:n {#2} } \group_end: } %% This function is called when parsing table specifications %% Note that #1 always includes an outer pair of braces \cs_new_protected:Npn \__tblr_set_vborder_aux:nn #1 #2 { \tblr_set_vborder:nn #1 {#2} } \cs_generate_variant:Nn \__tblr_set_vborder_aux:nn { Vn } \keys_define:nn { tblr-vborder } { leftspace .code:n = \__tblr_column_gput_left:ne { rightsep } { \dim_eval:n {#1} }, rightspace .code:n = \__tblr_column_gput:ne { leftsep } { \dim_eval:n {#1} }, leftspace+ .code:n = \__tblr_column_gadd_dimen_left:ne { rightsep } { \dim_eval:n {#1} }, rightspace+ .code:n = \__tblr_column_gadd_dimen:ne { leftsep } { \dim_eval:n {#1} }, } %%% -------------------------------------------------------- %%> \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 } { halign .code:n = \__tblr_cell_gput:nn { halign } {#1}, valign .code:n = \__tblr_cell_gput:nn { valign } {#1}, j .meta:n = { halign = j }, l .meta:n = { halign = l }, c .meta:n = { halign = c }, r .meta:n = { halign = r }, t .meta:n = { valign = t }, p .meta:n = { valign = t }, m .meta:n = { valign = m }, b .meta:n = { valign = b }, h .meta:n = { valign = h }, f .meta:n = { valign = f }, wd .code:n = \__tblr_cell_gput:ne { width } {#1}, bg .code:n = \__tblr_cell_gput:ne { background } {#1}, fg .code:n = \__tblr_cell_gput:ne { foreground } {#1}, font .code:n = \__tblr_cell_gput:nn { font } { #1 \selectfont }, mode .code:n = \__tblr_cell_gput:nn { mode } {#1}, $ .meta:n = { mode = math }, $$ .meta:n = { mode = dmath }, cmd .code:n = \__tblr_cell_gput:nn { cmd } {#1}, preto .code:n = \__tblr_cell_preto_text:n {#1}, appto .code:n = \__tblr_cell_appto_text:n {#1}, unknown .code:n = \__tblr_cell_unknown_key:V \l_keys_key_str, } \cs_new_protected:Npn \__tblr_cell_gput:nn #1 #2 { \__tblr_data_gput:neenn { cell } { \int_use:N \c@rownum } { \int_use:N \c@colnum } {#1} {#2} } \cs_generate_variant:Nn \__tblr_cell_gput:nn { ne } \cs_new_protected:Npn \__tblr_cell_gput:nnnn #1 #2 #3 #4 { \__tblr_data_gput:nnnnn { cell } {#1} {#2} {#3} {#4} } \cs_generate_variant:Nn \__tblr_cell_gput:nnnn { nenn, ennn, eenn, nene, enne, eene } \tl_new:N \l__tblr_cell_text_tl \cs_new_protected:Npn \__tblr_cell_preto_text:n #1 { \__tblr_cell_preto_text:een { \int_use:N \c@rownum } { \int_use:N \c@colnum } {#1} } \cs_new_protected:Npn \__tblr_cell_preto_text:nnn #1 #2 #3 { \tl_set:Nx \l__tblr_cell_text_tl { \__tblr_spec_item:nn { text } { [#1][#2] } } \tl_put_left:Nn \l__tblr_cell_text_tl {#3} \__tblr_spec_gput:nnV { text } { [#1][#2] } \l__tblr_cell_text_tl } \cs_generate_variant:Nn \__tblr_cell_preto_text:nnn { nen, enn, een } \cs_new_protected:Npn \__tblr_cell_appto_text:n #1 { \__tblr_cell_appto_text:een { \int_use:N \c@rownum } { \int_use:N \c@colnum } {#1} } \cs_new_protected:Npn \__tblr_cell_appto_text:nnn #1 #2 #3 { \tl_set:Nx \l__tblr_cell_text_tl { \__tblr_spec_item:ne { text } { [#1][#2] } } \tl_put_right:Nn \l__tblr_cell_text_tl {#3} \__tblr_spec_gput:neV { text } { [#1][#2] } \l__tblr_cell_text_tl } \cs_generate_variant:Nn \__tblr_cell_appto_text:nnn { nen, enn, een } \cs_new_protected:Npn \__tblr_cell_unknown_key:n #1 { \regex_match:NnTF \c__tblr_is_color_key_regex {#1} { \__tblr_data_gput:neene { cell } { \int_use:N \c@rownum } { \int_use:N \c@colnum } { background } {#1} } { \tl_set_rescan:Nnn \l__tblr_v_tl {} {#1} \__tblr_data_gput:neene { 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 { inner } { rowspan } { true } \__tblr_data_gput:neenn { cell } { \int_use:N \c@rownum } { \int_use:N \c@colnum } { rowspan } {#1} } \int_compare:nNnT { #2 } > { 1 } { \__tblr_prop_gput:nnn { inner } { colspan } { true } \__tblr_data_gput:neenn { 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_data_gput:neenn { cell } { \l__tblr_i_tl } { \l__tblr_j_tl } { omit } {1} } \int_compare:nNnF { \l__tblr_i_tl } = { \c@rownum } { \__tblr_spec_gput:nen { hline } { [\l__tblr_i_tl][\l__tblr_j_tl] / omit } {true} } \int_compare:nNnF { \l__tblr_j_tl } = { \c@colnum } { \__tblr_spec_gput:nee { vline } { [\l__tblr_i_tl][\l__tblr_j_tl] / omit } {true} } } } %% Make continuous borders for multirow cells \tl_set:Nx \l__tblr_n_tl { \int_max:nn { \__tblr_spec_item:ne { vline } { [\int_use:N \c@colnum] / @vline-count } } { 1 } } \int_step_variable:nnNn { \c@rownum } { \int_eval:n { \c@rownum + #1 - 2 } } \l__tblr_i_tl { \__tblr_spec_gput:nee { vline } { [\l__tblr_i_tl][\int_use:N \c@colnum](\l__tblr_n_tl) / belowpos } {1} \__tblr_spec_gput:nee { vline } { [\l__tblr_i_tl][\int_eval:n {\c@colnum + #2}](1) / belowpos } {1} } } \cs_generate_variant:Nn \__tblr_set_span_spec:nn { VV } %% Obsolete \multicolumn and \multirow commands \msg_new:nnn { tabularray } { obsolete-multicolumn } { \multicolumn ~ is ~ obsolete; ~ use ~ \SetCell ~ instead. } \msg_new:nnn { tabularray } { obsolete-multirow } { \multirow ~ is ~ obsolete; ~ use ~ \SetCell ~ instead. } \NewTableCommand \multicolumn [2] { \msg_error:nn { tabularray } { obsolete-multicolumn } } \NewTableCommand \multirow [3] [m] { \msg_error:nn { tabularray } { obsolete-multirow } } %%% -------------------------------------------------------- %%> \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 } { halign .code:n = \__tblr_column_gput_cell:nn { halign } {#1}, valign .code:n = \__tblr_column_gput_cell:nn { valign } {#1}, j .meta:n = { halign = j }, l .meta:n = { halign = l }, c .meta:n = { halign = c }, r .meta:n = { halign = r }, t .meta:n = { valign = t }, p .meta:n = { valign = t }, m .meta:n = { valign = m }, b .meta:n = { valign = b }, h .meta:n = { valign = h }, f .meta:n = { valign = f }, bg .code:n = \__tblr_column_gput_cell:nn { background } {#1}, fg .code:n = \__tblr_column_gput_cell:nn { foreground } {#1}, font .code:n = \__tblr_column_gput_cell:nn { font } { #1 \selectfont }, mode .code:n = \__tblr_column_gput_cell:nn { mode } {#1}, $ .meta:n = { mode = math }, $$ .meta:n = { mode = dmath }, cmd .code:n = \__tblr_column_gput_cell:nn { cmd } {#1}, wd .code:n = \__tblr_column_gput:ne { width } { \dim_eval:n {#1} }, co .code:n = \__tblr_column_gput:ne { coefficient } {#1}, preto .code:n = \__tblr_preto_text_for_every_column_cell:n {#1}, appto .code:n = \__tblr_appto_text_for_every_column_cell:n {#1}, leftsep .code:n = \__tblr_column_gput:ne { leftsep } { \dim_eval:n {#1} }, rightsep .code:n = \__tblr_column_gput:ne { rightsep } { \dim_eval:n {#1} }, colsep .meta:n = { leftsep = #1, rightsep = #1}, leftsep+ .code:n = \__tblr_column_gadd_dimen:ne { leftsep } { \dim_eval:n {#1} }, rightsep+ .code:n = \__tblr_column_gadd_dimen:ne { 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: key; #2: value \cs_new_protected:Npn \__tblr_column_gput:nn #1 #2 { \__tblr_data_gput:nenn { column } { \int_use:N \c@colnum } {#1} {#2} } \cs_generate_variant:Nn \__tblr_column_gput:nn { ne } \cs_new_protected:Npn \__tblr_column_gput_left:nn #1 #2 { \__tblr_data_gput:nenn { column } { \int_eval:n { \c@colnum - 1 } } {#1} {#2} } \cs_generate_variant:Nn \__tblr_column_gput_left:nn { ne } \cs_new_protected:Npn \__tblr_column_gadd_dimen:nn #1 #2 { \__tblr_data_gadd_dimen_value:nenn { column } { \int_use:N \c@colnum } {#1} {#2} } \cs_generate_variant:Nn \__tblr_column_gadd_dimen:nn { ne } \cs_new_protected:Npn \__tblr_column_gadd_dimen_left:nn #1 #2 { \__tblr_data_gadd_dimen_value:nenn { column } { \int_eval:n { \c@colnum - 1 } } {#1} {#2} } \cs_generate_variant:Nn \__tblr_column_gadd_dimen_left:nn { ne } %% #1: key; #2: value \cs_new_protected:Npn \__tblr_column_gput_cell:nn #1 #2 { \int_step_inline:nn { \c@rowcount } { \__tblr_cell_gput:nenn {##1} { \int_use:N \c@colnum } {#1} {#2} } } \cs_generate_variant:Nn \__tblr_column_gput_cell:nn { ne } \cs_new_protected:Npn \__tblr_preto_text_for_every_column_cell:n #1 { \int_step_inline:nn { \c@rowcount } { \__tblr_cell_preto_text:nen {##1} { \int_use:N \c@colnum } {#1} } } \cs_new_protected:Npn \__tblr_appto_text_for_every_column_cell:n #1 { \int_step_inline:nn { \c@rowcount } { \__tblr_cell_appto_text:nen {##1} { \int_use:N \c@colnum } {#1} } } \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_column_gput:ne { coefficient } {#1} } { \regex_match:NnTF \c__tblr_is_color_key_regex {#1} { \__tblr_column_gput_cell:nn { background } {#1} } { \tl_set_rescan:Nnn \l__tblr_v_tl {} {#1} \__tblr_column_gput:ne { 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 } { halign .code:n = \__tblr_row_gput_cell:nn { halign } {#1}, valign .code:n = \__tblr_row_gput_cell:nn { valign } {#1}, j .meta:n = { halign = j }, l .meta:n = { halign = l }, c .meta:n = { halign = c }, r .meta:n = { halign = r }, t .meta:n = { valign = t }, p .meta:n = { valign = t }, m .meta:n = { valign = m }, b .meta:n = { valign = b }, h .meta:n = { valign = h }, f .meta:n = { valign = f }, bg .code:n = \__tblr_row_gput_cell:nn { background } {#1}, fg .code:n = \__tblr_row_gput_cell:nn { foreground } {#1}, font .code:n = \__tblr_row_gput_cell:nn { font } { #1 \selectfont }, mode .code:n = \__tblr_row_gput_cell:nn { mode } {#1}, $ .meta:n = { mode = math }, $$ .meta:n = { mode = dmath }, cmd .code:n = \__tblr_row_gput_cell:nn { cmd } {#1}, ht .code:n = \__tblr_row_gput:ne { height } { \dim_eval:n {#1} }, co .code:n = \__tblr_row_gput:ne { coefficient } {#1}, preto .code:n = \__tblr_preto_text_for_every_row_cell:n {#1}, appto .code:n = \__tblr_appto_text_for_every_row_cell:n {#1}, abovesep .code:n = \__tblr_row_gput:ne { abovesep } { \dim_eval:n {#1} }, belowsep .code:n = \__tblr_row_gput:ne { belowsep } { \dim_eval:n {#1} }, rowsep .meta:n = { abovesep = #1, belowsep = #1}, abovesep+ .code:n = \__tblr_row_gadd_dimen:ne { abovesep } { \dim_eval:n {#1} }, belowsep+ .code:n = \__tblr_row_gadd_dimen:ne { belowsep } { \dim_eval:n {#1} }, rowsep+ .meta:n = { abovesep+ = #1, belowsep+ = #1}, baseline .code:n = \__tblr_outer_gput_spec:ne { baseline } { \int_use:N \c@rownum }, unknown .code:n = \__tblr_row_unknown_key:V \l_keys_key_str, } %% #1: key; #2: value \cs_new_protected:Npn \__tblr_row_gput:nn #1 #2 { \__tblr_data_gput:nenn { row } { \int_use:N \c@rownum } {#1} {#2} } \cs_generate_variant:Nn \__tblr_row_gput:nn { ne } \cs_new_protected:Npn \__tblr_row_gput_above:nn #1 #2 { \__tblr_data_gput:nenn { row } { \int_eval:n { \c@rownum - 1 } } {#1} {#2} } \cs_generate_variant:Nn \__tblr_row_gput_above:nn { ne } \cs_new_protected:Npn \__tblr_row_gadd_dimen:nn #1 #2 { \__tblr_data_gadd_dimen_value:nenn { row } { \int_use:N \c@rownum } {#1} {#2} } \cs_generate_variant:Nn \__tblr_row_gadd_dimen:nn { ne } \cs_new_protected:Npn \__tblr_row_gadd_dimen_above:nn #1 #2 { \__tblr_data_gadd_dimen_value:nenn { row } { \int_eval:n { \c@rownum - 1 } } {#1} {#2} } \cs_generate_variant:Nn \__tblr_row_gadd_dimen_above:nn { ne } %% #1: key; #2: value \cs_new_protected:Npn \__tblr_row_gput_cell:nn #1 #2 { \int_step_inline:nn { \c@colcount } { \__tblr_cell_gput:ennn { \int_use:N \c@rownum } {##1} {#1} {#2} } } \cs_generate_variant:Nn \__tblr_row_gput_cell:nn { ne } \cs_new_protected:Npn \__tblr_preto_text_for_every_row_cell:n #1 { \int_step_inline:nn { \c@colcount } { \__tblr_cell_preto_text:enn { \int_use:N \c@rownum } {##1} {#1} } } \cs_new_protected:Npn \__tblr_appto_text_for_every_row_cell:n #1 { \int_step_inline:nn { \c@colcount } { \__tblr_cell_appto_text:enn { \int_use:N \c@rownum } {##1} {#1} } } \cs_new_protected:Npn \__tblr_row_unknown_key:n #1 { \regex_match:NnTF \c__tblr_is_number_key_regex {#1} { \__tblr_data_gput:nene { row } { \int_use:N \c@rownum } { coefficient } {#1} } { \regex_match:NnTF \c__tblr_is_color_key_regex {#1} { \__tblr_row_gput_cell:nn { background } {#1} } { \tl_set_rescan:Nnn \l__tblr_v_tl {} {#1} \__tblr_row_gput:ne { height } { \dim_eval:n { \l__tblr_v_tl } } } } } \cs_generate_variant:Nn \__tblr_row_unknown_key:n { V } \NewTableCommand \pagebreak [1] [4] { \hborder { pagebreak = yes } } \NewTableCommand \nopagebreak [1] [4] { \hborder { pagebreak = no } } %%% -------------------------------------------------------- %%> \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_data_gput:nene { column } { \int_use:N \c@colnum } { leftsep } { \dim_eval:n {#1} } } \tl_if_blank:nF {#2} { \__tblr_preto_text_for_every_column_cell:n {#2} } \__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_data_gput:nene { row } { \int_use:N \c@rownum } { abovesep } { \dim_eval:n { #1 } } } \tl_if_blank:nF {#2} { \__tblr_preto_text_for_every_row_cell:n {#2} } \__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_data_gput:nene { column } { \int_eval:n {\c@colnum - 1} } { rightsep } { \dim_eval:n {#1} } } \tl_if_blank:nF {#2} { \group_begin: \int_decr:N \c@colnum \__tblr_appto_text_for_every_column_cell:n {#2} \group_end: } \__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_data_gput:nene { row } { \int_eval:n {\c@rownum - 1} } { belowsep } { \dim_eval:n {#1} } } \tl_if_blank:nF {#2} { \group_begin: \int_decr:N \c@rownum \__tblr_appto_text_for_every_row_cell:n {#2} \group_end: } \__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_error:nnnn { tabularray } { used-colrow-type } { Row } {#1} } { \msg_error: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] } \NewColumnRowType { j } { Q[j] } \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} { %% Note that #1 may be an active character (see issue #58) \cs:w tblr_ \g__tblr_column_or_row_tl _type_ \token_to_str:N #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{Set Environments and New Environments} %%% -------------------------------------------------------- \tl_new:N \l__tblr_initial_tblr_outer_tl \tl_set:Nn \l__tblr_initial_tblr_outer_tl { halign = c, baseline = m, headsep = 6pt, footsep = 6pt, presep = 1.5\bigskipamount, postsep = 1.5\bigskipamount, } %% #1: env name; #2: specifications \NewDocumentCommand \SetTblrInner { O{tblr} m } { \clist_map_inline:nn {#1} { \tl_put_right:cn { l__tblr_default_ ##1 _inner_tl } { , #2 } } \ignorespaces } \cs_new_eq:NN \SetTblrDefault \SetTblrInner %% #1: env name; #2: specifications \NewDocumentCommand \SetTblrOuter { O{tblr} m } { \clist_map_inline:nn {#1} { \tl_put_right:cn { l__tblr_default_ ##1 _outer_tl } { , #2 } } \ignorespaces } %% #1: env name \NewDocumentCommand \NewTblrEnviron { m } { \NewDocumentEnvironment {#1} { O{c} m +b } { \__tblr_environ_code:nnnn {#1} {##1} {##2} {##3} } { } \tl_new:c { l__tblr_default_ #1 _inner_tl } \tl_new:c { l__tblr_default_ #1 _outer_tl } \tl_set_eq:cN { l__tblr_default_ #1 _outer_tl } \l__tblr_initial_tblr_outer_tl } %% Create tblr and longtblr environments \NewTblrEnviron { tblr } \NewTblrEnviron { longtblr } \SetTblrOuter [ longtblr ] { long } \NewTblrEnviron { talltblr } \SetTblrOuter [ talltblr ] { tall } \tl_new:N \l__tblr_env_name_tl \bool_new:N \l__tblr_math_mode_bool %% Main environment code %% We need to add \group_align_safe_begin: and \group_align_safe_end: %% to make tabularray correctly nest in align environment (see issue #143) \cs_new_protected:Npn \__tblr_environ_code:nnnn #1 #2 #3 #4 { \group_align_safe_begin: \int_gincr:N \g__tblr_table_count_int \tl_set:Nn \l__tblr_env_name_tl {#1} \mode_if_math:TF { \bool_set_true:N \l__tblr_math_mode_bool } { \bool_set_false:N \l__tblr_math_mode_bool } \__tblr_builder:nnn {#2} {#3} {#4} \group_align_safe_end: } \bool_new:N \lTblrMeasuringBool %% Read, split and build the table \cs_new_protected:Npn \__tblr_builder:nnn #1 #2 #3 { \int_gincr:N \g_tblr_level_int \__tblr_hook_use:n { tabularray/trial/before } \bool_set_true:N \lTblrMeasuringBool \__tblr_clear_prop_lists: \__tblr_clear_spec_lists: \LogTblrTracing { step = init ~ table ~ outer ~ spec} \__tblr_init_table_outer_spec: \LogTblrTracing { step = parse ~ table ~ options } \__tblr_parse_table_option:n {#1} \LogTblrTracing { outer } \LogTblrTracing { option } \__tblr_enable_table_commands: \LogTblrTracing { step = split ~ table} \__tblr_split_table:n {#3} \LogTblrTracing { command } \bool_if:NT \g__tblr_use_intarray_bool { \__tblr_init_table_data: } \LogTblrTracing { step = init ~ table ~ inner ~ spec} \__tblr_init_table_inner_spec: \LogTblrTracing { inner } \LogTblrTracing { step = parse ~ table ~ inner ~ spec} \__tblr_parse_table_spec:n {#2} \LogTblrTracing { step = execute ~ table ~ commands} \__tblr_execute_table_commands: \__tblr_disable_table_commands: \__tblr_functional_calculation: \LogTblrTracing { step = calculate ~ cell ~ and ~ line ~ sizes} \__tblr_enable_content_commands: \__tblr_calc_cell_and_line_sizes: \bool_set_false:N \lTblrMeasuringBool \__tblr_hook_use:n { tabularray/trial/after } \LogTblrTracing { step = build ~ the ~ whole ~ table} \__tblr_build_whole: \int_gdecr:N \g_tblr_level_int } %%% -------------------------------------------------------- %%> \section{Split Table Contents} %%% -------------------------------------------------------- %% 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 } \tl_new:N \l__tblr_body_tl \seq_new:N \l__tblr_lines_seq %% Split table content to cells and store them %% #1: table content \cs_new_protected:Npn \__tblr_split_table:n #1 { \tl_set:Nn \l__tblr_body_tl {#1} \tblr_modify_table_body: \int_zero:N \c@rowcount \int_zero:N \c@colcount \__tblr_split_table_to_lines:NN \l__tblr_body_tl \l__tblr_lines_seq \__tblr_split_lines_to_cells:N \l__tblr_lines_seq } \tl_new:N \l__tblr_expand_tl \cs_set_eq:NN \__tblr_hook_split_before: \prg_do_nothing: \cs_new_protected:Npn \tblr_modify_table_body: { \__tblr_hook_split_before: \tl_set:Nx \l__tblr_expand_tl { \__tblr_spec_item:nn { outer } { expand } } \tl_map_inline:Nn \l__tblr_expand_tl { \__tblr_expand_table_body:NN \l__tblr_body_tl ##1 } } %% Expand every occurrence of the specified macro once %% #1: tl with table content; #2: macro to be expanded \cs_new_protected:Npn \__tblr_expand_table_body:NN #1 #2 { \tl_set_eq:NN \l_tmpa_tl #1 \tl_clear:N #1 \cs_set_protected:Npn \__tblr_expand_table_body_aux:w ##1 #2 { \tl_put_right:Nn #1 {##1} \peek_meaning:NTF \q_stop { \use_none:n } { \exp_last_unbraced:NV \__tblr_expand_table_body_aux:w #2 } } \exp_last_unbraced:NV \__tblr_expand_table_body_aux:w \l_tmpa_tl #2 \q_stop } %% Split table content to a sequence of lines %% #1: tl with table contents, #2: resulting sequence of lines \cs_new_protected:Npn \__tblr_split_table_to_lines:NN #1 #2 { \__tblr_insert_braces:N #1 \seq_set_split:NnV \l_tmpa_seq { \\ } #1 \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 { \hborder { pagebreak = no } } \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_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_data_gadd_dimen_value:nene { 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 \__tblr_trim_par_space_tokens:N \l_tmpa_tl \int_incr:N \c@colnum \__tblr_extract_table_commands:N \l_tmpa_tl \__tblr_trim_par_space_tokens:N \l_tmpa_tl \__tblr_spec_gput:neV { text } { [#1][\int_use:N \c@colnum] } \l_tmpa_tl } %% 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_all:nTF { { \int_compare_p:nNn {#1} = {\c@rowcount} } { \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 } } } \regex_const:Nn \c__tblr_trim_left_par_space_regex { ^ \c{par} ? \s * } \regex_const:Nn \c__tblr_trim_right_space_par_regex { \s * \c{par} ? $ } \cs_new_protected:Npn \__tblr_trim_par_space_tokens:N #1 { \regex_replace_once:NnN \c__tblr_trim_left_par_space_regex {} #1 \regex_replace_once:NnN \c__tblr_trim_right_space_par_regex {} #1 } %%% -------------------------------------------------------- %%> \section{Extract Table Commands from Cell Text} %%% -------------------------------------------------------- %% Extract table commands defined with \NewTableCommand from cell text \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 \exp_last_unbraced:NV \__tblr_extract_table_commands_next:n #1 \q_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 from a pair of braces \cs_new_protected:Npn \__tblr_extract_table_commands_next:n #1 { \tl_if_single_token:nTF {#1} { \clist_if_in:NnTF \g__tblr_table_commands_clist { #1 } { \__tblr_extract_one_table_command:N #1 } { \token_if_eq_meaning:NNF #1 \q_stop { \__tblr_save_real_cell_text:w #1 } } } { \__tblr_save_real_cell_text:w {#1} } } \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 } { \__tblr_extract_table_commands_next:n } } %% The outermost set of braces of cell text #1 will be removed \cs_new_protected:Npn \__tblr_save_real_cell_text:w #1 \q_stop { \tl_set:Nn \l__tblr_saved_cell_text_after_table_commands_tl {#1} } %%% -------------------------------------------------------- %%> \section{Initialize Table Inner Specifications} %%% -------------------------------------------------------- \prop_new:N \g__tblr_initial_table_prop \prop_new:N \g__tblr_initial_rows_prop \prop_new:N \g__tblr_initial_columns_prop \prop_new:N \g__tblr_initial_cells_prop \prop_new:N \g__tblr_initial_hlines_prop \prop_new:N \g__tblr_initial_vlines_prop \prop_gset_from_keyval:Nn \g__tblr_initial_table_prop { stretch = 1, rulesep = 2pt, } \prop_gset_from_keyval:Nn \g__tblr_initial_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_initial_columns_prop { leftsep = 6pt, rightsep = 6pt, width = -1pt, % column width unset coefficient = 0, % column coefficient unset @col-width = 0pt, } \prop_gset_from_keyval:Nn \g__tblr_initial_cells_prop { halign = j, valign = t, width = -1pt, % cell width unset rowspan = 1, colspan = 1, omit = 0, } \prop_gset_from_keyval:Nn \g__tblr_initial_hlines_prop { @hline-count = 0, } \prop_gset_from_keyval:Nn \g__tblr_initial_vlines_prop { @vline-count = 0, } \tl_new:N \l__tblr_inner_spec_measure_tl \tl_new:N \l__tblr_inner_spec_verb_tl \cs_new_protected:Npn \__tblr_init_table_inner_spec: { \prop_map_inline:Nn \g__tblr_initial_table_prop { \__tblr_prop_gput:nxn { inner } { ##1 } {##2} } \int_step_variable:nNn { \c@rowcount } \l__tblr_i_tl { \prop_map_inline:Nn \g__tblr_initial_rows_prop { \__tblr_data_gput:nVnn { row } \l__tblr_i_tl {##1} {##2} } \prop_map_inline:Nn \g__tblr_initial_hlines_prop { \__tblr_spec_gput:nen { hline } { [\l__tblr_i_tl] / ##1 } {##2} } \int_step_variable:nNn { \c@colcount } \l__tblr_j_tl { \prop_map_inline:Nn \g__tblr_initial_cells_prop { \__tblr_data_gput:neeen { cell } { \l__tblr_i_tl } { \l__tblr_j_tl } {##1} {##2} } } } \prop_map_inline:Nn \g__tblr_initial_hlines_prop { \__tblr_spec_gput:nen { hline } { [\int_eval:n { \c@rowcount + 1}] / ##1 } {##2} } \int_step_variable:nNn { \c@colcount } \l__tblr_j_tl { \prop_map_inline:Nn \g__tblr_initial_columns_prop { \__tblr_data_gput:nenn { column } { \l__tblr_j_tl } {##1} {##2} } \prop_map_inline:Nn \g__tblr_initial_vlines_prop { \__tblr_spec_gput:nen { vline } { [\l__tblr_j_tl] / ##1 } {##2} } } \prop_map_inline:Nn \g__tblr_initial_vlines_prop { \__tblr_spec_gput:nen { vline } { [\int_eval:n { \c@colcount + 1}] / ##1 } {##2} } \tl_clear:N \l__tblr_inner_spec_measure_tl \tl_clear:N \l__tblr_inner_spec_verb_tl \keys_set:nv { tblr } { l__tblr_default_ \l__tblr_env_name_tl _inner_tl } } %%% -------------------------------------------------------- %%> \section{Parse Table Inner Specifications} %%% -------------------------------------------------------- \clist_new:N \g__tblr_table_known_keys_clist \clist_gset:Nn \g__tblr_table_known_keys_clist { colspec, rowspec, column, row, cell, hline, vline, hborder, vborder, width, rowhead, rowfoot, columns, rows, cells, hlines, vlines, % hborders, vborders, leftsep, rightsep, colsep, abovesep, belowsep, rowsep, rulesep, baseline, hspan, vspan, stretch, verb, delimiter } \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}, vspan .code:n = \__tblr_keys_gput:nn { vspan } {#1}, stretch .code:n = \__tblr_keys_gput:nn { stretch } {#1}, verb .tl_set:N = \l__tblr_inner_spec_verb_tl, verb .default:n = lite, 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 }, rulesep .code:n = \__tblr_keys_gput:nn { rulesep } {#1}, rowhead .code:n = \__tblr_keys_gput:nn { rowhead } {#1}, rowfoot .code:n = \__tblr_keys_gput:nn { rowfoot } {#1}, delimiter .code:n = \__tblr_set_delimiter:n {#1}, baseline .code:n = \__tblr_outer_gput_spec:nn { baseline } {#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 { inner } {#1} {#2} } \cs_generate_variant:Nn \__tblr_keys_gput:nn { nx } \keys_define:nn { tblr-delimiter } { left .code:n = \__tblr_keys_gput:nn { delim-left } { \left #1 }, right .code:n = \__tblr_keys_gput:nn { delim-right } { \right #1 } } \cs_new_protected:Npn \__tblr_set_delimiter:n #1 { \keys_set:nn { tblr-delimiter } {#1} } %%% -------------------------------------------------------- %%> \section{Initialize and Parse Table Outer Specifications} %%% -------------------------------------------------------- \msg_new:nnn { tabularray } { used-theme-name } { theme ~ name ~ #1 ~ has ~ been ~ used! } %% #1: theme names; #2: template and style commands \NewDocumentCommand \NewTblrTheme { m +m } { \tl_if_exist:cTF { g__tblr_theme_ #1 _code_tl } { \msg_error:nnn { tabularray } { used-theme-name } { #1 } } { \tl_set:cn { g__tblr_theme_ #1 _code_tl } {#2} \ignorespaces } } \cs_new_protected:Npn \__tblr_use_theme:n #1 { \ignorespaces \tl_use:c { g__tblr_theme_ #1 _code_tl } } \cs_new_protected:Npn \__tblr_init_table_outer_spec: { \keys_set:nv { tblr-outer } { l__tblr_default_ \l__tblr_env_name_tl _outer_tl } } \cs_new_protected:Npn \__tblr_parse_table_option:n #1 { \keys_set:nn { tblr-outer } {#1} } \keys_define:nn { tblr-outer } { long .code:n = \__tblr_outer_gput_spec:nn { long } { true }, tall .code:n = \__tblr_outer_gput_spec:nn { tall } { true }, halign .code:n = \__tblr_outer_gput_spec:nn { halign } {#1}, baseline .code:n = \__tblr_outer_gput_spec:nn { baseline } {#1}, l .meta:n = { halign = l }, c .meta:n = { halign = c }, r .meta:n = { halign = r }, t .meta:n = { baseline = t }, T .meta:n = { baseline = T }, m .meta:n = { baseline = m }, M .meta:n = { baseline = M }, b .meta:n = { baseline = b }, B .meta:n = { baseline = B }, valign .meta:n = { baseline = #1 }, % obsolete, will be removed some day expand .code:n = \__tblr_outer_gput_spec:nn { expand } {#1}, expand+ .code:n = \__tblr_outer_gconcat_spec:nn { expand } {#1}, headsep .code:n = \__tblr_outer_gput_spec:nn { headsep } {#1}, footsep .code:n = \__tblr_outer_gput_spec:nn { footsep } {#1}, presep .code:n = \__tblr_outer_gput_spec:nn { presep } {#1}, postsep .code:n = \__tblr_outer_gput_spec:nn { postsep } {#1}, theme .code:n = \__tblr_use_theme:n {#1}, caption .code:n = \__tblr_outer_gput_spec:nn { caption } {#1}, entry .code:n = \__tblr_outer_gput_spec:nn { entry } {#1}, label .code:n = \__tblr_outer_gput_spec:nn { label } {#1}, unknown .code:n = \__tblr_table_option_key:Vn \l_keys_key_str {#1}, } \cs_new_protected:Npn \__tblr_outer_gput_spec:nn #1 #2 { \__tblr_spec_gput:nen { outer } {#1} {#2} } \cs_generate_variant:Nn \__tblr_outer_gput_spec:nn { ne } \cs_new_protected:Npn \__tblr_outer_gconcat_spec:nn #1 #2 { \__tblr_outer_gput_spec:ne {#1} { \__tblr_spec_item:nn { outer } { #1 } \exp_not:n { #2 } } } \regex_const:Nn \c__tblr_option_key_name_regex { ^ [A-Za-z\-] + $ } \msg_new:nnn { tabularray } { unknown-outer-key } { Unknown ~ outer ~ key ~ name ~ #1! } \cs_new_protected:Npn \__tblr_table_option_key:nn #1 #2 { \regex_match:NnTF \c__tblr_option_key_name_regex {#1} { \msg_error:nnn { tabularray } { unknown-outer-key } {#1} } { \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} } \tl_set:Nx \l__tblr_c_tl { \tl_head:N \l__tblr_b_tl } \use:c { __tblr_outer_gput_ \l__tblr_a_tl :Vn } \l__tblr_c_tl {#2} } } } \cs_generate_variant:Nn \__tblr_table_option_key:nn { Vn } \cs_new_protected:Npn \__tblr_outer_gput_note:nn #1 #2 { \__tblr_prop_gput:nnn { note } {#1} {#2} } \cs_generate_variant:Nn \__tblr_outer_gput_note:nn { Vn } \cs_new_protected:Npn \__tblr_outer_gput_remark:nn #1 #2 { \__tblr_prop_gput:nnn { remark } {#1} {#2} } \cs_generate_variant:Nn \__tblr_outer_gput_remark:nn { Vn } \cs_new_protected:Npn \__tblr_outer_gput_more:nn #1 #2 { \__tblr_prop_gput:nnn { more } {#1} {#2} } \cs_generate_variant:Nn \__tblr_outer_gput_more:nn { Vn } %%% -------------------------------------------------------- %%> \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_prepare_stretch: \__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: } %% prepare stretch option of the table \fp_new:N \l__tblr_stretch_fp \dim_new:N \l__tblr_strut_dp_dim \dim_new:N \l__tblr_strut_ht_dim \cs_new_protected:Npn \__tblr_prepare_stretch: { \fp_set:Nn \l__tblr_stretch_fp { \__tblr_prop_item:nn { inner } { stretch } } \fp_compare:nNnTF \l__tblr_stretch_fp > \c_zero_fp { \dim_set:Nn \l__tblr_strut_dp_dim { \fp_use:N \l__tblr_stretch_fp \box_dp:N \strutbox } \dim_set:Nn \l__tblr_strut_ht_dim { \fp_use:N \l__tblr_stretch_fp \box_ht:N \strutbox } \cs_set_eq:NN \__tblr_leave_vmode: \mode_leave_vertical: \cs_set_eq:NN \__tblr_process_stretch: \__tblr_process_stretch_real: } { \cs_set_eq:NN \__tblr_process_stretch: \prg_do_nothing: \fp_compare:nNnTF \l__tblr_stretch_fp < \c_zero_fp { \cs_set_eq:NN \__tblr_leave_vmode: \@setminipage } % for lists (see issue #99) { \cs_set_eq:NN \__tblr_leave_vmode: \mode_leave_vertical: } } } \cs_new_eq:NN \__tblr_leave_vmode: \mode_leave_vertical: \cs_new_protected:Npn \__tblr_process_stretch_real: { \dim_compare:nNnT \l__tblr_strut_dp_dim > { \box_dp:N \l_tmpb_box } { \box_set_dp:Nn \l_tmpa_box { \box_dp:N \l_tmpa_box - \box_dp:N \l_tmpb_box + \l__tblr_strut_dp_dim } \box_set_dp:Nn \l_tmpb_box { \l__tblr_strut_dp_dim } } \dim_compare:nNnT \l__tblr_strut_ht_dim > { \box_ht:N \l_tmpa_box } { \hbox_set:Nn \l_tmpa_box { \box_use:N \l_tmpa_box } \hbox_set:Nn \l_tmpb_box { \box_use:N \l_tmpb_box } \box_set_ht:Nn \l_tmpb_box { \box_ht:N \l_tmpb_box - \box_ht:N \l_tmpa_box + \l__tblr_strut_ht_dim } \box_set_ht:Nn \l_tmpa_box { \l__tblr_strut_ht_dim } %% return vbox for vertical-align: \c__tblr_middle_m_tl \vbox_set_top:Nn \l_tmpa_box { \box_use:N \l_tmpa_box } \vbox_set:Nn \l_tmpb_box { \box_use:N \l_tmpb_box } } } \cs_new_eq:NN \__tblr_process_stretch: \__tblr_process_stretch_real: %% 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_spec_item:ne { vline } { [#2] / @vline-count } } \int_compare:nNnT { \l__tblr_n_tl } > {0} { \tl_set:Nx \l__tblr_s_tl { \__tblr_prop_item:ne { inner } { 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_spec_gput_if_larger:nee { vline } { [#2](##1) / @vline-width } { \l__tblr_w_tl } \dim_add:Nn \l__tblr_w_dim { \__tblr_spec_item:nn { vline } { [#2](##1) / @vline-width } } \dim_add:Nn \l__tblr_w_dim { \l__tblr_s_tl } } \dim_add:Nn \l__tblr_w_dim { - \l__tblr_s_tl } } \__tblr_spec_gput_if_larger:nee { 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_spec_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_spec_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 } { %% When using text as vline, we need to omit abovepos and belowpos. \unskip \hbox_set:Nn \l__tblr_d_box { \bool_if:NTF \l__tblr_math_mode_bool { $ \l__tblr_b_tl $ } { \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 \vss } \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_spec_item:ne { hline } { [#1] / @hline-count } } \int_compare:nNnT { \l__tblr_n_tl } > {0} { \tl_set:Nx \l__tblr_s_tl { \__tblr_prop_item:ne { inner } { 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_spec_gput_if_larger:nee { hline } { [#1](##1) / @hline-height } { \l__tblr_h_tl } \dim_add:Nn \l__tblr_h_dim { \__tblr_spec_item:nn { hline } { [#1](##1) / @hline-height } } \dim_add:Nn \l__tblr_h_dim { \l__tblr_s_tl } } \dim_add:Nn \l__tblr_h_dim { - \l__tblr_s_tl } } \__tblr_spec_gput_if_larger:nee { 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_spec_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_spec_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 } { \bool_if:NTF \l__tblr_math_mode_bool { $ \l__tblr_b_tl $ } { \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_data_item:neen { cell } {#1} {#2} { halign } } \tl_set:Nx \l__tblr_v_tl { \__tblr_data_item:neen { 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 \__tblr_save_counters:n { table } \int_step_inline:nn { \c@rowcount } { \int_incr:N \c@rownum \int_zero:N \c@colnum \__tblr_update_rowsep_registers: \tl_set:Nx \l__tblr_h_tl { \__tblr_data_item:nen { row } { \int_use:N \c@rownum } { height } } %% We didn't initialize row heights with -1pt \dim_compare:nNnF { \l__tblr_h_tl } = { 0pt } { \__tblr_data_gput:nenV { row } { \int_use:N \c@rownum } { @row-height } \l__tblr_h_tl } \int_step_inline:nn { \c@colcount } { \int_incr:N \c@colnum \__tblr_update_colsep_registers: \__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 } } \__tblr_restore_counters:n { table } \int_step_inline:nn { \c@colcount } { \tl_set:Nx \l__tblr_w_tl { \__tblr_data_item:nen { column } {##1} { width } } \dim_compare:nNnF { \l__tblr_w_tl } < { 0pt } { \__tblr_data_gput:nenV { column } {##1} { @col-width } \l__tblr_w_tl } } } \cs_new_protected:Npn \__tblr_update_rowsep_registers: { \dim_set:Nn \abovesep { \__tblr_data_item:nen { row } { \int_use:N \c@rownum } { abovesep } } \dim_set:Nn \belowsep { \__tblr_data_item:nen { row } { \int_use:N \c@rownum } { belowsep } } } \cs_new_protected:Npn \__tblr_update_colsep_registers: { \dim_set:Nn \leftsep { \__tblr_data_item:nen { column } { \int_use:N \c@colnum } { leftsep } } \dim_set:Nn \rightsep { \__tblr_data_item:nen { column } { \int_use:N \c@colnum } { rightsep } } } %% 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 { \int_compare:nNnTF { \__tblr_data_item:neen { cell } {#1} {#2} { omit } } > {0} { \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 } } } \tl_new:N \l__tblr_cell_fg_tl \tl_new:N \l__tblr_cell_cmd_tl \tl_new:N \l__tblr_cell_mode_tl \bool_new:N \l__tblr_cell_math_mode_bool \tl_const:Nn \l__tblr_cell_math_style_tl { \relax } \tl_const:Nn \l__tblr_cell_imath_style_tl { \textstyle } \tl_const:Nn \l__tblr_cell_dmath_style_tl { \displaystyle } %% 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_spec_item:ne { text } {[#1][#2]} } %% when the cell text is guarded by a pair of curly braces, %% we unbrace it and ignore cmd option of the cell, see issue #90. \bool_lazy_and:nnTF { \tl_if_single_p:N \l__tblr_c_tl } { \exp_args:NV \tl_if_head_is_group_p:n \l__tblr_c_tl } { \exp_last_unbraced:NNV \tl_set:Nn \l__tblr_c_tl \l__tblr_c_tl } { \tl_set:Nx \l__tblr_cell_cmd_tl { \__tblr_data_item:neen { cell } {#1} {#2} { cmd } } \tl_if_empty:NF \l__tblr_cell_cmd_tl { \tl_set:Nx \l__tblr_c_tl { \exp_not:V \l__tblr_cell_cmd_tl { \exp_not:V \l__tblr_c_tl } } } } \tl_set:Nx \l__tblr_cell_mode_tl { \__tblr_data_item:neen { cell } {#1} {#2} { mode } } \tl_if_empty:NT \l__tblr_cell_mode_tl { \bool_if:NTF \l__tblr_math_mode_bool { \tl_set:Nn \l__tblr_cell_mode_tl { math } } { \tl_set:Nn \l__tblr_cell_mode_tl { text } } } \tl_if_eq:NnTF \l__tblr_cell_mode_tl { text } { \bool_set_false:N \l__tblr_cell_math_mode_bool } { \bool_set_true:N \l__tblr_cell_math_mode_bool \tl_put_left:Nv \l__tblr_c_tl { l__tblr_cell_ \l__tblr_cell_mode_tl _style_tl } \tl_put_left:Nn \l__tblr_c_tl { $ } \tl_put_right:Nn \l__tblr_c_tl { $ } } \tl_set:Nx \l__tblr_f_tl { \__tblr_data_item:neen { cell } {#1} {#2} { font } } \tl_set:Nx \l__tblr_w_tl { \__tblr_data_item:neen { cell } {#1} {#2} { width } } \dim_compare:nNnT { \l__tblr_w_tl } < { 0pt } % cell width unset { \int_compare:nNnT { \__tblr_data_item:neen { cell } {#1} {#2} { colspan } } < {2} { \tl_set:Nx \l__tblr_w_tl { \__tblr_data_item:nen { column } {#2} { width } } } } \dim_compare:nNnT { \l__tblr_w_tl } < { 0pt } % column width unset { \__tblr_save_counters:n { cell } \bool_if:NTF \l__tblr_cell_math_mode_bool { %% Note that font = \boldmath will increase cell width (issue #137) \hbox_set:Nn \l_tmpa_box { \l__tblr_f_tl \l__tblr_c_tl } \tl_set:Nx \l__tblr_w_tl { \box_wd:N \l_tmpa_box } } { \__tblr_get_cell_size_with_box: } \__tblr_restore_counters:n { cell } } \tl_put_left:NV \l__tblr_c_tl \l__tblr_f_tl \tl_set:Nx \l__tblr_cell_fg_tl { \__tblr_data_item:neen { cell } {#1} {#2} { foreground } } \tl_if_empty:NF \l__tblr_cell_fg_tl { \exp_args:NV \color \l__tblr_cell_fg_tl } \__tblr_get_vcell_and_sizes:NN \l__tblr_c_tl \l__tblr_w_tl \group_end: } \cs_new_protected:Npn \__tblr_get_cell_size_with_box: { \tl_if_eq:NnTF \l__tblr_inner_spec_measure_tl { vbox } { \__tblr_get_cell_size_with_vbox: } { \__tblr_get_cell_size_with_hbox: } } %% Varwidth won't work as expected when \color command occurs in it, %% and we can not fix this problem with \leavevmode command. %% See https://tex.stackexchange.com/q/460489. %% But we need to use \color command for fg option, %% or users may use it in the middle of the cell text, %% so we have redefine \color command and disable it before measuring cell. %% In order to correctly measure an enumerate environment, %% we need to enclose varwidth with NoHyper environment (see issue #196). \NewDocumentCommand \__tblr_fake_color_command:w { o m } { } \cs_new_protected:Npn \__tblr_get_cell_size_with_vbox: { \hbox_set:Nn \l_tmpa_box { \cs_set_eq:NN \color \__tblr_fake_color_command:w \begin{tblrNoHyper} \begin{varwidth}{\paperwidth} \l__tblr_f_tl \__tblr_rescan_cell_tokens:N \l__tblr_c_tl \end{varwidth} \end{tblrNoHyper} } \tl_set:Nx \l__tblr_w_tl { \box_wd:N \l_tmpa_box } } \cs_new_protected:Npn \__tblr_get_cell_size_with_hbox: { \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__tblr_f_tl \__tblr_rescan_cell_tokens:N \l_tmpa_tl } \tl_set:Nx \l__tblr_w_tl { \dim_max:nn { \l__tblr_w_tl } { \box_wd:N \l_tmpa_box } } } } %% #1: cell text; #2: box width \cs_new_protected:Npn \__tblr_get_vcell_and_sizes:NN #1 #2 { \group_begin: \vbox_set:Nn \l_tmpb_box { \__tblr_make_vcell_text:NN #1 #2 } \vbox_set_top:Nn \l_tmpa_box { \vbox_unpack:N \l_tmpb_box } \__tblr_process_stretch: \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: } %% #1: cell text; #2: box width %% All halign commands are defined at the beginning of the file \cs_new_protected:Npn \__tblr_make_vcell_text:NN #1 #2 { \dim_set:Nn \tex_hsize:D { #2 } \TblrParboxRestore \cs:w __tblr_halign_command_ \g__tblr_cell_halign_tl : \cs_end: \__tblr_leave_vmode: \bool_if:NTF \l__tblr_cell_math_mode_bool { #1 } { \__tblr_rescan_cell_tokens:N #1 } } %% When using verb option, there is an end-of-line character at the end. %% This character causes extra horizontal space at the end when "measure=hbox", %% or causes extra vertical space at the end with "measure=vbox". %% Therefore we have to use an \empty to remove it. %% See https://tex.stackexchange.com/q/213659 \cs_new_protected:Npn \__tblr_rescan_cell_tokens:N #1 { \tl_if_empty:NTF \l__tblr_inner_spec_verb_tl { #1 } { %% insert space characters after some control sequences first (issue #112) \regex_replace_all:nnN { (\c{[A-Za-z]*}) ([A-Za-z]) } { \1 \ \2 } #1 \regex_replace_all:nnN { . } { \c{string} \0 } #1 \tl_set:Nx #1 { #1 \noexpand \empty } \exp_args:NV \tex_scantokens:D #1 } } %% #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_data_item:neen { cell } {#1} {#2} { colspan } } \int_compare:nNnT { \l__tblr_c_tl } > {1} { \__tblr_data_gput:neene { 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_data_item:neen { cell } {#1} {#2} { rowspan } } \int_compare:nNnT { \l__tblr_r_tl } > {1} { \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_data_gput_if_larger:nene { 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_data_gput_if_larger:nene { 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_data_gput:neenV { cell } {#1} {#2} { @cell-height } \l__tblr_u_tl \__tblr_data_gput:neenV { 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_data_item:nen { 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_data_item:nen { row } {#1} { @row-upper } } \tl_set:Nx \l__tblr_b_tl { \__tblr_data_item:nen { 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_data_gput:nenV { 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_data_gput:nenV { 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_data_gput:nene { row } {#1} { @row-height } { \dim_eval:n { \l__tblr_a_tl + \l__tblr_b_tl } } } } { \tl_set:Nx \l__tblr_e_tl { \__tblr_data_item:nen { row } {#1} { @row-head } } \tl_set:Nx \l__tblr_f_tl { \__tblr_data_item:nen { row } {#1} { @row-foot } } \dim_compare:nNnT {#4} > {\l__tblr_e_tl} { \__tblr_data_gput:nene { row } {#1} { @row-head } { \dim_use:N #4 } } \dim_compare:nNnT {#5} > {\l__tblr_f_tl} { \__tblr_data_gput:nene { 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_data_gput:nene { 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_data_item:nen { 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_data_gput:nene { 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 } { \msg_warning:nnx { tabularray } { table-width-too-small } { \dim_abs:n { \l__column_target_dim } } } { \prop_if_empty:NF \l__column_coefficient_prop { \__tblr_adjust_extendable_column_width: } } } \cs_new_protected:Npn \__tblr_collect_extendable_column_width: { \tl_set:Nx \l_tmpa_tl { \__tblr_prop_item:nn { inner } { 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_data_item:nen { column } { \l__tblr_j_tl } { width } } \tl_set:Nx \l__tblr_b_tl { \__tblr_data_item:nen { column } { \l__tblr_j_tl } { coefficient } } \tl_set:Nx \l__tblr_c_tl { \__tblr_data_item:nen { column } { \l__tblr_j_tl } { @col-width } } \dim_compare:nNnTF { \l__tblr_a_tl } < { 0pt } % column width unset { \dim_compare:nNnTF { \l__tblr_b_tl pt } = { 0pt } { \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_spec_item:ne { vline } { [\l__tblr_j_tl] / @vline-width } } \tl_set:Nx \l__tblr_b_tl { \__tblr_data_item:nen { column } { \l__tblr_j_tl } { leftsep } } \tl_set:Nx \l__tblr_c_tl { \__tblr_data_item:nen { 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_spec_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. %% We reset @row-height, etc for \linewidth graphics in X columns (issue #80) \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_data_gput:nnne { column } {##1} { width } {##2} \__tblr_data_gput:nnnn { column } {##1} { @col-width } { 0pt } } \int_step_inline:nn { \c@rowcount } { \__tblr_data_gput:nnnn { row } {##1} { @row-height } { 0pt } \__tblr_data_gput:nnnn { row } {##1} { @row-head } { 0pt } \__tblr_data_gput:nnnn { row } {##1} { @row-foot } { 0pt } \__tblr_data_gput:nnnn { row } {##1} { @row-upper } { 0pt } \__tblr_data_gput:nnnn { row } {##1} { @row-lower } { 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 { inner } { colspan } { \__tblr_collect_column_widths_skips: \str_if_eq:xnTF { \__tblr_prop_item:ne { inner } { 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 { inner } { 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_data_item:nen { column } { \int_eval:n { \l__tblr_j_tl - 1 } } { rightsep } + \__tblr_spec_item:ne { vline } { [\l__tblr_j_tl] / @vline-width } + \__tblr_data_item:nen { 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_data_item:nen { 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_data_item:nen { row } { \int_eval:n {\l__tblr_i_tl - 1} } { belowsep } + \__tblr_spec_item:ne { hline } { [\l__tblr_i_tl] / @hline-height } + \__tblr_data_item:nen { 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_data_item:nen { 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_data_item:neen { cell } { \l__tblr_i_tl } { \l__tblr_j_tl } { colspan } } \int_compare:nNnT { \l__tblr_a_tl } > {1} { \__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_data_item:neen { 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_data_item:neen { cell } { \l__tblr_i_tl } { \l__tblr_j_tl } { rowspan } } \int_compare:nNnT { \l__tblr_a_tl } > {1} { \tl_set:Nx \l__tblr_v_tl { \__tblr_data_item:neen { 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_data_item:nen { row } { \l__tblr_i_tl } { @row-head } } \__tblr_data_gput:neenV { 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_data_item:nen { row } { \int_eval:n { \l__tblr_i_tl + \l__tblr_a_tl - 1 } } { @row-foot } } \__tblr_data_gput:neenV { 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_data_item:neen { cell } { \l__tblr_i_tl } { \l__tblr_j_tl } { @cell-height } + \__tblr_data_item:neen { 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: { \str_if_eq:xnTF { \__tblr_prop_item:ne { inner } { hspan } } { even } { \__tblr_distribute_span_sizes_even:xNN { \int_use:N \c@colcount } \l__tblr_col_item_skip_size_prop \l__tblr_col_span_size_prop } { \__tblr_distribute_span_sizes_default: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: { \str_if_eq:xnTF { \__tblr_prop_item:ne { inner } { vspan } } { even } { \__tblr_distribute_span_sizes_even:nNN { \int_use:N \c@rowcount } \l__tblr_row_item_skip_size_prop \l__tblr_row_span_size_prop } { \__tblr_distribute_span_sizes_default: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_distribute_span_sizes_default: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_distribute_span_sizes_default:nNN { x } %% #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_distribute_span_sizes_even:nNN #1 #2 #3 { \prop_clear:N \l_tmpa_prop \prop_map_inline:Nn #3 { \__tblr_get_span_from_to:w ##1 \dim_set:Nn \l_tmpa_dim {##2} \dim_sub:Nn \l_tmpa_dim { \prop_item:Ne #2 { item[\l__tblr_a_tl] } } \int_step_inline:nnn { \l__tblr_a_tl + 1 } { \l__tblr_b_tl } { \dim_sub:Nn \l_tmpa_dim { \prop_item:Ne #2 { skip[####1] } + \prop_item:Nn #2 { item[####1] } } } \__tblr_do_if_tracing:nn { cellspan } { \tl_log:x { \l__tblr_a_tl -> \l__tblr_b_tl : ~ \dim_use:N \l_tmpa_dim } } \dim_compare:nNnT {\l_tmpa_dim} > {0pt} { \tl_set:Nx \l_tmpa_tl { \dim_eval:n { \l_tmpa_dim / (\l__tblr_b_tl - \l__tblr_a_tl + 1) } } \int_step_inline:nnn { \l__tblr_a_tl } { \l__tblr_b_tl } { \__tblr_put_if_larger:NnV \l_tmpa_prop {####1} \l_tmpa_tl } } } \__tblr_do_if_tracing:nn { cellspan } { \prop_log:N \l_tmpa_prop } \prop_map_inline:Nn \l_tmpa_prop { \__tblr_add_dimen_value:Nnn #2 {item[##1]} {##2} } \__tblr_do_if_tracing:nn { cellspan } { \prop_log:N #2 } } \cs_generate_variant:Nn \__tblr_distribute_span_sizes_even:nNN { x } \cs_new_protected:Npn \__tblr_get_span_from_to:w (#1-#2) { \tl_set:Nn \l__tblr_a_tl {#1} \tl_set:Nn \l__tblr_b_tl {#2} } \cs_new_protected:Npn \__tblr_set_all_column_widths: { \int_step_variable:nNn { \c@colcount } \l__tblr_j_tl { \__tblr_data_gput:nene { column } { \l__tblr_j_tl } { 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_data_item:nen { row } { \l__tblr_i_tl } { @row-head } } \tl_set:Nx \l__tblr_d_tl { \__tblr_data_item:nen { 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_data_gput:nene { row } { \l__tblr_i_tl } { @row-height } { \l__tblr_a_tl } } } %% 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_data_item:neen { cell } { \l__tblr_i_tl } { \l__tblr_j_tl } { colspan } } \int_compare:nNnT { \l__tblr_a_tl } > {1} { \__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_data_gput:neene { 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_set:Nn #3 { \prop_item:Ne \l__tblr_col_item_skip_size_prop { item[#1] } } \int_step_inline:nnn { #1 + 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{Header and Footer Styles} %%% -------------------------------------------------------- \prop_new:N \l__tblr_element_styles_prop \cs_new_protected:Npn \__tblr_style_put:nn #1 #2 { \prop_put:Nnn \l__tblr_element_styles_prop {#1} {#2} } \cs_generate_variant:Nn \__tblr_style_put:nn { nV, ne, en, eV } \cs_new:Npn \__tblr_style_item:n #1 { \prop_item:Nn \l__tblr_element_styles_prop {#1} } \cs_new_protected:Npn \__tblr_style_log: { \prop_log:N \l__tblr_element_styles_prop } \tl_new:N \l__tblr_element_name_tl \tl_new:N \l__tblr_element_styles_tl %% #1: list of element names; #2: element styles \NewDocumentCommand \SetTblrStyle { m +m } { \tl_set:Nn \l__tblr_element_styles_tl {#2} \keys_set:nn { tblr-element } {#1} \ignorespaces } \keys_define:nn { tblr-element } { head .meta:n = { firsthead, middlehead, lasthead }, foot .meta:n = { firstfoot, middlefoot, lastfoot }, unknown .code:n = \__tblr_set_element_styles:V \l_keys_key_str, } \cs_new_protected:Npn \__tblr_set_element_styles:n #1 { \tl_set:Nn \l__tblr_element_name_tl {#1} \keys_set:nV { tblr-style } \l__tblr_element_styles_tl } \cs_generate_variant:Nn \__tblr_set_element_styles:n { V } \keys_define:nn { tblr-style } { halign .code:n = \__tblr_element_gput_style:nn { halign } {#1}, l .meta:n = { halign = l }, c .meta:n = { halign = c }, r .meta:n = { halign = r }, j .meta:n = { halign = j }, fg .code:n = \__tblr_element_gput_style:nn { fg } {#1}, font .code:n = \__tblr_element_gput_style:nn { font } {#1}, hang .code:n = \__tblr_element_gput_style:nn { hang } {#1}, indent .code:n = \__tblr_element_gput_style:nn { indent } {#1}, unknown .code:n = \__tblr_element_unknown_key:Vn \l_keys_key_str {#1}, } \cs_new_protected:Npn \__tblr_element_gput_style:nn #1 #2 { \__tblr_style_put:en { \l__tblr_element_name_tl / #1 } {#2} } \cs_new_protected:Npn \__tblr_element_unknown_key:nn #1 #2 { \regex_match:NnTF \c__tblr_is_color_key_regex {#1} { \__tblr_style_put:en { \l__tblr_element_name_tl / fg } {#1} } { %% unknown key name has been changed to string in \l_keys_key_str \tl_set_rescan:Nnn \l__tblr_f_tl {} {#1} \tl_if_head_eq_catcode:VNTF \l__tblr_f_tl \scan_stop: { \__tblr_style_put:eV { \l__tblr_element_name_tl / font } \l__tblr_f_tl } { \__tblr_style_put:en { \l__tblr_element_name_tl / #1 } {#2} } } } \cs_generate_variant:Nn \__tblr_element_unknown_key:nn { Vn } %%% -------------------------------------------------------- %%> \section{Helper Functions for Templates} %%% -------------------------------------------------------- \tl_new:N \l__tblr_template_name_tl \tl_new:N \l__tblr_template_code_tl \keys_define:nn { tblr-def-template } { unknown .code:n = \__tblr_def_template:V \l_keys_key_str, } %% #1: head/foot element; #2: template name; #3: template code %% If the template name = default, we enable the template at once %% Otherwise, we may enable the template by using \SetTblrTemplate command \NewDocumentCommand \DefTblrTemplate { m m +m } { \tl_set:Nn \l__tblr_template_name_tl {#2} \tl_set:Nn \l__tblr_template_code_tl {#3} \keys_set:nn { tblr-def-template } {#1} \ignorespaces } \cs_new_eq:NN \DeclareTblrTemplate \DefTblrTemplate \cs_new_protected:Npn \__tblr_def_template:n #1 { \tl_set_eq:cN { l__tblr_template_ #1 _ \l__tblr_template_name_tl _tl } \l__tblr_template_code_tl } \cs_generate_variant:Nn \__tblr_def_template:n { V } \keys_define:nn { tblr-set-template } { unknown .code:n = \__tblr_set_template:V \l_keys_key_str, } %% #1: head/foot element; #2: template name \NewDocumentCommand \SetTblrTemplate { m m } { \tl_set:Nn \l__tblr_template_name_tl {#2} \keys_set:nn { tblr-set-template } {#1} \ignorespaces } \cs_new_protected:Npn \__tblr_set_template:n #1 { \tl_set_eq:cc { l__tblr_template_ #1 _default_tl } { l__tblr_template_ #1 _ \l__tblr_template_name_tl _tl } } \cs_generate_variant:Nn \__tblr_set_template:n { V } \NewExpandableDocumentCommand \GetTblrStyle { m m } { \__tblr_style_item:n { #1 / #2 } } \NewDocumentCommand \UseTblrFont { m } { \GetTblrStyle {#1} { font } \selectfont } \tl_new:N \l__tblr_use_color_tl \NewDocumentCommand \UseTblrColor { m } { \tl_set:Nx \l__tblr_use_color_tl { \GetTblrStyle {#1} { fg } } \tl_if_empty:NF \l__tblr_use_color_tl { \color { \l__tblr_use_color_tl } } } %% All halign commands are defined at the beginning of the file \NewDocumentCommand \UseTblrAlign { m } { \use:c { __tblr_halign_command_ \GetTblrStyle {#1} { halign } : } } \tl_new:N \l__tblr_use_hang_tl \NewDocumentCommand \UseTblrHang { m } { \tl_set:Nx \l__tblr_use_hang_tl { \GetTblrStyle {#1} { hang } } \tl_if_empty:NF \l__tblr_use_hang_tl { \tl_put_left:Nn \l__tblr_use_hang_tl { \hangafter = 1 \relax \hangindent = } \tl_put_right:Nn \l__tblr_use_hang_tl { \relax } \exp_args:NV \everypar \l__tblr_use_hang_tl } } \tl_new:N \l__tblr_use_indent_tl \NewDocumentCommand \UseTblrIndent { m } { \tl_set:Nx \l__tblr_use_indent_tl { \GetTblrStyle {#1} { indent } } \tl_if_empty:NF \l__tblr_use_indent_tl { \exp_args:NNV \setlength \parindent \l__tblr_use_indent_tl } } \AtBeginDocument { \@ifpackageloaded{xcolor}{}{\RenewDocumentCommand \UseTblrColor {m} {}} } %% #1: head/foot element; #2: template name \NewExpandableDocumentCommand \ExpTblrTemplate { m m } { \tl_use:c { l__tblr_template_ #1 _ #2 _tl } } %% #1: head/foot element; #2: template name \NewDocumentCommand \UseTblrTemplate { m m } { \group_begin: \UseTblrFont {#1} \UseTblrColor {#1} \tl_use:c { l__tblr_template_ #1 _ #2 _tl } \group_end: } \NewDocumentCommand \MapTblrNotes { +m } { \__tblr_prop_map_inline:nn { note } { \tl_set_rescan:Nnn \InsertTblrNoteTag {} {##1} \tl_set:Nn \InsertTblrNoteText {##2} #1 } } \NewDocumentCommand \MapTblrRemarks { +m } { \__tblr_prop_map_inline:nn { remark } { \tl_set_rescan:Nnn \InsertTblrRemarkTag {} {##1} \tl_set:Nn \InsertTblrRemarkText {##2} #1 } } \NewExpandableDocumentCommand \InsertTblrText { m } { \__tblr_spec_item:nn { outer } {#1} } \NewExpandableDocumentCommand \InsertTblrMore { m } { \__tblr_prop_item:nn { more } {#1} } %%% -------------------------------------------------------- %%> \section{Table Continuation Templates} %%% -------------------------------------------------------- \tl_if_exist:NF \tblrcontfootname { \tl_set:Nn \tblrcontfootname { Continued ~ on ~ next ~ page } } \tl_if_exist:NF \tblrcontheadname { \tl_set:Nn \tblrcontheadname { ( Continued ) } } \DefTblrTemplate { contfoot-text } { normal } { \tblrcontfootname } \SetTblrTemplate { contfoot-text } { normal } \DefTblrTemplate { contfoot } { empty } { } \DefTblrTemplate { contfoot } { plain } { \noindent \raggedleft \UseTblrTemplate { contfoot-text } { default } \par } \DefTblrTemplate { contfoot } { normal } { %% need to set parindent after alignment \raggedleft \UseTblrAlign { contfoot } \UseTblrIndent { contfoot } \UseTblrHang { contfoot } \leavevmode \UseTblrTemplate { contfoot-text } { default } \par } \SetTblrTemplate { contfoot } { normal } \DefTblrTemplate { conthead-pre } { empty } { } \DefTblrTemplate { conthead-pre } { normal } { \space } \SetTblrTemplate { conthead-pre } { normal } \DefTblrTemplate { conthead-text } { normal } { \tblrcontheadname } \SetTblrTemplate { conthead-text } { normal } \DefTblrTemplate { conthead } { empty } { } \DefTblrTemplate { conthead } { plain } { \noindent \raggedright \UseTblrTemplate { conthead-text } { default } \par } \DefTblrTemplate { conthead } { normal } { %% need to set parindent after alignment \raggedright \UseTblrAlign { conthead } \UseTblrIndent { conthead } \UseTblrHang { conthead } \leavevmode \UseTblrTemplate { conthead-text } { default } \par } \SetTblrTemplate { conthead } { normal } %%% -------------------------------------------------------- %%> \section{Table Caption Templates} %%% -------------------------------------------------------- \tl_new:N \l__tblr_caption_short_tl \DefTblrTemplate { caption-lot } { empty } { } \DefTblrTemplate { caption-lot } { normal } { \tl_if_empty:NTF \lTblrEntryTl { \tl_set_eq:NN \l__tblr_caption_short_tl \lTblrCaptionTl } { \tl_set_eq:NN \l__tblr_caption_short_tl \lTblrEntryTl } \addcontentsline { lot } { table } { \protect\numberline { \thetable } { \l__tblr_caption_short_tl } } } \SetTblrTemplate { caption-lot } { normal } %% We need to use \hspace and \enskip, but not ~ or \space, %% since we want a correct hangindent caption paragraph. \DefTblrTemplate { caption-tag } { empty } { } \DefTblrTemplate { caption-tag } { normal } { \tablename\hspace{0.25em}\thetable } \SetTblrTemplate { caption-tag } { normal } \DefTblrTemplate { caption-sep } { empty } { } \DefTblrTemplate { caption-sep } { normal } { : \enskip } \SetTblrTemplate { caption-sep } { normal } \DefTblrTemplate { caption-text } { empty } { } \DefTblrTemplate { caption-text } { normal } { \InsertTblrText { caption } } \SetTblrTemplate { caption-text } { normal } \box_new:N \l__tblr_caption_box \box_new:N \l__tblr_caption_left_box \DefTblrTemplate { caption } { empty } { } \DefTblrTemplate { caption } { plain } { \hbox_set:Nn \l__tblr_caption_box { \UseTblrTemplate { caption-tag } { default } \UseTblrTemplate { caption-sep } { default } \UseTblrTemplate { caption-text } { default } } \dim_compare:nNnTF { \box_wd:N \l__tblr_caption_box } > { \hsize } { \noindent \hbox_unpack:N \l__tblr_caption_box \par } { \centering \makebox [\hsize] [c] { \box_use:N \l__tblr_caption_box } \par } } \DefTblrTemplate { caption } { normal } { \hbox_set:Nn \l__tblr_caption_box { \UseTblrTemplate { caption-tag } { default } \UseTblrTemplate { caption-sep } { default } \UseTblrTemplate { caption-text } { default } } \dim_compare:nNnTF { \box_wd:N \l__tblr_caption_box } > { \hsize } { \UseTblrAlign { caption } \UseTblrIndent { caption } \hbox_set:Nn \l__tblr_caption_left_box { \UseTblrTemplate { caption-tag } { default } \UseTblrTemplate { caption-sep } { default } } \hangindent = \box_wd:N \l__tblr_caption_left_box \hangafter = 1 \UseTblrHang { caption } \leavevmode \hbox_unpack:N \l__tblr_caption_box \par } { \centering \makebox [\hsize] [c] { \box_use:N \l__tblr_caption_box } \par } } \DefTblrTemplate { caption } { simple } { \UseTblrAlign { caption } \UseTblrIndent { caption } \UseTblrHang { caption } \leavevmode \UseTblrTemplate { caption-tag } { default } \UseTblrTemplate { caption-sep } { default } \UseTblrTemplate { caption-text } { default } \par } \SetTblrTemplate { caption } { normal } \DefTblrTemplate { capcont } { empty } { } \DefTblrTemplate { capcont } { plain } { \hbox_set:Nn \l__tblr_caption_box { \UseTblrTemplate { caption-tag } { default } \UseTblrTemplate { caption-sep } { default } \UseTblrTemplate { caption-text } { default } \UseTblrTemplate { conthead-pre } { default } \UseTblrTemplate { conthead-text } { default } } \dim_compare:nNnTF { \box_wd:N \l__tblr_caption_box } > { \hsize } { \noindent \hbox_unpack:N \l__tblr_caption_box \par } { \centering \makebox [\hsize] [c] { \box_use:N \l__tblr_caption_box } \par } } \DefTblrTemplate { capcont } { normal } { \hbox_set:Nn \l__tblr_caption_box { \UseTblrTemplate { caption-tag } { default } \UseTblrTemplate { caption-sep } { default } \UseTblrTemplate { caption-text } { default } \UseTblrTemplate { conthead-pre } { default } \UseTblrTemplate { conthead-text } { default } } \dim_compare:nNnTF { \box_wd:N \l__tblr_caption_box } > { \hsize } { \UseTblrAlign { capcont } \UseTblrIndent { capcont } \hbox_set:Nn \l__tblr_caption_left_box { \UseTblrTemplate { caption-tag } { default } \UseTblrTemplate { caption-sep } { default } } \hangindent = \box_wd:N \l__tblr_caption_left_box \hangafter = 1 \UseTblrHang { capcont } \leavevmode \hbox_unpack:N \l__tblr_caption_box \par } { \centering \makebox [\hsize] [c] { \box_use:N \l__tblr_caption_box } \par } } \DefTblrTemplate { capcont } { simple } { \UseTblrAlign { caption } \UseTblrIndent { caption } \UseTblrHang { caption } \leavevmode \UseTblrTemplate { caption-tag } { default } \UseTblrTemplate { caption-sep } { default } \UseTblrTemplate { caption-text } { default } \UseTblrTemplate { conthead-pre } { default } \UseTblrTemplate { conthead-text } { default } \par } \SetTblrTemplate { capcont} { normal } %%% -------------------------------------------------------- %%> \section{Table Notes Templates} %%% -------------------------------------------------------- %% By default the targets generated by \hypertarget are too low %% Therefore we need to use \Hy@raisedlink command to fix this problem %% See https://tex.stackexchange.com/questions/17057 %% We also use \use:c in case the private command \Hy@raisedlink is removed \cs_new_protected:Npn \__tblr_hyper_target:n #1 { \cs_if_exist:NT \hypertarget { \use:c { Hy@raisedlink } { \hypertarget { tblr / \int_use:N \g__tblr_table_count_int / \tl_to_str:n {#1} } { } } } } \cs_generate_variant:Nn \__tblr_hyper_target:n { V } \cs_new_protected:Npn \__tblr_hyper_link:nn #1 #2 { \cs_if_exist:NTF \hyperlink { \hyperlink { tblr / \int_use:N \g__tblr_table_count_int / \tl_to_str:n {#1} } { #2 } } { #2 } } \DefTblrTemplate { note-border } { empty } { \hypersetup { pdfborder = { 0 ~ 0 ~ 0 } } } \DefTblrTemplate { note-border } { normal } { \hypersetup { pdfborder = { 0 ~ 0 ~ 1 } } } \SetTblrTemplate { note-border } { empty } \cs_set_eq:NN \TblrOverlap \rlap \NewDocumentCommand \TblrNote { m } { \cs_if_exist:NT \hypersetup { \ExpTblrTemplate { note-border }{ default } } \TblrOverlap { \__tblr_hyper_link:nn {#1} { \textsuperscript { \sffamily \UseTblrFont { note-tag } #1 } } } } \DefTblrTemplate { note-tag } { empty } { } \DefTblrTemplate { note-tag } { normal } { \textsuperscript { \sffamily \UseTblrFont { note-tag } \InsertTblrNoteTag } } \SetTblrTemplate { note-tag } { normal } \DefTblrTemplate { note-target } { normal } { \__tblr_hyper_target:V \InsertTblrNoteTag } \SetTblrTemplate { note-target } { normal } \DefTblrTemplate { note-sep } { empty } { } \DefTblrTemplate { note-sep } { normal } { \space } \SetTblrTemplate { note-sep } { normal } \DefTblrTemplate { note-text } { empty } { } \DefTblrTemplate { note-text } { normal } { \InsertTblrNoteText } \SetTblrTemplate { note-text } { normal } \DefTblrTemplate { note } { empty } { } \DefTblrTemplate { note } { plain } { \MapTblrNotes { \noindent \UseTblrTemplate { note-tag } { default } \UseTblrTemplate { note-target } { default } \UseTblrTemplate { note-sep } { default } \UseTblrTemplate { note-text } { default } \par } } \DefTblrTemplate { note } { normal } { \UseTblrAlign { note } \UseTblrIndent { note } \MapTblrNotes { \hangindent = 0.7em \hangafter = 1 \UseTblrHang { note } \leavevmode \hbox_to_wd:nn { \the\hangindent } { \UseTblrTemplate { note-tag } { default } \UseTblrTemplate { note-target } { default } \hfil } \UseTblrTemplate { note-text } { default } \par } } \DefTblrTemplate { note } { inline } { \UseTblrAlign { note } \UseTblrIndent { note } \UseTblrHang { note } \leavevmode \MapTblrNotes { \UseTblrTemplate { note-tag } { default } \UseTblrTemplate { note-target } { default } \UseTblrTemplate { note-sep } { default } \UseTblrTemplate { note-text } { default } \quad } \par } \SetTblrTemplate { note } { normal } %%% -------------------------------------------------------- %%> \section{Table Remarks Templates} %%% -------------------------------------------------------- \DefTblrTemplate { remark-tag } { empty } { } \DefTblrTemplate { remark-tag } { normal } { \itshape \UseTblrFont { remark-tag } \InsertTblrRemarkTag } \SetTblrTemplate { remark-tag } { normal } \DefTblrTemplate { remark-sep } { empty } { } \DefTblrTemplate { remark-sep } { normal } { : \space } \SetTblrTemplate { remark-sep } { normal } \DefTblrTemplate { remark-text } { empty } { } \DefTblrTemplate { remark-text } { normal } { \InsertTblrRemarkText } \SetTblrTemplate { remark-text } { normal } \DefTblrTemplate { remark } { empty } { } \DefTblrTemplate { remark } { plain } { \MapTblrRemarks { \noindent \UseTblrTemplate { remark-tag } { default } \UseTblrTemplate { remark-sep } { default } \UseTblrTemplate { remark-text } { default } \par } } \DefTblrTemplate { remark } { normal } { \UseTblrAlign { remark } \UseTblrIndent { remark } \MapTblrRemarks { \hangindent = 0.7em \hangafter = 1 \UseTblrHang { remark } \leavevmode \UseTblrTemplate { remark-tag } { default } \UseTblrTemplate { remark-sep } { default } \UseTblrTemplate { remark-text } { default } \par } } \DefTblrTemplate { remark } { inline } { \UseTblrAlign { remark } \UseTblrIndent { remark } \UseTblrHang { remark } \leavevmode \MapTblrRemarks { \UseTblrTemplate { remark-tag } { default } \UseTblrTemplate { remark-sep } { default } \UseTblrTemplate { remark-text } { default } \quad } \par } \SetTblrTemplate { remark } { normal } %%% -------------------------------------------------------- %%> \section{Header and Footer Templates} %%% -------------------------------------------------------- \tl_new:N \g__tblr_template_firsthead_default_tl \tl_new:N \g__tblr_template_middlehead_default_tl \tl_new:N \g__tblr_template_lasthead_default_tl \tl_new:N \g__tblr_template_firstfoot_default_tl \tl_new:N \g__tblr_template_middlefoot_default_tl \tl_new:N \g__tblr_template_lastfoot_default_tl \keys_define:nn { tblr-def-template } { head .meta:n = { firsthead, middlehead, lasthead }, foot .meta:n = { firstfoot, middlefoot, lastfoot }, } \keys_define:nn { tblr-set-template } { head .meta:n = { firsthead, middlehead, lasthead }, foot .meta:n = { firstfoot, middlefoot, lastfoot }, } \DefTblrTemplate { head } { empty } { } \DefTblrTemplate { foot } { empty } { } \DefTblrTemplate { firsthead } { normal } { \UseTblrTemplate { caption } { default } } \DefTblrTemplate { middlehead, lasthead } { normal } { \UseTblrTemplate { capcont } { default } } \DefTblrTemplate { firstfoot, middlefoot } { normal } { \UseTblrTemplate { contfoot } { default } } \DefTblrTemplate { lastfoot } { normal } { \UseTblrTemplate { note } { default } \UseTblrTemplate { remark } { default } } \SetTblrTemplate { head } { normal } \SetTblrTemplate { foot } { normal } %%% -------------------------------------------------------- %%> \section{Build the Whole Table} %%% -------------------------------------------------------- \cs_new:Npn \__tblr_box_height:N #1 { \dim_eval:n { \box_ht:N #1 + \box_dp:N #1 } } \cs_new_protected:Npn \__tblr_build_head_foot: { \__tblr_build_row_head_foot: \__tblr_build_table_head_foot: } \tl_new:N \l__tblr_row_head_tl \tl_new:N \l__tblr_row_foot_tl \box_new:N \l__tblr_row_head_box \box_new:N \l__tblr_row_foot_box \dim_new:N \l__tblr_row_head_foot_dim \cs_new_protected:Npn \__tblr_build_row_head_foot: { %% \l__tblr_row_head_tl and \l__tblr_row_foot_tl may be empty \tl_set:Nx \l__tblr_row_head_tl { \__tblr_prop_item:ne { inner } { rowhead } } \int_compare:nNnTF { \l__tblr_row_head_tl + 0 } > { 0 } { \__tblr_build_one_table:nnNN {1} { \l__tblr_row_head_tl } \c_true_bool \c_true_bool } { \__tblr_build_one_hline:n {1} } \box_set_eq:NN \l__tblr_row_head_box \l__tblr_table_box \tl_set:Nx \l__tblr_row_foot_tl { \__tblr_prop_item:ne { inner } { rowfoot } } \int_compare:nNnTF { \l__tblr_row_foot_tl + 0 } > { 0 } { \__tblr_build_one_table:nnNN { \c@rowcount - \l__tblr_row_foot_tl + 1 } { \c@rowcount } \c_true_bool \c_true_bool } { \__tblr_build_one_hline:n { \int_eval:n { \c@rowcount + 1 } } } \box_set_eq:NN \l__tblr_row_foot_box \l__tblr_table_box \dim_set:Nn \l__tblr_row_head_foot_dim { \__tblr_box_height:N \l__tblr_row_head_box + \__tblr_box_height:N \l__tblr_row_foot_box } } \dim_new:N \tablewidth \cs_new_protected:Npn \__tblr_get_table_width: { \dim_zero:N \tablewidth \int_step_inline:nn { \c@colcount } { \dim_add:Nn \tablewidth { \__tblr_spec_item:nn { vline } { [##1] / @vline-width } + \__tblr_data_item:nnn { column } {##1} { leftsep } + \__tblr_data_item:nnn { column } {##1} { @col-width } + \__tblr_data_item:nnn { column } {##1} { rightsep } } } \dim_add:Nn \tablewidth { \__tblr_spec_item:ne { vline } { [\int_eval:n { \c@colcount + 1 }] / @vline-width } } } \box_new:N \l__tblr_table_firsthead_box \box_new:N \l__tblr_table_middlehead_box \box_new:N \l__tblr_table_lasthead_box \box_new:N \l__tblr_table_firstfoot_box \box_new:N \l__tblr_table_middlefoot_box \box_new:N \l__tblr_table_lastfoot_box \cs_new_protected:Npn \__tblr_build_table_head_foot: { \__tblr_get_table_width: % make each of \lTblrCaptionTl, \lTblrEntryTl, \lTblrLabelTl and the % three corresponding booleans available in all head-foot templates \__tblr_set_table_label_entry: \__tblr_build_table_head_aux:Nn \l__tblr_table_firsthead_box { \__tblr_build_table_label_entry: \UseTblrTemplate { firsthead } { default } } \__tblr_build_table_head_aux:Nn \l__tblr_table_middlehead_box { \UseTblrTemplate { middlehead } { default } } \__tblr_build_table_head_aux:Nn \l__tblr_table_lasthead_box { \UseTblrTemplate { lasthead } { default } } \__tblr_build_table_foot_aux:Nn \l__tblr_table_firstfoot_box { \UseTblrTemplate { firstfoot } { default } } \__tblr_build_table_foot_aux:Nn \l__tblr_table_middlefoot_box { \UseTblrTemplate { middlefoot } { default } } \__tblr_build_table_foot_aux:Nn \l__tblr_table_lastfoot_box { \UseTblrTemplate { lastfoot } { default } } } \bool_new:N \l__tblr_table_no_title_bool \bool_new:N \l__tblr_table_no_entry_bool \bool_new:N \l__tblr_table_no_label_bool \tl_const:Nn \c_tblr_none_tl { none } \cs_new_protected:Npn \__tblr_set_table_label_entry: { \tl_set:Nx \lTblrCaptionTl { \InsertTblrText { caption } } \tl_set:Nx \lTblrEntryTl { \InsertTblrText { entry } } \tl_set:Nx \lTblrLabelTl { \InsertTblrText { label } } \bool_set:Nn \l__tblr_table_no_title_bool { \tl_if_empty_p:N \lTblrCaptionTl } \bool_set:Nn \l__tblr_table_no_entry_bool { \tl_if_eq_p:NN \lTblrEntryTl \c_tblr_none_tl } \bool_set:Nn \l__tblr_table_no_label_bool { \tl_if_eq_p:NN \lTblrLabelTl \c_tblr_none_tl } \bool_if:NT \l__tblr_table_no_title_bool { \SetTblrTemplate { conthead-pre } { empty } } \bool_if:NT \l__tblr_table_no_label_bool { \SetTblrTemplate { caption-tag }{ empty } \SetTblrTemplate { caption-sep }{ empty } } } \cs_new_protected:Npn \__tblr_build_tall_table_head_foot: { \__tblr_get_table_width: \__tblr_set_table_label_entry: \__tblr_build_table_head_aux:Nn \l__tblr_table_firsthead_box { \__tblr_build_table_label_entry: \UseTblrTemplate { firsthead } { default } } \__tblr_build_table_foot_aux:Nn \l__tblr_table_lastfoot_box { \UseTblrTemplate { lastfoot } { default } } } \tl_new:N \lTblrCaptionTl \tl_new:N \lTblrEntryTl \tl_new:N \lTblrLabelTl \clist_new:N \lTblrRefMoreClist \cs_new_protected:Npn \__tblr_build_table_label_entry: { \bool_if:NF \l__tblr_table_no_label_bool { \refstepcounter { table } \tl_if_empty:NF \lTblrLabelTl { \clist_map_inline:Nn \lTblrRefMoreClist { \ExpTblrTemplate { caption-ref } { ##1 } } \exp_args:NV \label \lTblrLabelTl } } %% We put caption-lot code at last, so that a user can modify \lTblrEntryTl %% in a caption-label template. For example, a user may want to use %% short caption in nameref, but at the same time not to add LoT entry. \bool_if:NF \l__tblr_table_no_entry_bool { \UseTblrTemplate { caption-lot } { default } } } \cs_new_protected:Npn \__tblr_build_table_head_aux:Nn #1 #2 { \vbox_set:Nn #1 { \hsize = \tablewidth \TblrParboxRestore % it will set \linewidth = \hsize \vbox_set:Nn \l_tmpa_box {#2} \box_use:N \l_tmpa_box \dim_compare:nNnT { \box_ht:N \l_tmpa_box + \box_dp:N \l_tmpa_box } > { 0pt } { \skip_vertical:n { \__tblr_spec_item:nn { outer } { headsep } } } } } \cs_new_protected:Npn \__tblr_build_table_foot_aux:Nn #1 #2 { \vbox_set:Nn #1 { \hsize = \tablewidth \TblrParboxRestore % it will set \linewidth = \hsize \vbox_set:Nn \l_tmpb_box {#2} \dim_compare:nNnT { \box_ht:N \l_tmpb_box + \box_dp:N \l_tmpb_box } > { 0pt } { \skip_vertical:n { \__tblr_spec_item:nn { outer } { footsep } } } \box_use:N \l_tmpb_box } } \cs_new_protected:Npn \__tblr_build_whole: { \__tblr_hook_use:n { tabularray/table/before } \tl_if_eq:enTF { \__tblr_spec_item:nn { outer } { long } } { true } { \__tblr_build_long_table:e { \__tblr_spec_item:nn { outer } { halign } } } { \tl_if_eq:enTF { \__tblr_spec_item:nn { outer } { tall } } { true } { \__tblr_build_tall_table:e { \__tblr_spec_item:nn { outer } { baseline } } } { \__tblr_build_short_table:e { \__tblr_spec_item:nn { outer } { baseline } } } } \__tblr_hook_use:n { tabularray/table/after } } \dim_new:N \l__tblr_remain_height_dim \int_new:N \l__tblr_long_from_int \int_new:N \l__tblr_long_to_int \int_new:N \l__tblr_curr_i_int \int_new:N \l__tblr_prev_i_int \int_new:N \l__tblr_table_page_int \bool_new:N \l__tblr_page_break_curr_bool \bool_new:N \l__tblr_page_break_prev_bool %% #1: table alignment %% For long table, we need to leave hmode first to get correct \pagetotal %% Also remove topskip and presep if we are at the beginning of the page \cs_new_protected:Npn \__tblr_build_long_table:n #1 { \LogTblrTracing { page } \par \skip_zero:N \parskip % see issue #203 \LogTblrTracing { page } \dim_compare:nNnTF { \pagegoal } = { \maxdimen } { \hbox{}\kern-\topskip\nobreak } { \skip_vertical:n { \__tblr_spec_item:nn { outer } { presep } } } \LogTblrTracing { page } \nointerlineskip \mode_leave_vertical: % enter horizontal mode to update \pagetotal \LogTblrTracing { page } \hrule height ~ 0pt \nobreak % prevent page break after \hrule (see issue #42) \LogTblrTracing { page } \int_set:Nn \l__tblr_table_page_int {1} \__tblr_build_head_foot: \dim_set:Nn \l__tblr_remain_height_dim { \pagegoal - \pagetotal - \l__tblr_row_head_foot_dim } \int_set:Nn \l__tblr_long_from_int { \l__tblr_row_head_tl + 1 } \int_set:Nn \l__tblr_long_to_int { \c@rowcount - ( \l__tblr_row_foot_tl + 0 ) } \int_set:Nn \l__tblr_curr_i_int { \l__tblr_long_from_int - 1 } \int_do_while:nNnn { \l__tblr_curr_i_int } < { \l__tblr_long_to_int } { \int_set_eq:NN \l__tblr_prev_i_int \l__tblr_curr_i_int \__tblr_get_next_table_rows:NNNN \l__tblr_long_to_int \l__tblr_curr_i_int \l_tmpa_dim \l__tblr_page_break_curr_bool \__tblr_check_table_page_break:NNN \l__tblr_remain_height_dim \l_tmpa_dim \l__tblr_page_break_prev_bool \__tblr_do_if_tracing:nn { page } { \int_log:N \l__tblr_curr_i_int } \bool_if:NTF \l__tblr_page_break_prev_bool { \int_compare:nNnTF { \l__tblr_long_from_int } > { \l__tblr_prev_i_int } { % See issue #42: if longtblr starts at the bottom of a page, % \pagetotal maybe exceed \pagegoal after adding presep, % or after adding rowhead or rowfoot of the table. % In these cases, we will not typeset table in this page, % but rather force a page break. \group_begin: \dim_set:Nn \l_tmpb_dim { % Enough to overfill the page (including shrink). \pagegoal - \pagetotal + \l_tmpa_dim + \__tblr_box_height:N \l__tblr_table_firsthead_box + \__tblr_box_height:N \l__tblr_table_firstfoot_box } \skip_vertical:n { \l_tmpb_dim } \tex_penalty:D 9999 \skip_vertical:n { -\l_tmpb_dim } \group_end: } { \__tblr_build_page_table:nnx {#1} { \int_use:N \l__tblr_long_from_int } { \int_use:N \l__tblr_prev_i_int } \int_incr:N \l__tblr_table_page_int \int_set:Nn \l__tblr_long_from_int { \l__tblr_prev_i_int + 1 } \TblrNewPage } \hbox{}\kern-\topskip\nobreak \noindent \LogTblrTracing { page } \dim_set:Nn \l__tblr_remain_height_dim { \pagegoal - \pagetotal - \l__tblr_row_head_foot_dim - \l_tmpa_dim } } { \bool_if:NTF \l__tblr_page_break_curr_bool { \__tblr_build_page_table:nnx {#1} { \int_use:N \l__tblr_long_from_int } { \int_use:N \l__tblr_curr_i_int } \int_incr:N \l__tblr_table_page_int \TblrNewPage \hbox{}\kern-\topskip\nobreak \noindent \LogTblrTracing { page } \dim_set:Nn \l__tblr_remain_height_dim { \pagegoal - \pagetotal - \l__tblr_row_head_foot_dim } \int_set:Nn \l__tblr_long_from_int { \l__tblr_curr_i_int + 1 } } { \dim_add:Nn \l__tblr_remain_height_dim { -\l_tmpa_dim } } } } \int_compare:nNnTF { \l__tblr_table_page_int } = {1} { \box_set_eq:NN \l__tblr_table_head_box \l__tblr_table_firsthead_box \box_set_eq:NN \l__tblr_table_foot_box \l__tblr_table_lastfoot_box } { \box_set_eq:NN \l__tblr_table_head_box \l__tblr_table_lasthead_box \box_set_eq:NN \l__tblr_table_foot_box \l__tblr_table_lastfoot_box } \__tblr_build_page_table:nnn {#1} { \int_use:N \l__tblr_long_from_int } { \int_use:N \l__tblr_long_to_int } \skip_vertical:n { \__tblr_spec_item:nn { outer } { postsep } } % In the past we used "\hrule height ~ 0pt" to get strict postsep, % but the postsep was not discarded when page breaks, see issue #39. % Therefore we use \nointerlineskip here. \nointerlineskip } \cs_generate_variant:Nn \__tblr_build_long_table:n { e } %% #1: int with index of the last row; #2: int with index of current row; %% #3: row dimension; #4: break page or not. \cs_new_protected:Npn \__tblr_get_next_table_rows:NNNN #1 #2 #3 #4 { \bool_set_true:N \l_tmpa_bool \dim_zero:N #3 \bool_set_false:N #4 \bool_while_do:Nn \l_tmpa_bool { \int_incr:N #2 \dim_add:Nn #3 { \__tblr_data_item:nen { row } { \int_use:N #2 } { abovesep } + \__tblr_data_item:nen { row } { \int_use:N #2 } { @row-height } + \__tblr_data_item:nen { row } { \int_use:N #2 } { belowsep } + \__tblr_spec_item:ne { hline } { [ \int_eval:n { #2 + 1 } ] / @hline-height } } \int_compare:nNnTF {#2} < {#1} { \tl_set:Nx \l__tblr_b_tl { \__tblr_spec_item:ne { hline } { [ \int_eval:n { #2 + 1 } ] / @pagebreak } } % Note that \l__tblr_b_tl may be empty \int_compare:nNnTF { \l__tblr_b_tl + 0 } < { 0 } { \bool_set_true:N \l_tmpa_bool } { \bool_set_false:N \l_tmpa_bool \int_compare:nNnT { \l__tblr_b_tl + 0 } > { 0 } { \bool_set_true:N #4 } } } { \bool_set_false:N \l_tmpa_bool } } } \box_new:N \l__tblr_table_head_box \box_new:N \l__tblr_table_foot_box \dim_new:N \l__tblr_table_head_foot_dim \dim_new:N \l__tblr_table_head_body_foot_dim %% #1: remain dimension; #2: row dimension; #3: break page or not \cs_new_protected:Npn \__tblr_check_table_page_break:NNN #1 #2 #3 { \int_compare:nNnTF { \l__tblr_table_page_int } = {1} { \dim_set:Nn \l__tblr_table_head_body_foot_dim { \__tblr_box_height:N \l__tblr_table_firsthead_box + #2 + \__tblr_box_height:N \l__tblr_table_firstfoot_box } \box_set_eq:NN \l__tblr_table_head_box \l__tblr_table_firsthead_box \dim_compare:nNnTF { \l__tblr_table_head_body_foot_dim } > {#1} { \bool_set_true:N #3 \box_set_eq:NN \l__tblr_table_foot_box \l__tblr_table_firstfoot_box } { \bool_set_false:N #3 } } { \dim_set:Nn \l__tblr_table_head_body_foot_dim { \__tblr_box_height:N \l__tblr_table_middlehead_box + #2 + \__tblr_box_height:N \l__tblr_table_middlefoot_box } \box_set_eq:NN \l__tblr_table_head_box \l__tblr_table_middlehead_box \dim_compare:nNnTF { \l__tblr_table_head_body_foot_dim } > {#1} { \bool_set_true:N #3 \box_set_eq:NN \l__tblr_table_foot_box \l__tblr_table_middlefoot_box } { \bool_set_false:N #3 } } } \box_new:N \l__tblr_table_box %% #1: table alignment; #2: row from; #3: row to \cs_new_protected:Npn \__tblr_build_page_table:nnn #1 #2 #3 { \__tblr_build_one_table:nnNN {#2} {#3} \c_false_bool \c_false_bool \vbox_set:Nn \l__tblr_table_box { \box_use:N \l__tblr_table_head_box \__tblr_cover_two_vboxes:NN \l__tblr_row_head_box \l__tblr_table_box \box_use:N \l__tblr_row_foot_box \hrule height ~ 0pt \box_use:N \l__tblr_table_foot_box } \__tblr_halign_whole:Nn \l__tblr_table_box {#1} } \cs_generate_variant:Nn \__tblr_build_page_table:nnn { nnx } %% To solve the problem of missing hlines of long tables in some PDF readers, %% We need to draw body rows before head rows (see issue #88). \cs_new_protected:Npn \__tblr_cover_two_vboxes:NN #1 #2 { \dim_set:Nn \l_tmpa_dim { \box_ht:N #1 + \box_dp:N #1 } \dim_set:Nn \l_tmpb_dim { \box_ht:N #2 + \box_dp:N #2 } \skip_vertical:N \l_tmpa_dim \hrule height ~ 0pt \box_use:N #2 \skip_vertical:n { - \l_tmpa_dim - \l_tmpb_dim } \hrule height ~ 0pt \box_use:N #1 \skip_vertical:N \l_tmpb_dim \hrule height ~ 0pt } \cs_new_protected:Npn \__tblr_halign_whole:Nn #1 #2 { \noindent \hbox_to_wd:nn { \linewidth } { \tl_if_eq:nnF {#2} {l} { \hfil } \box_use:N #1 \tl_if_eq:nnF {#2} {r} { \hfil } } } %% #1: table alignment %% For tall table, we need to leave vmode first. %% Since there may be \centering in table environment, %% We use \raggedright to reset alignement for table head/foot. \cs_new_protected:Npn \__tblr_build_tall_table:n #1 { \mode_leave_vertical: \__tblr_build_tall_table_head_foot: \__tblr_build_one_table:nnNN {1} {\c@rowcount} \c_true_bool \c_true_bool \vbox_set:Nn \l__tblr_table_box { \box_use:N \l__tblr_table_firsthead_box \hrule height ~ 0pt \box_use:N \l__tblr_table_box \hrule height ~ 0pt \box_use:N \l__tblr_table_lastfoot_box } \__tblr_valign_whole:Nn \l__tblr_table_box {#1} } \cs_generate_variant:Nn \__tblr_build_tall_table:n { e } %% #1: table alignment %% For short table, we need to leave vmode first \cs_new_protected:Npn \__tblr_build_short_table:n #1 { \mode_leave_vertical: \__tblr_build_one_table:nnNN {1} {\c@rowcount} \c_true_bool \c_true_bool \__tblr_valign_whole:Nn \l__tblr_table_box {#1} } \cs_generate_variant:Nn \__tblr_build_short_table:n { e } \box_new:N \l__tblr_table_hlines_box \box_new:N \l__tblr_hline_box \box_new:N \l__tblr_row_box %% #1: row from; #2: row to %% #3: whether build first hline or not; #4: whether build last hline or not %% To fix disappeared hlines with colorful tables in Adobe Reader (see #76), %% we collect all hlines and draw them at the end of the table. \cs_new_protected:Npn \__tblr_build_one_table:nnNN #1 #2 #3 #4 { \box_clear:N \l__tblr_table_hlines_box \tblr_vbox_set:Nn \l__tblr_table_box { \int_step_variable:nnNn {#1} {#2} \l__tblr_i_tl { \bool_lazy_or:nnT { \int_compare_p:nNn { \l__tblr_i_tl } > {#1} } { \bool_if_p:N #3 } { \__tblr_put_one_hline:n { \__tblr_build_hline:V \l__tblr_i_tl } } \tblr_hrule_ht:n { 0pt } % remove lineskip between hlines and rows \__tblr_put_one_row:n { \__tblr_build_row:N \l__tblr_i_tl } \tblr_hrule_ht:n { 0pt } } \bool_if:NT #4 { \__tblr_put_one_hline:n { \__tblr_build_hline:n { \int_eval:n {#2 + 1} } } } \skip_vertical:n { - \box_ht:N \l__tblr_table_hlines_box - \box_dp:N \l__tblr_table_hlines_box } \tblr_box_use:N \l__tblr_table_hlines_box } } \cs_new_protected:Npn \__tblr_put_one_hline:n #1 { \hbox_set:Nn \l__tblr_hline_box {#1} \skip_vertical:n { \box_ht:N \l__tblr_hline_box + \box_dp:N \l__tblr_hline_box } \vbox_set:Nn \l__tblr_table_hlines_box { \vbox_unpack:N \l__tblr_table_hlines_box \box_use:N \l__tblr_hline_box } } \cs_new_protected:Npn \__tblr_put_one_row:n #1 { \hbox_set:Nn \l__tblr_row_box {#1} \vbox_set:Nn \l__tblr_table_hlines_box { \vbox_unpack:N \l__tblr_table_hlines_box \skip_vertical:n { \box_ht:N \l__tblr_row_box + \box_dp:N \l__tblr_row_box } } \box_use:N \l__tblr_row_box } %% #1: hline number \cs_new_protected:Npn \__tblr_build_one_hline:n #1 { \vbox_set:Nn \l__tblr_table_box { \hbox:n { \__tblr_build_hline:n { #1 } } } } \tl_new:N \__tblr_vbox_align_tl \tl_const:Nn \__tblr_vbox_t_tl {t} \tl_const:Nn \__tblr_vbox_T_tl {T} \tl_const:Nn \__tblr_vbox_m_tl {m} \tl_const:Nn \__tblr_vbox_M_tl {M} \tl_const:Nn \__tblr_vbox_c_tl {c} \tl_const:Nn \__tblr_vbox_b_tl {b} \tl_const:Nn \__tblr_vbox_B_tl {B} \regex_const:Nn \c__tblr_is_positive_integer_regex { ^ \d+ $ } \regex_const:Nn \c__tblr_is_negative_integer_regex { ^ - \d+ $ } \tl_new:N \l__tblr_delim_left_tl \tl_new:N \l__tblr_delim_right_tl \cs_new_protected:Npn \__tblr_valign_whole:Nn #1 #2 { \group_begin: \tl_set:Nx \l__tblr_delim_left_tl { \__tblr_prop_item:nn { inner } { delim-left } } \tl_set:Nx \l__tblr_delim_right_tl { \__tblr_prop_item:nn { inner } { delim-right } } \tl_set:Nn \__tblr_vbox_align_tl {#2} \dim_set:Nn \l__tblr_t_dim { \box_ht:N #1 + \box_dp:N #1 } \tl_case:NnF \__tblr_vbox_align_tl { \__tblr_vbox_m_tl { \__tblr_valign_whole_middle:N #1 } \__tblr_vbox_c_tl { \__tblr_valign_whole_middle:N #1 } \__tblr_vbox_M_tl { \__tblr_valign_whole_middle_row_or_border:N #1 } \__tblr_vbox_t_tl { \__tblr_valign_whole_top:N #1 } \__tblr_vbox_T_tl { \tl_set:Nn \__tblr_vbox_align_tl {1} \__tblr_valign_whole_at_row_from_above:N #1 } \__tblr_vbox_b_tl { \__tblr_valign_whole_bottom:N #1 } \__tblr_vbox_B_tl { \tl_set:Nx \__tblr_vbox_align_tl { \int_use:N \c@rowcount } \__tblr_valign_whole_at_row_from_below:N #1 } } { \regex_match:NVTF \c__tblr_is_positive_integer_regex \__tblr_vbox_align_tl { \__tblr_valign_whole_at_row:N #1 } { \regex_match:NVTF \c__tblr_is_negative_integer_regex \__tblr_vbox_align_tl { \__tblr_valign_whole_at_border:N #1 } { \__tblr_valign_whole_middle:N #1 } } } %% we have done the job when valign is m or c \box_if_empty:NF #1 { \__tblr_add_delimiters_to_box:N #1 } \group_end: } %% We use the idea of delarray package to shift table box %% when there are delimiters around the table \cs_new_protected:Npn \__tblr_add_delimiters_to_box:N #1 { \tl_if_empty:NTF \l__tblr_delim_left_tl { \box_use_drop:N #1 } { \box_move_down:nn { ( \box_dp:N #1 - \box_ht:N #1 ) / 2 + \tex_fontdimen:D 22 \tex_textfont:D 2 } { \__tblr_get_vcenter_box:N #1 } } } \cs_new_protected:Npn \__tblr_get_vcenter_box:N #1 { \hbox:n { $ \m@th \l__tblr_delim_left_tl \tex_vcenter:D { \vbox_unpack_drop:N #1 } \l__tblr_delim_right_tl $ } } \cs_new_protected:Npn \__tblr_valign_whole_middle:N #1 { \__tblr_get_vcenter_box:N #1 } \cs_new_protected:Npn \__tblr_valign_whole_top:N #1 { \dim_set:Nn \l__tblr_h_dim { \__tblr_valign_get_hline_total:n {1} } \dim_compare:nNnT \l__tblr_h_dim = { 0pt } { \dim_add:Nn \l__tblr_h_dim { \__tblr_valign_get_row_height:n {1} } } \box_set_ht:Nn #1 { \l__tblr_h_dim } \box_set_dp:Nn #1 { \l__tblr_t_dim - \l__tblr_h_dim } } \cs_new_protected:Npn \__tblr_valign_whole_bottom:N #1 { \dim_set:Nn \l__tblr_d_dim { \__tblr_valign_get_hline_total:n { \int_eval:n { \c@rowcount + 1 } } } \dim_compare:nNnTF \l__tblr_d_dim = { 0pt } { \dim_set:Nn \l__tblr_d_dim { \__tblr_valign_get_row_depth:n { \int_use:N \c@rowcount } } } { \dim_zero:N \l__tblr_d_dim } \box_set_ht:Nn #1 { \l__tblr_t_dim - \l__tblr_d_dim } \box_set_dp:Nn #1 { \l__tblr_d_dim } } \cs_new_protected:Npn \__tblr_valign_whole_middle_row_or_border:N #1 { \int_if_odd:nTF { \c@rowcount } { \tl_set:Nx \__tblr_vbox_align_tl { \int_eval:n { (\c@rowcount + 1) / 2 } } \__tblr_valign_whole_at_row_from_above:N #1 } { \tl_set:Nx \__tblr_vbox_align_tl { \int_eval:n { \c@rowcount / 2 + 1 } } \__tblr_valign_whole_at_border_from_above:N #1 } } \cs_new_protected:Npn \__tblr_valign_whole_at_row:N #1 { \int_compare:nNnTF { 2 * \__tblr_vbox_align_tl } > { \c@rowcount } { \__tblr_valign_whole_at_row_from_below:N #1 } { \__tblr_valign_whole_at_row_from_above:N #1 } } \cs_new_protected:Npn \__tblr_valign_whole_at_row_from_above:N #1 { \dim_set:Nn \l__tblr_h_dim { \__tblr_valign_get_hline_total:n { \__tblr_vbox_align_tl } } \dim_add:Nn \l__tblr_h_dim { \__tblr_valign_get_row_height:n { \__tblr_vbox_align_tl } } \int_step_inline:nn { \__tblr_vbox_align_tl - 1 } { \dim_add:Nn \l__tblr_h_dim { \__tblr_valign_get_hline_total:n {##1} } \dim_add:Nn \l__tblr_h_dim { \__tblr_valign_get_row_total:n {##1} } } \box_set_ht:Nn #1 { \l__tblr_h_dim } \box_set_dp:Nn #1 { \l__tblr_t_dim - \l__tblr_h_dim } } \cs_new_protected:Npn \__tblr_valign_whole_at_row_from_below:N #1 { \dim_set:Nn \l__tblr_d_dim { \__tblr_valign_get_hline_total:n { \int_eval:n {\c@rowcount + 1} } } \dim_add:Nn \l__tblr_d_dim { \__tblr_valign_get_row_depth:n { \__tblr_vbox_align_tl } } \int_step_inline:nnn { \__tblr_vbox_align_tl + 1 } { \c@rowcount } { \dim_add:Nn \l__tblr_d_dim { \__tblr_valign_get_hline_total:n {##1} } \dim_add:Nn \l__tblr_d_dim { \__tblr_valign_get_row_total:n {##1} } } \box_set_dp:Nn #1 { \l__tblr_d_dim } \box_set_ht:Nn #1 { \l__tblr_t_dim - \l__tblr_d_dim } } \cs_new_protected:Npn \__tblr_valign_whole_at_border:N #1 { \tl_set:Nx \__tblr_vbox_align_tl { \int_eval:n { - \__tblr_vbox_align_tl } } \int_compare:nNnTF { 2 * \__tblr_vbox_align_tl - 2 } > { \c@rowcount } { \__tblr_valign_whole_at_border_from_below:N #1 } { \__tblr_valign_whole_at_border_from_above:N #1 } } \cs_new_protected:Npn \__tblr_valign_whole_at_border_from_above:N #1 { \dim_set:Nn \l__tblr_h_dim { \__tblr_valign_get_hline_total:n { \__tblr_vbox_align_tl } } \int_step_inline:nn { \__tblr_vbox_align_tl - 1 } { \dim_add:Nn \l__tblr_h_dim { \__tblr_valign_get_hline_total:n {##1} } \dim_add:Nn \l__tblr_h_dim { \__tblr_valign_get_row_total:n {##1} } } \box_set_ht:Nn #1 { \l__tblr_h_dim } \box_set_dp:Nn #1 { \l__tblr_t_dim - \l__tblr_h_dim } } \cs_new_protected:Npn \__tblr_valign_whole_at_border_from_below:N #1 { \dim_zero:N \l__tblr_d_dim \int_step_inline:nnn { \__tblr_vbox_align_tl } { \c@rowcount } { \dim_add:Nn \l__tblr_d_dim { \__tblr_valign_get_row_total:n {##1} } \dim_add:Nn \l__tblr_d_dim { \__tblr_valign_get_hline_total:n { \int_eval:n { ##1 + 1 } } } } \box_set_dp:Nn #1 { \l__tblr_d_dim } \box_set_ht:Nn #1 { \l__tblr_t_dim - \l__tblr_d_dim } } \cs_new_nopar:Npn \__tblr_valign_get_hline_total:n #1 { \__tblr_spec_item:ne { hline } { [#1] / @hline-height } } \cs_new_nopar:Npn \__tblr_valign_get_row_total:n #1 { \__tblr_data_item:nnn { row } {#1} { abovesep } + \__tblr_data_item:nnn { row } {#1} { @row-height } + \__tblr_data_item:nnn { row } {#1} { belowsep } } \cs_new_nopar:Npn \__tblr_valign_get_row_height:n #1 { \__tblr_data_item:nnn { row } {#1} { abovesep } + ( \__tblr_data_item:nnn { row } {#1} { @row-height } + \__tblr_data_item:nnn { row } {#1} { @row-upper } - \__tblr_data_item:nnn { row } {#1} { @row-lower } ) / 2 } \cs_new_nopar:Npn \__tblr_valign_get_row_depth:n #1 { ( \__tblr_data_item:nen { row } {#1} { @row-height } - \__tblr_data_item:nen { row } {#1} { @row-upper } + \__tblr_data_item:nen { row } {#1} { @row-lower } ) / 2 + \__tblr_data_item:nnn { row } {#1} { belowsep } } %%% -------------------------------------------------------- %%> \section{Build Table Components} %%% -------------------------------------------------------- \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_spec_item:ne { hline } { [#1] / @hline-count } } \tl_set:Nx \l__tblr_o_tl { \__tblr_spec_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 { \int_compare:nNnT { \l__tblr_n_tl } > {0} { \__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 { inner } { 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_spec_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 } { \__tblr_get_hline_left_right_skips:nnn {#1} {#2} {##1} \skip_horizontal:N \l__tblr_hline_leftskip_dim \tl_set:Nx \l__tblr_f_tl { \__tblr_spec_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} \skip_horizontal:N \l__tblr_hline_rightskip_dim } \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_spec_item:ne { vline } { [\int_eval:n {#1 + 1}] / @vline-width } } \dim_set:Nn #2 { \__tblr_spec_item:ne { vline } { [#1] / @vline-width } + \__tblr_data_item:nen { column } {#1} { leftsep } + \__tblr_data_item:nen { column } {#1} { @col-width } + \__tblr_data_item:nen { column } {#1} { rightsep } + #3 } } \dim_new:N \l__tblr_hline_leftskip_dim \dim_new:N \l__tblr_hline_rightskip_dim %% Calculate left and right skips from leftpos and rightpos specifications %% #1: row number; #2: column number; #3: hline index; \cs_new_protected:Npn \__tblr_get_hline_left_right_skips:nnn #1 #2 #3 { \tl_set:Nx \l__tblr_hline_leftpos_tl { \__tblr_spec_item:ne { hline } { [#1][#2](#3) / leftpos } } \tl_if_empty:NT \l__tblr_hline_leftpos_tl { \tl_set:Nn \l__tblr_hline_leftpos_tl {1} } % default position \tl_set:Nx \l__tblr_hline_rightpos_tl { \__tblr_spec_item:ne { hline } { [#1][#2](#3) / rightpos } } \tl_if_empty:NT \l__tblr_hline_rightpos_tl { \tl_set:Nn \l__tblr_hline_rightpos_tl {1} } % default position \fp_compare:nNnT { \l__tblr_hline_leftpos_tl } < {1} { \dim_set:Nn \l_tmpa_dim { \__tblr_spec_item:ne { vline } { [#2] / @vline-width } } \dim_set:Nn \l_tmpb_dim { \__tblr_data_item:nen { column } {#2} { leftsep } } \fp_compare:nNnTF { \l__tblr_hline_leftpos_tl } < {0} { \dim_set:Nn \l__tblr_hline_leftskip_dim { \l_tmpa_dim - \l__tblr_hline_leftpos_tl \l_tmpb_dim } } { \dim_set:Nn \l__tblr_hline_leftskip_dim { \l_tmpa_dim - \l__tblr_hline_leftpos_tl \l_tmpa_dim } } } \fp_compare:nNnT { \l__tblr_hline_rightpos_tl } < {1} { \dim_set:Nn \l_tmpa_dim { \__tblr_spec_item:ne { vline } { [\int_eval:n { #2 + 1 }] / @vline-width } } \dim_set:Nn \l_tmpb_dim { \__tblr_data_item:nen { column } {#2} { rightsep } } \fp_compare:nNnTF { \l__tblr_hline_rightpos_tl } < {0} { \dim_set:Nn \l__tblr_hline_rightskip_dim { \l_tmpa_dim - \l__tblr_hline_rightpos_tl \l_tmpb_dim } } { \dim_set:Nn \l__tblr_hline_rightskip_dim { \l_tmpa_dim - \l__tblr_hline_rightpos_tl \l_tmpa_dim } } } } \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 \box_new:N \l__tblr_row_vlines_box \box_new:N \l__tblr_vline_box \box_new:N \l__tblr_cell_box %% Build current row, #1: row number %% To fix disappeared vlines with colorful tables in Adobe Reader (see #76), %% we collect all vlines and draw them at the end of the row. \cs_new_protected:Npn \__tblr_build_row:N #1 { \int_set:Nn \c@rownum {#1} \__tblr_update_rowsep_registers: \__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 \__tblr_hook_use:n { tabularray/row/before } \tblr_vrule_wd_ht_dp:nnn {0pt} {\l__tblr_row_ht_dim} {\l__tblr_row_dp_dim} \hbox_set:Nn \l__tblr_row_vlines_box { \tblr_vrule_wd_ht_dp:nnn {0pt} {\l__tblr_row_ht_dim} {\l__tblr_row_dp_dim} } \int_step_variable:nNn { \c@colcount } \l__tblr_j_tl { \__tblr_put_one_vline:n { \__tblr_build_vline_segment:nn {#1} { \l__tblr_j_tl } } \__tblr_put_one_cell:n { \__tblr_build_cell:NN #1 \l__tblr_j_tl } } \__tblr_put_one_vline:n { \__tblr_build_vline_segment:nn {#1} { \int_eval:n {\c@colcount + 1} } } \skip_horizontal:n { - \box_wd:N \l__tblr_row_vlines_box } \box_use:N \l__tblr_row_vlines_box \__tblr_hook_use:n { tabularray/row/after } } %% 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_data_item:nen { row } {#1} { abovesep } } \dim_set:Nn #5 { \__tblr_data_item:nen { row } {#1} { belowsep } } \dim_set:Nn \l__row_upper_dim { \__tblr_data_item:nen { row } {#1} { @row-upper } } \dim_set:Nn \l__row_lower_dim { \__tblr_data_item:nen { row } {#1} { @row-lower } } \dim_set:Nn \l__row_vpace_dim { ( \__tblr_data_item:nen { 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 } \cs_new_protected:Npn \__tblr_put_one_vline:n #1 { \hbox_set:Nn \l__tblr_vline_box {#1} \skip_horizontal:n { \box_wd:N \l__tblr_vline_box } \hbox_set:Nn \l__tblr_row_vlines_box { \hbox_unpack:N \l__tblr_row_vlines_box \box_use:N \l__tblr_vline_box } } \cs_new_protected:Npn \__tblr_put_one_cell:n #1 { \hbox_set:Nn \l__tblr_cell_box {#1} \hbox_set:Nn \l__tblr_row_vlines_box { \hbox_unpack:N \l__tblr_row_vlines_box \skip_horizontal:n { \box_wd:N \l__tblr_cell_box } } \box_use:N \l__tblr_cell_box } %% #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_spec_item:ne { vline } { [#2] / @vline-count } } \tl_set:Nx \l__tblr_o_tl { \__tblr_spec_item:ne { vline } { [#1][#2] / omit } } \tl_if_empty:NTF \l__tblr_o_tl { \int_compare:nNnT { \l__tblr_n_tl } > {0} { \__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_spec_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 { inner } { rulesep } } \hbox_set:Nn \l__tblr_a_box { \int_step_inline:nn { \l__tblr_n_tl } { \tl_set:Nx \l__tblr_w_tl { \__tblr_spec_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_spec_item:ne { vline } { [#1][#2](##1) / fg } } \tl_if_empty:NF \l__tblr_f_tl { \color{\l__tblr_f_tl} } \__tblr_get_vline_above_below_skips:nnn {#1} {#2} {##1} \skip_vertical:N \l__tblr_vline_aboveskip_dim \__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 } } \skip_vertical:N \l__tblr_vline_belowskip_dim } \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 } \dim_new:N \l__tblr_vline_aboveskip_dim \dim_new:N \l__tblr_vline_belowskip_dim %% Calculate above and below skips from abovepos and belowpos specifications %% #1: row number; #2: column number; #3: vline index; \cs_new_protected:Npn \__tblr_get_vline_above_below_skips:nnn #1 #2 #3 { \tl_set:Nx \l__tblr_vline_abovepos_tl { \__tblr_spec_item:ne { vline } { [#1][#2](#3) / abovepos } } \tl_if_empty:NT \l__tblr_vline_abovepos_tl { \tl_set:Nn \l__tblr_vline_abovepos_tl {0} % default position } \fp_compare:nNnF { \l__tblr_vline_abovepos_tl } = {0} { \dim_set:Nn \l_tmpa_dim { \__tblr_spec_item:ne { hline } { [#1] / @hline-height } } \fp_compare:nNnTF { \l__tblr_vline_abovepos_tl } < {0} { \dim_set:Nn \l__tblr_vline_aboveskip_dim { - \l__tblr_vline_abovepos_tl \l__tblr_row_abovesep_dim } } { \dim_set:Nn \l__tblr_vline_aboveskip_dim { - \l__tblr_vline_abovepos_tl \l_tmpa_dim } } } %% To join two vline segment above and below a cline, %% we choose to extend every vline downwards a little (#55, #272). \tl_set:Nx \l__tblr_vline_belowpos_tl { \__tblr_spec_item:ne { vline } { [#1][#2](#3) / belowpos } } \tl_if_empty:NTF \l__tblr_vline_belowpos_tl { \dim_set:Nn \l__tblr_vline_belowskip_dim { - \__tblr_spec_item:ne { hline } { [\int_eval:n { #1 + 1 }](1) / @hline-height } + 0pt } } { \dim_set:Nn \l_tmpa_dim { \__tblr_spec_item:ne { hline } { [\int_eval:n { #1 + 1 }] / @hline-height } } \fp_compare:nNnTF { \l__tblr_vline_belowpos_tl } < {0} { \dim_set:Nn \l__tblr_vline_belowskip_dim { - \l__tblr_vline_belowpos_tl \l__tblr_row_belowsep_dim } } { \dim_set:Nn \l__tblr_vline_belowskip_dim { - \l__tblr_vline_belowpos_tl \l_tmpa_dim } } } } %% These public variables are updated by default before building a cell \tl_new:N \lTblrCellRowSpanTl \tl_new:N \lTblrCellColSpanTl \tl_new:N \lTblrCellBackgroundTl \bool_new:N \lTblrCellOmittedBool \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@colnum {#2} \__tblr_update_colsep_registers: \group_begin: \tl_set:Nx \l__tblr_w_tl { \__tblr_data_item:nen { column } {#2} { @col-width } } \tl_set:Nx \l__tblr_h_tl { \__tblr_data_item:nen { row } {#1} { @row-height } } \tl_set:Nx \l__tblr_x_tl { \__tblr_data_item:nen { column } {#2} { leftsep} } \tl_set:Nx \l__tblr_y_tl { \__tblr_data_item:nen { column } {#2} { rightsep } } \tl_set:Nx \lTblrCellColSpanTl { \__tblr_data_item:neen { cell } {#1} {#2} { colspan } } \int_compare:nNnTF { \lTblrCellColSpanTl } < {2} { \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 \lTblrCellRowSpanTl { \__tblr_data_item:neen { cell } {#1} {#2} { rowspan } } \int_compare:nNnTF { \lTblrCellRowSpanTl } < {2} { \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: } %% These public variables are updated by html library before building a cell \tl_new:N \lTblrCellAboveBorderStyleTl \tl_new:N \lTblrCellAboveBorderWidthTl \tl_new:N \lTblrCellAboveBorderColorTl \tl_new:N \lTblrCellBelowBorderStyleTl \tl_new:N \lTblrCellBelowBorderWidthTl \tl_new:N \lTblrCellBelowBorderColorTl \tl_new:N \lTblrCellLeftBorderStyleTl \tl_new:N \lTblrCellLeftBorderWidthTl \tl_new:N \lTblrCellLeftBorderColorTl \tl_new:N \lTblrCellRightBorderStyleTl \tl_new:N \lTblrCellRightBorderWidthTl \tl_new:N \lTblrCellRihgtBorderColorTl %% #1: row number in tl; #2: column number in tl %% This function is called only when html library is loaded. %% The properties can be used by tagpdf, tex4ht and lwarp packages \cs_new_protected:Npn \__tblr_expose_cell_properties:NN #1 #2 { \__tblr_expose_cell_border:NNnn #1 #2 { hline } { Above } \tl_set:Nx \l_tmpa_tl { \int_eval:n { #1 + \lTblrCellRowSpanTl } } \__tblr_expose_cell_border:NNnn \l_tmpa_tl #2 { hline } { Below } \__tblr_expose_cell_border:NNnn #1 #2 { vline } { Left } \tl_set:Nx \l_tmpb_tl { \int_eval:n { #2 + \lTblrCellColSpanTl } } \__tblr_expose_cell_border:NNnn #1 \l_tmpb_tl { vline } { Right } } \tl_new:N \l__tblr_dash_value_tl \tl_new:N \l__tblr_dash_value_head_tl \tl_new:N \l__tblr_dash_value_tail_tl \tl_new:N \l__tblr_width_value_tl \tl_new:N \l__tblr_color_value_tl %% #1: row number in tl; #2: column number in tl; %% #3: hline or vline; #4: position of border (Above/Below/Left/Right). \cs_new_protected:Npn \__tblr_expose_cell_border:NNnn #1 #2 #3 #4 { %% get border style \tl_set:Nx \l__tblr_dash_value_tl %% may be empty { \__tblr_spec_item:ne { #3 } { [#1][#2](1) / @dash } } \tl_set:Nx \l__tblr_dash_value_head_tl { \tl_head:N \l__tblr_dash_value_tl } \tl_set:Nx \l__tblr_dash_value_tail_tl { \tl_tail:N \l__tblr_dash_value_tl } \exp_args:NV \tl_if_eq:NNTF \l__tblr_dash_value_head_tl \@tblr@dash { \tl_set_eq:cN { lTblrCell #4 BorderStyleTl } \l__tblr_dash_value_tail_tl %% get border width \tl_set:Nx \l__tblr_width_value_tl { \__tblr_spec_item:ne { #3 } { [#1][#2](1) / wd } } \tl_if_empty:NTF \l__tblr_width_value_tl { \tl_set:cn { lTblrCell #4 BorderWidthTl } { 0.4pt } } { \tl_set_eq:cN { lTblrCell #4 BorderWidthTl } \l__tblr_width_value_tl } %% get border color \tl_set:cx { lTblrCell #4 BorderColorTl } { \__tblr_spec_item:ne { #3 } { [#1][#2](1) / fg } } } { \tl_clear:c { lTblrCell #4 BorderStyleTl } \tl_set:cn { lTblrCell #4 BorderWidthTl } { 0pt } \tl_clear:c { lTblrCell #4 BorderColorTl } } } \cs_new_protected:Npn \__tblr_build_cell_content:NN #1 #2 { \bool_if:NT \l__tblr_html_variables_bool { \__tblr_expose_cell_properties:NN #1 #2 } \__tblr_hook_use:n { tabularray/cell/before } \hbox_set_to_wd:Nnn \l__tblr_a_box { \l__tblr_cell_wd_dim } { \tl_if_eq:NnTF \g__tblr_cell_halign_tl {j} % cell width may be less than column width for j cells { \__tblr_get_cell_text:nn {#1} {#2} \hfil } { \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 \int_compare:nNnT { \lTblrCellRowSpanTl } < {2} { \box_set_ht:Nn \l__tblr_a_box { \__tblr_data_item:nen { row } {#1} { @row-upper } } \box_set_dp:Nn \l__tblr_a_box { \__tblr_data_item:nen { 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_data_item:nen { row } {#1} { @row-head } } \box_use:N \l__tblr_a_box \vfil } \c__tblr_valign_f_tl { \vfil \int_compare:nNnTF { \lTblrCellRowSpanTl } < {2} { \box_set_dp:Nn \l__tblr_a_box { \__tblr_data_item:nen { row } {#1} { @row-foot } } } { \box_set_dp:Nn \l__tblr_a_box { \__tblr_data_item:nen { row } { \int_eval:n { #1 + \lTblrCellRowSpanTl - 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 } \__tblr_hook_use:n { tabularray/cell/after } } \cs_new_protected:Npn \__tblr_build_cell_background:NN #1 #2 { \bool_set:Nn \lTblrCellOmittedBool { \int_compare_p:nNn { \__tblr_data_item:neen { cell } {#1} {#2} { omit } } = {1} } \bool_if:NF \lTblrCellOmittedBool { \tl_set:Nx \lTblrCellBackgroundTl { \__tblr_data_item:neen { cell } {#1} {#2} { background } } \group_begin: \tl_if_empty:NF \lTblrCellBackgroundTl { \__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 } { \lTblrCellBackgroundTl } } \group_end: } } %% #1: row number; #2: column number; #3 resulting dimension \cs_new_protected:Npn \__tblr_get_cell_background_width:NNN #1 #2 #3 { \int_compare:nNnTF { \lTblrCellColSpanTl } < {2} { \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 { \int_compare:nNnTF { \lTblrCellRowSpanTl } < {2} { \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 %% But when vspan=even, 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_vertical_sizes:NNNNN #1 #2 #3 #4 #5 { \dim_set:Nn #3 { \__tblr_data_item:nen { row } {#1} { abovesep } } \dim_zero:N #4 \dim_add:Nn #4 { \prop_item:Ne \l__tblr_row_item_skip_size_prop { item[#1] } } \int_step_inline:nnn { #1 + 1 } { #1 + \lTblrCellRowSpanTl - 1 } { \dim_add:Nn #4 { \prop_item:Ne \l__tblr_row_item_skip_size_prop { skip[##1] } + \prop_item:Ne \l__tblr_row_item_skip_size_prop { item[##1] } } } \dim_set:Nn #5 { \__tblr_data_item:nen { row } { \int_eval:n { #1 + \lTblrCellRowSpanTl - 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=even or 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_data_item:nen { column } {#2} { leftsep } } \dim_zero:N #4 \dim_add:Nn #4 { \prop_item:Ne \l__tblr_col_item_skip_size_prop { item[#2] } } \int_step_inline:nnn { #2 + 1 } { #2 + \lTblrCellColSpanTl - 1 } { \dim_add:Nn #4 { \prop_item:Ne \l__tblr_col_item_skip_size_prop { skip[##1] } + \prop_item:Ne \l__tblr_col_item_skip_size_prop { item[##1] } } } \dim_set:Nn #5 { \__tblr_data_item:nen { column } { \int_eval:n {#2 + \lTblrCellColSpanTl - 1} } { rightsep } } %\tl_log:x { cell[#1][#2] ~:~ \dim_use:N #3, \dim_use:N #4, \dim_use:N #5 } } %%% -------------------------------------------------------- %%> \section{Tracing Tabularray} %%% -------------------------------------------------------- \NewDocumentCommand \SetTblrTracing { m } { \keys_set:nn { tblr-set-tracing } {#1} } \bool_new:N \g__tblr_tracing_text_bool \bool_new:N \g__tblr_tracing_command_bool \bool_new:N \g__tblr_tracing_option_bool \bool_new:N \g__tblr_tracing_theme_bool \bool_new:N \g__tblr_tracing_outer_bool \bool_new:N \g__tblr_tracing_inner_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 \bool_new:N \g__tblr_tracing_intarray_bool \bool_new:N \g__tblr_tracing_page_bool \bool_new:N \g__tblr_tracing_step_bool \bool_gset_true:N \g__tblr_tracing_step_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, +option .code:n = \bool_gset_true:N \g__tblr_tracing_option_bool, -option .code:n = \bool_gset_false:N \g__tblr_tracing_option_bool, +theme .code:n = \bool_gset_true:N \g__tblr_tracing_theme_bool, -theme .code:n = \bool_gset_false:N \g__tblr_tracing_theme_bool, +outer .code:n = \bool_gset_true:N \g__tblr_tracing_outer_bool, -outer .code:n = \bool_gset_false:N \g__tblr_tracing_outer_bool, +inner .code:n = \bool_gset_true:N \g__tblr_tracing_inner_bool, -inner .code:n = \bool_gset_false:N \g__tblr_tracing_inner_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, +intarray .code:n = \bool_gset_true:N \g__tblr_tracing_intarray_bool, -intarray .code:n = \bool_gset_false:N \g__tblr_tracing_intarray_bool, +page .code:n = \bool_gset_true:N \g__tblr_tracing_page_bool, -page .code:n = \bool_gset_false:N \g__tblr_tracing_page_bool, +step .code:n = \bool_gset_true:N \g__tblr_tracing_step_bool, -step .code:n = \bool_gset_false:N \g__tblr_tracing_step_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_option_bool \bool_gset_true:N \g__tblr_tracing_theme_bool \bool_gset_true:N \g__tblr_tracing_outer_bool \bool_gset_true:N \g__tblr_tracing_inner_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 \bool_gset_true:N \g__tblr_tracing_intarray_bool \bool_gset_true:N \g__tblr_tracing_page_bool \bool_gset_true:N \g__tblr_tracing_step_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_option_bool \bool_gset_false:N \g__tblr_tracing_theme_bool \bool_gset_false:N \g__tblr_tracing_outer_bool \bool_gset_false:N \g__tblr_tracing_inner_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 \bool_gset_false:N \g__tblr_tracing_intarray_bool \bool_gset_false:N \g__tblr_tracing_page_bool \bool_gset_false:N \g__tblr_tracing_step_bool } \NewDocumentCommand \LogTblrTracing { m } { \keys_set:nn { tblr-log-tracing } {#1} } \keys_define:nn { tblr-log-tracing } { step .code:n = \__tblr_log_tracing_step:n {#1}, 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_spec_log:n { text } } \cs_new_protected:Npn \__tblr_log_tracing_command: { \__tblr_prop_log:n { command } } \cs_new_protected:Npn \__tblr_log_tracing_option: { \__tblr_prop_log:n { note } \__tblr_prop_log:n { remark } \__tblr_prop_log:n { more } } \cs_new_protected:Npn \__tblr_log_tracing_theme: { \__tblr_style_log: } \cs_new_protected:Npn \__tblr_log_tracing_outer: { \__tblr_spec_log:n { outer } } \cs_new_protected:Npn \__tblr_log_tracing_inner: { \__tblr_prop_log:n { inner } } \cs_new_protected:Npn \__tblr_log_tracing_column: { \__tblr_data_log:n { column } } \cs_new_protected:Npn \__tblr_log_tracing_row: { \__tblr_data_log:n { row } } \cs_new_protected:Npn \__tblr_log_tracing_cell: { \__tblr_data_log:n { cell } } \cs_new_protected:Npn \__tblr_log_tracing_vline: { \__tblr_spec_log:n { vline } } \cs_new_protected:Npn \__tblr_log_tracing_hline: { \__tblr_spec_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_log_tracing_page: { \dim_log:N \pagegoal \dim_log:N \pagetotal } \cs_new_protected:Npn \__tblr_log_tracing_step:n #1 { \bool_if:NT \g__tblr_tracing_step_bool { \tl_log:x {Step :~ #1} } } \cs_new_protected:Npn \__tblr_do_if_tracing:nn #1 #2 { \bool_if:cT { g__tblr_tracing_ #1 _bool } {#2} } %%% -------------------------------------------------------- %%> \section{Tabularray Libraries} %%% -------------------------------------------------------- %% \NewTblrLibrary and \UseTblrLibrary commands \NewDocumentCommand \NewTblrLibrary { m m } { \cs_new_protected:cpn { __tblr_use_lib_ #1: } {#2} } \NewDocumentCommand \UseTblrLibrary { m } { \clist_map_inline:nn {#1} { \use:c { __tblr_use_lib_ ##1: } \cs_undefine:c { __tblr_use_lib_ ##1: } } } %% Library amsmath and environments +array, +matrix, +cases, ... \NewTblrLibrary { amsmath } { \RequirePackage { amsmath } \NewTblrEnviron { +array } \SetTblrInner[+array]{colsep = 5pt} \NewDocumentEnvironment { +matrix } { O{} +b } { \begin{+array}{ column{1} = {leftsep = 0pt}, column{Z} = {rightsep = 0pt}, cells = {c}, ##1 } ##2 \end{+array} } { } \NewDocumentEnvironment { +bmatrix } { O{} +b } { \begin{+array}{ column{1} = {leftsep = 0pt}, column{Z} = {rightsep = 0pt}, cells = {c}, delimiter = {left = [, right = ]}, ##1 } ##2 \end{+array} } { } \NewDocumentEnvironment { +Bmatrix } { O{} +b } { \begin{+array} { column{1} = {leftsep = 0pt}, column{Z} = {rightsep = 0pt}, cells = {c}, delimiter = {left = \lbrace, right = \rbrace}, ##1 } ##2 \end{+array} } { } \NewDocumentEnvironment { +pmatrix } { O{} +b } { \begin{+array} { column{1} = {leftsep = 0pt}, column{Z} = {rightsep = 0pt}, cells = {c}, delimiter = {left = (, right = )}, ##1 } ##2 \end{+array} } { } \NewDocumentEnvironment { +vmatrix } { O{} +b } { \begin{+array} { column{1} = {leftsep = 0pt}, column{Z} = {rightsep = 0pt}, cells = {c}, delimiter = {left = \lvert, right = \rvert}, ##1 } ##2 \end{+array} } { } \NewDocumentEnvironment { +Vmatrix } { O{} +b } { \begin{+array} { column{1} = {leftsep = 0pt}, column{Z} = {rightsep = 0pt}, cells = {c}, delimiter = {left = \lVert, right = \rVert}, ##1 } ##2 \end{+array} } { } \NewDocumentEnvironment { +cases } { O{} +b } { \begin{+array} { column{1} = {leftsep = 0pt}, column{Z} = {rightsep = 0pt}, colspec = {ll}, stretch = 1.2, delimiter = {left=\lbrace, right=.}, ##1 } ##2 \end{+array} } { } } %% Library booktabs and commands \toprule, \midrule, \bottomrule \NewTblrLibrary { booktabs } { % We only use dimensions \aboverulesep and \belowrulesep in booktabs package \RequirePackage { booktabs } \newcommand \tblr@booktabs@hline [1] [] { \hline [##1] } \newcommand \tblr@booktabs@oldhline [1] [] { \hline [##1] \hborder { abovespace = \aboverulesep, belowspace = \belowrulesep } } \newcommand \tblr@booktabs@cline [2] [] { \cline [##1] {##2} } \newcommand \tblr@booktabs@oldcline [2] [] { \cline [##1] {##2} \hborder { abovespace = \aboverulesep, belowspace = \belowrulesep } } \newcommand \tblr@booktabs@cline@more [2] [] { \SetHline [+] {##2} {##1} } \newcommand \tblr@booktabs@oldcline@more [2] [] { \SetHline [+] {##2} {##1} \hborder { abovespace = \aboverulesep, belowspace = \belowrulesep } } \NewTableCommand \toprule [1] [] { \tblr@booktabs@hline [wd=\heavyrulewidth, ##1] } \NewTableCommand \midrule [1] [] { \tblr@booktabs@hline [wd=\lightrulewidth, ##1] } \NewTableCommand \bottomrule [1] [] { \tblr@booktabs@hline [wd=\heavyrulewidth, ##1] } \NewTableCommand \cmidrule [2] [] { \tblr@booktabs@cline [wd=\cmidrulewidth, endpos, ##1] {##2} } \NewTableCommand \cmidrulemore [2] [] { \tblr@booktabs@cline@more [wd=\cmidrulewidth, endpos, ##1] {##2} } \newcommand \tblr@booktabs@change@more [1] { \cmidrulemore } \NewTableCommand \morecmidrules { \peek_meaning:NTF \cmidrule { \tblr@booktabs@change@more } { \relax } } \NewTblrEnviron { booktabs } \NewTblrEnviron { longtabs } \NewTblrEnviron { talltabs } \SetTblrInner [ booktabs ] { rowsep = 0pt } \SetTblrInner [ longtabs ] { rowsep = 0pt } \SetTblrInner [ talltabs ] { rowsep = 0pt } \SetTblrOuter [ longtabs ] { long } \SetTblrOuter [ talltabs ] { tall } \RequirePackage { etoolbox } \newcommand \tblr@booktabs@begin@hook { \let \tblr@booktabs@hline = \tblr@booktabs@oldhline \let \tblr@booktabs@cline = \tblr@booktabs@oldcline \let \tblr@booktabs@cline@more = \tblr@booktabs@oldcline@more } \AtBeginEnvironment { booktabs } { \tblr@booktabs@begin@hook } \AtBeginEnvironment { longtabs } { \tblr@booktabs@begin@hook } \AtBeginEnvironment { talltabs } { \tblr@booktabs@begin@hook } \NewTableCommand \specialrule [3] { \hline [##1] \hborder { abovespace = ##2, belowspace = ##3 } } \NewTableCommand \addrowspace [1] [\defaultaddspace] { \hborder { abovespace+ = (##1) / 2, belowspace+ = (##1) / 2 } } \NewTableCommand \addlinespace [1] [\defaultaddspace] { \hborder { abovespace+ = (##1) / 2, belowspace+ = (##1) / 2 } } } %% Library counter for resetting all counters \tl_new:N \__tblr_saved_trial_counters_tl \tl_new:N \__tblr_saved_cell_counters_tl \cs_new_protected:Npn \__tblr_save_counters:n #1 { } \cs_new_protected:Npn \__tblr_restore_counters:n #1 { } %% We use code from tabularx package for resetting all LaTeX counters, %% where internal macro \cl@@ckpt looks like the following: %% \@elt{page} \@elt{equation} \@elt{enumi} \@elt{enumii} \@elt{enumiii} ... \NewTblrLibrary { counter } { \cs_set_protected:Npn \__tblr_save_counters:n ##1 { \def \@elt ####1 { \global\value{####1} = \the\value{####1} \relax } \tl_set:cx { __tblr_saved_ ##1 _counters_tl } { \cl@@ckpt } \let \@elt = \relax } \cs_set_protected:Npn \__tblr_restore_counters:n ##1 { \tl_use:c { __tblr_saved_ ##1 _counters_tl } } } %% Library diagbox and command \diagbox \NewTblrLibrary { diagbox } { \RequirePackage{ diagbox } \cs_set_eq:NN \__tblr_lib_saved_diagbox:w \diagbox \NewContentCommand \diagbox [3] [] { \__tblr_lib_diagbox_fix:n { \__tblr_lib_saved_diagbox:w [ leftsep=\leftsep, rightsep=\rightsep, ##1 ] { \__tblr_lib_diagbox_math_or_text:n {##2} } { \__tblr_lib_diagbox_math_or_text:n {##3} } } } \NewContentCommand \diagboxthree [4] [] { \__tblr_lib_diagbox_fix:n { \__tblr_lib_saved_diagbox:w [ leftsep=\leftsep, rightsep=\rightsep, ##1 ] { \__tblr_lib_diagbox_math_or_text:n {##2} } { \__tblr_lib_diagbox_math_or_text:n {##3} } { \__tblr_lib_diagbox_math_or_text:n {##4} } } } } \cs_new_protected:Npn \__tblr_lib_diagbox_math_or_text:n #1 { \bool_if:NTF \l__tblr_cell_math_mode_bool {$#1$} {#1} } \box_new:N \l__tblr_diag_box \cs_new_protected:Npn \__tblr_lib_diagbox_fix:n #1 { \hbox_set:Nn \l__tblr_diag_box {#1} \box_set_ht:Nn \l__tblr_diag_box { \box_ht:N \l__tblr_diag_box - \abovesep } \box_set_dp:Nn \l__tblr_diag_box { \box_dp:N \l__tblr_diag_box - \belowsep } \box_use:N \l__tblr_diag_box } %% Library functional with evaluate and process options \cs_set_eq:NN \__tblr_functional_calculation: \prg_do_nothing: \NewTblrLibrary { functional } { \RequirePackage { functional } %% Add outer specification "evaluate" \keys_define:nn { tblr-outer } { evaluate .code:n = \__tblr_outer_gput_spec:nn { evaluate } {##1} } \tl_new:N \l__tblr_evaluate_tl \cs_set_protected:Npn \__tblr_hook_split_before: { \tl_set:Nx \l__tblr_evaluate_tl { \__tblr_spec_item:nn { outer } { evaluate } } \tl_if_empty:NF \l__tblr_evaluate_tl { \tl_if_eq:NnTF \l__tblr_evaluate_tl { all } { \tlSet \l__tblr_body_tl { \evalWhole {\expValue \l__tblr_body_tl} } } { \exp_last_unbraced:NNV \__tblr_evaluate_table_body:NN \l__tblr_body_tl \l__tblr_evaluate_tl } } } %% Evaluate every occurrence of the specified function %% Note that funtional package runs every return processor inside a group %% #1: tl with table content; #2: function to be evaluated \tl_new:N \g__tblr_functional_result_tl \cs_new_protected:Npn \__tblr_evaluate_table_body:NN ##1 ##2 { \tl_gclear:N \g__tblr_functional_result_tl \cs_set_protected:Npn \__tblr_evaluate_table_body_aux:w ####1 ##2 { \tl_gput_right:Nn \g__tblr_functional_result_tl {####1} \peek_meaning:NTF \q_stop { \use_none:n } {##2} } \fun_run_return_processor:nn { \exp_last_unbraced:NV \__tblr_evaluate_table_body_aux:w \gResultTl } { \exp_last_unbraced:NV \__tblr_evaluate_table_body_aux:w ##1 ##2 \q_stop } \tl_set_eq:NN ##1 \g__tblr_functional_result_tl } %% Add inner specification "process" \clist_put_right:Nn \g__tblr_table_known_keys_clist { process } \keys_define:nn { tblr } { process .code:n = \__tblr_keys_gput:nn { process } {##1} } \cs_set:Npn \__tblr_functional_calculation: { \LogTblrTracing { step = do ~ functional ~ calculation } \__tblr_prop_item:nn { inner } { process } } \prgNewFunction \cellGetText { m m } { \expWhole { \__tblr_spec_item:nn { text } { [##1][##2] } } } \prgNewFunction \cellSetText { m m m } { \__tblr_spec_gput:nnn { text } { [##1][##2] } {##3} } \prgNewFunction \cellSetStyle { m m m } { \tblr_set_cell:nnnn {##1} {##2} {} {##3} } \prgNewFunction \rowSetStyle { m m } { \tblr_set_row:nnn {##1} {} {##2} } \prgNewFunction \columnSetStyle { m m } { \tblr_set_column:nnn {##1} {} {##2} } } %% Library hook provides some public hooks \cs_new_protected:Npn \__tblr_hook_use:n #1 {} \NewTblrLibrary { hook } { \cs_set_eq:NN \__tblr_hook_use:n \hook_use:n \hook_new_pair:nn { tabularray/trial/before } { tabularray/trial/after } \hook_new_pair:nn { tabularray/table/before } { tabularray/table/after } \hook_new_pair:nn { tabularray/row/before } { tabularray/row/after } \hook_new_pair:nn { tabularray/cell/before } { tabularray/cell/after } } %% Library html provides more public variables %% These variables can be used by tagpdf, tex4ht and lwarp packages \bool_new:N \l__tblr_html_variables_bool \NewTblrLibrary { html } { \bool_set_true:N \l__tblr_html_variables_bool } %% Library nameref and its caption-ref template \NewTblrLibrary { nameref } { \RequirePackage { nameref } \clist_if_in:NnF \lTblrRefMoreClist { nameref } { \clist_put_right:Nn \lTblrRefMoreClist { nameref } \DefTblrTemplate { caption-ref }{ nameref } { \tl_if_eq:NnTF \lTblrEntryTl { none } { \exp_args:NV \GetTitleString \lTblrCaptionTl } { \tl_if_empty:NTF \lTblrEntryTl { \exp_args:NV \GetTitleString \lTblrCaptionTl } { \exp_args:NV \GetTitleString \lTblrEntryTl } } \tl_set_eq:NN \@currentlabelname \GetTitleStringResult } } } %% Library siunitx and S columns \NewTblrLibrary { siunitx } { \RequirePackage { siunitx } \NewColumnType { S } [1] [] { Q[si = {##1}, c] } \NewColumnType { s } [1] [] { Q[si = {##1}, c, cmd = \TblrUnit] } \__tblr_data_new_key:nnn { cell } { si } { str } \keys_define:nn { tblr-column } { si .code:n = \__tblr_siunitx_setcolumn:n {##1} } \cs_new_protected:Npn \__tblr_siunitx_setcolumn:n ##1 { \__tblr_column_gput_cell:nn { si } {##1} \__tblr_column_gput_cell:nn { cmd } { \TblrNum } } \NewDocumentCommand \TblrNum { m } { \__tblr_siunitx_process:Nn \tablenum {##1} } \NewDocumentCommand \TblrUnit { m } { \__tblr_siunitx_process:Nn \si {##1} } \cs_new_protected:Npn \__tblr_siunitx_process:Nn ##1 ##2 { \tl_if_head_is_group:nTF {##2} { ##2 } { \group_begin: \tl_set:Nx \l_tmpa_tl { \__tblr_data_item:neen { cell } { \int_use:N \c@rownum } { \int_use:N \c@colnum } { si } } \exp_args:NV \sisetup \l_tmpa_tl ##1 {##2} \group_end: } } \keys_define:nn { tblr-cell-spec } { guard .meta:n = { cmd = } } \keys_define:nn { tblr-row } { guard .meta:n = { cmd = } } \keys_define:nn { tblr-column } { guard .meta:n = { cmd = } } } %% Library varwidth and measure option \NewTblrLibrary { varwidth } { \RequirePackage { varwidth } \clist_gput_left:Nn \g__tblr_table_known_keys_clist { measure } \keys_define:nn { tblr } { measure .tl_set:N = \l__tblr_inner_spec_measure_tl } } %% Library zref and its caption-ref template \NewTblrLibrary { zref } { \RequirePackage { zref-user } \clist_if_in:NnF \lTblrRefMoreClist { zref } { \clist_put_right:Nn \lTblrRefMoreClist { zref } \DefTblrTemplate { caption-ref }{ zref } { \exp_args:NV \zlabel \lTblrLabelTl } } }