From e0c6872cf40896c7be36b11dcc744620f10adf1d Mon Sep 17 00:00:00 2001 From: Norbert Preining Date: Mon, 2 Sep 2019 13:46:59 +0900 Subject: Initial commit --- macros/latex/contrib/topiclongtable/README.md | 25 ++ .../contrib/topiclongtable/topiclongtable-doc.pdf | Bin 0 -> 377106 bytes .../contrib/topiclongtable/topiclongtable-doc.tex | 303 ++++++++++++++ .../contrib/topiclongtable/topiclongtable.sty | 433 +++++++++++++++++++++ 4 files changed, 761 insertions(+) create mode 100644 macros/latex/contrib/topiclongtable/README.md create mode 100644 macros/latex/contrib/topiclongtable/topiclongtable-doc.pdf create mode 100644 macros/latex/contrib/topiclongtable/topiclongtable-doc.tex create mode 100644 macros/latex/contrib/topiclongtable/topiclongtable.sty (limited to 'macros/latex/contrib/topiclongtable') diff --git a/macros/latex/contrib/topiclongtable/README.md b/macros/latex/contrib/topiclongtable/README.md new file mode 100644 index 0000000000..2d678387a8 --- /dev/null +++ b/macros/latex/contrib/topiclongtable/README.md @@ -0,0 +1,25 @@ +# topiclongtable + +This work is sponsored by Human Predictions, LLC (). + +This work is maintained by Paolo Brasolin (). + +This work is licensed under MIT License. + +This work is a LaTeX package consisting of the following files: + * README.md + * topiclongtable.sty + * topiclongtable-doc.tex + * topiclongtable-doc.pdf + +--- + +This LaTeX package extends `longtable` implementing cells that: + +* merge with the one above if it has the same content, +* do not merge with the one above unless the ones on the left are merged, +* are well behaved with respect to `longtable` chunking on page breaks, +* and automatically draw the correct separation lines. + +The typical use case is a table spanning multiple pages that contains a list of hierarchically organized topics (hence the package name). + diff --git a/macros/latex/contrib/topiclongtable/topiclongtable-doc.pdf b/macros/latex/contrib/topiclongtable/topiclongtable-doc.pdf new file mode 100644 index 0000000000..0680f61911 Binary files /dev/null and b/macros/latex/contrib/topiclongtable/topiclongtable-doc.pdf differ diff --git a/macros/latex/contrib/topiclongtable/topiclongtable-doc.tex b/macros/latex/contrib/topiclongtable/topiclongtable-doc.tex new file mode 100644 index 0000000000..9e2d05f187 --- /dev/null +++ b/macros/latex/contrib/topiclongtable/topiclongtable-doc.tex @@ -0,0 +1,303 @@ +% +% This work is sponsored by Human Predictions, LLC (). +% +% This work is maintained by Paolo Brasolin (). +% +% This work is licensed under MIT License. +% +% This work is a LaTeX package consisting of the following files: +% * README.md +% * topiclongtable.sty +% * topiclongtable-doc.tex +% * topiclongtable-doc.pdf +% +\documentclass[full,kernel]{l3doc} + +\usepackage{topiclongtable} + +\begin{document} + +\title{% + The \pkg{topiclongtable} package% + \thanks{Development of this package was sponsored by Human Predictions, + LLC (\href{http://www.humanpredictions.com}{www.humanpredictions.com}).}\\ + Autocollapsing cells in longtables +} + +\author{ + Paolo Brasolin\\ + \href{mailto:paolo.brasolin@gmail.com}{paolo.brasolin@gmail.com} +} + +\date{2019/07/09 v1.3.1} + +\maketitle + +\begin{documentation} + + +This \LaTeX\ package extends \env{longtable}s implementing +\env{topiclongtable}s, in which vertically adjacent cells merge if +and only if they have the same content and the cells on their left +are merged. Furthermore the merging is well behaved, in that it +does not happen after page breaks and correct separation lines are +drawn automatically. + +The typical use case is a table that spans multiple pages and +contains a list of hierarchically organized topics (hence the package +name). + + + + +\section*{Usage} + +To use the package install it in your tree (or put \file{topiclongtable.sty} in your work folder) and require it in your preamble: + +\begin{verbatim} + \usepackage{topiclongtable} +\end{verbatim} + + + + +\subsection*{Environment} + +\begin{function}[updated=2019-07-08]{topiclongtable} + \begin{syntax} + \tn[no-index]{begin}\{\env{topiclongtable}\}\marg{column specification} + \ \ \meta{cells specification} + \tn[no-index]{end}\{\env{topiclongtable}\} + \end{syntax} + The \env{topiclongtable} environment extends \env{longtable}. + They share the same syntax and all features of the latter are + available in the former. + + To use \pkg{topiclongtable} macros you must \emph{prepend} \enquote{\texttt{F}} + to the first column specification and \enquote{\texttt{T}} to all others: + \begin{verbatim} + \begin{topiclongtable}{|Fl|Tl|Tl|Tr|Tl|Tr} + % ... + \end{topiclongtable} + \end{verbatim} +\end{function}\smallskip + + + + +\subsection*{Macros} + +\begin{function}{\Topic} + \begin{syntax} + \tn{Topic}\oarg{content} + \end{syntax} + This is the main macro of the package; you can use it to wrap the + optional \meta{content} of a cell in order to allow it to merge with + adjacent cells. +\end{function}\smallskip + +The behaviour of \tn{Topic} can be observed in the following complete example: +\begin{verbatim} + \begin{topiclongtable}{|Fl|Tl|Tl|Tl|} + \Topic[Topic 1] & \Topic[Subtopic 1] & \Topic[Subsubtopic 1] & Foo \\ + \Topic & \Topic & \Topic[Subsubtopic 2] & Bar \\ + \Topic & \Topic[Subtopic 1] & \Topic[Subsubtopic 2] & Baz \\ + \Topic[Topic 2] & \Topic[Subtopic 1] & \Topic[Subsubtopic 3] & Qux \\ + \Topic & \Topic[Subtopic 2] & \Topic[Subsubtopic 4] & Zod \\ + \Topic & \Topic & \Topic & Bop \\ + \end{topiclongtable} +\end{verbatim} +\begin{topiclongtable}{|Fl|Tl|Tl|Tl|} + \Topic[Topic 1] & \Topic[Subtopic 1] & \Topic[Subsubtopic 1] & Foo \\ + \Topic & \Topic & \Topic[Subsubtopic 2] & Bar \\ + \Topic & \Topic[Subtopic 1] & \Topic[Subsubtopic 2] & Baz \\ + \Topic[Topic 2] & \Topic[Subtopic 1] & \Topic[Subsubtopic 3] & Qux \\ + \Topic & \Topic[Subtopic 2] & \Topic[Subsubtopic 4] & Zod \\ + \Topic & \Topic & \Topic & Bop \\ +\end{topiclongtable} + +Here is a breakdown of the manifested behaviour: +\begin{itemize}[nosep] + \item the parameter is optional; + \item merging happens when it is omitted (rows 1--3 on column 1); + \item merging happens when its value is equal to the one above + (rows 2--3 on columns 2 and 3); + \item merging does not happen (rows 3--4 on column 2) when the + cells on the left are not merged (rows 3--4 on column 1). +\end{itemize} + + +\begin{function}{\TopicLine} + \begin{syntax} + \tn{TopicLine} + \end{syntax} + \tn{TopicLine} can be used at the start of a row to automatically + draw the horizontal lines that separates it from the row above + and correctly accounts for merged cells. + + By default no horizontal lines are drawn on top and bottom of + table chunks to allow for maximal flexibility. You can use longtable + footer and headers to easily draw them (or whichever footer/header + you may desire) as shown in the next example. +\end{function}\smallskip + +Here is the previous example with lines added: +\begin{verbatim} + \begin{topiclongtable}{|Fl|Tl|Tl|Tl|} + \hline\endhead + \hline\endfoot + \TopicLine \Topic[T1] & \Topic[ST1] & \Topic[SST1] & Foo \\ + \TopicLine \Topic & \Topic & \Topic[SST2] & Bar \\ + \TopicLine \Topic & \Topic[ST1] & \Topic[SST2] & Baz \\ + \TopicLine \Topic[T2] & \Topic[ST1] & \Topic[SST3] & Qux \\ + \TopicLine \Topic & \Topic[ST2] & \Topic[SST4] & Zod \\ + \TopicLine \Topic & \Topic & \Topic & Bop \\ + \end{topiclongtable} +\end{verbatim} +\begin{topiclongtable}{|Fl|Tl|Tl|Tl|} + \hline\endhead + \hline\endfoot + \TopicLine \Topic[T1] & \Topic[ST1] & \Topic[SST1] & Foo \\ + \TopicLine \Topic & \Topic & \Topic[SST2] & Bar \\ + \TopicLine \Topic & \Topic[ST1] & \Topic[SST2] & Baz \\ + \TopicLine \Topic[T2] & \Topic[ST1] & \Topic[SST3] & Qux \\ + \TopicLine \Topic & \Topic[ST2] & \Topic[SST4] & Zod \\ + \TopicLine \Topic & \Topic & \Topic & Bop \\ +\end{topiclongtable} + + + + +\subsection*{Settings} + +All settings described in this section are global and can be changed between tables. + +\begin{function}{\TopicSetContinuationCode} + \begin{syntax} + \tn{TopicSetContinuationCode}\marg{\TeX\ code} + \end{syntax} + Cells \emph{continuing} from the previous page can be explicitly + marked. You can set a code fragment to append to such cells using + \tn{TopicSetContinuationCode}: + \begin{verbatim} + \TopicSetContinuationCode{\ (cont.)} + \end{verbatim} + By default no mark is appended and you can reset to the default + using + \begin{verbatim} + \TopicSetContinuationCode{} + \end{verbatim} +\end{function}\smallskip + +Consider this example across two pages: +\begin{verbatim} + \TopicSetContinuationCode{\ (cont.)} + \begin{topiclongtable}{|Fl|Tl|Tl|Tl|} + \hline\endhead + \hline\endfoot + \TopicLine \Topic[A] & \Topic[B] & 1 \\ + \TopicLine \Topic & \Topic & 2 \\ + % ... + \TopicLine \Topic & \Topic[C] & 10 \\ + \TopicLine \Topic & \Topic & 11 \\ + % ... + \TopicLine \Topic & \Topic & 20 \\ + \end{topiclongtable} +\end{verbatim} +\TopicSetContinuationCode{\ (cont.)} +\begin{topiclongtable}{|Fl|Tl|Tl|} + \hline\endhead + \hline\endfoot + \TopicLine \Topic[A] & \Topic[B] & 1 \\ + \TopicLine \Topic & \Topic & 2 \\ + \TopicLine \Topic & \Topic & 3 \\ + \TopicLine \Topic & \Topic & 4 \\ + \TopicLine \Topic & \Topic & 5 \\ + \TopicLine \Topic & \Topic & 6 \\ + \TopicLine \Topic & \Topic & 7 \\ + \TopicLine \Topic & \Topic & 8 \\ + \TopicLine \Topic & \Topic & 9 \\ + \TopicLine \Topic & \Topic[C] & 10 \\ + \TopicLine \Topic & \Topic & 11 \\ + \TopicLine \Topic & \Topic & 12 \\ + \TopicLine \Topic & \Topic & 13 \\ + \TopicLine \Topic & \Topic & 14 \\ + \TopicLine \Topic & \Topic & 15 \\ + \TopicLine \Topic & \Topic & 16 \\ + \TopicLine \Topic & \Topic & 17 \\ + \TopicLine \Topic & \Topic & 18 \\ + \TopicLine \Topic & \Topic & 19 \\ + \TopicLine \Topic & \Topic & 20 \\ +\end{topiclongtable} +\TopicSetContinuationCode{} + +\begin{function}{\TopicSetVPos} + \begin{syntax} + \tn{TopicSetVPos}\marg{vertical position specification} + \end{syntax} + You can set the vertical position for \tn{Topic} cells by using + \tn{TopicSetVPos}. Allowed \meta{vertical position specification}s + are \enquote{\texttt{b}} (bottom), \enquote{\texttt{c}} (center) + and the default \enquote{\texttt{t}} (top). All \tn{Topic} cells + will align in the same way: +\end{function}\smallskip + +Here is an example of \emph{bottom} alignment: +\begin{verbatim} + \TopicSetVPos{b} + \begin{topiclongtable}{|Fl|Tl|Tl|Tl|} + \hline\endhead + \hline\endfoot + \TopicLine \Topic[T1] & \Topic[ST1] & Foo \\ + \TopicLine \Topic & \Topic[ST2] & Bar \\ + \TopicLine \Topic & \Topic & Baz \\ + \end{topiclongtable} +\end{verbatim} +\TopicSetVPos{b} +\begin{topiclongtable}{|Fl|Tl|Tl|Tl|} + \hline\endhead + \hline\endfoot + \TopicLine \Topic[T1] & \Topic[ST1] & \Topic[SST1] & Foo \\ + \TopicLine \Topic & \Topic & \Topic[SST2] & Bar \\ + \TopicLine \Topic & \Topic[ST1] & \Topic[SST2] & Baz \\ +\end{topiclongtable} +\TopicSetVPos{t} + +\begin{function}{\TopicSetWidth} + \begin{syntax} + \tn{TopicSetVPos}\marg{cell width specification} + \end{syntax} + You can set the width of the \tn{Topic} cells by using + \tn{TopicSetWidth}. Allowed \meta{cell width specification}s are + \enquote{\texttt{=}} (fit column width) and the default + \enquote{\texttt{*}} (fit natural content width). +\end{function}\smallskip + +Note \enquote{\texttt{=}} will be useful mostly when coupled with +column specifiers like \texttt{p}\Arg{width} to force the overflowing +content inside the column width: +\begin{verbatim} + \TopicSetWidth{=} + \begin{topiclongtable}{|Fp{.5in}|Tl|} + \hline\endhead + \hline\endfoot + \TopicLine \Topic[Fits column width] & A \\ + \TopicLine \Topic & B \\ + \TopicLine \Topic & C \\ + \end{topiclongtable} +\end{verbatim} +\TopicSetWidth{=} +\begin{topiclongtable}{|Fp{.5in}|Tl|} + \hline\endhead + \hline\endfoot + \TopicLine \Topic[Fits column width] & A \\ + \TopicLine \Topic & B \\ + \TopicLine \Topic & C \\ +\end{topiclongtable} +\TopicSetWidth{*} + +\end{documentation} + +\PrintIndex + +\end{document} diff --git a/macros/latex/contrib/topiclongtable/topiclongtable.sty b/macros/latex/contrib/topiclongtable/topiclongtable.sty new file mode 100644 index 0000000000..d75295cdd5 --- /dev/null +++ b/macros/latex/contrib/topiclongtable/topiclongtable.sty @@ -0,0 +1,433 @@ +% +% This work is sponsored by Human Predictions, LLC (). +% +% This work is maintained by Paolo Brasolin (). +% +% This work is licensed under MIT License. +% +% This work is a LaTeX package consisting of the following files: +% * README.md +% * topiclongtable.sty +% * topiclongtable-doc.tex +% * topiclongtable-doc.pdf +% +\NeedsTeXFormat{LaTeX2e}[2017-04-15] + +\RequirePackage{zref-abspage}[2016/05/21] +\RequirePackage{xparse}[2017/11/14] +\RequirePackage{expl3}[2017/11/14] +\RequirePackage{array}[2016/10/06] +\RequirePackage{multirow}[2016/11/25] +\RequirePackage{longtable}[2014/10/28] +\PassOptionsToPackage{longtable}{multirow} + +\ProvidesExplPackage {topiclongtable} {2019/07/09} {1.3.1} {Renders autocollapsing cells in longtables} + +\ProcessOptions\relax + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +%% topics bookeeping (current table) +\seq_new:N \g_tlt_topics_stack_seq +\prop_new:N \g_tlt_topics_labels_prop + +%% tracks row spans of topics (for computation) +\prop_new:N \g_tlt_rows_spans_prop +%% holds computed multirow heights (for rendering) +\clist_new:N \g_tlt_multirows_heights_clist + +%% holds dumped data +\prop_new:N \g_tlt_raw_data_prop +%% holds dumped data (parsed for eas of access) +\prop_new:N \g_tlt_loaded_data_prop + +%% hold obvious col/row indices +\int_new:N \g_tlt_row_idx_int +\int_new:N \g_tlt_col_idx_int +\int_new:N \g_tlt_col_tot_int + +%% io stream +\iow_new:N \g_tlt_stream_iow + +%% tracks table index (id) +\tl_new:N \g_tlt_cur_table_id_tl + +%% hold continuation code +\tl_new:N \g_tlt_continuation_code_tl + +%% hold multirow options +\tl_new:N \g_tlt_multirow_vpos_tl +\tl_new:N \g_tlt_multirow_width_tl +%% whose defaults are +\tl_gset:Nn \g_tlt_multirow_vpos_tl {t} +\tl_gset:Nn \g_tlt_multirow_width_tl {*} + +%% just a handy helper +\cs_generate_variant:Nn \prop_item:Nn { NV } + +%% INIT PROCEDURES %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +%% initialize the document +\cs_new:Nn \tlt_init: { + %% load computed data if it exists + \tl_set:Nn \l_tmpa_tl { \c_sys_jobname_str.tlt } + \file_if_exist:nTF { \l_tmpa_tl } { + \ior_open:Nn \g_tlt_stream_iow { \l_tmpa_tl } + \ior_map_inline:Nn \g_tlt_stream_iow { ##1 } + \ior_close:N \g_tlt_stream_iow + } { + %% file was not found - that's ok + } + %% leave an open stream to dump computed data + \iow_open:Nn \g_tlt_stream_iow { \c_sys_jobname_str.tlt } +} + +%% initialize a table with given id +\cs_new:Nn \tlt_init_table:n { + %% fetch serialized data (clist of clists) if found, + %% initialize an empty list otherwise + %% NOTE: \g_tlt_raw_data_prop is defined in the (generated) *.tlt file + \prop_get:NnNTF \g_tlt_raw_data_prop { #1 } \l_tmpa_tl { + \clist_set:NV \l_tmpa_clist \l_tmpa_tl + } { + \clist_set_eq:NN \l_tmpa_clist \c_empty_clist + } + %% transform clist into indexed prop for ease of access + \int_zero:N \l_tmpa_int + \clist_map_inline:Nn \l_tmpa_clist { + \int_incr:N \l_tmpa_int + \prop_gput:NVn \g_tlt_loaded_data_prop \l_tmpa_int { ##1 } + } + %% reset counters and table variables + \int_gzero:N \g_tlt_col_idx_int + \int_gzero:N \g_tlt_row_idx_int + \prop_gclear:N \g_tlt_topics_labels_prop + \clist_gset:Nn \g_tlt_multirows_heights_clist \c_empty_clist + \clist_gset:Nn \g_tlt_rows_spans_prop \c_empty_clist +} + +\cs_generate_variant:Nn \tlt_init_table:n { V } + +%% EXIT PROCEDURES %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +%% we need to load data through an ordinary macro as at load time we won't have expl3 +\DeclareExpandableDocumentCommand{\TltSetDataRow}{mm}{ + \prop_gput:Nnn \g_tlt_raw_data_prop { #1 }{ #2 } +} + +%% finalize a given table +\cs_new:Nn \tlt_exit_table:n { + %% compute table data + \tlt_compute_data: + %% dump computed data + \iow_now:Nx \g_tlt_stream_iow { + \noexpand\TltSetDataRow { #1 }{ \g_tlt_multirows_heights_clist } + } +} + +\cs_generate_variant:Nn \tlt_exit_table:n { V } + +%% finalize document +\cs_new:Nn \tlt_exit: { + %% close the output stream + \iow_close:N \g_tlt_stream_iow +} + +%% MULTIROW HEIGHT FETCHING %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +\cs_new:Nn \tlt_calc_cell_height:n { + %% get loaded data for current column + \prop_get:NVNTF \g_tlt_loaded_data_prop \g_tlt_col_idx_int \l_tmpa_tl { + \clist_set:NV \l_tmpa_clist \l_tmpa_tl + } { + \clist_clear:N \l_tmpa_clist + } + %% get the first element (if present) and 1 otherwise + \clist_pop:NNTF \l_tmpa_clist \l_tmpb_tl {} { \tl_set:Nn \l_tmpb_tl { 1 } } + \prop_gput:NVV \g_tlt_loaded_data_prop \g_tlt_col_idx_int \l_tmpa_clist + \int_set:Nn { #1 } \l_tmpb_tl +} + +%% MULTIROW CONFIGURATION %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +\NewDocumentCommand{\TopicSetVPos}{m}{ + \tl_gset:Nn \g_tlt_multirow_vpos_tl {#1} +} + +\NewDocumentCommand{\TopicSetWidth}{m}{ + \tl_gset:Nn \g_tlt_multirow_width_tl {#1} +} + +%% TOPICS BOOKKEEPING AND RENDERING %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +\NewDocumentCommand{\TopicSetContinuationCode}{m}{ + \tl_gset:Nn \g_tlt_continuation_code_tl {#1} +} + +%% internal used to mark/render new topic +\cs_new:Nn \mark_new_topic:nn { + % reset stack up until #1 + \seq_if_in:NnTF \g_tlt_topics_stack_seq {#1} { + \bool_do_until:Nn \l_tmpa_bool { + \seq_gpop:NN \g_tlt_topics_stack_seq \l_tmpa_tl + \prop_gpop:NoN \g_tlt_topics_labels_prop \l_tmpa_tl \l_tmpb_tl + \tl_set:Nn \l_tmpb_tl {#1} + \bool_gset:Nn \l_tmpa_bool { \tl_if_eq_p:NN \l_tmpa_tl \l_tmpb_tl } + } + } {} + \prop_get:NVNTF \g_tlt_rows_spans_prop \g_tlt_col_idx_int \l_tmpa_tl { + \clist_set:NV \l_tmpa_clist \l_tmpa_tl + } { + \clist_clear:N \l_tmpa_clist + } + \clist_push:Nn \l_tmpa_clist { 1 } + \prop_gput:NVV \g_tlt_rows_spans_prop \g_tlt_col_idx_int \l_tmpa_clist + \tlt_calc_cell_height:n \l_tmpb_int + \seq_gpush:Nn \g_tlt_topics_stack_seq {#1} + \prop_gput:Nnn \g_tlt_topics_labels_prop {#1} {#2} + % render the multirow + \multirow[\tl_use:N \g_tlt_multirow_vpos_tl]{\int_use:N \l_tmpb_int}{\tl_use:N \g_tlt_multirow_width_tl}{\prop_item:Nn \g_tlt_topics_labels_prop {#1}} +} + +%% internal used to mark/render reappearing topic +\cs_new:Nn \mark_old_topic: { + \prop_get:NVNTF \g_tlt_rows_spans_prop \g_tlt_col_idx_int \l_tmpa_tl { + \clist_set:NV \l_tmpa_clist \l_tmpa_tl + } { + \ERROR %% unreachable by user by definition + } + + \on_first_row_of_page:N \l_tmpa_bool + \bool_if:NTF \l_tmpa_bool { + \prop_get:NVNTF \g_tlt_rows_spans_prop \g_tlt_col_idx_int \l_tmpa_tl { + \clist_set:NV \l_tmpa_clist \l_tmpa_tl + } { + \clist_clear:N \l_tmpa_clist + } + \clist_push:Nn \l_tmpa_clist { 1 } + \prop_gput:NVV \g_tlt_rows_spans_prop \g_tlt_col_idx_int \l_tmpa_clist + \tlt_calc_cell_height:n \l_tmpb_int + %% \seq_gpush:Nn \g_tlt_topics_stack_seq {#1} + %% \prop_gput:Nnn \g_tlt_topics_labels_prop {#1} {#2} + %% render the multirow cell + %% \int_use:N \l_tmpb_int + % render multirow + \multirow[\tl_use:N \g_tlt_multirow_vpos_tl]{\int_use:N \l_tmpb_int}{\tl_use:N \g_tlt_multirow_width_tl}{\prop_item:NV \g_tlt_topics_labels_prop \g_tlt_col_idx_int \tl_use:N \g_tlt_continuation_code_tl} + %% \clist_pop:NN \l_tmpa_clist \l_tmpa_tl + %% \int_set:Nn \l_tmpa_int \l_tmpa_tl + %% \int_incr:N \l_tmpa_int + %% \clist_push:NV \l_tmpa_clist \l_tmpa_int + %% \prop_gput:NVV \g_tlt_rows_spans_prop \g_tlt_col_idx_int \l_tmpa_clist + %% \tlt_calc_cell_height:n \l_tmpb_int + } { + \clist_pop:NN \l_tmpa_clist \l_tmpa_tl + \int_set:Nn \l_tmpa_int \l_tmpa_tl + \int_incr:N \l_tmpa_int + \clist_push:NV \l_tmpa_clist \l_tmpa_int + \prop_gput:NVV \g_tlt_rows_spans_prop \g_tlt_col_idx_int \l_tmpa_clist + \tlt_calc_cell_height:n \l_tmpb_int + % render nothing + } +} + +%% HIGH LEVEL GET/SET TOPICS OPERATIONS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +\msg_new:nnn {topiclongtable} {invalid-index} { + You~tried~to~use~an~uninitialized~topic~index~\msg_line_context:. +} + +% together, the two macros compose the high-level \Topic command + +\cs_new:Nn \tlt_set_topic:nn { + \tl_set:Nn \l_tmpa_tl { #2 } + \tl_set:Nx \l_tmpb_tl { \prop_item:Nn \g_tlt_topics_labels_prop { #1 } } + \tl_if_eq:NNTF \l_tmpa_tl \l_tmpb_tl + { \tlt_get_topic:n { #1 } } + { \mark_new_topic:nn {#1} {#2} } +} + +\cs_new:Nn \tlt_get_topic:n { + \seq_if_in:NnTF \g_tlt_topics_stack_seq {#1} { + \mark_old_topic: + } { + \msg_error:nn {topiclongtable} {invalid-index} + } +} + +\cs_generate_variant:Nn \tlt_get_topic:n { V } +\cs_generate_variant:Nn \tlt_set_topic:nn { Vn } + +%% HEIGHTS COMPUTATIONS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +\cs_new:Nn \tlt_compute_data: { + \clist_gclear:N \g_tlt_multirows_heights_clist + %% iterate over column indices; idx still has the last (maximum) value + \int_step_inline:nnnn { 1 } { 1 } { \g_tlt_col_idx_int } { + %% fetch memorized cell spans, ordered from bottom to top + \prop_get:NnNTF \g_tlt_rows_spans_prop { ##1 } \l_tmpb_tl { + \clist_set:NV \l_tmpa_clist \l_tmpb_tl + } { + \clist_set:NV \l_tmpa_clist \c_empty_clist + } + \clist_reverse:N \l_tmpa_clist + %% empty tmp list (for outer step cycle) + \clist_clear:N \l_tmpb_clist + %% iterate over cell spans + \clist_map_inline:Nn \l_tmpa_clist { + % store column index + \int_set:Nn \l_tmpa_int { ####1 } + % push full height (n) for first cell of multirow + \clist_push:NV \l_tmpb_clist \l_tmpa_int + % push n-1 zeroes for the other cells spanned by the multirow + \int_while_do:nNnn { \l_tmpa_int } { > } { 1 } { + \clist_push:Nn \l_tmpb_clist { 0 } + \int_decr:N \l_tmpa_int + } + } + \clist_reverse:N \l_tmpb_clist + \clist_gput_right:Nx \g_tlt_multirows_heights_clist {{\l_tmpb_clist}} + } +} + +%% CONDITIONALS ON ROW POSITIONS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +\cs_new:Nn \is_last_row_of_page:n { + % get page of given row + \int_set:Nn \l_tmpa_int { #1 } + \tl_set:Nx \l_tmpa_tl {TLT-\theLT@tables-\int_use:N \l_tmpa_int} + \tl_set:Nx \l_tmpa_tl {\expandafter\zref@extract{\tl_use:N \l_tmpa_tl}{abspage}} + % get page of next row + \int_incr:N \l_tmpa_int + \tl_set:Nx \l_tmpb_tl {TLT-\theLT@tables-\int_use:N \l_tmpa_int} + \tl_set:Nx \l_tmpb_tl {\expandafter\zref@extract{\tl_use:N \l_tmpb_tl}{abspage}} + % true if different + \bool_not_p:n { \tl_if_eq_p:NN \l_tmpa_tl \l_tmpb_tl } +} + +\cs_new:Nn \is_first_row_of_page:n { + % get page of given row + \int_set:Nn \l_tmpa_int { #1 } + \tl_set:Nx \l_tmpa_tl {TLT-\theLT@tables-\int_use:N \l_tmpa_int} + \tl_set:Nx \l_tmpa_tl {\expandafter\zref@extract{\tl_use:N \l_tmpa_tl}{abspage}} + % get page of prev row + \int_decr:N \l_tmpa_int + \tl_set:Nx \l_tmpb_tl {TLT-\theLT@tables-\int_use:N \l_tmpa_int} + \tl_set:Nx \l_tmpb_tl {\expandafter\zref@extract{\tl_use:N \l_tmpb_tl}{abspage}} + % true if different + \bool_set:Nn \l_tmpa_bool { \bool_not_p:n { \tl_if_eq_p:NN \l_tmpa_tl \l_tmpb_tl } } + \l_tmpa_bool +} + +\cs_new:Nn \on_first_row_of_page:N { + % get page of given row + \int_set_eq:NN \l_tmpa_int \g_tlt_row_idx_int + \tl_set:Nx \l_tmpa_tl {TLT-\theLT@tables-\int_use:N \l_tmpa_int} + \tl_set:Nx \l_tmpa_tl {\expandafter\zref@extract{\tl_use:N \l_tmpa_tl}{abspage}} + % get page of prev row + \int_decr:N \l_tmpa_int + \tl_set:Nx \l_tmpb_tl {TLT-\theLT@tables-\int_use:N \l_tmpa_int} + \tl_set:Nx \l_tmpb_tl {\expandafter\zref@extract{\tl_use:N \l_tmpb_tl}{abspage}} + % true if different + \bool_set:Nn { #1 } { \bool_not_p:n { \tl_if_eq_p:NN \l_tmpa_tl \l_tmpb_tl } } +} + +\cs_new:Nn \on_last_row_of_page:N { + % get page of given row + \int_set_eq:NN \l_tmpa_int \g_tlt_row_idx_int + \tl_set:Nx \l_tmpa_tl {TLT-\theLT@tables-\int_use:N \l_tmpa_int} + \tl_set:Nx \l_tmpa_tl {\expandafter\zref@extract{\tl_use:N \l_tmpa_tl}{abspage}} + % get page of prev row + \int_incr:N \l_tmpa_int + \tl_set:Nx \l_tmpb_tl {TLT-\theLT@tables-\int_use:N \l_tmpa_int} + \tl_set:Nx \l_tmpb_tl {\expandafter\zref@extract{\tl_use:N \l_tmpb_tl}{abspage}} + % true if different + \bool_set:Nn { #1 } { \bool_not_p:n { \tl_if_eq_p:NN \l_tmpa_tl \l_tmpb_tl } } +} + +\cs_generate_variant:Nn \is_first_row_of_page:n { V } +\cs_generate_variant:Nn \is_last_row_of_page:n { V } + +%% LINE DRAWING %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +\cs_new_protected:Nn \CLINE_split:n { + \clist_clear:N \l_tmpb_clist + \prop_map_inline:Nn \g_tlt_loaded_data_prop { + \tl_set:Nn \l_tmpa_tl {##1} + % get first element as integer A + \clist_set:Nn \l_tmpa_clist {##2} + \clist_pop:NNTF \l_tmpa_clist \l_tmpb_tl { + \int_set:Nn \l_tmpa_int \l_tmpb_tl + } { + \int_set_eq:NN \l_tmpa_int \c_one + } + % if integer A is zero, skip + \int_compare:nNnTF { \l_tmpa_int } { = } { 0 } {} { + \clist_gpush:NV \l_tmpb_clist \l_tmpa_tl + } + } + \clist_sort:Nn \l_tmpb_clist { + \int_compare:nNnTF { ##1 } { > } { ##2 } { \sort_return_swapped: } { \sort_return_same: } + } + \clist_pop:NNTF \l_tmpb_clist \l_tmpb_tl {} { \tl_set:Nn \l_tmpb_tl { #1 } } + + \tl_gset:Nx \g_tmpa_tl { \noexpand\cline{\l_tmpb_tl-#1} } + + \on_last_row_of_page:N \l_tmpa_bool + % note the tricky post-group expansion + \bool_if:NTF \l_tmpa_bool {} { \group_insert_after:N \g_tmpa_tl } +} + +\cs_generate_variant:Nn \CLINE_split:n { V } + +%% USER INTERFACE %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +%% used on the first (F) and other (T) columns, hook in the column counter +\newcolumntype{F}{>{\int_gset:Nn \g_tlt_col_idx_int {1}}} +\newcolumntype{T}{>{\int_gincr:N \g_tlt_col_idx_int}} + +%% draws the separators, handles the row counter and labels to track pagebreaks +\DeclareExpandableDocumentCommand{\TopicLine}{}{ + \noalign{ \CLINE_split:V \g_tlt_col_tot_int } + \int_gincr:N \g_tlt_row_idx_int + \tl_set:Nx \l_tmpa_tl {TLT-\theLT@tables-\int_use:N \g_tlt_row_idx_int} + \expandafter\zref@labelbyprops{\tl_use:N \l_tmpa_tl}{abspage} +} + +%% the main environment +\NewDocumentEnvironment {topiclongtable} {m} {% + %% set next table id + \int_set_eq:NN \l_tmpa_int \theLT@tables + \int_incr:N \l_tmpa_int + \tl_gset:Nx \g_tlt_cur_table_id_tl {TLT\int_use:N \l_tmpa_int} + %% initialize table and open environment + \tlt_init_table:V \g_tlt_cur_table_id_tl + %% \savenotes + \begin{longtable}{#1} + \noalign{\int_gset:Nn \g_tlt_col_tot_int {\LT@cols}} +}{ + %% close environment and finalize table + \end{longtable} + %% \spewnotes + \tlt_exit_table:V \g_tlt_cur_table_id_tl +} + +%% the main command +\NewDocumentCommand{\Topic}{o}{% + \IfValueTF{#1}{\tlt_set_topic:Vn{\g_tlt_col_idx_int}{#1}}{\tlt_get_topic:V{\g_tlt_col_idx_int}}% +} +%% NOTE: it'd be nice to have \Topic[vpos][width][vmove]{text} +%% with the same meanings as \multirow[vpos]{nrows}[bigstruts]{width}[vmove]{text} +%% with specification \NewDocumentCommand{\Topic}{D[]{t}D[]{*}D[]{0sp}g} +%% but the page breaks make it impossible to predict behaviour, +%% especially in relation to vmove - so in essence the settings would end +%% up being maximally local (equivalent to handmade multirows). + +%% HOOKS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +%% these are needed so set up and tear down io streams +\AtBeginDocument{\tlt_init:} +\AtEndDocument{\tlt_exit:} + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +\endinput -- cgit v1.2.3