lvjr / tabularray

Typeset tabulars and arrays with LaTeX3
https://ctan.org/pkg/tabularray
247 stars 22 forks source link

Nested tblr breaks vertical alignment #401

Open nihil-admirari opened 1 year ago

nihil-admirari commented 1 year ago

In the following, both columns should be header-aligned

\documentclass[paper=b5]{scrartcl}
\usepackage{tabularray}

\newsavebox\SomeNumbers
\sbox\SomeNumbers{\begin{tblr}{hlines, vlines}1 & 2 & 3\end{tblr}}

\begin{document}
  \begin{tblr}{colspec={X[l, h]X[l, h]} , hlines, vlines}
    {Fill \\ some \\ boxes} & \begin{tblr}{hlines, vlines}1 & 2 & 3\end{tblr} \\
    {Fill \\ some \\ more \\ boxes} & \usebox\SomeNumbers
  \end{tblr}
\end{document}

but they aren't

test

Footer-alignment suffers from the same problem. As a workaround, saving nested tblr is a box restores the proper alignment, as the example shows, but nonetheless.

lvjr commented 1 year ago

I can confirm this is a bug. In this case, you could use t alignment as another workaround.

  \begin{tblr}{colspec={X[l, t]X[l, t]} , hlines, vlines}
    {Fill \\ some \\ boxes} & \begin{tblr}{hlines, vlines}1 & 2 & 3\end{tblr} \\
    {Fill \\ some \\ more \\ boxes} & \usebox\SomeNumbers
  \end{tblr}
muzimuzhi commented 1 year ago

\g__tblr_cell_valign_tl is altered by (the last cell of) nested tblr env.

Looks like \g__tblr_cell_valign_tl (at least as well as \g__tblr_cell_halign_tl and \g__tblr_cell_middle_tl) should be stored in a per nesting-level basis, for example in \cs{g__tblr_cell_ \int_use:N \g_tblr_level_int _prop}, and set locally.

muzimuzhi commented 1 year ago

Just a workaround: resetting cell alignments each time after a cell text is retrieved.

I still think it's better to set \g__tblr_cell_(halign|valign|middle)_tl locally (and rename them to \l__tblr_cell_...).

diff --git a/tabularray.sty b/tabularray.sty
index 15f9dd8..7f21a13 100644
--- a/tabularray.sty
+++ b/tabularray.sty
@@ -3629,7 +3629,10 @@
         \dim_gzero:N \g__tblr_cell_head_dim
         \dim_gzero:N \g__tblr_cell_foot_dim
       }
-      { \__tblr_get_cell_text_real:nn { #1 } { #2 } }
+      {
+        \__tblr_get_cell_text_real:nn { #1 } { #2 }
+        \__tblr_get_cell_alignments:nn { #1 } { #2 }
+      }
   }

 \tl_new:N \l__tblr_cell_fg_tl

image

Full example

```tex \documentclass{article} \usepackage[landscape]{geometry} \usepackage{tabularray} \SetTblrOuter{baseline=t} \SetTblrInner{hlines, vlines} \newsavebox\SomeNumbers \sbox\SomeNumbers{\begin{tblr}{}1 & 2 & 3\end{tblr}} \begin{document} \def\test#1{ \begin{tblr}{#1} \SetCell[c=2]{c} \texttt{\detokenize{#1}} \\ {Fill \\ some \\ boxes} & \begin{tblr}{} 1 & 2 & 3\end{tblr} \\ {Fill \\ some \\ boxes} & \usebox\SomeNumbers \end{tblr} } \def\tests{\par \test{colspec={Q[t] Q[t]}}\quad \test{colspec={Q[m] Q[m]}}\quad \test{colspec={Q[b] Q[b]}}\quad \test{colspec={Q[h] Q[h]}}\quad \test{colspec={Q[f] Q[f]}} } \subsection*{Before} \tests \ExplSyntaxOn \cs_gset_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 } \__tblr_get_cell_alignments:nn { #1 } { #2 } % <<< added } } \ExplSyntaxOff \subsection*{After} \tests \end{document} ```

lvjr commented 1 year ago

I don't remember why these variables were made global. They should be local because in the future we will need to make them public for hook usages.

muzimuzhi commented 1 year ago

They were changed from local to global in commit 839749e (global variables for cell alignments, 2021-04-30), prior to the first public release (2021H).

lvjr commented 1 year ago

image

The alignment in the result image seems wrong. I think it could be better to compare subtable cells with normal text cells.

muzimuzhi commented 1 year ago

The alignment in the result image seems wrong.

If you meant the effect of colspec={Q[b] Q[b]} in both Before and After, it's probably caused by the global \SetTblrOuter{baseline=t}. Removing it and adding outer style baseline=t to the level 1 tblr env, and adding a row of normal text cells, image

Full example, v2

```tex \documentclass{article} \usepackage[landscape]{geometry} \usepackage{tabularray} \SetTblrInner{hlines, vlines} %\SetTblrOuter{baseline=t} \newsavebox\SomeNumbers \sbox\SomeNumbers{\begin{tblr}{}1 & 2 & 3\end{tblr}} \begin{document} \def\test#1{ \begin{tblr}[baseline=t]{colspec={Q[#1] Q[#1]}} \SetCell[c=2]{c} \texttt{\detokenize{Q[#1]}} \\ {Fill \\ some \\ boxes} & text \\ {Fill \\ some \\ boxes} & \begin{tblr}{} 1 & 2 & 3\end{tblr} \\ {Fill \\ some \\ boxes} & \usebox\SomeNumbers \end{tblr} } \def\tests{\par \test{t}\quad \test{m}\quad \test{b}\quad \test{h}\quad \test{f} } \subsection*{Before} \tests \ExplSyntaxOn \cs_gset_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 } \__tblr_get_cell_alignments:nn { #1 } { #2 } % <<< added } } \ExplSyntaxOff \subsection*{After} \tests \end{document} ```

muzimuzhi commented 1 year ago

Just a workaround: resetting cell alignments each time after a cell text is retrieved.

[...]

diff --git a/tabularray.sty b/tabularray.sty
index 15f9dd8..7f21a13 100644
--- a/tabularray.sty
+++ b/tabularray.sty
@@ -3629,7 +3629,10 @@
         \dim_gzero:N \g__tblr_cell_head_dim
         \dim_gzero:N \g__tblr_cell_foot_dim
       }
-      { \__tblr_get_cell_text_real:nn { #1 } { #2 } }
+      {
+        \__tblr_get_cell_text_real:nn { #1 } { #2 }
+        \__tblr_get_cell_alignments:nn { #1 } { #2 }
+      }
   }

 \tl_new:N \l__tblr_cell_fg_tl

The patch above may be a bit confusing because I wanted to minimize the size of patch.

This v2 patch is easier to understand: set cell alignments again just after something like \hbox_set:Nn \l_tmpa_box { \__tblr_get_cell_text:nn {#1} {#2} }. If a cell contains a nested tblr table, then storing it in a temp box may change \g__tblr_cell_(halign|valign|middle)_tl (globally) whose values will be used for current cell later. Thus a resetting is needed and helpful.

v2 patch, generated with git diff -U11 to show more context

```diff diff --git a/tabularray.sty b/tabularray.sty index 15f9dd8..a4e996d 100644 --- a/tabularray.sty +++ b/tabularray.sty @@ -3606,22 +3606,23 @@ \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_get_cell_alignments: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 @@ -6579,22 +6580,23 @@ \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 } } } + \__tblr_get_cell_alignments:nn {#1} {#2} \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 } } ```