From 70486f94e9067354cad7d753c629ab8b30446f5d Mon Sep 17 00:00:00 2001 From: Karl Berry Date: Mon, 16 Dec 2019 21:42:56 +0000 Subject: sdaps (16dec19) git-svn-id: svn://tug.org/texlive/trunk@53147 c570f23f-e606-0410-a88d-b1316a301751 --- Master/texmf-dist/tex/latex/sdaps/sdapsarray.sty | 1015 ++++++++++++++++++++++ 1 file changed, 1015 insertions(+) create mode 100644 Master/texmf-dist/tex/latex/sdaps/sdapsarray.sty (limited to 'Master/texmf-dist/tex/latex/sdaps/sdapsarray.sty') diff --git a/Master/texmf-dist/tex/latex/sdaps/sdapsarray.sty b/Master/texmf-dist/tex/latex/sdaps/sdapsarray.sty new file mode 100644 index 00000000000..c57c389b347 --- /dev/null +++ b/Master/texmf-dist/tex/latex/sdaps/sdapsarray.sty @@ -0,0 +1,1015 @@ +%% +%% This is file `sdapsarray.sty', +%% generated with the docstrip utility. +%% +%% The original source files were: +%% +%% sdapsarray.dtx (with options: `package') +%% +%% This is a generated file. +%% +%% Copyright (C) 2015 by Benjamin Berg +%% +%% This work may be distributed and/or modified under the +%% conditions of the LaTeX Project Public License, either version 1.3 +%% of this license or (at your option) any later version. +%% The latest version of this license is in +%% http://www.latex-project.org/lppl.txt +%% and version 1.3 or later is part of all distributions of LaTeX +%% version 2005/12/01 or later. +%% +\NeedsTeXFormat{LaTeX2e}[1999/12/01] +\ProvidesPackage{sdapsarray} + [2015/04/10 v0.1 Initial version of SDAPS array package] +\RequirePackage{expl3}[2011/08/23] +\ExplSyntaxOn +\ExplSyntaxOff + +\RequirePackage{xparse} +\RequirePackage{sdapsbase} + +\ExplSyntaxOn + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +\cs_if_exist:NF \ior_str_get:NN { \cs_set_eq:Nc \ior_str_get:NN { ior_get_str:NN } } + +\bool_new:N \g__sdaps_array_info_open +\bool_gset_false:N \g__sdaps_array_info_open +\iow_new:N \g__sdaps_array_info_iow +\ior_new:N \g__sdaps_array_info_ior + +\cs_generate_variant:Nn \int_set:Nn { Nf } +\cs_generate_variant:Nn \coffin_join:NnnNnnnn { NnVNnVnn } + +\cs_new_protected_nopar:Nn \_sdaps_array_open_tmpfiles: +{ + % Guard against being executed multiple times + \bool_if:NF \g__sdaps_array_info_open { + \bool_gset_true:N \g__sdaps_array_info_open + + % Also ensures toc file exists (i.e. is readable) + \iow_open:Nn \g__sdaps_array_info_iow { \c_sys_jobname_str .sdapsarraytoc } + \file_if_exist:nTF { \c_sys_jobname_str .sdapsarraytic } { + % Copy into toc file, then open that. + \ior_open:Nn \g__sdaps_array_info_ior { \c_sys_jobname_str .sdapsarraytic } + + \ior_str_map_inline:Nn \g__sdaps_array_info_ior { \iow_now:Nn \g__sdaps_array_info_iow { ##1 } } + \ior_close:N \g__sdaps_array_info_ior + + \ior_open:Nn \g__sdaps_array_info_ior { \c_sys_jobname_str .sdapsarraytoc } + } { + } + \iow_close:N \g__sdaps_array_info_iow + + \iow_open:Nn \g__sdaps_array_info_iow { \c_sys_jobname_str .sdapsarraytic } + } +} + +\tl_new:N \g__sdaps_array_last_row_tl +\tl_gset_eq:NN \g__sdaps_array_last_row_tl \c_empty_tl + +\cs_new_protected_nopar:Nn \_sdaps_array_check_insert_header:N +{ + \bool_gset_eq:NN #1 \c_false_bool + \ior_if_eof:NF \g__sdaps_array_info_ior { + \ior_str_get:NN \g__sdaps_array_info_ior \l_tmpa_tl + + \tl_if_eq:NNF \g__sdaps_array_last_row_tl \c_empty_tl { + \tl_if_eq:NNF \g__sdaps_array_last_row_tl \l_tmpa_tl { + \bool_gset_eq:NN #1 \c_true_bool + } + } + \tl_gset_eq:NN \g__sdaps_array_last_row_tl \l_tmpa_tl + } +} + + +\prop_new:N \g__sdaps_array_layouter_prop + +\int_new:N \g_sdaps_array_row_penalty_tl +\int_gset:Nn \g_sdaps_array_row_penalty_tl { 10 } + + +\tl_new:N \g_sdaps_array_shared_data_tl +\tl_new:N \g_sdaps_array_stored_data_tl +\tl_new:N \g_sdaps_array_local_data_tl +\tl_new:N \g_sdaps_array_local_data_new_tl +\prop_new:N \g__sdaps_array_stored_data_prop +\prop_new:N \g__sdaps_array_shared_data_prop +\prop_new:N \g__sdaps_array_local_data_prop + +\cs_generate_variant:Nn \prop_item:Nn { NV } + +\cs_new_protected_nopar:Nn \_sdaps_array_load_data: +{ + \tl_gset:Nx \g_sdaps_array_stored_data_tl { \prop_item:NV \g__sdaps_array_stored_data_prop \l__sdaps_array_global_name_tl } + \tl_gset:Nx \g_sdaps_array_shared_data_tl { \prop_item:NV \g__sdaps_array_shared_data_prop \l__sdaps_array_global_name_tl } + \tl_gset:Nx \g_sdaps_array_local_data_tl { \prop_item:NV \g__sdaps_array_local_data_prop \l__sdaps_array_local_name_tl } +} + +\cs_new_protected_nopar:Nn \_sdaps_array_store_data: +{ + % Do not overwrite the "stored" data that we have right now. + \prop_gput:NVV \g__sdaps_array_shared_data_prop \l__sdaps_array_global_name_tl \g_sdaps_array_shared_data_tl + + \immediate\write\@auxout{\exp_not:n{\sdapsarrayloadstoreddata}{\l__sdaps_array_global_name_tl}{\g_sdaps_array_shared_data_tl}} + \tl_if_empty:NF \g_sdaps_array_local_data_new_tl { + \immediate\write\@auxout{\exp_not:n{\sdapsarrayloadlocaldata}{\l__sdaps_array_local_name_tl}{\g_sdaps_array_local_data_new_tl}} + } +} + +\cs_new_protected_nopar:Nn \sdaps_array_load_stored_data:nn { + \prop_gput:Nnn \g__sdaps_array_stored_data_prop { #1 } { #2 } +} +\cs_new_eq:NN \sdapsarrayloadstoreddata \sdaps_array_load_stored_data:nn + +\cs_new_protected_nopar:Nn \sdaps_array_load_local_data:nn { + \prop_gput:Nnn \g__sdaps_array_local_data_prop { #1 } { #2 } +} +\cs_new_eq:NN \sdapsarrayloadlocaldata \sdaps_array_load_local_data:nn + + +\int_new:N \g__sdaps_array_current_id_int +\tl_new:N \l__sdaps_array_global_name_tl +\tl_new:N \l__sdaps_array_local_name_tl + +\cs_generate_variant:Nn \keys_set:nn { nf } +\cs_new_protected_nopar:Nn \sdaps_array_begin:nn +{ + \tl_set:Nx \l__sdaps_array_local_name_tl { sdapsarray \int_use:N\g__sdaps_array_current_id_int } + \int_gincr:N \g__sdaps_array_current_id_int + \tl_if_empty:nTF { #2 } { + \tl_set:NV \l__sdaps_array_global_name_tl \l__sdaps_array_local_name_tl + } { + \tl_set:Nx \l__sdaps_array_global_name_tl { #2 } + } + + \_sdaps_array_load_data: + + \keys_set:nf { sdaps / array } { \prop_item:Nn \g__sdaps_array_layouter_prop { #1 } } + + \_sdaps_array_open_tmpfiles: + + \tl_gset_eq:NN \g__sdaps_array_last_row_tl \c_empty_tl + \bool_gset_true:N \g_sdaps_array_first_row_bool + + \l__sdaps_array_begin_tl +} +\cs_generate_variant:Nn \sdaps_array_begin:nn { Vn } +\cs_generate_variant:Nn \sdaps_array_begin:nn { VV } +\cs_generate_variant:Nn \sdaps_array_begin:nn { nV } + +\cs_new_protected_nopar:Nn \sdaps_array_begin:n +{ + \sdaps_array_begin:nn { #1 } { } +} + +\cs_new_protected_nopar:Nn \sdaps_array_row_start: +{ + \l__sdaps_array_row_start_tl +} + +\cs_new_protected_nopar:Nn \_sdaps_array_assign_use: { + \box_use:N \l_tmpa_box + \egroup +} + +\cs_new_protected_nopar:Npn \sdaps_array_assign_colhead:Nw #1 +{ + \l__sdaps_array_colhead_tl #1 +} + +\cs_new_protected_nopar:Npn \sdaps_array_colhead:w +{ + \bgroup + \l__sdaps_array_colhead_tl \l_tmpa_box \bgroup + \group_insert_after:N\_sdaps_array_assign_use: + % swallow group opening token + \tex_let:D\next= +} + +\cs_new_protected:Npn \sdaps_array_assign_rowhead:Nw #1 +{ + \l__sdaps_array_rowhead_tl #1 +} + +\cs_new_protected:Npn \sdaps_array_rowhead:w +{ + \bgroup + \l__sdaps_array_rowhead_tl \l_tmpa_box \bgroup + \group_insert_after:N\_sdaps_array_assign_use: + % swallow group opening token + \tex_let:D\next= +} + +\cs_new_protected_nopar:Npn \sdaps_array_assign_cell:Nw #1 +{ + \l__sdaps_array_cell_tl #1 +} + +\cs_new_protected_nopar:Npn \sdaps_array_cell:w +{ + \bgroup + \l__sdaps_array_cell_tl \l_tmpa_box \bgroup + \group_insert_after:N\_sdaps_array_assign_use: + % swallow group opening token + \tex_let:D\next= +} + +\box_new:N \g__sdaps_array_header_box +\dim_new:N \g__sdaps_array_header_dim + +\cs_new_protected_nopar:Nn \_sdaps_array_calc_interlineskip:nnN +{ + \dim_compare:nNnTF { #1 } > { -1000pt } { + \skip_set:Nn #3 { \baselineskip - #1 - #2 } + \dim_compare:nNnF { #3 } > { \lineskiplimit } { + \skip_set:Nn #3 { \lineskip } + } + } { + \skip_set:Nn #3 { 0pt } + } + \skip_set:Nn #3 { #3 + \l_sdaps_sdapsarray_rowsep_dim } +} + +\cs_new_protected_nopar:Nn \sdaps_array_row:NN +{ + \if_mode_vertical: + \else: + \msg_error:nn { sdapsarray } { wrong_mode } + \fi + + % Pagebreak detection + \_sdaps_array_check_insert_header:N \g_tmpa_bool + % XXX: \l_tmpa_dim is the height to the first baseline in the box. Note that + % we use the real baseline in the case of the header row! + \l__sdaps_array_row_tl #1 #2 \l_tmpb_box \l_tmpa_dim + + \bool_if:NT \g_sdaps_array_first_row_bool { + % Stow away the box for later use (before rewriting it to save the position) + \box_gset_eq:NN \g__sdaps_array_header_box \l_tmpb_box + \dim_gset:Nn \g__sdaps_array_header_dim { \box_ht:N \g__sdaps_array_header_box + \box_dp:N \g__sdaps_array_header_box } + } + + \hbox_set:Nn \l_tmpb_box { + \pdfsavepos + \iow_shipout_x:Nn \g__sdaps_array_info_iow { + \thepage, + \the\pdflastxpos + } + \box_use:N \l_tmpb_box + } + + \bool_if:NTF \g_sdaps_array_first_row_bool { + \bool_gset_false:N \g_sdaps_array_first_row_bool + + \box_use:N \l_tmpb_box + % Do not ever allow a break after the header line. + \nobreak + } { + % The idea is simple. Before every line the header is re-inserted (either + % the real one or an empty box with the same dimensions). In the case that + % there is *no* page break we insert a corresponding negative skip so that + % the resulting skip (including interline skip) is exactly the normal + % interline skip between the rows. + % In the case that the skip *is* discarded we end up with the header box + % and the normal interline skip between the new row and the header. i.e.: + % skip = 1 * ( interlineskip_for_row - interlineskip_for_header - header_height - header_depth ) + header_height + header_depth + interlineksip_for_header + % or + % skip = 0 * ( interlineskip_for_row - interlineskip_for_header - header_height - header_depth ) + header_height + header_depth + interlineksip_for_header + + % Calculate interlineksip_for_row and interlineskip_for_header + \_sdaps_array_calc_interlineskip:nnN { \prevdepth } { \l_tmpa_dim } \l_tmpa_skip + \_sdaps_array_calc_interlineskip:nnN { \box_dp:N \g__sdaps_array_header_box } { \l_tmpa_dim } \l_tmpb_skip + \nointerlineskip + \skip_vertical:n { \l_tmpa_skip - \l_tmpb_skip } + \kern -\g__sdaps_array_header_dim + + % Inser the real or fake box + \bool_if:NTF \g_tmpa_bool { + \box_use:N \g__sdaps_array_header_box + } { + \hrule height \box_ht:N \g__sdaps_array_header_box depth \box_dp:N \g__sdaps_array_header_box width 0pt + } + \nobreak + % Insert the calculated interline skip (in the same way the TeX would do it. + \nointerlineskip + \skip_vertical:N \l_tmpb_skip + + \box_use:N \l_tmpb_box + + % And insert the sdapsarray sepecific inter row penalty. + \penalty\int_use:N\g_sdaps_array_row_penalty_tl + } +} + +\cs_new_protected_nopar:Nn \sdaps_array_end: +{ + \l__sdaps_array_end_tl + \box_gclear:N \g__sdaps_array_header_box +} + + +\tl_new:N \l__sdaps_array_begin_tl +\tl_new:N \l__sdaps_array_row_start_tl +\tl_new:N \l__sdaps_array_colhead_tl +\tl_new:N \l__sdaps_array_rowhead_tl +\tl_new:N \l__sdaps_array_cell_tl +\tl_new:N \l__sdaps_array_row_tl +\tl_new:N \l__sdaps_array_end_tl + +\keys_define:nn { sdaps / array } +{ + begin .tl_set:N = \l__sdaps_array_begin_tl, + row_start .tl_set:N = \l__sdaps_array_row_start_tl, + colhead .tl_set:N = \l__sdaps_array_colhead_tl, + rowhead .tl_set:N = \l__sdaps_array_rowhead_tl, + cell .tl_set:N = \l__sdaps_array_cell_tl, + row .tl_set:N = \l__sdaps_array_row_tl, + end .tl_set:N = \l__sdaps_array_end_tl, +} + +\seq_new:N \g_sdaps_array_overhangs_left_seq +\seq_new:N \g_sdaps_array_overhangs_right_seq +\seq_new:N \g_sdaps_array_shared_colwidths_seq +\seq_new:N \g_sdaps_array_stored_colwidths_seq + +\cs_new_protected_nopar:Npn \_sdaps_array_rowhead_default:Nw #1 +{ + \tl_if_empty:NTF \g_sdaps_array_local_data_tl { + \tl_if_empty:NTF \g_sdaps_array_local_data_new_tl { + \dim_set:Nn \l_tmpa_dim { 0.5 \hsize } + } { + \dim_set:Nn \l_tmpa_dim { \g_sdaps_array_local_data_new_tl } + } + } { + \dim_set:Nn \l_tmpa_dim { \g_sdaps_array_local_data_tl } + } + % \vbox_set_top:Nw is still missing as of 2017-08-11 + \tex_setbox:D #1 \tex_vtop:D \bgroup + \sdaps_if_rtl:TF { + \raggedright + } { + \raggedleft + } + \hsize=\dim_use:N\l_tmpa_dim + \group_begin:\bgroup + \group_insert_after:N \group_end: + \group_insert_after:N \egroup + % swallow group opening token + \tex_let:D\next= +} + +\cs_new_protected_nopar:Npn \_sdaps_array_cell_default:Nw #1 +{ + \hbox_set:Nw #1 \bgroup + % swallow group opening token + \group_insert_after:N \hbox_set_end: + \tex_let:D\next= +} + +\cs_new:Nn \_sdaps_array_cell_rotated_end: { + \hbox_set_end: + \dim_set:Nn \l_tmpa_dim { \box_ht:N \l_tmpa_box } + \dim_set:Nn \l_tmpb_dim { \box_dp:N \l_tmpa_box } + + \dim_set:Nn \l_tmpa_dim { \l_sdaps_sdapsarray_angle_sine_tl \l_tmpa_dim } + \dim_set:Nn \l_tmpb_dim { \l_sdaps_sdapsarray_angle_sine_tl \l_tmpb_dim } + + \box_rotate:Nn \l_tmpa_box { \l_sdaps_sdapsarray_angle_int } + + % We want the baseline of the box to be centered, that only works if we + % leave the same space both ways. + % That is not ideal, but we cannot move the cell content accordingly. + \dim_set:Nn \l_tmpb_dim { \dim_max:nn { \l_tmpa_dim } { \l_tmpb_dim } } + \skip_horizontal:n { \l_tmpb_dim } + \rlap{ + \skip_horizontal:n { -\l_tmpa_dim } + \box_use:N \l_tmpa_box + } + \skip_horizontal:n { \l_tmpb_dim } + % dummy skip that will be removed again by other code + \skip_horizontal:n { 0pt } + + \dim_set:Nn \l_tmpa_dim { \l_tmpa_dim + \l_tmpb_dim } + + \dim_set:Nn \l_tmpb_dim { \box_wd:N \l_tmpa_box } + \dim_set:Nn \l_tmpa_dim { \dim_max:nn { 0pt } { \l_tmpb_dim - \l_tmpa_dim } } + + \seq_gpush:Nn \g_sdaps_array_overhangs_left_seq { 0pt } + \seq_gpush:Nx \g_sdaps_array_overhangs_right_seq { \dim_use:N \l_tmpa_dim } + \egroup + \hbox_set_end: +} + +\cs_new_protected_nopar:Npn \_sdaps_array_cell_rotated:Nw #1 +{ + \hbox_set:Nw #1 \bgroup + \hbox_set:Nw \l_tmpa_box + \bgroup + \group_insert_after:N \_sdaps_array_cell_rotated_end: + % swallow group opening token + \tex_let:D\next= +} + + +\cs_new_protected_nopar:Nn \_sdaps_array_begin_default: +{ + \tl_if_empty:NTF \g_sdaps_array_shared_data_tl { + \seq_clear:N \g_sdaps_array_shared_colwidths_seq + } { + \seq_gset_split:NnV \g_sdaps_array_shared_colwidths_seq { ~ } \g_sdaps_array_shared_data_tl + } + \tl_if_empty:NTF \g_sdaps_array_stored_data_tl { + \seq_clear:N \g_sdaps_array_stored_colwidths_seq + } { + \seq_gset_split:NnV \g_sdaps_array_stored_colwidths_seq { ~ } \g_sdaps_array_stored_data_tl + } +} + +\cs_new_protected_nopar:Nn \_sdaps_array_end_default: +{ + \tl_gset:Nx \g_sdaps_array_shared_data_tl { \seq_use:Nn \g_sdaps_array_shared_colwidths_seq { ~ } } + \tl_gset:Nx \g_sdaps_array_stored_data_tl { \seq_use:Nn \g_sdaps_array_stored_colwidths_seq { ~ } } + + % Clear the global sequences, to save memory + \seq_gclear:N \g_sdaps_array_overhangs_left_seq + \seq_gclear:N \g_sdaps_array_overhangs_right_seq + \seq_gclear:N \g_sdaps_array_shared_colwidths_seq + \seq_gclear:N \g_sdaps_array_stored_colwidths_seq + + \_sdaps_array_store_data: +} + +\cs_new_protected_nopar:Nn \_sdaps_array_row_start_default: +{ + \seq_gclear:N \g_sdaps_array_overhangs_left_seq + \seq_gclear:N \g_sdaps_array_overhangs_right_seq +} + +\cs_new_protected_nopar:Nn \_sdaps_array_row:NNNN +{ + % #1: A vbox with baseline on the *first* item containing the row header + % (\vtop in plain TeX). + % #2: Data cells packed into an hbox. Each of these needs to be set to the + % correct width and inserted. + % #3: The box register to store the resulting hbox in. The depth of this box + % needs to be correct to calculate the interline glue to the following + % row. + % #4: A dim register to store the height of the box in for the purpose of + % calculating the interline glue in front of the produced row. + % + % { \dim_use:N\@totalleftmargin } { \dim_use:N\linewidth } + % The macro should create an hbox which is exactly \linewidth wide and also + % contains internal indentation by \@totalleftmargin into the box register #3. + % The box will be used while in vertical mode and may% be inserted multiple + % times in the case of the header row. + % To simplify the iteration it is guaranteed that the data cell boxes are not + % completely empty. This means the code can simply unbox until it sees a box + % that is void. + + \seq_gclear:N \g_tmpa_seq + + % Insert the boxes into a local hbox to work with them + \hbox_set:Nn #2 { + \hbox_unpack:N #2 + + % Handle the overhang, note that we modify the \g_sdaps_array_overhangs_right_seq locally only! + \seq_pop:NNTF \g_sdaps_array_overhangs_right_seq \l_tmpa_tl { + \dim_set:Nn \l_tmpb_dim { \l_tmpa_tl } + } { + \dim_set:Nn \l_tmpb_dim { 0pt } + } + % Implicit "last" column that contains the overhang + \seq_gpop:NNTF \g_sdaps_array_shared_colwidths_seq \l_tmpa_tl { + \dim_set:Nn \l_tmpa_dim { \l_tmpa_tl } + } { + \dim_set:Nn \l_tmpa_dim { 0pt } + } + \dim_set:Nn \l_tmpa_dim { \dim_max:nn { \l_tmpa_dim } { \l_tmpb_dim } } + + % MAX with stored values (NOTE: sequence only modified in local scope) + \seq_pop:NNTF \g_sdaps_array_stored_colwidths_seq \l_tmpa_tl { + \dim_set:Nn \l_tmpb_dim { \l_tmpa_tl } + } { + \dim_set:Nn \l_tmpb_dim { 0pt } + } + % Store value from this run, and then calculate max with previous run + \seq_gput_right:Nx \g_tmpa_seq { \dim_use:N \l_tmpa_dim } + \dim_set:Nn \l_tmpa_dim { \dim_max:nn { \l_tmpa_dim } { \l_tmpb_dim } } + + % Insert the overhang space + \hbox_set:Nn \l_tmpa_box { \skip_horizontal:n { \l_tmpa_dim } } + + % Now grab the first of the cells, and then loop over the rest + \box_set_to_last:N #2 + \bool_do_while:nn { ! \box_if_empty_p:N #2 } { + % Strip any trailing glue (i.e. space) coming from the user (for the + % leading side we ensure that \tex_ignorespaces:D is called). + % Note that this may remove e.g. a trailing \hfill from the user, the + % user needs to work around that (in the same way as is required in e.g. + % tabular). + \hbox_set:Nn #2 { \hbox_unpack:N #2 \unskip } + + % Pop the target width for the current box (i.e. we don't globally + % modify the clist here). + \seq_gpop:NNTF \g_sdaps_array_shared_colwidths_seq \l_tmpa_tl { + \dim_set:Nn \l_tmpa_dim { \l_tmpa_tl } + } { + \dim_set:Nn \l_tmpa_dim { 0pt } + } + % Calculate the maximum width of current and previous items + \dim_set:Nn \l_tmpa_dim { \dim_max:nn { \box_wd:N #2 } { \l_tmpa_dim } } + + % MAX with stored values (NOTE: sequence only modified in local scope) + \seq_pop:NNTF \g_sdaps_array_stored_colwidths_seq \l_tmpa_tl { + \dim_set:Nn \l_tmpb_dim { \l_tmpa_tl } + } { + \dim_set:Nn \l_tmpb_dim { 0pt } + } + % Store value from this run, and then calculate max with previous run + \seq_gput_right:Nx \g_tmpa_seq { \dim_use:N \l_tmpa_dim } + \dim_set:Nn \l_tmpa_dim { \dim_max:nn { \l_tmpa_dim } { \l_tmpb_dim } } + + % Set the box into a new box with the correct width which contains fil + % to center it. + \hbox_set_to_wd:Nnn \l_tmpb_box \l_tmpa_dim { \hfil \hbox_unpack:N #2 \hfil } + + % This loops works backward, so attach the cell on the right side. + % We used to make sure that it is layed out in order, but that is now + % obsolete and doing it out of order is simpler in RTL mode. + \sdaps_if_rtl:TF { + % The boxes are shown on the page from LTR + \hbox_set:Nn \l_tmpa_box { \box_use:N \l_tmpa_box \skip_horizontal:n { \l_sdaps_sdapsarray_colsep_dim } \box_use:N \l_tmpb_box \skip_horizontal:n { \l_sdaps_sdapsarray_colsep_dim } } + } { + \hbox_set:Nn \l_tmpa_box { \skip_horizontal:n { \l_sdaps_sdapsarray_colsep_dim } \box_use:N \l_tmpb_box \skip_horizontal:n { \l_sdaps_sdapsarray_colsep_dim } \box_use:N \l_tmpa_box } + } + + % Grab next cell + \box_set_to_last:N #2 + } + + % Get the coffin out of the nested scope by placing it into the box and + % placing that into it again ... + \box_use:N \l_tmpa_box + } + \hcoffin_set:Nn \l_tmpa_coffin { \box_use_drop:N #2 } + + \seq_gconcat:NNN \g_sdaps_array_shared_colwidths_seq \g_tmpa_seq \g_sdaps_array_shared_colwidths_seq + \seq_gclear:N \g_tmpa_seq + + % Calculate the space that is left for the header column + \dim_set:Nn \l_tmpa_dim { \linewidth - \coffin_wd:N \l_tmpa_coffin - 2\l_sdaps_sdapsarray_colsep_dim } + \tl_gset:Nx \g_sdaps_array_local_data_new_tl { \dim_use:N \l_tmpa_dim } + + % TODO: The \hfil here is a hack to prevent a warning if the vbox is empty. + % Unfortunately checking for an empty box does not work for some reason. + \dim_set:Nn \l_tmpb_dim { \box_ht:N #1 } + \sdaps_if_rtl:TF { + \hcoffin_set:Nn \l_tmpb_coffin { \hbox_to_wd:nn \l_tmpa_dim { \hfil \vbox:n { \vbox_unpack_clear:N #1 } } \skip_horizontal:n { \l_sdaps_sdapsarray_colsep_dim } } + \tl_set:Nn \l_tmpa_tl { l } + \tl_set:Nn \l_tmpb_tl { r } + } { + \hcoffin_set:Nn \l_tmpb_coffin { \skip_horizontal:n { \l_sdaps_sdapsarray_colsep_dim } \hbox_to_wd:nn \l_tmpa_dim { \hfil \vbox:n { \vbox_unpack_clear:N #1 } } } + \tl_set:Nn \l_tmpa_tl { r } + \tl_set:Nn \l_tmpb_tl { l } + } + \dim_set:Nn \l_tmpa_dim { \coffin_ht:N \l_tmpb_coffin } + + % If the first/last baseline differ then center the vbox, otherwise align the + % baseline with the cells + \dim_compare:nNnTF { \l_tmpa_dim } = { \l_tmpb_dim } { + \coffin_join:NnVNnVnn \l_tmpb_coffin { H } \l_tmpa_tl \l_tmpa_coffin { H } \l_tmpb_tl { \l_sdaps_sdapsarray_colsep_dim } { 0pt } + \dim_set:Nn #4 { \coffin_ht:N \l_tmpb_coffin } + } { + \coffin_join:NnVNnVnn \l_tmpb_coffin { vc } \l_tmpa_tl \l_tmpa_coffin { vc } \l_tmpb_tl { \l_sdaps_sdapsarray_colsep_dim } { 0pt } + % XXX: Assume that the header is higher than the content cells + \dim_set:Nn #4 { \l_tmpb_dim } + } + + \hbox_set:Nn #3 { \skip_horizontal:N \@totalleftmargin \coffin_typeset:Nnnnn \l_tmpb_coffin { H } { l } { 0pt } { 0pt } } +} + +\prop_gput:Nnn \g__sdaps_array_layouter_prop { default } { + begin = { \_sdaps_array_begin_default: }, + row_start = { \_sdaps_array_row_start_default: }, + rowhead = { \_sdaps_array_rowhead_default:Nw }, + colhead = { \_sdaps_array_cell_default:Nw }, + cell = { \_sdaps_array_cell_default:Nw }, + row = { \_sdaps_array_row:NNNN }, + end = { \_sdaps_array_end_default: }, +} + +\prop_gput:Nnn \g__sdaps_array_layouter_prop { rotated } { + begin = { \_sdaps_array_begin_default: }, + row_start = { \_sdaps_array_row_start_default: }, + rowhead = { \_sdaps_array_rowhead_default:Nw }, + colhead = { \_sdaps_array_cell_rotated:Nw }, + cell = { \_sdaps_array_cell_default:Nw }, + row = { \_sdaps_array_row:NNNN }, + end = { \_sdaps_array_end_default: }, +} + + +\bool_new:N \g_sdaps_array_first_row_bool +\bool_new:N \l__sdaps_sdapsarray_in_top_group_bool +\bool_new:N \l__sdaps_sdapsarray_have_content_bool +\bool_new:N \l_sdaps_sdapsarray_flip_bool +\tl_new:N \l_sdaps_sdapsarray_layouter_tl +\tl_new:N \l_sdaps_sdapsarray_align_tl +\bool_new:N \l_sdaps_sdapsarray_keepenv_bool +\int_new:N \l_sdaps_sdapsarray_angle_int +\tl_new:N \l_sdaps_sdapsarray_angle_sine_tl +\dim_new:N \l_sdaps_sdapsarray_colsep_dim +\dim_new:N \l_sdaps_sdapsarray_rowsep_dim + +\keys_define:nn { sdaps / sdapsarray } +{ + flip .bool_set:N = \l_sdaps_sdapsarray_flip_bool, + flip .initial:n = false, + flip .default:n = true, + layouter .tl_set:N = \l_sdaps_sdapsarray_layouter_tl, + layouter .initial:n = default, + align .tl_set:N = \l_sdaps_sdapsarray_align_tl, + align .initial:n = { }, + keepenv .bool_set:N = \l_sdaps_sdapsarray_keepenv_bool, + keepenv .initial:n = false, + keepenv .default:n = true, + + angle .code:n = { + \int_set:Nn \l_sdaps_sdapsarray_angle_int {#1} + \tl_set:Nx \l_sdaps_sdapsarray_angle_sine_tl { \fp_to_decimal:n {sind(#1)}} + }, + angle .initial:n = 70, + colsep .dim_set:N = \l_sdaps_sdapsarray_colsep_dim, + colsep .initial:n = 6pt, + rowsep .dim_set:N = \l_sdaps_sdapsarray_rowsep_dim, + rowsep .initial:n = 0pt, +} + + +\cs_new_nopar:Nn \l_sdaps_sdapsarray_alignment_set_have_content: +{ + \bool_set_true:N\l__sdaps_sdapsarray_have_content_bool +} + +\cs_new_nopar:Nn \_sdaps_sdapsarray_alignment: { + % End the last group, which will be the group that was begun earlier. + % If the earlier cell was the first one, then this egroup also starts the + % hbox to temporarily store the cells. + \egroup + \bool_if:NF \l__sdaps_sdapsarray_in_top_group_bool { + \msg_error:nnn { sdapsarray } { unmatched_grouping_level } { an~alignment~tab } + } + + % We need to notify the outside scope that there are items, will be inserted + % multiple times, but that does not matter. + \group_insert_after:N\l_sdaps_sdapsarray_alignment_set_have_content: + + % Just in case someone leaked a change into our scope + \bool_if:NF \l_sdaps_sdapsarray_keepenv_bool { + \cs_set_eq:NN \cr \_sdaps_sdapsarray_newline: + \cs_set_eq:NN \\ \cr + } + + % Define a cell now, we can just put everything into a new cell, and that + % should work fine. + % Note that cells are not safe for fragile commands at the moment. + \_sdaps_sdapsarray_cell:w \bgroup + \bool_set_false:N \l__sdaps_sdapsarray_in_top_group_bool + \cs_set_eq:NN \\ \cr + \tex_ignorespaces:D +} + +\msg_new:nnn { sdapsarray } { unmatched_grouping_level } { The~grouping~level~of~a~cell~was~not~even.~Please~ensure~all~braces~are~balanced~out!~This~error~occured~at~#1. } +\msg_new:nnn { sdapsarray } { unequal_cols } { The~number~of~columns~is~not~equal~for~all~rows. } +\msg_new:nnn { sdapsarray } { no_new_line_at_end } { You~have~terminated~the~last~line~with~\textbackslash\textbackslash~or~similar.~This~can~have~side~effects,~please~remove~it. } +\msg_new:nnn { sdapsarray } { wrong_mode } { The~sdapsarray~environment~can~only~function~in~vertical~mode~(both~inner~and~outer). } + +\cs_new:Nn \_sdaps_sdapsarray_start_cells: { + \bool_if:NF \l__sdaps_sdapsarray_in_top_group_bool { + \msg_error:nnn { sdapsarray } { unmatched_grouping_level } { the~end~of~a~row~header } + } + \egroup + + % We are in the environment scope again here + + % Notify code that we are going to generate cells for a new row. + \sdaps_array_row_start: + + % Start an hbox to stow away the cells. + % The rest of the setup happens in the alignment handler + \hbox_set:Nw \l_tmpb_box \bgroup + \group_insert_after:N \hbox_set_end: + \bool_set_true:N \l__sdaps_sdapsarray_in_top_group_bool +} + +\cs_new_nopar:Nn \_sdaps_sdapsarray_linestart: { + \sdaps_array_assign_rowhead:Nw \l_tmpa_box + \bgroup + \cs_set_eq:NN \\ \cr + + \bool_set_true:N \l__sdaps_sdapsarray_in_top_group_bool + \bgroup + \group_insert_after:N \_sdaps_sdapsarray_start_cells: + \bool_set_false:N \l__sdaps_sdapsarray_in_top_group_bool + % Ignore following spaces by the user + \tex_ignorespaces:D +} + +\cs_new_nopar:Nn \_sdaps_sdapsarray_newline: { + \egroup + \bool_if:NF \l__sdaps_sdapsarray_in_top_group_bool { + \msg_error:nnn { sdapsarray } { unmatched_grouping_level } { the~end~of~a~row } + } + \egroup + + % We are in the environment scope again here + % Output the last line if the cells were non-empty. + \bool_if:NT \l__sdaps_sdapsarray_have_content_bool { + \sdaps_array_row:NN \l_tmpa_box \l_tmpb_box + } + \bool_set_false:N \l__sdaps_sdapsarray_have_content_bool + + \cs_set_eq:NN \_sdaps_sdapsarray_cell:w \sdaps_array_cell:w + \_sdaps_sdapsarray_linestart: +} + +\cs_new:Npn\sdaps_array_nested_alignenv: { + \char_set_catcode_alignment:N & + \cs_set_eq:NN \cr \sdaps_orig_cr + \cs_set_eq:NN \\ \sdaps_orig_backslash +} + +\cs_new:Npn\sdaps_array_nested_alignenv:w { + \bgroup + \sdaps_array_nested_alignenv: + % swallow group opening token + \tex_let:D\next= +} +\cs_new_eq:NN \sdapsnested \sdaps_array_nested_alignenv:w + +\group_begin: +\char_set_catcode_active:N & +\cs_new:Nn \_sdaps_sdapsarray_defines: { + \cs_set_eq:NN \sdaps_orig_cr \cr + \cs_set_eq:NN \sdaps_orig_backslash \\ + \bool_if:NF \l_sdaps_sdapsarray_keepenv_bool { + \char_set_catcode_active:N & + \cs_set_eq:NN \cr \sdaps_array_newline: + \cs_set_eq:NN \\ \cr + \cs_set_eq:NN & \sdaps_array_alignment: + } +} +\group_end: + +%%%%%% +%%%%%% + + +\box_new:N \l_sdaps_sdapsarray_headers_box +\box_new:N \l_sdaps_sdapsarray_boxlist_head_box +\box_new:N \l_sdaps_sdapsarray_boxlist_tail_box + +\cs_new_protected:Nn \_sdaps_sdapsarray_prepend_box:NN { + \hbox_set:Nn #2 { + \box_use:N #1 + \hbox_unpack:N #2 + } + \box_clear:N #1 +} + +\cs_new_protected:Nn \_sdaps_sdapsarray_append_box:NN { + \hbox_set:Nn #2 { + \hbox_unpack:N #2 + \box_use:N #1 + } + \box_clear:N #1 +} + +\cs_new_protected:Nn \_sdaps_sdapsarray_pop_last_box:NN { + \hbox_set:Nn #2 { + \hbox_unpack:N #2 + \box_gset_to_last:N \g_tmpa_box + } + \box_set_eq:NN #1 \g_tmpa_box + \box_gclear:N \g_tmpa_box +} + +\cs_new_protected:Nn \_sdaps_sdapsarray_pop_last_hbox_unpack:NN { + \_sdaps_sdapsarray_pop_last_box:NN #1 #2 + \hbox_set:Nn #1 { + \hbox_unpack:N #1 + \box_gset_to_last:N \g_tmpa_box + } + \box_set_eq:NN #1 \g_tmpa_box + \box_gclear:N \g_tmpa_box +} + +\cs_new_protected:Nn \_sdaps_sdapsarray_boxlist_void_if_empty:N { + \hbox_set:Nn #1 { + \hbox_unpack:N #1 + \box_set_to_last:N #1 + \box_if_empty:NTF #1 { + \bool_gset_true:N \g_tmpa_bool + } { + \box_use:N #1 + \bool_gset_false:N \g_tmpa_bool + } + } + \bool_if:NT \g_tmpa_bool { + \box_clear:N #1 + } +} + + +\cs_new_protected_nopar:Nn \_sdaps_sdapsarray_alignment_flip: +{ + \_sdaps_sdapsarray_end_cell_flip: + + % Just in case someone leaked a change into our scope + \bool_if:NF \l_sdaps_sdapsarray_keepenv_bool { + \cs_set_eq:NN \cr \_sdaps_sdapsarray_newline_flip: + \cs_set_eq:NN \\ \cr + } + + % Next up is either a cell or a row header. We can figure that out by checking + % that the row headings box is void + \box_if_empty:NTF \l_sdaps_sdapsarray_headers_box { + \sdaps_array_assign_rowhead:Nw \l_tmpa_box \bgroup + \bool_set_false:N \l__sdaps_sdapsarray_in_top_group_bool + \cs_set_eq:NN \\ \cr + % Ignore following spaces by the user + \tex_ignorespaces:D + } { + \sdaps_array_assign_cell:Nw \l_tmpa_box \bgroup + \bool_set_false:N \l__sdaps_sdapsarray_in_top_group_bool + \cs_set_eq:NN \\ \cr + \tex_ignorespaces:D + } +} + +\cs_new_nopar:Nn \_sdaps_sdapsarray_end_cell_flip: { + \egroup % Finish of the cell + \bool_if:NF \l__sdaps_sdapsarray_in_top_group_bool { + \msg_error:nnn { sdapsarray } { unmatched_grouping_level } { end~of~cell~or~row } + } + + % Get last box from head + \_sdaps_sdapsarray_pop_last_box:NN \l_tmpb_box \l_sdaps_sdapsarray_boxlist_head_box + % Append the new box to the list of boxes for this row + \_sdaps_sdapsarray_append_box:NN \l_tmpa_box \l_tmpb_box + % Prepend the new box to the tail + \_sdaps_sdapsarray_prepend_box:NN \l_tmpb_box \l_sdaps_sdapsarray_boxlist_tail_box +} + +\cs_new_nopar:Nn \_sdaps_sdapsarray_end_line_flip: { + % At the end of the line, move tail into head. + % First check that head is empty. + \_sdaps_sdapsarray_boxlist_void_if_empty:N \l_sdaps_sdapsarray_boxlist_head_box + \box_if_empty:NF \l_sdaps_sdapsarray_boxlist_head_box { + \msg_error:nn { sdapsarray } { unequal_cols } + } + \box_set_eq:NN \l_sdaps_sdapsarray_boxlist_head_box \l_sdaps_sdapsarray_boxlist_tail_box + \box_clear:N \l_sdaps_sdapsarray_boxlist_tail_box + + % If this was the first row, store it away, these are the headings. + \box_if_empty:NT \l_sdaps_sdapsarray_headers_box { + \box_set_eq:NN \l_sdaps_sdapsarray_headers_box \l_sdaps_sdapsarray_boxlist_head_box + \box_clear:N \l_sdaps_sdapsarray_boxlist_head_box + } +} + +\cs_new_nopar:Nn \_sdaps_sdapsarray_newline_flip: { + \_sdaps_sdapsarray_end_cell_flip: + \_sdaps_sdapsarray_end_line_flip: + + % Create next box to store away, this has to be a colhead at this point + \sdaps_array_assign_colhead:Nw \l_tmpa_box \bgroup + \bool_set_false:N \l__sdaps_sdapsarray_in_top_group_bool + \cs_set_eq:NN \\ \cr + \tex_ignorespaces:D +} + +\NewDocumentEnvironment { sdapsarray } { o } +{ + \bool_set_false:N \l__sdaps_sdapsarray_in_top_group_bool + \group_begin: + + \tl_set:Nn \l_tmpa_tl { } + \IfNoValueF { #1 } { + \tl_set:Nn \l_tmpa_tl { #1 } + } + + \box_clear:N \l_tmpa_box + \box_clear:N \l_tmpb_box + + % Ensure vertical mode. + \tex_par:D + \if_mode_vertical: + \else: + \msg_error:nn { sdapsarray } { wrong_mode } + \fi + + % This needs to be initialized here as otherwise the values would be + % expanded at import time. + \keys_set:nV { sdaps / sdapsarray } \l_tmpa_tl + + \sdaps_array_begin:VV \l_sdaps_sdapsarray_layouter_tl \l_sdaps_sdapsarray_align_tl + + % Note, this environment is fragile; we redefine & to be active. + % One can go back into normal mode by using \sdapsnested{} though. + + \bool_if:NTF \l_sdaps_sdapsarray_flip_bool { + \cs_set_eq:NN \sdaps_array_newline: \_sdaps_sdapsarray_newline_flip: + \cs_set_eq:NN \sdaps_array_alignment: \_sdaps_sdapsarray_alignment_flip: + + \_sdaps_sdapsarray_defines: + + % Two hboxes to hold the content, note that + % a: row headers, b: cells/col headers + \box_clear:N \l_sdaps_sdapsarray_headers_box + \box_clear:N \l_sdaps_sdapsarray_boxlist_head_box + \box_clear:N \l_sdaps_sdapsarray_boxlist_tail_box + + % not sure why we need this group, but nothing works without it + \bgroup + % This is a bit creative to say the least + \sdaps_array_row_start: + + \bool_set_true:N \l__sdaps_sdapsarray_in_top_group_bool + \sdaps_array_assign_rowhead:Nw \l_tmpa_box \bgroup + \bool_set_false:N \l__sdaps_sdapsarray_in_top_group_bool + \cs_set_eq:NN \\ \cr + % Ignore following spaces by the user + \tex_ignorespaces:D + } { + \cs_set_eq:NN \sdaps_array_newline: \_sdaps_sdapsarray_newline: + \cs_set_eq:NN \sdaps_array_alignment: \_sdaps_sdapsarray_alignment: + \_sdaps_sdapsarray_defines: + + \cs_set_eq:NN \_sdaps_sdapsarray_cell:w \sdaps_array_colhead:w + \_sdaps_sdapsarray_linestart: + } + + % If we redefine &, then the next character might have the wrong catcode + % (i.e. it could still be an alignment character). Execute the alignment + % code directly if the next character is &. + \bool_if:NF \l_sdaps_sdapsarray_keepenv_bool { + \peek_charcode_remove:NT & { \sdaps_array_alignment: } + } +} +{ + \bool_if:NTF \l_sdaps_sdapsarray_flip_bool { + \_sdaps_sdapsarray_end_cell_flip: + + % At this point we should have swallowed all items from the head list. + % If not, then someone likely add a stray \\ command or similar + \_sdaps_sdapsarray_boxlist_void_if_empty:N \l_sdaps_sdapsarray_boxlist_head_box + \box_if_empty:NTF \l_sdaps_sdapsarray_boxlist_head_box { + \_sdaps_sdapsarray_end_line_flip: + } { + \msg_error:nn { sdapsarray } { no_new_line_at_end } + } + + % Now we can have fun! + % Pop cells and heading, until we cannot find any new ones. + \_sdaps_sdapsarray_pop_last_hbox_unpack:NN \l_tmpa_box \l_sdaps_sdapsarray_headers_box + \_sdaps_sdapsarray_pop_last_box:NN \l_tmpb_box \l_sdaps_sdapsarray_boxlist_head_box + \bool_do_while:nn { ! \box_if_empty_p:N \l_tmpa_box || ! \box_if_empty_p:N \l_tmpb_box } { + \sdaps_array_row:NN \l_tmpa_box \l_tmpb_box + + \sdaps_array_row_start: + + \_sdaps_sdapsarray_pop_last_hbox_unpack:NN \l_tmpa_box \l_sdaps_sdapsarray_headers_box + \_sdaps_sdapsarray_pop_last_box:NN \l_tmpb_box \l_sdaps_sdapsarray_boxlist_head_box + } + + \egroup + } { + \egroup + + \bool_if:NF \l__sdaps_sdapsarray_in_top_group_bool { + \msg_error:nnn { sdapsarray } { unmatched_grouping_level } { the~end~of~the~environment } + } + \egroup + + % We are in the environment scope again here + % Output the last line if the cells were non-empty. + \bool_if:NT \l__sdaps_sdapsarray_have_content_bool { + \sdaps_array_row:NN \l_tmpa_box \l_tmpb_box + } + } + + \sdaps_array_end: + + \group_end: +} + +\ExplSyntaxOff + + +\endinput +%% +%% End of file `sdapsarray.sty'. -- cgit v1.2.3