% \iffalse meta-comment % % File: siunitx-table.dtx Copyright (C) 2016-2019,2021-2024 Joseph Wright % % It may be distributed and/or modified under the conditions of the % LaTeX Project Public License (LPPL), either version 1.3c of this % license or (at your option) any later version. The latest version % of this license is in the file % % https://www.latex-project.org/lppl.txt % % This file is part of the "siunitx bundle" (The Work in LPPL) % and all files in that bundle must be distributed together. % % The released version of this bundle is available from CTAN. % % ----------------------------------------------------------------------- % % The development version of the bundle can be found at % % https://github.com/josephwright/siunitx % % for those people who are interested. % % ----------------------------------------------------------------------- % %<*driver> \documentclass{l3doc} % Additional commands needed in this source \ProvideDocumentCommand\email{m}{\href{mailto:#1}{\nolinkurl{#1}}} \ProvideDocumentCommand\foreign{m}{\textit{#1}} % The next line is needed so that \GetFileInfo will be able to pick up % version data \usepackage{siunitx} \begin{document} \DocInput{\jobname.dtx} \end{document} % % \fi % % \GetFileInfo{siunitx.sty} % % \title{^^A % \pkg{siunitx-table} -- Formatting numbers in tables^^A % \thanks{This file describes \fileversion, % last revised \filedate.}^^A % } % % \author{^^A % Joseph Wright^^A % \thanks{^^A % E-mail: % \email{joseph@texdev.net}^^A % }^^A % } % % \date{Released \filedate} % % \maketitle % % \begin{documentation} % % \section{Numbers in tables} % % This submodule is concerned with formatting numbers in table cells or % similar fixed-width contexts. The main function, \cs{siunitx_cell_begin:w}, % is designed to work with the normal \LaTeXe{} tablular cell construct % featuring \cs{ignorespaces}. Therefore, if used outside of a \LaTeXe{} % tabular, it is necessary to provide this token. % % \begin{function}{\siunitx_cell_begin:w, \siunitx_cell_end:} % \begin{syntax} % \cs{siunitx_cell_begin:w} \meta{preamble} \cs{ignorespaces} % \meta{content} % \cs{siunitx_cell_end:} % \end{syntax} % Collects the \meta{preamble} and \meta{content} tokens, and determines % if it is text or a number (as parsed by \cs{siunitx_number_parse:nN}). % It produces output of a fixed width suitable for alignment in a table, % although it is not \emph{required} that the code is used within a cell. % Note that |\ignorespaces| must occur in the \enquote{cell}: it marks % the end of the \TeX{} |\halign| template. % \end{function} % % \subsection{Key--value options} % % The options defined by this submodule are available within the \pkg{l3keys} % |siunitx| tree. % % \begin{function}{table-align-comparator} % \begin{syntax} % |table-align-comparator| = |true|\verb"|"|false| % \end{syntax} % Switch which determines whether alignment of comparators is attempted % within table cells. The standard setting is |true|. % \end{function} % % \begin{function}{table-align-exponent} % \begin{syntax} % |table-align-exponent| = |true|\verb"|"|false| % \end{syntax} % Switch which determines whether alignment of exponents is attempted % within table cells. The standard setting is |true|. % \end{function} % % \begin{function}{table-align-text-after} % \begin{syntax} % |table-align-text-after| = |true|\verb"|"|false| % \end{syntax} % Switch which determines whether alignment of text falling after a number % is attempted within table cells. The standard setting is |true|. % \end{function} % % \begin{function}{table-align-text-before} % \begin{syntax} % |table-align-text-before| = |true|\verb"|"|false| % \end{syntax} % Switch which determines whether alignment of text falling before a number % is attempted within table cells. The standard setting is |true|. % \end{function} % % \begin{function}{table-align-uncertainty} % \begin{syntax} % |table-align-uncertainty| = |true|\verb"|"|false| % \end{syntax} % Switch which determines whether alignment of separated uncertainty values % is attempted within table cells. The standard setting is |true|. % \end{function} % % \begin{function}{table-alignment} % \begin{syntax} % |table-alignment| = |center|\verb"|"|left|\verb"|"|right| % \end{syntax} % Selects the alignment of all tabular content with the margins of the table % cell (or other boundary). See also |table-number-alignment| and % |table-text-alignment|. The standard setting is |center|. % \end{function} % % \begin{function}{table-alignment-mode} % \begin{syntax} % |table-alignment-mode| = |format|\verb"|"|marker|\verb"|"|none| % \end{syntax} % Selects the method used to align numbers with the desired position in the % cell (set by |table-alignment|). When set to |format|, a dedicated % amount of space is calculated from the |table-format|. When |marker| % is selected, alignment is carried out symmetrically around the decimal % marker. Finally, |none| switches off all alignment: numbers are parsed % and formatted but with no attempt at placement within the cell. The % standard setting is |marker|. % \end{function} % % \begin{function}{table-auto-round} % \begin{syntax} % |table-auto-round| = |true|\verb"|"|false| % \end{syntax} % Switch which determines whether numbers are rounded to fit within % the |table-format| specification (if possible). The standard % setting is |false|. % \end{function} % % \begin{function}{table-column-width} % \begin{syntax} % |table-column-width| = \meta{width} % \end{syntax} % Sets the width of the table column used for numbers. This is only used % when |table-fixed-width| is |true|. % \end{function} % % \begin{function}{table-fixed-width} % \begin{syntax} % |table-fixed-width| = |true|\verb"|"|false| % \end{syntax} % Switch which determines whether a fixed-width column is used for numbers % in tables. When |true|, the width is taken from |table-column-width|. The % standard setting is |false|. % \end{function} % % \begin{function}{table-format} % \begin{syntax} % |table-format| = \meta{format} % \end{syntax} % Describes the amount of space that should be reserved when % |table-alignment-mode| is set to |format|. The \meta{format} takes the % same general form as input for a table cell, with the numerical parts % describing how many digits to reserve space for. For example, |1.2e3| % would allow space for one digit in the integer part, two in the decimal % part and three in the exponent part. Signs can be allowed for using any % valid input sign, so for example |+1.2 \pm 1.2| would allow for a sign, % a number with one integer and two decimal digits and an uncertainty of % the same size. % \end{function} % % \begin{function}{table-model-setup} % \begin{syntax} % |table-model-setup| = \meta{commands} % \end{syntax} % Additional commands to be inserted when using the |table-format| to % create a model for alignment of cells. Typically this will be used to % handle variable-width fonts in columns. The standard setting is empty. % \end{function} % % \begin{function}{table-number-alignment} % \begin{syntax} % |table-number-alignment| = |center|\verb"|"|left|\verb"|"|right| % \end{syntax} % Selects the alignment of numerical content with the margins of the table % cell (or other boundary). See also |table-alignment| and % |table-text-alignment|. The standard setting is |center|. % \end{function} % % \begin{function}{table-text-alignment} % \begin{syntax} % |table-text-alignment| = |center|\verb"|"|left|\verb"|"|none|\verb"|"|right| % \end{syntax} % Selects the alignment of non-numerical content with the margins of the % table cell (or other boundary). See also |table-alignment| and % |table-number-alignment|. Notice the additional support for |none| here. % The standard setting is |center|. % \end{function} % % \end{documentation} % % \begin{implementation} % % \section{\pkg{siunitx-table} implementation} % % Start the \pkg{DocStrip} guards. % \begin{macrocode} %<*package> % \end{macrocode} % % Identify the internal prefix (\LaTeX3 \pkg{DocStrip} convention): only % internal material in this \emph{submodule} should be used directly. % \begin{macrocode} %<@@=siunitx_table> % \end{macrocode} % % Required core variants. % \begin{macrocode} \cs_generate_variant:Nn \keys_set:nn { nx } % \end{macrocode} % % \begin{variable}{\l_@@_tmp_box, \l_@@_tmp_dim, \l_@@_tmp_tl} % Scratch space. % \begin{macrocode} \box_new:N \l_@@_tmp_box \dim_new:N \l_@@_tmp_dim \tl_new:N \l_@@_tmp_tl % \end{macrocode} % \end{variable} % % \subsection{Interface functions} % % \begin{variable}{\l_@@_text_bool} % Used to track that a cell is purely text. % \begin{macrocode} \bool_new:N \l_@@_text_bool % \end{macrocode} % \end{variable} % % \begin{macro}{\siunitx_cell_begin:w} % \begin{macro}{\siunitx_cell_end:} % The start and end of the cell need to deal with the possibility of % a cell containing only text. % \begin{macrocode} \cs_new_protected:Npn \siunitx_cell_begin:w { \bool_set_false:N \l_@@_text_bool \bool_if:NTF \l_siunitx_number_parse_bool { \@@_collect_begin: } { \@@_direct_begin: } } \cs_new_protected:Npn \siunitx_cell_end: { \bool_if:NF \l_@@_text_bool { \bool_if:NTF \l_siunitx_number_parse_bool { \@@_collect_end: } { \@@_direct_end: } } } % \end{macrocode} % \end{macro} % \end{macro} % % \subsection{Collecting tokens} % % \begin{variable}{\l_@@_collect_tl} % Space for tokens. % \begin{macrocode} \tl_new:N \l_@@_collect_tl % \end{macrocode} % \end{variable} % % \begin{macro}{\@@_collect_begin:} % \begin{macro}{\@@_collect_begin:w} % Collecting a tabular cell means doing a token-by-token collection. % In previous versions of \pkg{siunitx} that was done along with picking % out the numerical part, but the code flow ends up very tricky. Here, % therefore, we just collect up the unchanged tokens first. Whilst each % cell forms a group, as we require definitions to say local % to the collections code, an additional group is required. % We use an auxiliary to % fish out the |\ignorespaces| from the template: that has to go to avoid % issues with the peek-ahead code (everything before the |#| needs to be % read \emph{before} the Appendix~D trick gets applied). Some packages % add additional tokens before the |\ignorespaces|, which are dealt with by % the delimited argument. % \begin{macrocode} \cs_new_protected:Npn \@@_collect_begin: { \group_begin: \tl_clear:N \l_@@_collect_tl \@@_collect_begin:w } \cs_new_protected:Npn \@@_collect_begin:w #1 \ignorespaces { \@@_collect_loop: #1 } % \end{macrocode} % \end{macro} % \end{macro} % % \begin{macro}{\@@_collect_loop:} % \begin{macro}{\@@_collect_group:n} % \begin{macro}{\@@_collect_token:N, \@@_collect_token_aux:N} % \begin{macro}{\@@_collect_relax:N} % \begin{macro}{\@@_collect_search:NnF} % \begin{macro}{\@@_collect_search_aux:NNn} % Collecting up the cell content needs a loop: this is done using % a |peek| approach as it's most natural. (A slower approach is possible % using something like the |\text_lowercase:n| loop code.) The set of % possible tokens is somewhat limited compared to an arbitrary cell % (\foreign{cf.}~the approach in \pkg{collcell}): the special cases are % pulled out for manual handling. The flexible lookup approach is more-or-less % the same idea as in the kernel |case| functions. The |\relax| special case % covers the case where |\\| has been expanded in an empty cell. This has to % be an explicit token as we can get the same meaning from |\protect|. % \begin{macrocode} \cs_new_protected:Npn \@@_collect_loop: { \peek_remove_spaces:n { \peek_catcode:NTF \c_group_begin_token { \@@_collect_group:n } { \@@_collect_token:N } } } \cs_new_protected:Npn \@@_collect_group:n #1 { \tl_put_right:Nn \l_@@_collect_tl { {#1} } \@@_collect_loop: } \cs_new_protected:Npn \@@_collect_token:N #1 { \@@_collect_search:NnF #1 { \unskip { \@@_collect_loop: } \textonly@unskip { \@@_collect_loop: } \end { \@@_collect_pre_cr: \tabularnewline \end } \relax { \@@_collect_relax:N #1 } \tabularnewline { \@@_collect_pre_cr: \tabularnewline } \siunitx_cell_end: { \siunitx_cell_end: } } { \@@_collect_token_aux:N #1 } } \cs_new_protected:Npn \@@_collect_token_aux:N #1 { \tl_put_right:Nn \l_@@_collect_tl {#1} \@@_collect_loop: } \cs_new_protected:Npn \@@_collect_relax:N #1 { \str_if_eq:nnTF {#1} { \relax } { \relax } { \@@_collect_token_aux:N #1 } } \AtBeginDocument { \@ifpackageloaded { mdwtab } { \cs_set_protected:Npn \@@_collect_token:N #1 { \@@_collect_search:NnF #1 { \@maybe@unskip { \@@_collect_loop: } \tab@setcr { \@@_collect_loop: } \unskip { \@@_collect_loop: } \end { \@@_collect_pre_cr: \tabularnewline \end } \relax { \@@_collect_relax:N #1 } \tabularnewline { \@@_collect_pre_cr: \tabularnewline } \siunitx_cell_end: { \siunitx_cell_end: } } { \@@_collect_token_aux:N #1 } } } { } } \cs_new_protected:Npn \@@_collect_search:NnF #1#2#3 { \@@_collect_search_aux:NNn #1 #2 #1 {#3} \q_stop } \cs_new_protected:Npn \@@_collect_search_aux:NNn #1#2#3 { \token_if_eq_meaning:NNTF #1 #2 { \use_i_delimit_by_q_stop:nw {#3} } { \@@_collect_search_aux:NNn #1 } } % \end{macrocode} % \end{macro} % \end{macro} % \end{macro} % \end{macro} % \end{macro} % \end{macro} % % \begin{macro}{\@@_collect_pre_cr:} % The definition of \cs{cr} is used to allow collection of any tokens % from the \tn{halign} template after |#| when we are in the last cell of % the row. (The approach is based on that in \pkg{collcell}.) Note that % \TeX{} inserts these tokens when it sees the \tn{cr} primitive, so % there is no expansion to consider. Whilst in most cases the group formed % by each cell will tidy up, nested tabulars (for example in a header row) % will break if \tn{cr} is redefined too widely. Thus we use a targetted % approach: only apply when needed, and use the additional group inside the % cell to keep control. % \begin{macrocode} \cs_new_protected:Npn \@@_collect_pre_cr: { \if_false: { \fi: \cs_set_protected:Npn \cr { \@@_collect_loop: \tex_cr:D } \if_false: } \fi: } % \end{macrocode} % \end{macro} % % \subsection{Separating collected material} % % The input needs to be divided into numerical tokens and those which appear % before and after them. This needs a second loop and validation. % % \begin{variable}{\l_@@_before_tl, \l_@@_number_tl, \l_@@_after_tl} % Space for tokens. % \begin{macrocode} \tl_new:N \l_@@_before_tl \tl_new:N \l_@@_number_tl \tl_new:N \l_@@_after_tl % \end{macrocode} % \end{variable} % % \begin{macro}{\@@_collect_end:} % \begin{macro}{\@@_collect_end:n} % \begin{macro}[EXP]{\@@_collect_end_aux:n} % \begin{macro}[EXP]{\@@_collect_end:w} % At the end of the cell, escape the group and check for expansion. We % only do that if the entire content is not a brace group: there is more % likely to be problematic content in the case of a header. % \begin{macrocode} \cs_new_protected:Npn \@@_collect_end: { \exp_args:NNV \group_end: \@@_collect_end:n \l_@@_collect_tl \@@_split:VNNN \l_@@_collect_tl \l_@@_before_tl \l_@@_number_tl \l_@@_after_tl \tl_if_empty:NTF \l_@@_number_tl { \@@_print_text:V \l_@@_before_tl } { \@@_print:VVV \l_@@_before_tl \l_@@_number_tl \l_@@_after_tl } } % \end{macrocode} % To cover the use of REV\TeX{}, we need to allow for the insertion of % \cs{array@row@rst} into cell content: that explodes inside % \cs{protected@edef}. We use the classical solution of making locally % equal to \cs{scan_stop:}. % \begin{macrocode} \cs_new_protected:Npn \@@_collect_end:n #1 { \str_if_eq:eeTF { \exp_not:n {#1} } { { \@@_collect_end_aux:n {#1} } } { \tl_set:Nn } { \cs_if_exist:NT \array@row@rst { \cs_set_eq:NN \array@row@rst \scan_stop: } \protected@edef } \l_@@_collect_tl {#1} } \cs_new:Npn \@@_collect_end_aux:n #1 { \exp_after:wN \@@_collect_end:w #1 \q_stop } \cs_new:Npn \@@_collect_end:w #1 \q_stop { \exp_not:n {#1} } % \end{macrocode} % \end{macro} % \end{macro} % \end{macro} % \end{macro} % % \begin{macro}{\@@_split:nNNN, \@@_split:VNNN} % \begin{macro}{\@@_split_loop:NNN} % \begin{macro}{\@@_split_group:NNNn} % \begin{macro}{\@@_split_token:NNNN} % Splitting into parts uses the fact that numbers cannot contain groups % and that we can track where we are up to based on the content of the % token lists. % \begin{macrocode} \cs_new_protected:Npn \@@_split:nNNN #1#2#3#4 { \tl_clear:N #2 \tl_clear:N #3 \tl_clear:N #4 \@@_split_loop:NNN #2#3#4 #1 \q_recursion_tail \q_recursion_stop \@@_split_tidy:N #2 \@@_split_tidy:N #4 } \cs_generate_variant:Nn \@@_split:nNNN { V } \cs_new_protected:Npn \@@_split_loop:NNN #1#2#3 { \peek_remove_spaces:n { \peek_catcode:NTF \c_group_begin_token { \@@_split_group:NNNn #1#2#3 } { \@@_split_token:NNNN #1#2#3 } } } \cs_new_protected:Npn \@@_split_group:NNNn #1#2#3#4 { \tl_if_empty:NTF #2 { \tl_put_right:Nn #1 { {#4} } } { \tl_put_right:Nn #3 { {#4} } } \@@_split_loop:NNN #1#2#3 } \cs_new_protected:Npn \@@_split_token:NNNN #1#2#3#4 { \quark_if_recursion_tail_stop:N #4 \tl_if_empty:NTF \l_@@_after_tl { \siunitx_if_number_token:NTF #4 { \tl_put_right:Nn #2 {#4} } { \tl_if_empty:NTF #2 { \tl_put_right:Nn #1 {#4} } { \tl_put_right:Nn #3 {#4} } } } { \tl_put_right:Nn #3 {#4} } \@@_split_loop:NNN #1#2#3 } % \end{macrocode} % \end{macro} % \end{macro} % \end{macro} % \end{macro} % % \begin{macro}{\@@_split_tidy:N} % \begin{macro}{\@@_split_tidy:Nn, \@@_split_tidy:NV} % A quick test for the entire content being surrounded by a set of braces: % rather than look explicitly, use the fact that a string comparison can % detect the same thing. The auxiliary is needed to avoid having to go % \foreign{via} a |:D| function (for the expansion behaviour). % \begin{macrocode} \cs_new_protected:Npn \@@_split_tidy:N #1 { \tl_if_empty:NF #1 { \@@_split_tidy:NV #1 #1 } } \cs_new_protected:Npn \@@_split_tidy:Nn #1#2 { \str_if_eq:onT { \exp_after:wN { \use:n #2 } } {#2} { \tl_set:No #1 { \use:n #2 } } } \cs_generate_variant:Nn \@@_split_tidy:Nn { NV } % \end{macrocode} % \end{macro} % \end{macro} % % \subsection{Printing numbers in cells: spacing} % % Getting the general alignment correct in tables is made more complex than one % would like by the \pkg{colortbl} package. In the original \LaTeXe{} % definition, cell material is centred by a construction of the (primitive) % form % \begin{verbatim} % \hfil % # % \hfil % \end{verbatim} % which only uses \texttt{fil} stretch. That is altered by \pkg{colortbl} to % broadly % \begin{verbatim} % \hskip 0pt plus 0.5fill % \kern 0pt % # % \hskip 0pt plus 0.5fill % \end{verbatim} % which means there is \texttt{fill} stretch to worry about and the kern as % well. % % \begin{macro}{\@@_skip:n} % To prevent combination of skips, a kern is inserted after each one. % This is best handled as a short auxiliary. % \begin{macrocode} \cs_new_protected:Npn \@@_skip:n #1 { \skip_horizontal:n {#1} \tex_kern:D \c_zero_skip } % \end{macrocode} % \end{macro} % % \begin{variable}{\l_@@_column_width_dim, \l_@@_fixed_width_bool} % Settings which apply to aligned columns in general. % \begin{macrocode} \dim_new:N \l_@@_column_width_dim \keys_define:nn { siunitx } { table-column-width .code:n = { \dim_set:Nn \l_@@_column_width_dim {#1} \dim_compare:nNnT \l_@@_column_width_dim > \c_zero_dim { \bool_set_true:N \l_@@_fixed_width_bool } } , table-fixed-width .bool_set:N = \l_@@_fixed_width_bool } % \end{macrocode} % \end{variable} % % \begin{macro} % {\@@_align_center:n, \@@_align_left:n, \@@_align_right:n, \@@_align_none:n} % \begin{macro}{\@@_align_auxi:nn, \@@_align_auxii:nn, \@@_align_auxiii:nn} % The beginning and end of each table cell have to adjust the position of % the content using glue. When \pkg{colortbl} is loaded the glue is done in % two parts: one for our positioning and one to explicitly override that from % the package. Using a two-step auxiliary chain avoids needing to repeat any % code and the impact of the extra expansion should be trivial. There is a % further wrinkle, in that if \pkg{tabularray} is being used, we need to skip % the \pkg{colortbl} fix (see % \url{https://github.com/lvjr/tabularray/issues/512}). % \begin{macrocode} \cs_new_protected:Npn \@@_align_center:n #1 { \@@_align_auxi:nn {#1} { 0pt~plus~0.5fill } } \cs_new_protected:Npn \@@_align_left:n #1 { \@@_align_auxi:nn {#1} { 0pt } } \cs_new_protected:Npn \@@_align_right:n #1 { \@@_align_auxi:nn {#1} { 0pt~plus~1fill } } \cs_new_protected:Npn \@@_align_none:n #1 { \bool_if:NTF \l_@@_fixed_width_bool { \hbox_to_wd:nn \l_@@_column_width_dim } { \use:n } {#1} } \cs_new_protected:Npn \@@_align_auxi:nn #1#2 { \bool_if:NTF \l_@@_fixed_width_bool { \hbox_to_wd:nn \l_@@_column_width_dim } { \use:n } { \@@_skip:n {#2} #1 \@@_skip:n { 0pt~plus~1fill - #2 } } } \AtBeginDocument { \@ifpackageloaded { colortbl } { \cs_new_eq:NN \@@_align_auxii:nn \@@_align_auxi:nn \cs_gset_protected:Npn \@@_align_auxi:nn #1#2 { \@@_skip:n{ 0pt~plus~-0.5fill } \@@_align_auxii:nn {#1} {#2} \@@_skip:n { 0pt~plus~-0.5fill } } \@ifpackageloaded { tabularray } { \cs_new_eq:NN \@@_align_auxiii:nn \@@_align_auxi:nn \cs_gset_protected:Npn \@@_align_auxi:nn #1#2 { \int_compare:nNnTF \g_tblr_level_int = 0 { \@@_align_auxii:nn } { \@@_align_auxiii:nn } {#1} {#2} } } { } } { } } % \end{macrocode} % \end{macro} % \end{macro} % % \subsection{Printing just text} % % In cases where there is no numerical part, \pkg{siunitx} allows alignment % of the \enquote{escaped} text independent of the underlying column type. % % \begin{variable}{\l_@@_align_text_tl} % Alignment is handled using a |tl| as this allows a fast lookup at the % point of use. % \begin{macrocode} \keys_define:nn { siunitx } { table-text-alignment .choices:nn = { center , left , right , none } { \tl_set:Nn \l_@@_align_text_tl {#1} } , } \tl_new:N \l_@@_align_text_tl % \end{macrocode} % \end{variable} % % \begin{macro}{\@@_print_text:n, \@@_print_text:V} % Printing escaped text is easy: just place it in correctly in the % column. % \begin{macrocode} \cs_new_protected:Npn \@@_print_text:n #1 { \bool_set_true:N \l_@@_text_bool \use:c { @@_align_ \l_@@_align_text_tl :n } {#1} } \cs_generate_variant:Nn \@@_print_text:n { V } % \end{macrocode} % \end{macro} % % \subsection{Number alignment: core ideas} % % \begin{variable}{\l_@@_integer_box, \l_@@_decimal_box} % Boxes for the content before and after the decimal marker. % \begin{macrocode} \box_new:N \l_@@_integer_box \box_new:N \l_@@_decimal_box % \end{macrocode} % \end{variable} % % \begin{variable}{\l_@@_uncert_box} % An extra one for aligning separated uncertainty values. % \begin{macrocode} \box_new:N \l_@@_uncert_box % \end{macrocode} % \end{variable} % % \begin{macro}{\@@_fil:, \@@_fill:} % Primitives renamed. % \begin{macrocode} \cs_new_eq:NN \@@_fil: \tex_hfil:D \cs_new_eq:NN \@@_fill: \tex_hfill:D % \end{macrocode} % \end{macro} % % \begin{macro}{\@@_cleanup_decimal:w} % To remove the excess marker tokens in a decimal part. % \begin{macrocode} \cs_new:Npn \@@_cleanup_decimal:w #1 \q_nil #2 \q_nil #3 \q_nil #4 \q_nil #5 \q_nil #6 \q_nil #7 \q_nil { #1#2#3#4#5#6#7 } % \end{macrocode} % \end{macro} % % \begin{macro}{\@@_color_check:N} % \begin{macro}{\@@_color_check:w} % \begin{macro}{\@@_color_check:Nnw} % Handle the fact that splitting a number can leave a negative color % dangling. % \begin{macrocode} \cs_new_protected:Npn \@@_color_check:N #1 { \exp_after:wN \@@_color_check:w #1 \q_stop } \cs_new_protected:Npn \@@_color_check:w #1 \q_nil #2 \q_stop { \tl_if_head_eq_meaning:nNT {#1} \color { \@@_color_check:Nnw #1 \q_stop } } \cs_new_protected:Npn \@@_color_check:Nnw #1#2#3 \q_stop { \keys_set:nn { siunitx } { number-color = #2 } } % \end{macrocode} % \end{macro} % \end{macro} % \end{macro} % % \begin{macro}{\@@_center_marker:} % When centering on the decimal marker, the easiest approach is to simply % re-box the two parts. That is needed whether or not we are parsing numbers, % so is best as a short auxiliary. Notice that we need to allow for the width % of the decimal marker itself. When not aligning non-numerical material, we % put the extra space into the boxes around the number. % \begin{macrocode} \cs_new_protected:Npn \@@_center_marker: { \hbox_set:Nn \l_@@_tmp_box { \ensuremath { \mathord { \l_siunitx_number_output_decimal_tl } } } \dim_compare:nNnTF { \box_wd:N \l_@@_integer_box } > { \box_wd:N \l_@@_decimal_box - \box_wd:N \l_@@_tmp_box } { \bool_if:NTF \l_@@_align_after_bool { \@@_center_marker_aux:Nnnn \l_@@_decimal_box { \box_wd:N \l_@@_integer_box + \box_wd:N \l_@@_tmp_box } } { \@@_center_marker_aux:Nnnn \l_@@_after_box { \box_wd:N \l_@@_after_box + \box_wd:N \l_@@_integer_box - \box_wd:N \l_@@_decimal_box + \box_wd:N \l_@@_tmp_box } } { } { \@@_fil: } } { \bool_if:NTF \l_@@_align_before_bool { \@@_center_marker_aux:Nnnn \l_@@_integer_box { \box_wd:N \l_@@_decimal_box - \box_wd:N \l_@@_tmp_box } } { \@@_center_marker_aux:Nnnn \l_@@_before_box { \box_wd:N \l_@@_before_box + \box_wd:N \l_@@_decimal_box - \box_wd:N \l_@@_integer_box - \box_wd:N \l_@@_tmp_box } } { \@@_fil: } { } } } \cs_new_protected:Npn \@@_center_marker_aux:Nnnn #1#2#3#4 { \hbox_set_to_wd:Nnn #1 {#2} { #3 \hbox_unpack:N #1 #4 } } % \end{macrocode} % \end{macro} % % \begin{variable} % { % \l_@@_auto_round_bool, % \l_@@_model_setup_tl , % \l_@@_align_mode_tl , % \l_@@_align_number_tl % } % Options for tables with defined space. % \begin{macrocode} \keys_define:nn { siunitx } { table-alignment .meta:n = { table-number-alignment = #1 , table-text-alignment = #1 }, table-alignment-mode .choices:nn = { none , format , marker } { \tl_set_eq:NN \l_@@_align_mode_tl \l_keys_choice_tl } , table-auto-round .bool_set:N = \l_@@_auto_round_bool , table-format .code:n = { \group_begin: \protected@edef \l_@@_tmp_tl {#1} \exp_args:NNV \group_end: \@@_split:nNNN \l_@@_tmp_tl \l_@@_before_model_tl \l_@@_model_tl \l_@@_after_model_tl \@@_generate_model:V \l_@@_model_tl \tl_set:Nn \l_@@_align_mode_tl { format } } , table-model-setup .tl_set:N = \l_@@_model_setup_tl , table-number-alignment .choices:nn = { center , left , right } { \tl_set_eq:NN \l_@@_align_number_tl \l_keys_choice_tl } } \tl_new:N \l_@@_align_mode_tl \tl_new:N \l_@@_align_number_tl % \end{macrocode} % \end{variable} % % \begin{variable}{\l_@@_format_tl, \l_@@_model_tl} % The input and output versions of the model entry in a table. % \begin{macrocode} \tl_new:N \l_@@_format_tl \tl_new:N \l_@@_before_model_tl \tl_new:N \l_@@_model_tl \tl_new:N \l_@@_after_model_tl % \end{macrocode} % \end{variable} % % \begin{macro}{\@@_generate_model:n, \@@_generate_model:V} % \begin{macro}{\@@_generate_model:nnnnnnn} % \begin{macro}[EXP]{\@@_generate_model_S:nnw} % \begin{macro}[EXP]{\@@_generate_model_S:nnn, \@@_generate_model_S:een} % Creating a model for a table at this stage means parsing the format and % converting that to an appropriate model. Things are quite straight-forward % other than the uncertainty part. At this stage there is no point in % formatting the model: that has to happen at point-of-use. Notice that % the uncertainty part needs to allow for the case where we cross the % decimal. % \begin{macrocode} \cs_new_protected:Npn \@@_generate_model:n #1 { \group_begin: \bool_set_true:N \l_siunitx_number_parse_bool \keys_set:nn { siunitx } { retain-explicit-plus = true } \siunitx_number_parse:nN {#1} \l_@@_format_tl \exp_args:NNNV \group_end: \tl_set:Nn \l_@@_format_tl \l_@@_format_tl \tl_if_empty:NF \l_@@_format_tl { \exp_after:wN \@@_generate_model:nnnnnnn \l_@@_format_tl } } \cs_generate_variant:Nn \@@_generate_model:n { V } \cs_new_protected:Npn \@@_generate_model:nnnnnnn #1#2#3#4#5#6#7 { \tl_set:Nx \l_@@_model_tl { \exp_not:n { {#1} {#2} } { \int_compare:nNnTF {#3} = 0 { 0 } { \prg_replicate:nn {#3} { 8 } } } { \prg_replicate:nn { 0 #4 } { 8 } } { \tl_if_blank:nF {#5} { \use:c { @@_generate_model_ \tl_head:n {#5} :nnw } {#4} #5 } } \exp_not:n { {#6} } { \int_compare:nNnTF { 0#7 } = 0 { 0 } { \prg_replicate:nn { 0#7 } { 8 } } } } } \cs_new:Npn \@@_generate_model_S:nnw #1#2#3 { { S } { \@@_generate_model_S:een { \tl_count:n {#1} } { \tl_count:n {#3} } {#3} } } \cs_new:Npn \@@_generate_model_S:nnn #1#2#3 { \prg_replicate:nn { \int_compare:nNnTF {#2} > {#1} { \str_range:nnn {#3} { 1 } {#1} + \str_range:nnn {#3} { 1 + #1 } {#2} } {#3} } { 8 } } \cs_generate_variant:Nn \@@_generate_model_S:nnn { ee } % \end{macrocode} % \end{macro} % \end{macro} % \end{macro} % \end{macro} % % \subsection{Directly printing without collection} % % Collecting the number allows for various effects but is not as fast % as simply aligning on the first token that is a decimal marker. The % strategy here is that used by \pkg{dcolumn}. % % \begin{macro}{\@@_direct_begin:} % \begin{macro}{\@@_direct_begin:w} % \begin{macro}{\@@_direct_end:} % \begin{macro} % { % \@@_direct_marker: , % \@@_direct_marker_switch: , % \@@_direct_marker_end: % } % \begin{macro}{\@@_direct_format:} % \begin{macro}[EXP]{\@@_direct_format:nnnnnnn} % \begin{macro}{\@@_direct_format:w} % \begin{macro} % { % \@@_direct_format_switch: , % \@@_direct_format_end: % } % \begin{macro}{\@@_direct_none:, \@@_direct_none_end:} % After removing the |\ignorespaces| at the start of the cell (see comments % for \cs{@@_collect_begin:N}), check to see if there is a |{| and branch % as appropriate. % \begin{macrocode} \cs_new_protected:Npn \@@_direct_begin: { \@@_direct_begin:w } \cs_new_protected:Npn \@@_direct_begin:w #1 \ignorespaces { #1 \peek_remove_spaces:n { \peek_catcode:NTF \c_group_begin_token { \@@_print_text:n } { \m@th \use:c { @@_direct_ \l_@@_align_mode_tl : } } } } \cs_new_protected:Npn \@@_direct_end: { \use:c { @@_direct_ \l_@@_align_mode_tl _end: } } % \end{macrocode} % When centring the content about a decimal marker, the trick is % to collect everything into two boxes and then compare the sizes. % As we are always in math mode, we can use a math active token % to make the switch. The up-front setting of the |decimal| box deals % with the case where there is no decimal part. % \begin{macrocode} \cs_new_protected:Npn \@@_direct_marker: { \hbox_set:Nn \l_@@_tmp_box { \ensuremath { \mathord { \l_siunitx_number_output_decimal_tl } } } \hbox_set_to_wd:Nnn \l_@@_decimal_box { \box_wd:N \l_@@_tmp_box } { \@@_fil: } \hbox_set:Nw \l_@@_integer_box \c_math_toggle_token \tl_map_inline:Nn \l_siunitx_number_input_decimal_tl { \char_set_active_eq:NN ##1 \@@_direct_marker_switch: \char_set_mathcode:nn { `##1 } { "8000 } } } \cs_new_protected:Npn \@@_direct_marker_switch: { \c_math_toggle_token \hbox_set_end: \hbox_set:Nw \l_@@_decimal_box \c_math_toggle_token \l_siunitx_number_output_decimal_tl } % \end{macrocode} % We set the two alignment booleans here so that a single auxiliary can % cover this case as well as the one for centering the marker when also % parsing. % \begin{macrocode} \cs_new_protected:Npn \@@_direct_marker_end: { \c_math_toggle_token \hbox_set_end: \bool_set_true:N \l_@@_align_before_bool \bool_set_true:N \l_@@_align_after_bool \@@_center_marker: \use:c { @@_align_ \l_@@_align_text_tl :n } { \box_use_drop:N \l_@@_integer_box \box_use_drop:N \l_@@_decimal_box } } % \end{macrocode} % For the version where there is space reserved, first format and decompose % that, then create appropriately-sized boxes. % \begin{macrocode} \cs_new_protected:Npn \@@_direct_format: { \tl_set:Nx \l_@@_tmp_tl { \siunitx_number_output:NN \l_@@_model_tl \q_nil } \exp_after:wN \@@_direct_format_aux:w \l_@@_tmp_tl \q_stop } \cs_new_protected:Npn \@@_direct_format_aux:w #1 \q_nil #2 \q_nil #3 \q_nil #4 \q_stop { \hbox_set:Nn \l_@@_tmp_box { \ensuremath { \@@_cleanup_decimal:w #4 } } \hbox_set_to_wd:Nnn \l_@@_decimal_box { \box_wd:N \l_@@_tmp_box } { \@@_fil: } \hbox_set:Nn \l_@@_tmp_box { \ensuremath { #1#2#3 } } \hbox_set_to_wd:Nnw \l_@@_integer_box { \box_wd:N \l_@@_tmp_box } \c_math_toggle_token \tl_map_inline:Nn \l_siunitx_number_input_decimal_tl { \char_set_active_eq:NN ##1 \@@_direct_format_switch: \char_set_mathcode:nn { `##1 } { "8000 } } \@@_fill: } \cs_new_protected:Npn \@@_direct_format_switch: { \c_math_toggle_token \hbox_set_end: \hbox_set_to_wd:Nnw \l_@@_decimal_box { \box_wd:N \l_@@_decimal_box } \c_math_toggle_token \mathord { \l_siunitx_number_output_decimal_tl } } \cs_new_protected:Npn \@@_direct_format_end: { \c_math_toggle_token \@@_fil: \hbox_set_end: \use:c { @@_align_ \l_@@_align_number_tl :n } { \box_use_drop:N \l_@@_integer_box \box_use_drop:N \l_@@_decimal_box } } % \end{macrocode} % No parsing and no alignment is easy. % \begin{macrocode} \cs_new_protected:Npn \@@_direct_none: { \c_math_toggle_token } \cs_new_protected:Npn \@@_direct_none_end: { \c_math_toggle_token } % \end{macrocode} % \end{macro} % \end{macro} % \end{macro} % \end{macro} % \end{macro} % \end{macro} % \end{macro} % \end{macro} % \end{macro} % % \subsection{Printing numbers in cells: main functions} % % \begin{variable}{\l_@@_before_box, \l_@@_after_box} % For alignment of text outside of a number. % \begin{macrocode} \box_new:N \l_@@_before_box \box_new:N \l_@@_after_box % \end{macrocode} % \end{variable} % % \begin{variable}{\l_@@_before_dim} % Space reserved for any non-numerical text before the number: as we need % to allow for this to be available after setting the integer part, we need % to carry it along for a bit. % \begin{macrocode} \dim_new:N \l_@@_before_dim % \end{macrocode} % \end{variable} % % \begin{variable}{\l_@@_carry_dim} % Used to \enquote{carry forward} the amount of white space which needs to % be inserted after the decimal marker. % \begin{macrocode} \dim_new:N \l_@@_carry_dim % \end{macrocode} % \end{variable} % % \begin{variable} % { % \l_@@_align_comparator_bool , % \l_@@_align_exponent_bool , % \l_@@_align_after_bool , % \l_@@_align_before_bool , % \l_@@_align_uncertainty_bool % } % \begin{macrocode} \keys_define:nn { siunitx } { table-align-comparator .bool_set:N = \l_@@_align_comparator_bool , table-align-exponent .bool_set:N = \l_@@_align_exponent_bool , table-align-text-after .bool_set:N = \l_@@_align_after_bool , table-align-text-before .bool_set:N = \l_@@_align_before_bool , table-align-uncertainty .bool_set:N = \l_@@_align_uncertainty_bool } % \end{macrocode} % \end{variable} % % \begin{macro}{\@@_print:nnn, \@@_print:VVV} % \begin{macro}{\@@_print_marker:nnn} % \begin{macro}{\@@_print_marker:w} % \begin{macro}[EXP]{\@@_print_marker_aux:w} % \begin{macro}{\@@_print_format:nnn} % \begin{macro}[EXP]{\@@_print_format:nnnnnn} % \begin{macro} % { % \@@_print_format_auxi:w , % \@@_print_format_auxii:w , % \@@_print_format_auxiii:w , % \@@_print_format_auxiv:w , % \@@_print_format_auxv:w , % \@@_print_format_auxvi:w , % \@@_print_format_auxvii:w , % \@@_print_format_auxviii:w , % \@@_print_format_auxix:w , % \@@_print_format_auxx:w , % \@@_print_format_auxxi:w , % \@@_print_format_auxxii:w , % \@@_print_format_auxxiii:w % } % \begin{macro} % { % \@@_print_format_box:Nn, \@@_print_format_box:No , % \@@_print_model_box:Nn, \@@_print_model_box:No % } % \begin{macro}{\@@_print_format_box:nNn, \@@_print_format_box:VNn} % \begin{macro}{\@@_print_format_after:N} % \begin{macro}{\@@_print_none:nnn} % \begin{macrocode} \cs_new_protected:Npn \@@_print:nnn #1#2#3 { \use:c { @@_print_ \l_@@_align_mode_tl :nnn } {#1} {#2} {#3} } \cs_generate_variant:Nn \@@_print:nnn { VVV } % \end{macrocode} % When centering on the decimal marker, alignment is relatively simple, and % close in concept to that used without parsing. First we need to deal with any % text before or after the number. For text \emph{before}, there's the case % where is has no width and might be a font or color change: that has to be % filtered out first. Then we can adjust the size of this material and that % after the number such that they are equal. The number itself can then be % formatted, splitting at he decimal marker. A bit more size adjustment, then % the number itself and any text at the end can be inserted. % \begin{macrocode} \cs_new_protected:Npn \@@_print_marker:nnn #1#2#3 { \hbox_set:Nn \l_@@_before_box {#1} \dim_compare:nNnT { \box_wd:N \l_@@_before_box } = { 0pt } { \box_clear:N \l_@@_before_box #1 } \hbox_set:Nn \l_@@_after_box {#3} \dim_compare:nNnTF { \box_wd:N \l_@@_after_box } > { \box_wd:N \l_@@_before_box } { \hbox_set_to_wd:Nnn \l_@@_before_box { \box_wd:N \l_@@_after_box } { \@@_fil: \hbox_unpack:N \l_@@_before_box } } { \hbox_set_to_wd:Nnn \l_@@_after_box { \box_wd:N \l_@@_before_box } { \hbox_unpack:N \l_@@_after_box \@@_fil: } } \siunitx_number_parse:nN {#2} \l_@@_tmp_tl \siunitx_number_process:NN \l_@@_tmp_tl \l_@@_tmp_tl \tl_set:Nx \l_@@_tmp_tl { \siunitx_number_output:NN \l_@@_tmp_tl \q_nil } \@@_color_check:N \l_@@_tmp_tl \exp_after:wN \@@_print_marker:w \l_@@_tmp_tl \q_stop } \cs_new_protected:Npn \@@_print_marker:w #1 \q_nil #2 \q_nil #3 \q_nil #4 \q_stop { \hbox_set:Nn \l_@@_integer_box { \siunitx_print_number:n { #1#2#3 } } \hbox_set:Nn \l_@@_decimal_box { \siunitx_print_number:e { \@@_print_marker_aux:w #4 \q_stop } } \@@_center_marker: \use:c { @@_align_ \l_@@_align_text_tl :n } { \box_use_drop:N \l_@@_before_box \box_use_drop:N \l_@@_integer_box \box_use_drop:N \l_@@_decimal_box \box_use_drop:N \l_@@_after_box } } \cs_new:Npn \@@_print_marker_aux:w #1 \q_nil #2 \q_nil #3 \q_nil #4 \q_nil #5 \q_nil #6 \q_nil #7 \q_nil #8 \q_stop { \bool_lazy_and:nnTF { \tl_if_blank_p:n {#1#2} } { ! \tl_if_blank_p:n {#3} } { { } } { \exp_not:n {#1#2} } \exp_not:n {#3#4#5#6} \tl_if_blank:nT {#1#2#3#4#5#6} { { } } \exp_not:n {#7#8} } % \end{macrocode} % For positioning based on a format, we have to work part-by-part as there % are a number of alignment points to get right. As for the |marker| approach, % first we check if the material before the numerical content is of zero % width. Next we need to format the model and content numbers, before % starting an auxiliary chain to pick out the various parts in order. We have % to carry the amount of space for the non-numerical material before the cell % forward: this may end up being enlarged by unused parts of the integer. % \begin{macrocode} \cs_new_protected:Npn \@@_print_format:nnn #1#2#3 { \hbox_set:Nn \l_@@_tmp_box { \l_@@_before_model_tl } \hbox_set:Nn \l_@@_before_box {#1} \dim_compare:nNnT { \box_wd:N \l_@@_before_box } = { 0pt } { \box_clear:N \l_@@_before_box #1 } \dim_set:Nn \l_@@_before_dim { \box_wd:N \l_@@_tmp_box } \siunitx_number_parse:nN {#2} \l_@@_tmp_tl \group_begin: \bool_if:NT \l_@@_auto_round_bool { \keys_set:nx { siunitx } { round-mode = places , round-pad = true , round-precision = \exp_after:wN \@@_print_format:nnnnnn \l_@@_format_tl } } \siunitx_number_process:NN \l_@@_tmp_tl \l_@@_tmp_tl \exp_args:NNNV \group_end: \tl_set:Nn \l_@@_tmp_tl \l_@@_tmp_tl \tl_set:Nx \l_@@_tmp_tl { \siunitx_number_output:NN \l_@@_model_tl \q_nil \exp_not:N \q_mark \siunitx_number_output:NN \l_@@_tmp_tl \q_nil } \exp_after:wN \@@_print_format_auxi:w \l_@@_tmp_tl \q_stop \hbox_set:Nn \l_@@_tmp_box { \l_@@_after_model_tl } \hbox_set_to_wd:Nnn \l_@@_after_box { \box_wd:N \l_@@_tmp_box + \l_@@_carry_dim } { \bool_if:NT \l_@@_align_after_bool { \skip_horizontal:n { \l_@@_carry_dim } } #3 \@@_fil: } \use:c { @@_align_ \l_@@_align_number_tl :n } { \box_use_drop:N \l_@@_before_box \box_use_drop:N \l_@@_integer_box \box_use_drop:N \l_@@_decimal_box \box_use_drop:N \l_@@_after_box } } \cs_new:Npn \@@_print_format:nnnnnn #1#2#3#4#5#6#7 { 0 #4 } % \end{macrocode} % The first numerical part to handle is the comparator. Any white space we % need to add goes into the text part \emph{if} alignment is not active % (\foreign{i.e.}~we are looking \enquote{backwards} to place this filler). % \begin{macrocode} \cs_new_protected:Npn \@@_print_format_auxi:w #1 \q_nil #2 \q_mark #3 \q_nil #4 \q_stop { \@@_color_check:w #3 \q_nil \q_stop \@@_print_model_box:Nn \l_@@_tmp_box {#1} \bool_if:NTF \l_@@_align_before_bool { \hbox_set_to_wd:Nnn \l_@@_integer_box { \box_wd:N \l_@@_tmp_box } { \@@_fil: \tl_if_blank:nF {#3} { \siunitx_print_number:n {#3} } } } { \@@_print_format_box:Nn \l_@@_integer_box {#3} \dim_add:Nn \l_@@_before_dim { \box_wd:N \l_@@_tmp_box - \box_wd:N \l_@@_integer_box } } \@@_print_format_auxii:w #2 \q_mark #4 \q_stop } % \end{macrocode} % The integer part follows much the same pattern, except now it is control % of the comparator alignment that determines where the white space goes. % We want to make sure that \emph{if} the integer is too long, it pokes out % to the left, and at the same time we want to issue exactly one overfull % box warning. That is achieved using an explicit test, such that if there % is an issue we force the size of the content appropriately. That process % will lead to a hit but only for \enquote{defective} input. % % As we already have content in the |integer| box, we need to measure how % much \emph{extra} material has been added. % % As the integer part is completed here, we are able to finalise the width % of the pre-numeral part, reboxing it to have the correct width and possibly % to force a single overfull warning if appropriate. % \begin{macrocode} \cs_new_protected:Npn \@@_print_format_auxii:w #1 \q_nil #2 \q_nil #3 \q_mark #4 \q_nil #5 \q_nil #6 \q_stop { \@@_print_model_box:Nn \l_@@_tmp_box {#1#2} \dim_set:Nn \l_@@_tmp_dim { \box_wd:N \l_@@_tmp_box } \@@_print_format_box:Nn \l_@@_tmp_box {#4#5} \dim_compare:nNnT { \box_wd:N \l_@@_tmp_box } > \l_@@_tmp_dim { \hbox_set_to_wd:Nnn \l_@@_tmp_box \l_@@_tmp_dim { \hbox_unpack:N \l_@@_tmp_box } \hbox_set_to_wd:Nnn \l_@@_tmp_box \l_@@_tmp_dim { \tex_hss:D \hbox_unpack:N \l_@@_tmp_box } } \bool_lazy_and:nnTF { \l_@@_align_comparator_bool } { \dim_compare_p:nNn { \box_wd:N \l_@@_integer_box } > { 0pt } } { \hbox_set_to_wd:Nnn \l_@@_integer_box { \box_wd:N \l_@@_integer_box + \l_@@_tmp_dim } { \hbox_unpack:N \l_@@_integer_box \@@_fill: \hbox_unpack:N \l_@@_tmp_box } } { \bool_if:NTF \l_@@_align_before_bool { \hbox_set_to_wd:Nnn \l_@@_integer_box { \box_wd:N \l_@@_integer_box + \l_@@_tmp_dim } { \@@_fil: \hbox_unpack:N \l_@@_integer_box \hbox_unpack:N \l_@@_tmp_box } } { \hbox_set:Nn \l_@@_integer_box { \hbox_unpack:N \l_@@_integer_box \hbox_unpack:N \l_@@_tmp_box } \dim_add:Nn \l_@@_before_dim { \l_@@_tmp_dim - \box_wd:N \l_@@_tmp_box } } } \hbox_set_to_wd:Nnn \l_@@_before_box \l_@@_before_dim { \@@_fil: \hbox_unpack:N \l_@@_before_box } \@@_print_format_auxiii:w ? #3 \q_mark ? #6 \q_stop } % \end{macrocode} % We now deal with the decimal part: there is nothing already in the % |decimal| box, so the basics are easy. We need to \enquote{carry forward} % any white space, as where it gets inserted depends on the options for % subsequent parts. The \cs{use_none:n} here remove the two |?| above: % those are added to preserve braces around the decimal marker. % \begin{macrocode} \cs_new_protected:Npn \@@_print_format_auxiii:w #1 \q_nil #2 \q_nil #3 \q_mark #4 \q_nil #5 \q_nil #6 \q_stop { \@@_print_model_box:No \l_@@_tmp_box { \use_none:n #1#2 } \@@_print_format_box:No \l_@@_decimal_box { \use_none:n #4#5 } \dim_set:Nn \l_@@_carry_dim { \box_wd:N \l_@@_tmp_box - \box_wd:N \l_@@_decimal_box } \@@_print_format_auxiv:w #3 \q_mark #6 \q_stop } % \end{macrocode} % Any separated uncertainty is now picked up. That has a number of parts, so % the first step is to look for a sign (which will be |#1|). We then split, % either simply tidying up the markers if there is no uncertainty, or % setting it. % \begin{macrocode} \cs_new_protected:Npn \@@_print_format_auxiv:w #1 \q_nil #2 \q_mark #3 \q_nil #4 \q_stop { \tl_if_blank:nTF {#1} { \@@_print_format_auxv:w } { \@@_print_format_auxvii:w } #1 \q_nil #2 \q_mark #3 \q_nil #4 \q_stop } \cs_new_protected:Npn \@@_print_format_auxv:w #1 \q_nil #2 \q_nil #3 \q_nil #4 \q_nil #5 \q_mark #6 \q_stop { \@@_print_format_auxvi:w #5 \q_mark #6 \q_stop } \cs_new_protected:Npn \@@_print_format_auxvi:w #1 \q_mark #2 \q_nil #3 \q_nil #4 \q_nil #5 \q_nil #6 \q_stop { \@@_print_format_auxxiii:w #1 \q_mark #6 \q_stop } % \end{macrocode} % Sorting out the placement of the uncertainty requires both the model and % real data widths, so we store the former to avoiding needing more boxes. % It's then just a case of putting the carry-over white space in the right % place. % \begin{macrocode} \cs_new_protected:Npn \@@_print_format_auxvii:w #1 \q_mark #2 \q_stop { \bool_if:NTF \l_@@_align_uncertainty_bool { \@@_print_format_auxviii:w } { \@@_print_format_auxx:w } #1 \q_mark #2 \q_stop } % \end{macrocode} % For an aligned uncertainty, we need to work with the different parts. % \begin{macrocode} \cs_new_protected:Npn \@@_print_format_auxviii:w #1 \q_nil #2 \q_nil #3 \q_mark #4 \q_nil #5 \q_nil #6 \q_stop { \hbox_set:Nn \l_@@_tmp_box { \siunitx_print_number:n { { } #1#2 { } } } \hbox_set_to_wd:Nnn \l_@@_uncert_box { \box_wd:N \l_@@_tmp_box } { \siunitx_print_number:n { { } #4 { } } \@@_fil: \tl_if_blank:nF {#5} { \siunitx_print_number:n {#5} } } \@@_print_format_auxix:w #3 \q_mark #6 \q_stop } \cs_new_protected:Npn \@@_print_format_auxix:w #1 \q_nil #2 \q_nil #3 \q_mark #4 \q_nil #5 \q_nil #6 \q_stop { \@@_print_model_box:Nn \l_@@_tmp_box {#1#2} \hbox_set_to_wd:Nnn \l_@@_tmp_box { \box_wd:N \l_@@_uncert_box + \box_wd:N \l_@@_tmp_box } { \box_use:N \l_@@_uncert_box \tl_if_blank:nF {#2#5} { \siunitx_print_number:n {#4#5} } \@@_fil: } \dim_set:Nn \l_@@_tmp_dim { \box_wd:N \l_@@_tmp_box } \@@_print_format_auxxii:w #3 \q_mark #6 \q_stop } % \end{macrocode} % Non-aligned uncertainty: we have all of the information needed as % |#1|/|#3|, so the work is trivial. % \begin{macrocode} \cs_new_protected:Npn \@@_print_format_auxx:w #1 \q_nil #2 \q_nil #3 \q_nil #4 \q_mark #5 \q_nil #6 \q_nil #7 \q_nil #8 \q_stop { \@@_print_format_auxxi:w #1#2#3#4 \q_mark #5#6#7#8 \q_stop } \cs_new_protected:Npn \@@_print_format_auxxi:w #1 \q_nil #2 \q_mark #3 \q_nil #4 \q_stop { \@@_print_model_box:Nn \l_@@_tmp_box { { } #1 } \dim_set:Nn \l_@@_tmp_dim { \box_wd:N \l_@@_tmp_box } \@@_print_format_box:Nn \l_@@_tmp_box { { } #3 } \@@_print_format_auxxii:w #2 \q_mark #4 \q_stop } % \end{macrocode} % Back together to put the uncertainty part onto the main value, then % move on to the exponent. % \begin{macrocode} \cs_new_protected:Npn \@@_print_format_auxxii:w #1 \q_mark #2 \q_stop { \@@_print_format_after:N \l_@@_align_uncertainty_bool \@@_print_format_auxxiii:w #1 \q_mark #2 \q_stop } % \end{macrocode} % Finally, we get to the exponent part: the multiplication symbol is % |#1| and the number itself is |#2|. The code is almost the same as for % uncertainties, which allows a shared auxiliary to be used. As |#3| could % be entirely empty, there needs to be a bit of work to ensure alignment % is retained in all cases. % \begin{macrocode} \cs_new_protected:Npn \@@_print_format_auxxiii:w #1 \q_nil #2 \q_mark #3 \q_nil #4 \q_stop { \tl_if_blank:nF {#2} { \@@_print_model_box:Nn \l_@@_tmp_box { { } #1#2 } \dim_set:Nn \l_@@_tmp_dim { \box_wd:N \l_@@_tmp_box } \@@_print_format_box:Nn \l_@@_tmp_box { \bool_lazy_and:nnT { \l_@@_align_exponent_bool } { \tl_if_blank_p:n {#3} } { \@@_print_model_box:Nn \l_@@_tmp_box { { } #1 { } } \@@_skip:n { \box_wd:N \l_@@_tmp_box } } { } #3#4 } \@@_print_format_after:N \l_@@_align_exponent_bool } } % \end{macrocode} % A simple auxiliary to avoid relatively expensive use of the print routine % for empty parts. There is a separate function for the model as this allows % for the case where different font widths are in use. % \begin{macrocode} \cs_new_protected:Npn \@@_print_format_box:Nn #1#2 { \@@_print_format_box:nNn { } #1 {#2} } \cs_generate_variant:Nn \@@_print_format_box:Nn { No } \cs_new_protected:Npn \@@_print_model_box:Nn #1#2 { \@@_print_format_box:VNn \l_@@_model_setup_tl #1 {#2} } \cs_generate_variant:Nn \@@_print_model_box:Nn { No } \cs_new_protected:Npn \@@_print_format_box:nNn #1#2#3 { \hbox_set:Nn #2 { \tl_if_blank:nF {#3} { #1 \siunitx_print_number:n {#3} } } } \cs_generate_variant:Nn \@@_print_format_box:nNn { V } % \end{macrocode} % A common routine for placing material after the decimal marker and % \enquote{shuffling}. % \begin{macrocode} \cs_new_protected:Npn \@@_print_format_after:N #1 { \bool_if:NTF #1 { \hbox_set_to_wd:Nnn \l_@@_decimal_box { \box_wd:N \l_@@_decimal_box + \l_@@_carry_dim + \box_wd:N \l_@@_tmp_box } { \hbox_unpack:N \l_@@_decimal_box \@@_fil: \hbox_unpack:N \l_@@_tmp_box } \dim_set:Nn \l_@@_carry_dim { \l_@@_tmp_dim - \box_wd:N \l_@@_tmp_box } } { \hbox_set:Nn \l_@@_decimal_box { \hbox_unpack:N \l_@@_decimal_box \hbox_unpack:N \l_@@_tmp_box } \dim_add:Nn \l_@@_carry_dim { \l_@@_tmp_dim - \box_wd:N \l_@@_tmp_box } } } % \end{macrocode} % With no alignment, everything supplied is treated more-or-less the % same as \cs{num} (but without the \pkg{xparse} wrapper). % \begin{macrocode} \cs_new_protected:Npn \@@_print_none:nnn #1#2#3 { \use:c { @@_align_ \l_@@_align_number_tl :n } { #1 \siunitx_number_format:nN {#2} \l_@@_tmp_tl \siunitx_print_number:V \l_@@_tmp_tl #3 } } % \end{macrocode} % \end{macro} % \end{macro} % \end{macro} % \end{macro} % \end{macro} % \end{macro} % \end{macro} % \end{macro} % \end{macro} % \end{macro} % \end{macro} % % \subsection{Standard settings for module options} % % Some of these follow naturally from the point of definition % (\foreign{e.g.}~boolean variables are always |false| to begin with), % but for clarity everything is set here. % \begin{macrocode} \keys_set:nn { siunitx } { table-align-comparator = true , table-align-exponent = true , table-align-text-after = true , table-align-text-before = true , table-align-uncertainty = true , table-alignment = center , table-auto-round = false , table-column-width = 0pt , table-fixed-width = false , table-format = 2.2 , table-number-alignment = center , table-text-alignment = center , % \end{macrocode} % Out of order as |table-format| sets this implicitly too. % \begin{macrocode} table-alignment-mode = marker } % \end{macrocode} % % \begin{macrocode} % % \end{macrocode} % % \end{implementation} % % \PrintIndex