% \iffalse meta-comment
%
% Copyright (C) 2021 by Qu Yi <quee123@foxmail.com>
%
% This work may be distributed and/or modified under the
% conditions of the CC-BY 4.0 License.
% The latest version of this license is in
%   https://creativecommons.org/licenses/by/4.0/legalcode
% \fi
%
% \iffalse
%<*driver>
\documentclass{l3doc}
\usepackage{spbmark,siunitx}
\usepackage[noto]{newtxmath}
\OnlyDescription
\begin{document}
  \DocInput{\jobname.dtx}
  \PrintIndex
\end{document}
%</driver>
% \fi
%
% \title{\pkg{spbmark} -- Customize superscripts and subscripts}
% \author{Qu Yi}
% \date{2021/07/22\hskip 1em\relax v1.02\thanks{\url{https://github.com/texl3/spbmark}}}
%
% \maketitle
% \begin{abstract}
%   \pkg{spbmark} provides three commands \tn{super}, \tn{sub} and \tn{supersub} to improve the layout of superscripts and subscripts which can be adjusted the relative position and format, and can be used in text and math mode.
% \end{abstract}
% \tableofcontents
%
% \section{Macro package options}
% \label{sec:macro package options}
% \begin{function}[added = 2021-06-26]{ctex}
% This option works with the C\TeX{} document class used by simplified Chinese users, there's actually no functional change. Adding path \texttt{style} to \tn{ctexset} command, which is equivalent to using \tn{spbset}.
% \end{function}
%
% \begin{function}[added = 2021-06-30]{newsupb}
% \texttt{newsupb} is a boolean option with a default value of true that redefines the \LaTeX{} kernel commands to cause \tn{sp} and \tn{textsuperscript} are equivalent to \tn{super} command, and \tn{sb} and \tn{textsubscript} are equivalent to \tn{sub} command.
% \end{function}
%
% \section{User commands}
% \begin{function}[added = 2021-06-26,updated = 2021-06-28]{\super,\sub,\supersub}
% \begin{syntax}
%   \tn{super*}\oarg{vshift}[<height>,<depth>]\parg{code}\marg{content}\oarg{hshift}
%   \tn{sub*}\oarg{vshift}[<height>,<depth>]\parg{code}\marg{content}\oarg{hshift}
%   \tn{supersub*}\oarg{script sep}\oarg{script align}\parg{super format code}
%   \          \marg{super content}\marg{sub content}
%   \          \oarg{horizontal shift}\parg{sub format code}
% \end{syntax}
% \tn{super} command is used to output superscripts, \tn{sub} command is used to output subscripts, \tn{supersub} command is used to output both superscripts and subscripts.
% \end{function}
%
% They match the corresponding pattern depending on whether they are currently in text mode or mathematical mode, or they can use an asterisk variant to force the mathematical pattern. The \meta{vshift} and \meta{hshift} represent respectively vertical and horizontal offsets of superscript or subscript, their default value is 0 pt, and the parameters of \meta{script align} are the same as the \texttt{supersubalign}.
%
% The second optional parameter generally does not need to be used. The \meta{height} and \meta{depth} are separated by commas. Only the front part indicates the height of the superscript or subscript, and only the back part indicates the depth.
%
% The \meta{code} is used to format superscripts and subscripts, and can insert commands such as fonts or colors. The last command in the code can take an argument, which is either a superscript or a subscript.
%
% \begin{function}[added = 2021-06-30]{\sp,\sb,\spb,\textsuperscript,\textsubscript}
% Except for the \tn{spb} command which is equivalent to \tn{supersub}, the others are \LaTeX{} kernel commands. If you use the \texttt{newsupb} package option then they will be redefined to make the command shorter, see section \ref{sec:macro package options}.
%
% If you need to use the original definitions of \tn{textsuperscript} and \tn{textsubscript}, then \tn{spb@textsuperscript@save} and \tn{spb@textsubscript@save} will be available. If you need to use the original definitions of \tn{sp} and \tn{sb}, then \tn{spb@sp@save} and \tn{spb@sb@save} will be available.
% \end{function}
%
% \section{Global control interface}
% \begin{function}[added = 2021-06-26]{\spbset}
% \begin{syntax}
%   \tn{spbset}\marg{key-value list}
% \end{syntax}
% \pkg{spbmark} uses the \tn{spbset} command to control the global default format of superscripts and subscripts. The values ​​set by it will be overwritten by the optional parameters of the superscript and other commands. The list of key values it has is as follows:
% \end{function}
%
% \begin{function}{super-vshift,super-hshift,sub-vshift,sub-hshift,supersubsep,supersubalign}
% \begin{syntax}
%   super-vshift = <fixed length>
%   super-hshift = <fixed length>
%   sub-vshift = <fixed length>
%   sub-hshift = <fixed length>
%   supersubsep = <fixed length>
%   supersubalign = <horizontal alignment>
% \end{syntax}
% \end{function}
%
% \noindent
% \DescribeOption{super-vshift} Extra vertical shift of the superscripts. \\
% \DescribeOption{super-hshift} Extra horizontal shift of the superscripts. \\
% \DescribeOption{sub-vshift} Extra vertical shift of the subscripts. \\
% \DescribeOption{sub-hshift} Extra horizontal shift of the subscripts. \\
% \DescribeOption{supersubsep} The distance between superscript and subscript when using \tn{supersub}, its default value is 0.3 ex. \\
% \DescribeOption{supersubalign} The alignment of superscript and subscript when using \tn{supersub}, which contains l, c, and r parameters respectively for left, center, and right alignment. \\
%
% \section{Examples of use}
% Here is a list of the three commands, please pay attention to the delimiter and usage of optional parameters:
% \begin{quotation}
%   \noindent
%   |A\super[0.2ex](\color{red}){exam}[0.2em]B| \\
%   |$A\sub(\mathsf){exam}B$| \\
%   |A\supersub[0.3ex][c]{examsuper}{sub}B| \\
%   A\super[0.2ex](\color{red}){exam}[0.2em]B \\
%   $A\sub(\mathsf){exam}B$ \\
%   A\supersub[0.3ex][c]{examsuper}{sub}B
% \end{quotation}
%
% It can also be used with the \pkg{siunitx} package to output superscripts and subscripts in the unit:
% \begin{quotation}
%   \noindent
%   |\unit[mode = math]{kg.m/s\super*[-1pt]{2}}| \\
%   |\qty[mode = text]{30}{A\supersub(\color{red}){b}{c}[1pt]}| \\
%   |\spbset{sub-hshift = 2pt}\unit[mode = text]{A_b}| \\
%   \unit[mode = math]{kg.m/s\super*[-1pt]{2}} \\
%   \qty[mode = text]{30}{A\supersub(\color{red}){b}{c}[1pt]} \\
%   \spbset{sub-hshift = 2pt}\unit[mode = text]{A_b}
% \end{quotation}
%
% \pkg{spbmark} also patches the footer markers for standard document class and \cls{KOMA-Script}. You can format the footer markers by redefining the \tn{fnmarkfont} command. Note that extra horizontal shift does not work with footnote markers.
%
% \section{Known issues}
% At present, the vertical and horizontal offsets are effective for the unit commands in the \pkg{siunitx} macro package. However, due to the special mechanism that the decimal point is not recognized correctly because it is converted to a space in the \tn{unit} command, it is recommended to use \texttt{pt} as the unit of offset.
%
% \newcommand\urlprefix{\newline\hspace*{\fill}}
% \let\OldUrl\url
% \renewcommand\url[2][]{{\small\textit{#1}~\OldUrl{#2}}}
% \begin{thebibliography}{99}
% \bibitem{realscripts}
% \textsc{Will Robertson}.
% \newblock \textit{The realscripts package} [CP/OL].
% \newblock version 0.3d,
% \newblock (2016-02-13)
% \urlprefix\url{https://ctan.org/pkg/realscripts}
% \end{thebibliography}
%
% \StopEventually{}
%<*package>
\NeedsTeXFormat{LaTeX2e}[2018/12/31]
\RequirePackage{xparse,l3keys2e}
\ProvidesExplPackage{spbmark}{2021/07/22}{1.02}
  {Customize superscripts and subscripts}

\cs_new_protected:Npn \spb_define:n { \keys_define:nn { spbmark } }
\cs_new_protected:Npn \spbset { \keys_set:nn { spbmark } }
\keys_define:nn { spbmark/option }
  {
    ctex .code:n        =
      {
        \cs_set:Npn \spb_define:n { \keys_define:nn { ctex/style } }
        \cs_set:Npn \spbset { \keys_set:nn { ctex/style } }
      },
    newsupb .bool_set:N = \l__spb_new_supersub_bool,
    newsupb .default:n  = true,
    newsupb .initial:n  = true
  }

\ProcessKeysOptions{spbmark/option}

\spb_define:n
  {
    super-vshift .dim_set:N   = \l__spb_super_script_vshift_dim,
    super-vshift .initial:n   = 0pt,
    super-hshift .dim_set:N   = \l__spb_super_script_hshift_dim,
    super-hshift .initial:n   = 0pt,
    sub-vshift .dim_set:N     = \l__spb_sub_script_vshift_dim,
    sub-vshift .initial:n     = 0pt,
    sub-hshift .dim_set:N     = \l__spb_sub_script_hshift_dim,
    sub-hshift .initial:n     = 0pt,
    supersubsep .dim_set:N    = \l__spb_supersub_script_sep_dim,
    supersubsep .initial:n    = 0.3ex,
    supersubalign .tl_set:N   = \l__spb_supersub_script_align_tl,
    supersubalign .initial:n  = l
  }

\tl_set:Nn \l__spb_boxa_wd_tl { \box_wd:N \l_tmpa_box }
\tl_set:Nn \l__spb_boxb_wd_tl { \box_wd:N \l_tmpb_box }
\cs_new:Npn \spb_tmp_boxa_use: { \box_use:N \l_tmpa_box }
\cs_new:Npn \spb_tmp_boxb_use: { \box_use:N \l_tmpb_box }
\cs_new:Npn \spb_tmp_hboxa_set:n #1 { \hbox_set:Nn \l_tmpa_box {#1} }
\cs_new:Npn \spb_tmp_hboxb_set:n #1 { \hbox_set:Nn \l_tmpb_box {#1} }
\cs_new:Npn \spb_box_max_dim_horizontal:
  {
    \skip_horizontal:n
      { \dim_max:nn { \l__spb_boxa_wd_tl } { \l__spb_boxb_wd_tl } }
  }
\cs_new:Npn \spb_boxwd_subtract:nnnn #1#2#3#4
  {
    \skip_horizontal:n
      { #1\tl_use:c { l__spb_#3_wd_tl } - #2\tl_use:c { l__spb_#4_wd_tl } }
  }
\cs_new:Npn \spb_boxwd_script_align:nn #1#2
  {
    \spb_boxwd_subtract:nnnn { 0.5 } { 0.5 } {#1} {#2}
    \use:c { spb_tmp_#2_use: }
    \spb_boxwd_subtract:nnnn { -0.5 } { 0.5 } {#1} {#2}
    \use:c { spb_tmp_#1_use: }
  }
\cs_set_eq:NN \spb@textsuperscript@save \textsuperscript
\cs_set_eq:NN \spb@textsubscript@save \textsubscript
\cs_set_eq:NN \spb@sp@save \sp
\cs_set_eq:NN \spb@sb@save \sb
\cs_new:Npn \spb_math:nn #1
  {
    \str_case:nn {#1}
      {
        { super } { \spb@sp@save }
        { sub } { \spb@sb@save }
      }
  }
\cs_new_protected:Npn \spb_box_ht_dp_assign:nn #1#2
  {
    \tl_if_empty:nF {#1} { \box_set_ht:Nn \l_tmpb_box {#1} }
    \tl_if_novalue:nF {#2} { \box_set_dp:Nn \l_tmpb_box {#2} }
  }
\clist_map_inline:nn
  { super,sub }
  {
    \exp_args:Nc \NewDocumentCommand {#1}
      {
        sO{\dim_use:c { l__spb_#1_script_vshift_dim }}
        >{\SplitArgument{1}{,}}O{}D(){}
        mO{\dim_use:c { l__spb_#1_script_hshift_dim }}
      }
      {
        \IfBooleanTF{##1}
          {
            \spb_tmp_hboxa_set:n
              { \ensuremath{##4{\spb_math:nn {#1} {##5}}} }
          }{
            \mode_if_math:TF
              {
                \spb_tmp_hboxa_set:n
                  { \ensuremath{##4{\spb_math:nn {#1} {##5}}} }
              }{
                \spb_tmp_hboxa_set:n
                  { ##4{\use:c { spb@text#1script@save }{##5}} }
              }
          }
        \hbox_set_to_wd:Nnn \l_tmpb_box { \l__spb_boxa_wd_tl + ##6 }
          { \hss\box_move_up:nn {##2} { \spb_tmp_boxa_use: } }
        \spb_box_ht_dp_assign:nn ##3
        \spb_tmp_boxb_use:
      }
  }
\NewDocumentCommand{\supersub}
  {
    sO{\l__spb_supersub_script_sep_dim}
    O{\l__spb_supersub_script_align_tl}D(){}mmO{0pt}D(){}
  }
  {
    \IfBooleanTF{#1}
      {
        \spb_tmp_hboxa_set:n { \ensuremath{#4{\spb@sp@save{#5}}} }
        \spb_tmp_hboxb_set:n  { \ensuremath{#8{\spb@sb@save{#6}}} }
      }{
        \mode_if_math:TF
          {
            \spb_tmp_hboxa_set:n { \ensuremath{#4{\spb@sp@save{#5}}} }
            \spb_tmp_hboxb_set:n  { \ensuremath{#8{\spb@sb@save{#6}}} }
          }
          {
            \spb_tmp_hboxa_set:n { #4{\spb@textsuperscript@save{#5}} }
            \spb_tmp_hboxb_set:n  { #8{\spb@textsubscript@save{#6}} }
          }
      }
    \hbox_set_to_wd:Nnn \l_tmpa_box { \l__spb_boxa_wd_tl + #7 }
      { \hss\box_move_up:nn {#2} { \spb_tmp_boxa_use: } }
    \hbox_set_to_wd:Nnn \l_tmpb_box { \l__spb_boxb_wd_tl + #7 }
      { \hss\box_move_down:nn {#2} { \spb_tmp_boxb_use: } }
    \str_case:onF {#3}
      {
        { l } { \use_i:nnn }
        { c } { \use_ii:nnn }
        { r } { \use_iii:nnn }
      }
      {
        \msg_warning:nn { spbmark } { unsupported-align }
        \use_i:nnn
      }
      {
        \hbox_overlap_right:n { \spb_tmp_boxa_use: }
        \hbox_overlap_right:n { \spb_tmp_boxb_use: }
        \spb_box_max_dim_horizontal:
      }
      {
        \dim_compare:nTF { \l__spb_boxa_wd_tl > \l__spb_boxb_wd_tl }
          { \spb_boxwd_script_align:nn { boxa } { boxb } }
          { \spb_boxwd_script_align:nn { boxb } { boxa } }
      }
      {
        \spb_box_max_dim_horizontal:
        \hbox_overlap_left:n { \spb_tmp_boxa_use: }
        \hbox_overlap_left:n { \spb_tmp_boxb_use: }
      }
  }
\msg_new:nnn { spbmark } { unsupported-align }
  {
    Alignment~only~supports~l,~c,~and~r. \\
    The~l~parameter~will~be~used~by~default.
  }

\bool_if:NT \l__spb_new_supersub_bool
  {
    \clist_map_inline:nn
      {
        { sp,sb },
        { textsuperscript,textsubscript }
      }
      {
        \cs_set_eq:cN { \clist_item:nn {#1} { 1 } } \super
        \cs_set_eq:cN { \clist_item:nn {#1} { 2 } } \sub
      }
  }
\cs_set_eq:NN \spb \supersub
\cs_set:Npn \@makefnmark
  { \hbox:n { \fnmarkfont\super{\@thefnmark}[0pt] } }
\cs_if_exist:NTF \ftntm@font
  { \cs_new:Npn \fnmarkfont { \ftntm@font } }
  { \cs_new:Npn \fnmarkfont { \normalfont } }
%</package>
\endinput