% \iffalse meta-comment % % File: siunitx-print.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-print} -- Printing material with font control^^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{Printing quantities} % % This submodule is focussed on providing controlled printing for numbers and % units. Key to this is control of font: conventions for printing quantities % mean that the exact nature of the output is important. At the same time, this % module provides flexibility for the user in terms of which aspects of the font % are responsive to the surrounding general text. Printing material may also % take place in text or math mode. % % The printing routines assume that normal \LaTeXe{} font selection commands % are available, in particular % \begin{itemize} % \item \cs{bfseries}, % \item \cs{mathrm}, % \item \cs{mathversion}, % \item \cs{fontfamily}, % \item \cs{fontseries}, % \item \cs{fontshape}, % \item \cs{familydefault}, % \item \cs{seriesdefault}, % \item \cs{shapedefault} and % \item \cs{selectfont}. % \end{itemize} % It also requires the standard \LaTeXe{} kernel commands % \begin{itemize} % \item \cs{ensuremath}, % \item \cs{mbox}, % \item \cs{textsubscript} and % \item \cs{textsuperscript} % \end{itemize} % for printing in text mode. The following packages are % also required to provide the functionality detailed. % \begin{itemize} % \item \pkg{color}: support for color using \cs{textcolor} % \item \pkg{textcomp}: \cs{textminus}, \cs{textpm}, \item \cs{texttimes} and % \cs{textcenteredperiod} for printing in text mode % \item \pkg{amstext}: the \cs{text} command for printing in text mode % \end{itemize} % For detection of math mode fonts, as well as \cs{mathrm}, the existence of % \cs{symoperators} is assumed; other math font commands are not % \emph{required} to exist. % % \begin{function} % { % \siunitx_print_number:n, \siunitx_print_number:V, % \siunitx_print_number:e, % \siunitx_print_unit:n, \siunitx_print_unit:V, \siunitx_print_unit:e, % \siunitx_print_unit:o % } % \begin{syntax} % \cs{siunitx_print_number:n} \Arg{material} % \cs{siunitx_print_unit:n} \Arg{material} % \end{syntax} % Prints the \meta{material} according the the prevailing settings for the % submodule as applicable to the \meta{type} of content (|number| or |unit|). % The \meta{material} should comprise normal % \LaTeX{} mark-up for numbers or units. In particular, units will typically % use |\mathrm| to indicate material to be printed in the current upright % roman font, and |^| and |_| will typically be used to indicate super- and % subscripts, respectively. These elements will be correctly handled when % printing for example using |\mathsf| in math mode, or using only text % fonts. No printing takes place if the \cs{material} is entirely empty % after a single expansion. % \end{function} % % \begin{function} % {\siunitx_print_match:n, \siunitx_print_math:n, \siunitx_print_text:n} % \begin{syntax} % \cs{siunitx_print_match:n} \Arg{material} % \cs{siunitx_print_math:n} \Arg{material} % \cs{siunitx_print_text:n} \Arg{material} % \end{syntax} % Prints the \meta{material} as described for \cs{siunitx_print_\dots:n} but % with a fixed text or math mode output. The printing does \emph{not} set % color (which is managed on a |unit|/|number| basis), but otherwise sets % the font as described above. The |match| function uses either the prevailing % math or text mode. No printing takes place if the \cs{material} is entirely % empty after a single expansion. % \end{function} % % \subsection{Key--value options} % % The options defined by this submodule are available within the \pkg{l3keys} % |siunitx| tree. % % \begin{function}{color} % \begin{syntax} % |color| = \meta{color} % \end{syntax} % Color to apply to printed output: the latter should be a named color % defined for use with \cs{textcolor}. The standard setting is empty (no % color). % \end{function} % % \begin{function}{mode} % \begin{syntax} % |mode| = |match|\verb"|"|math|\verb"|"|text| % \end{syntax} % Selects which mode (math or text) the output is printed in: a choice % from the options |match|, |math| or |text|. The option |match| matches % the mode prevailing at the point \cs{siunitx_print_\dots:n} is called. The % |math| and |text| options choose the relevant \TeX{} mode for printing. % The standard setting is |math|. % \end{function} % % \begin{function}{number-color} % \begin{syntax} % |number-color| = \meta{color} % \end{syntax} % Color to apply to numbers in output: the latter should be a named color % defined for use with \cs{textcolor}. The standard setting is empty (no % color). % \end{function} % % \begin{function}{number-mode} % \begin{syntax} % |number-mode| = |match|\verb"|"|math|\verb"|"|text| % \end{syntax} % Selects which mode (math or text) the numbers are printed in: a choice % from the options |match|, |math| or |text|. The option |match| matches % the mode prevailing at the point \cs{siunitx_prin_number:n} is called. The % |math| and |text| options choose the relevant \TeX{} mode for printing. % The standard setting is |math|. % \end{function} % % \begin{function}{propagate-math-font} % \begin{syntax} % |propagate-math-font| = |true|\verb"|"|false| % \end{syntax} % Switch to determine if the currently-active math font is applied within % printed output. This is relevant only when \cs{siunitx_print_\dots:n} is % called from within math mode: in text mode there is not active math % font. When not active, math mode material will be typeset using % standard math mode fonts without any changes being made to the % supplied argument. The standard setting is |false|. % \end{function} % % \begin{function}{reset-math-version} % \begin{syntax} % |reset-math-version| = |true|\verb"|"|false| % \end{syntax} % Switch to determine whether the active \cs{mathversion} is reset to % |normal| when printing in math mode. Note that math version is typically % used to select \cs{boldmath}, though it is also be used by % \foreign{e.g.}~\pkg{sansmath}. The standard setting is |true|. % \end{function} % % \begin{function}{reset-text-family} % \begin{syntax} % |reset-text-family| = |true|\verb"|"|false| % \end{syntax} % Switch to determine whether the active text family is reset to % \cs{rmfamily} when printing in text mode. The standard setting is |true|. % \end{function} % % \begin{function}{reset-text-series} % \begin{syntax} % |reset-text-series| = |true|\verb"|"|false| % \end{syntax} % Switch to determine whether the active text series is reset to % \cs{mdseries} when printing in text mode. The standard setting is |true|. % \end{function} % % \begin{function}{reset-text-shape} % \begin{syntax} % |reset-text-shape| = |true|\verb"|"|false| % \end{syntax} % Switch to determine whether the active text shape is reset to % \cs{upshape} when printing in text mode. The standard setting is |true|. % \end{function} % % \begin{function}{text-family-to-math} % \begin{syntax} % |text-family-to-math| = |true|\verb"|"|false| % \end{syntax} % Switch to determine if the family of the current text font should be % applied (where possible) to printing in math mode. The standard setting is % |false|. % \end{function} % % \begin{function}{text-font-command} % \begin{syntax} % |text-font-command| = \meta{cmd} % \end{syntax} % Command applied to text during output, inserted after any reset of % font set-up. This can therefore be used to apply non-standard font % set up when printing in text mode. The standard setting is empty. % \end{function} % % \begin{function}{text-series-to-math} % \begin{syntax} % |text-series-to-math| = |true|\verb"|"|false| % \end{syntax} % Switch to determine if the weight of the current text font should be % applied (where possible) to printing in math mode. This is achieved by % setting the \cs{mathversion}, and so will override |reset-math-version|. % The mappings between text and math weight are set . The standard setting is |false|. % \end{function} % % \begin{function}{text-subscript-command, text-superscript-command} % \begin{syntax} % |text-subscript-command| = \meta{cmd} % |text-superscript-command| = \meta{cmd} % \end{syntax} % Sets the command used when printing material in sub- or superscript % positions in text mode. The standard settings are \cs{textsubscript} % and \cs{textsuperscript}, respectively. % \end{function} % % \begin{function}{unit-color} % \begin{syntax} % |unit-color| = \meta{color} % \end{syntax} % Color to apply to units in output: the latter should be a named color % defined for use with \cs{textcolor}. The standard setting is empty (no % color). % \end{function} % % \begin{function}{unit-mode} % \begin{syntax} % |unit-mode| = |match|\verb"|"|math|\verb"|"|text| % \end{syntax} % Selects which mode (math or text) units are printed in: a choice % from the options |match|, |math| or |text|. The option |match| matches % the mode prevailing at the point \cs{siunitx_print_\dots:n} is called. The % |math| and |text| options choose the relevant \TeX{} mode for printing. % The standard setting is |math|. % \end{function} % % \begin{function}{series-version-mapping} % \begin{syntax} % |series-version-mapping| |/| \meta{weight} = \meta{version} % \end{syntax} % Defines how \pkg{siunitx} maps from text font weight to math font % version. The pre-defined weights are those used as-standard by % \pkg{autoinst}: % \begin{itemize} % \item \texttt{ul} % \item \texttt{el} % \item \texttt{l} % \item \texttt{sl} % \item \texttt{m} % \item \texttt{sb} % \item \texttt{b} % \item \texttt{eb} % \item \texttt{ub} % \end{itemize} % As standard, the \texttt{m} weight maps to \texttt{normal} math version % whilst all of the \texttt{b} weights map to \texttt{bold} and all of the % \texttt{l} weights map to \texttt{light}. % \end{function} % % \end{documentation} % % \begin{implementation} % % \section{\pkg{siunitx-print} 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_print> % \end{macrocode} % % \subsection{Initial set up} % % \begin{macro}{\@@_ams_text:n} % The printing routines depend on \pkg{amstext} for text mode working. % We also save the definition of \cs{text} here to use it internally, as the % nature of the \emph{document} command may need to vary. % \begin{macrocode} \RequirePackage { amstext } \cs_new_eq:NN \@@_ams_text:n \text % \end{macrocode} % \end{macro} % % Color support is always required; to avoid a potential clash we delay to % the start of the document. % \begin{macrocode} \cs_if_exist:NTF \AddToHook { \AddToHook { begindocument / before } } { \AtBeginDocument } { \RequirePackage { color } } % \end{macrocode} % % Required variants. % \begin{macrocode} \cs_generate_variant:Nn \tl_replace_all:Nnn { NV } % \end{macrocode} % % \begin{variable}{\l_@@_tmp_tl} % Scratch space. % \begin{macrocode} \tl_new:N \l_@@_tmp_tl % \end{macrocode} % \end{variable} % % \subsection{Printing routines} % % \begin{variable} % { % \l_@@_number_color_tl , % \l_@@_number_mode_tl , % \l_@@_unit_color_tl , % \l_@@_unit_mode_tl , % \l_@@_math_font_bool , % \l_@@_math_version_bool , % \l_@@_math_family_bool , % \l_@@_text_font_tl , % \l_@@_text_sub_tl , % \l_@@_text_super_tl , % \l_@@_math_series_bool % } % Options which apply to the main formatting routine, and so are not tied % to either symbolic or literal input. % \begin{macrocode} \tl_new:N \l_@@_number_mode_tl \tl_new:N \l_@@_unit_mode_tl \keys_define:nn { siunitx } { color .meta:n = { number-color = #1 , unit-color = #1 } , mode .meta:n = { number-mode = #1 , unit-mode = #1 } , number-color .tl_set:N = \l_@@_number_color_tl , number-mode .choices:nn = { match , math , text } { \tl_set_eq:NN \l_@@_number_mode_tl \l_keys_choice_tl } , propagate-math-font .bool_set:N = \l_@@_math_font_bool , reset-math-version .bool_set:N = \l_@@_math_version_bool , reset-text-family .bool_set:N = \l_@@_text_family_bool , reset-text-series .bool_set:N = \l_@@_text_series_bool , reset-text-shape .bool_set:N = \l_@@_text_shape_bool , text-family-to-math .bool_set:N = \l_@@_math_family_bool , text-font-command .tl_set:N = \l_@@_text_font_tl , text-subscript-command .tl_set:N = \l_@@_text_sub_tl , text-superscript-command .tl_set:N = \l_@@_text_super_tl , text-series-to-math .bool_set:N = \l_@@_math_series_bool , unit-color .tl_set:N = \l_@@_unit_color_tl , unit-mode .choices:nn = { match , math , text } { \tl_set_eq:NN \l_@@_unit_mode_tl \l_keys_choice_tl } } % \end{macrocode} % \end{variable} % % \begin{variable} % { % \l_@@_version_ul_tl , % \l_@@_version_el_tl , % \l_@@_version_l_tl , % \l_@@_version_sl_tl , % \l_@@_version_m_tl , % \l_@@_version_sb_tl , % \l_@@_version_b_tl , % \l_@@_version_eb_tl , % \l_@@_version_ub_tl % } % One set of \enquote{focussed} options. % \begin{macrocode} \keys_define:nn { siunitx / series-version-mapping } { ul . tl_set:N = \l_@@_version_ul_tl , el . tl_set:N = \l_@@_version_el_tl , l . tl_set:N = \l_@@_version_l_tl , sl . tl_set:N = \l_@@_version_sl_tl , m . tl_set:N = \l_@@_version_m_tl , sb . tl_set:N = \l_@@_version_sb_tl , b . tl_set:N = \l_@@_version_b_tl , eb . tl_set:N = \l_@@_version_eb_tl , ub . tl_set:N = \l_@@_version_ub_tl } % \end{macrocode} % \end{variable} % % \begin{macro} % { % \siunitx_print_number:n, \siunitx_print_number:V, % \siunitx_print_number:e, % \siunitx_print_unit:n, \siunitx_print_unit:V, \siunitx_print_unit:e, % \siunitx_print_unit:o % } % \begin{macro}{\@@_aux:nn} % \begin{macro}{\@@_number:n, \@@_unit:n} % The main printing function doesn't actually need to do very much: just set % the color and select the correct sub-function. The \cs{tl_if_empty:oF} is % needed to allow a leading \texttt{1} to be omitted in the case of a unit % starting \cs{per} in cases where we can't simply use an entirely empty % value: see the \pkg{siunitx-compound} submodule. % \begin{macrocode} \cs_new_protected:Npn \siunitx_print_number:n #1 { \@@_aux:nn { number } {#1} } \cs_generate_variant:Nn \siunitx_print_number:n { V , e , x } \cs_new_protected:Npn \siunitx_print_unit:n #1 { \@@_aux:nn { unit } {#1} } \cs_generate_variant:Nn \siunitx_print_unit:n { V , e , o , x } \cs_new_protected:Npn \@@_aux:nn #1#2 { \tl_if_empty:oF {#2} { \tl_if_empty:cTF { l_@@_ #1 _color_tl } { \use:n } { \ExpandArgs { v } \textcolor { l_@@_ #1 _color_tl } } { \use:c { @@ _ #1 :n } {#2} } } } \cs_new_protected:Npn \@@_number:n #1 { \bool_if:NTF \l_siunitx_number_parse_bool { \use:c { siunitx_print_ \l_@@_number_mode_tl :n } } { \siunitx_print_math:n } {#1} } \cs_new_protected:Npn \@@_unit:n #1 { \use:c { siunitx_print_ \l_@@_unit_mode_tl :n } {#1} } % \end{macrocode} % \end{macro} % \end{macro} % \end{macro} % % \begin{macro}{\siunitx_print_match:n} % When the \emph{output} mode should match the input, a simple selection of % route can be made. % \begin{macrocode} \cs_new_protected:Npn \siunitx_print_match:n #1 { \mode_if_math:TF { \siunitx_print_math:n {#1} } { \siunitx_print_text:n {#1} } } % \end{macrocode} % \end{macro} % % \begin{macro}{\@@_replace_font:N} % A simple auxiliary for \enquote{zapping} the unit font. % \begin{macrocode} \cs_new_protected:Npn \@@_replace_font:N #1 { \tl_if_empty:NF \l_siunitx_unit_font_tl { \tl_replace_all:NVn #1 \l_siunitx_unit_font_tl { \use:n } } } % \end{macrocode} % \end{macro} % % \begin{variable} % { % \c_@@_series_uc_tl , % \c_@@_series_ecl_tl , % \c_@@_series_c_tl , % \c_@@_series_sc_tl , % \c_@@_series_sx_tl , % \c_@@_series_x_tl , % \c_@@_series_ex_tl , % \c_@@_series_ux_tl % } % Font widths where the |m| for weight is omitted. % \begin{macrocode} \clist_map_inline:nn { uc , ec , c , sc , sx , x , ex , ux } { \tl_const:cn { c_@@_series_ #1 _tl } { m } } % \end{macrocode} % \end{variable} % % \begin{variable} % { % \c_@@_series_l_tl , % \c_@@_series_m_tl , % \c_@@_series_b_tl % } % Font widths with one letter. % \begin{macrocode} \clist_map_inline:nn { l , m , b } { \tl_const:cn { c_@@_series_ #1 _tl } { #1 } } % \end{macrocode} % \end{variable} % % \begin{variable}{\l_@@_math_html_tl} % \begin{macrocode} \tl_new:N \l_@@_math_html_tl % \end{macrocode} % \end{variable} % % \begin{macro}{\siunitx_print_math:n} % \begin{macro}[EXP]{\@@_extract_series:Nw} % \begin{macro}[EXP]{\@@_convert_series:n, \@@_convert_series:v} % \begin{macro}{\@@_math_version:nn, \@@_math_version:Vn} % \begin{macro} % { % \@@_math_auxi:n, % \@@_math_auxii:n, % } % \begin{macro}{\@@_math_text_setup:} % \begin{macro}{\@@_math_replace:Nn} % \begin{macro} % { % \@@_math_auxiii:n , % \@@_math_auxiii:V , % \@@_math_auxiv:n , % \@@_math_auxv:n % } % \begin{macro}{\@@_math_auxvi:Nn, \@@_math_auxvi:NV} % \begin{macro}{\@@_math_auxvii:N} % \begin{macro}{\@@_math_auxviii:w} % \begin{macro}{\@@_math_auxix:Nn} % \begin{macro}{\@@_math_sub:n, \@@_math_super:n, \@@_math_script:n} % \begin{macro}{\@@_math_ensure:n} % The first step in setting in math mode is to check on the math version. % The starting point is the question of whether text series needs to % propagate to math mode: if so, check on the mapping, otherwise check on % the current math version. % \begin{macrocode} \cs_new_protected:Npn \siunitx_print_math:n #1 { \bool_lazy_and:nnTF { \l_@@_math_series_bool } { \str_if_eq_p:Vn \math@version { normal } } { \tl_set:Nx \l_@@_tmp_tl { \exp_after:wN \@@_extract_series:Nw \f@series ? \q_stop } \tl_if_empty:NTF \l_@@_tmp_tl { \@@_math_auxi:n {#1} } { \@@_math_version:Vn \l_@@_tmp_tl {#1} } } { \@@_math_auxi:n {#1} } } % \end{macrocode} % Look up the math version from the text series. The weight is omitted % if it is |m| plus there are either one or two letters, so we have a little % work to do. To keep things fast, we use a hash table based lookup rather % than a sequence or property list. % \begin{macrocode} \cs_new:Npn \@@_extract_series:Nw #1#2 ? #3 \q_stop { \cs_if_exist:cTF { c_@@_series_ #1#2 _tl } { \@@_convert_series:v { c_@@_series_ #1#2 _tl } } { \cs_if_exist:cTF { c_@@_series_ #1 _tl } { \@@_convert_series:v { c_@@_series_ #1 _tl } } { \@@_convert_series:n {#1#2} } } } \cs_new:Npn \@@_convert_series:n #1 { \tl_use:c { l_@@_version_ #1 _tl } } \cs_generate_variant:Nn \@@_convert_series:n { v } \cs_new_protected:Npn \@@_math_auxi:n #1 { \bool_if:NTF \l_@@_math_version_bool { \@@_math_version:nn { normal } {#1} } { \@@_math_auxii:n {#1} } } % \end{macrocode} % Any setting which changes the math version can only be set from text mode % (as it applies at the level of a formula). As such, the first test is to % see if that needs to be to check if the math version has to be set: if so, % switch to text mode, sort it out and switch back. That of course means % that in such cases, line breaking will not be possible. % \begin{macrocode} \cs_new_protected:Npn \@@_math_version:nn #1#2 { \str_if_eq:VnTF \math@version { #1 } { \@@_math_auxii:n {#2} } { \mode_if_math:TF { \text } { \use:n } { \mathversion {#1} \@@_math_auxii:n {#2} } } } \cs_generate_variant:Nn \@@_math_version:nn { V } % \end{macrocode} % At this point, force math mode then start dealing with setting math font % based on text family. If the text family is roman, life is slightly % different to if it is sanserif or monospaced. In all cases, the outcomes % can be handled using the same routines as for normal math mode treatment. % The test here is on a string basis as |\f@family| and the |\...default| % commands have different |\long| status. % \begin{macrocode} \cs_new_protected:Npn \@@_math_auxii:n #1 { \group_begin: \tl_if_head_eq_meaning:nNTF {#1} \sfrac { \cs_set_protected:Npn \text ##1 { \cs_set_eq:NN \text \@@_ams_text:n \@@_math_text_setup: ##1 } } { \@@_math_text_setup: } \tl_set:Nn \l_@@_tmp_tl {#1} \exp_after:wN \@@_math_replace:Nn \l_@@_math_html_tl \q_recursion_tail { } \q_recursion_stop \@@_math_auxiii:V \l_@@_tmp_tl \group_end: } % \end{macrocode} % Within the math mode argument, there might be a nested \cs{text}, at which % point we need to apply the text-mode support. Within \emph{that}, we need % to reset \cs{text} to normal. There is one wrinkle, which is covered in the % function above: the \cs{sfrac} command from \pkg{xfrac}. In the latter, % there is a \cs{text} applied to the two arguments, and the material that is % passed contains a lot of code: we cannot apply search-and-replace there. % Instead, we delay things by one expansion. % \begin{macrocode} \cs_new_protected:Npn \@@_math_text_setup: { \cs_set_protected:Npn \text ##1 { \cs_set_eq:NN \text \@@_ams_text:n \siunitx_print_text:n {##1} } } \cs_new_protected:Npn \@@_math_replace:Nn #1#2 { \quark_if_recursion_tail_stop:N #1 \tl_replace_all:Nnn \l_@@_tmp_tl {#1} {#2} \@@_math_replace:Nn } \cs_new_protected:Npn \@@_math_auxiii:n #1 { \bool_if:NTF \l_@@_math_family_bool { \str_case_e:nnF { \f@family } { { \rmdefault } { \@@_math_auxv:n } { \sfdefault } { \@@_math_auxix:Nn \mathsf } { \ttdefault } { \@@_math_auxix:Nn \mathtt } } { \@@_math_auxiv:n } } { \@@_math_auxiv:n } {#1} } \cs_generate_variant:Nn \@@_math_auxiii:n { V } % \end{macrocode} % Now we deal with the font selection in math mode. There are two possible % cases. First, we are retaining the current math font, and the active one is % \cs{mathsf} or \cs{mathtt}: that needs to be applied to the argument. % Alternatively, if the current font is not retained, ensure that % normal math mode rules are active. % \begin{macrocode} \cs_new_protected:Npn \@@_math_auxiv:n #1 { \bool_if:NTF \l_@@_math_font_bool { \@@_math_auxvii:N \mathbf \mathit \mathsf \mathtt \q_recursion_tail \q_recursion_stop } { \@@_math_auxv:n } {#1} } \cs_new_protected:Npn \@@_math_auxv:n #1 { \bool_lazy_or:nnTF { \int_compare_p:nNn \fam = { -1 } } { \int_compare_p:nNn \fam = \symoperators } { \@@_math_ensure:n } { \@@_math_auxvi:Nn \mathrm } {#1} } \cs_new_protected:Npn \@@_math_auxvi:Nn #1#2 { \@@_math_ensure:n { #1 {#2} } } \cs_generate_variant:Nn \@@_math_auxvi:Nn { NV } \cs_new_protected:Npn \@@_math_auxvii:N #1 { \quark_if_recursion_tail_stop_do:Nn #1 { \@@_math_auxv:n } \exp_after:wN \exp_after:wN \exp_after:wN \@@_math_auxviii:w \cs:w \cs_to_str:N #1 \c_space_tl \cs_end: \use@mathgroup ? { -2 } \q_stop #1 } \cs_new_protected:Npn \@@_math_auxviii:w #1 \use@mathgroup #2#3 #4 \q_stop #5 { \int_compare:nNnTF \fam = {#3} { \use_i_delimit_by_q_recursion_stop:nw { \@@_math_auxix:Nn #5 } } { \@@_math_auxvii:N } } % \end{macrocode} % Search-and-replace fun: deal with any font commands in the argument and % also inside sub/superscripts. % \begin{macrocode} \cs_new_protected:Npx \@@_math_auxix:Nn #1#2 { \group_begin: \tl_set:Nn \exp_not:N \l_@@_tmp_tl {#2} \@@_replace_font:N \exp_not:N \l_@@_tmp_tl \tl_replace_all:Nnn \exp_not:N \l_@@_tmp_tl { \char_generate:nn { `\_ } { 8 } } { \exp_not:N \@@_math_sub:n } \tl_replace_all:Nnn \exp_not:N \l_@@_tmp_tl { ^ } { \exp_not:N \@@_math_super:n } \@@_math_auxvi:NV #1 \exp_not:N \l_@@_tmp_tl \group_end: } \cs_new_protected:Npx \@@_math_sub:n #1 { \char_generate:nn { `\_ } { 8 } { \exp_not:N \@@_math_script:n {#1} } } \cs_new_protected:Npn \@@_math_super:n #1 { ^ { \@@_math_script:n {#1} } } \cs_new_protected:Npn \@@_math_script:n #1 { \group_begin: \tl_set:Nn \l_@@_tmp_tl {#1} \@@_replace_font:N \l_@@_tmp_tl \tl_use:N \l_@@_tmp_tl \group_end: } \cs_new_protected:Npn \@@_math_ensure:n #1 { \tl_if_blank:nF {#1} { \mode_if_math:TF {#1} { $ #1 $ } } } % \end{macrocode} % For \pkg{tex4ht}, we need to have category code $12$ |^| tokens in math % mode. % \begin{macrocode} \AtBeginDocument { \@ifpackageloaded { tex4ht } { \tl_set:Nx \l_@@_math_html_tl { ^ { \token_to_str:N ^ } } } { } } % \end{macrocode} % \end{macro} % \end{macro} % \end{macro} % \end{macro} % \end{macro} % \end{macro} % \end{macro} % \end{macro} % \end{macro} % \end{macro} % \end{macro} % \end{macro} % \end{macro} % \end{macro} % % \begin{macro}{\siunitx_print_text:n} % \begin{macro}{\@@_text_replace:n, \@@_text_replace_aux:n} % \begin{macro}{\@@_text_replace_font:N} % \begin{macro}[EXP]{\@@_text_replace_first:N} % \begin{macro}{\@@_text_replace:N} % \begin{macro}{\@@_text_replace:NNn} % \begin{macro}{\@@_text_replace:Nnnn} % \begin{macro}{\@@_text_replace_frac:n} % \begin{macro}{\@@_text_sub:n, \@@_text_super:n} % \begin{macro}{\@@_text_scripts:nnN} % \begin{macro}{\@@_text_scripts:} % \begin{macro}{\@@_text_scripts_one:NnN} % \begin{macro}{\@@_text_scripts_two:NnNn} % \begin{macro}{\@@_text_scripts_two:nn} % \begin{macro}{\@@_text_scripts_two:n} % \begin{macro}{\@@_text_fraction:Nnn} % Typesetting in text mode is easy in font control terms but more tricky % in the manipulation of the input. The easy part comes first. % \begin{macrocode} \cs_new_protected:Npn \siunitx_print_text:n #1 { \text { \bool_if:NT \l_@@_text_family_bool { \fontfamily { \familydefault } } \bool_if:NT \l_@@_text_series_bool { \fontseries { \seriesdefault } } \bool_if:NT \l_@@_text_shape_bool { \fontshape { \shapedefault } } \bool_lazy_any:nT { { \l_@@_text_family_bool } { \l_@@_text_series_bool } { \l_@@_text_shape_bool } } { \selectfont } \tl_use:N \l_@@_text_font_tl \exp_last_unbraced:Nno \tl_if_head_eq_meaning:nNTF {#1} \l_siunitx_unit_fraction_tl { \@@_text_fraction:Nnn #1 } { \@@_text_replace:n {#1} } } } % \end{macrocode} % To get math mode material to print in text mode, various search-and-replace % steps are needed. We use \tn{protected@edef} to deal with commands, partly % for speed and partly as it deals with anything nested inside \cs{mathord}. % On the other hand, the unit font command has to be handled at exactly one % level, so that it does not affect anything inside for example % \cs{ensuremath}. That means preventing any change inside the % \tn{protected@edef}, hence the \cs{cs_set_eq:NN} to \cs{scan_stop:} % (for \LaTeXe{} robust commands). % \begin{macrocode} \cs_new_protected:Npn \@@_text_replace:n #1 { \group_begin: \tl_if_head_eq_meaning:nNTF {#1} \mathchoice { \@@_text_replace:Nnnnn #1 } { \tl_set:Nn \l_@@_tmp_tl {#1} \tl_if_empty:NF \l_@@_tmp_tl { \group_begin: \@@_text_replace_font:N \l_@@_tmp_tl \cs_set:Npn \mathord ##1 { \@@_text_replace_first:N ##1 } \tl_if_empty:NF \l_siunitx_unit_font_tl { \exp_after:wN \cs_set_eq:NN \l_siunitx_unit_font_tl \scan_stop: } \tl_map_inline:nn { \mp \approx \sim \ge \le \geq \leq \gg \ll \angle } { \cs_set:Npn ##1 { \exp_not:N \ensuremath { \exp_not:N ##1 } } } \cs_set:Npn \sqrt ##1 { \exp_not:N \ensuremath { \exp_not:N \sqrt { \exp_not:N \text { \@@_text_replace:n {##1} } } } } \protected@edef \l_@@_tmp_tl { \exp_after:wN \@@_text_replace_first:N \l_@@_tmp_tl } \exp_args:NNNV \group_end: \tl_set:Nn \l_@@_tmp_tl \l_@@_tmp_tl \@@_text_replace:N \l_@@_tmp_tl \@@_text_replace_aux:n { \tl_use:N \l_@@_tmp_tl } } } \group_end: } \cs_new_protected:Npn \@@_text_replace_aux:n #1 {#1} \cs_new_protected:Npn \@@_text_replace_font:N #1 { \tl_if_empty:NF \l_siunitx_unit_font_tl { \tl_replace_all:NVn #1 \l_siunitx_unit_font_tl { \use:n } } } \cs_new:Npn \@@_text_replace_first:N #1 { \str_case:nnF {#1} { { \cdot } { \exp_not:N \textperiodcentered } { \pm } { \exp_not:N \textpm } { \times } { \exp_not:N \texttimes } } {#1} } \cs_new_protected:Npx \@@_text_replace:N #1 { \exp_not:N \@@_text_replace:NNn #1 - { \exp_not:N \textminus } \char_generate:nn { `\_ } { 8 } { \exp_not:N \@@_text_sub:n } ^ { \exp_not:N \@@_text_super:n } \exp_not:N \cdot { \exp_not:N \: \exp_not:N \textperiodcentered \exp_not:N \: } \exp_not:N \pm { \exp_not:N \: \exp_not:N \textpm \exp_not:N \: } \exp_not:N \times { \exp_not:N \: \exp_not:N \texttimes \exp_not:N \: } \exp_not:N \q_recursion_tail { ? } \exp_not:N \q_recursion_stop \@@_text_replace_font:N #1 } \cs_new_protected:Npn \@@_text_replace:NNn #1#2#3 { \quark_if_recursion_tail_stop:N #2 \tl_replace_all:Nnn #1 {#2} {#3} \@@_text_replace:NNn #1 } \cs_new_protected:Npn \@@_text_replace:Nnnnn #1#2#3#4#5 { \ensuremath { \mathchoice { \@@_print_replace_frac:n {#2} } { \@@_print_replace_frac:n {#3} } { \@@_print_replace_frac:n {#4} } { \@@_print_replace_frac:n {#5} } } } % \end{macrocode} % Almost the same as the lead-off but here we need to deal with re-inserting % a text mode shift. % \begin{macrocode} \cs_new_protected:Npn \@@_print_replace_frac:n #1 { \exp_last_unbraced:Nno \tl_if_head_eq_meaning:nNTF {#1} \l_siunitx_unit_fraction_tl { \@@_text_fraction:Nnn #1 } { \mbox { \@@_text_replace:n {#1} } } } % \end{macrocode} % When the \pkg{bidi} package is loaded, we need to make sure % that \cs{text} is doing the correct thing. % \begin{macrocode} \sys_if_engine_xetex:T { \AtBeginDocument { \@ifpackageloaded { bidi } { \cs_set_protected:Npn \@@_text_replace_aux:n #1 { \LRE {#1} } } { } } } % \end{macrocode} % Sub- and superscripts can be in any order in the source. The first step % of handling them is therefore to do a look-ahead to work out whether % only one or both are present. % \begin{macrocode} \cs_new_protected:Npn \@@_text_sub:n #1 { \@@_text_scripts:VnN \l_@@_text_sub_tl {#1} \@@_text_super:n } \cs_new_protected:Npn \@@_text_super:n #1 { \@@_text_scripts:VnN \l_@@_text_super_tl {#1} \@@_text_sub:n } \cs_new_protected:Npn \@@_text_scripts:nnN #1#2#3 { \cs_set_protected:Npn \@@_text_scripts: { \if_meaning:w \l_peek_token #3 \exp_after:wN \@@_text_scripts_two:NnNn \else: \exp_after:wN \@@_text_scripts_one:Nn \fi: #1 {#2} } \peek_after:Nw \@@_text_scripts: } \cs_generate_variant:Nn \@@_text_scripts:nnN { V } \cs_new_protected:Npn \@@_text_scripts: { } % \end{macrocode} % In the simple case of one script item, we have to do a search-and-replace % to deal with anything inside the argument. % \begin{macrocode} \cs_new_protected:Npn \@@_text_scripts_one:Nn #1#2 { \group_begin: \tl_set:Nn \l_@@_tmp_tl {#2} \@@_text_replace:N \l_@@_tmp_tl \exp_args:NNV \group_end: #1 \l_@@_tmp_tl } % \end{macrocode} % For the two scripts case, we cannot use |\textsubscript|/|\textsuperscript| % as they don't stack directly. Instead, we sort out the ordering then use % an implementation for both parts that is the same as the kernel text % scripts. % \begin{macrocode} \cs_new_protected:Npn \@@_text_scripts_two:NnNn #1#2#3#4 { \cs_if_eq:NNTF #1 \textsubscript { \@@_text_scripts_two:nn {#4} {#2} } { \@@_text_scripts_two:nn {#2} {#4} } } \cs_new_protected:Npx \@@_text_scripts_two:nn #1#2 { \group_begin: \exp_not:N \m@th \exp_not:N \ensuremath { ^ { \exp_not:N \@@_text_scripts_two:n {#1} } \char_generate:nn { `\_ } { 8 } { \exp_not:N \@@_text_scripts_two:n {#2} } } \group_end: } \cs_new_protected:Npn \@@_text_scripts_two:n #1 { \mbox { \fontsize \sf@size \z@ \selectfont \@@_text_scripts_one:Nn \use:n {#1} } } % \end{macrocode} % Fraction commands are always math mode, so we have to go back and forth: % this is done after general font setting for performance reasons. % \begin{macrocode} \cs_new_protected:Npn \@@_text_fraction:Nnn #1#2#3 { \ensuremath { #1 { \mbox { \@@_text_replace:n {#2} } } { \mbox { \@@_text_replace:n {#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} % \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 } { color = , mode = math , number-color = , number-mode = math , propagate-math-font = false , reset-math-version = true , reset-text-shape = true , reset-text-series = true , reset-text-family = true , text-family-to-math = false , text-font-command = , text-subscript-command = \textsubscript , text-superscript-command = \textsuperscript , text-series-to-math = false , unit-color = , unit-mode = math } % \end{macrocode} % % These are separate as they all fall inside the same key. % \begin{macrocode} \keys_set:nn { siunitx / series-version-mapping } { ul = light , el = light , l = light , sl = light , m = normal , sb = bold , b = bold , eb = bold , ub = bold } % \end{macrocode} % % \begin{macrocode} % % \end{macrocode} % % \end{implementation} % % \PrintIndex