%%% Package   : fontscale -- A flexible interface for setting font sizes
%%% Copyright : 2024-2025 (c) Oliver Beery <beeryoliver@gmail.com>
%%% CTAN      : https://ctan.org/pkg/fontscale
%%% Repository: https://github.com/beeryoliver/fontscale
%%% License   : The LaTeX Project Public License 1.3c

%%> \section{Loading the package}

% LaTeX2e version 2023-11-01 added \IfExplAtLeastTF.
\NeedsTeXFormat{LaTeX2e}[2023-11-01]
\DeclareRelease{v3}{2025-02-13}{fontscale-v3.sty}
\DeclareCurrentRelease{}{2025-04-30}
\ProvidesExplPackage{fontscale}{2025-04-30}{4.2.0}
  {A flexible interface for setting font sizes}

% l3kernel version 2023-11-09 added documentation for \c_nan_fp.
\IfExplAtLeastTF { 2023-11-09 } { }
  {
    \msg_new:nnn { fontscale } { l3kernel-out-of-date }
      {
        The~ fontscale~ package~ could~ not~ load. \\
        This~ package~ requires~
        L3~ programming~ layer~ version~ 2023-11-09~ or~ newer.
      }
    \msg_critical:nn { fontscale } { l3kernel-out-of-date }
  }

% fontscale defines \scalefont.
\cs_new_protected:Npn \__fontscale_hook_scalefnt:
  {
    \msg_new:nnn { fontscale } { package-scalefnt }
      {
        The~ fontscale~ package~ is~ incompatible~ with~ the~ 'scalefnt'~
        package.
      }
    \msg_error:nn { fontscale } { package-scalefnt }
  }
\hook_gput_code:nnn { package / scalefnt / before } { fontscale }
  { \__fontscale_hook_scalefnt: }

%%> \section{Messages}

% Used in \fontscalesetup.
\msg_new:nnn { fontscale } { key-musical-base-invalid }
  {
    Invalid~ value~ '#1'~ \msg_line_context:. \\
    The~ value~ of~ the~ key~ 'musical / base'~ must~ be~ a~ positive~ length~
    and~ less~ than~ 2048pt.
  }
\msg_new:nnn { fontscale } { key-musical-ratio-invalid }
  {
    Invalid~ value~ '#1'~ \msg_line_context:. \\
    The~ value~ of~ the~ key~ 'musical / ratio'~ must~ be~ greater~ than~ 1.
  }
\msg_new:nnn { fontscale } { key-musical-notes-invalid }
  {
    Invalid~ value~ '#1'~ \msg_line_context:. \\
    The~ value~ of~ the~ key~ 'musical / notes'~ must~ be~ a~ positive~
    integer.
  }
\msg_new:nnn { fontscale } { key-magscale-invalid }
  {
    Invalid~ value~ '#1'~ \msg_line_context:. \\
    The~ value~ of~ the~ key~ 'magscale'~ must~ be~ positive.
  }
\msg_new:nnn { fontscale } { name-font-size-invalid }
  {
    Invalid~ font~ size~ of~ \iow_char:N \\#1~ '#2'~ \msg_line_context:. \\
    The~ font~ size~ must~ be~ a~ positive~ length~ and~ less~ than~ 2048pt.
  }
\msg_new:nnn { fontscale } { key-name-scale-overwritten }
  {
    The~ font~ size~ set~ by~ the~ key~ '#1 / scale'~
    has~ been~ overwritten~ by~ the~ key~ '#1 / size'~ \msg_line_context:.
  }
\msg_new:nnn { fontscale } { key-name-baselineskip-size-ratio-overwritten }
  {
    The~ font~ baselineskip~ set~ by~ the~ key~ '#1 / baselineskip-size-ratio'~
    has~ been~ overwritten~ by~ the~ key~ '#1 / baselineskip'~
    \msg_line_context:.
  }
\msg_new:nnn { fontscale } { font-sizes-wrong-order }
  {
    The~ font~ sizes~ are~ not~ in~ the~ correct~ order~ \msg_line_context:. \\
    The~ lengths~ of~ the~ font~ sizes~ should~ be~ ordered~ from~
    \iow_char:N \\tiny~ to~ \iow_char:N \\Huge.
  }

% This package tests for math mode only at the user level because this is not
% necessary for programmers. To avoid confusion, this package issues a warning
% when \fontsize + \selectfont would be used in math mode because it would not
% change the math font size.
\msg_new:nnn { fontscale } { math-mode-warning }
  { '#1'~ does~ nothing~ if~ used~ in~ math~ mode~ \msg_line_context:. }
% \@ and \par cannot be used in math mode.
\msg_new:nnn { fontscale } { math-mode-error }
  { '#1'~ cannot~ be~ used~ in~ math~ mode~ \msg_line_context:. }
\msg_new:nnn { fontscale } { font-size-invalid }
  {
    Invalid~ font~ size~ '#1'~ \msg_line_context:. \\
    The~ font~ size~ must~ be~ a~ positive~ length~ and~ less~ than~ 2048pt.
  }
\msg_new:nnn { fontscale } { font-step-invalid }
  {
    Invalid~ font~ step~ '#1'~ \msg_line_context:. \\
    The~ font~ step,~ rounded~ to~ 5~ decimal~ places,~
    must~ equal~ the~ font~ step~ of~
    any~ font~ size~ command~ from~ \iow_char:N \\tiny~ to~ \iow_char:N \\Huge~
    unless~ the~ value~ of~ the~ key~ 'typographic-scale'~ is~ 'musical'.
  }
\msg_new:nnn { fontscale } { current-font-step-invalid }
  {
    The~ new~ font~ step~ could~ not~ be~ calculated~
    because~ the~ current~ font~ step~ is~ undefined~ \msg_line_context:.
  }

%%> \section{Some variables}

% Declares and initializes the font step, font scale, font size, and font
% baselineskip of each font size command from \tiny to \Huge.

\fp_const:Nn \c_fontscale_tiny_step_fp         { -4 }
\fp_const:Nn \c_fontscale_scriptsize_step_fp   { -3 }
\fp_const:Nn \c_fontscale_footnotesize_step_fp { -2 }
\fp_const:Nn \c_fontscale_small_step_fp        { -1 }
\fp_const:Nn \c_fontscale_normalsize_step_fp   {  0 }
\fp_const:Nn \c_fontscale_large_step_fp        {  1 }
\fp_const:Nn \c_fontscale_Large_step_fp        {  2 }
\fp_const:Nn \c_fontscale_LARGE_step_fp        {  3 }
\fp_const:Nn \c_fontscale_huge_step_fp         {  4 }
\fp_const:Nn \c_fontscale_Huge_step_fp         {  5 }

\fp_const:Nn \c_fontscale_normalsize_scale_fp { 1 }

\fp_new:N \l_fontscale_tiny_scale_fp
\fp_new:N \l_fontscale_scriptsize_scale_fp
\fp_new:N \l_fontscale_footnotesize_scale_fp
\fp_new:N \l_fontscale_small_scale_fp
\fp_new:N \l_fontscale_large_scale_fp
\fp_new:N \l_fontscale_Large_scale_fp
\fp_new:N \l_fontscale_LARGE_scale_fp
\fp_new:N \l_fontscale_huge_scale_fp
\fp_new:N \l_fontscale_Huge_scale_fp

\dim_new:N \l_fontscale_tiny_size_dim
\dim_new:N \l_fontscale_scriptsize_size_dim
\dim_new:N \l_fontscale_footnotesize_size_dim
\dim_new:N \l_fontscale_small_size_dim
\dim_new:N \l_fontscale_normalsize_size_dim
\dim_new:N \l_fontscale_large_size_dim
\dim_new:N \l_fontscale_Large_size_dim
\dim_new:N \l_fontscale_LARGE_size_dim
\dim_new:N \l_fontscale_huge_size_dim
\dim_new:N \l_fontscale_Huge_size_dim

\skip_new:N \l_fontscale_tiny_baselineskip_skip
\skip_new:N \l_fontscale_scriptsize_baselineskip_skip
\skip_new:N \l_fontscale_footnotesize_baselineskip_skip
\skip_new:N \l_fontscale_small_baselineskip_skip
\skip_new:N \l_fontscale_normalsize_baselineskip_skip
\skip_new:N \l_fontscale_large_baselineskip_skip
\skip_new:N \l_fontscale_Large_baselineskip_skip
\skip_new:N \l_fontscale_LARGE_baselineskip_skip
\skip_new:N \l_fontscale_huge_baselineskip_skip
\skip_new:N \l_fontscale_Huge_baselineskip_skip

\fp_set:Nn \l_fontscale_tiny_scale_fp         { 0.6 }
\fp_set:Nn \l_fontscale_scriptsize_scale_fp   { 0.7 }
\fp_set:Nn \l_fontscale_footnotesize_scale_fp { 0.8 }
\fp_set:Nn \l_fontscale_small_scale_fp        { 0.9 }
\fp_set:Nn \l_fontscale_large_scale_fp        { 1.1 }
\fp_set:Nn \l_fontscale_Large_scale_fp        { 1.2 }
\fp_set:Nn \l_fontscale_LARGE_scale_fp        { 1.4 }
\fp_set:Nn \l_fontscale_huge_scale_fp         { 1.6 }
\fp_set:Nn \l_fontscale_Huge_scale_fp         { 1.8 }

\dim_set:Nn \l_fontscale_tiny_size_dim         {  6pt }
\dim_set:Nn \l_fontscale_scriptsize_size_dim   {  7pt }
\dim_set:Nn \l_fontscale_footnotesize_size_dim {  8pt }
\dim_set:Nn \l_fontscale_small_size_dim        {  9pt }
\dim_set:Nn \l_fontscale_normalsize_size_dim   { 10pt }
\dim_set:Nn \l_fontscale_large_size_dim        { 11pt }
\dim_set:Nn \l_fontscale_Large_size_dim        { 12pt }
\dim_set:Nn \l_fontscale_LARGE_size_dim        { 14pt }
\dim_set:Nn \l_fontscale_huge_size_dim         { 16pt }
\dim_set:Nn \l_fontscale_Huge_size_dim         { 18pt }

\skip_set:Nn \l_fontscale_tiny_baselineskip_skip         {  7.2pt }
\skip_set:Nn \l_fontscale_scriptsize_baselineskip_skip   {  8.4pt }
\skip_set:Nn \l_fontscale_footnotesize_baselineskip_skip {  9.6pt }
\skip_set:Nn \l_fontscale_small_baselineskip_skip        { 10.8pt }
\skip_set:Nn \l_fontscale_normalsize_baselineskip_skip   { 12  pt }
\skip_set:Nn \l_fontscale_large_baselineskip_skip        { 13.2pt }
\skip_set:Nn \l_fontscale_Large_baselineskip_skip        { 14.4pt }
\skip_set:Nn \l_fontscale_LARGE_baselineskip_skip        { 16.8pt }
\skip_set:Nn \l_fontscale_huge_baselineskip_skip         { 19.2pt }
\skip_set:Nn \l_fontscale_Huge_baselineskip_skip         { 21.6pt }

% Stores the current font step, font scale, font size, and font baselineskip.
% They are set in the selectfont hook.
\fp_new:N   \l_fontscale_step_fp
\fp_new:N   \l_fontscale_scale_fp
\dim_new:N  \l_fontscale_size_dim
\skip_new:N \l_fontscale_baselineskip_skip

% Some optimizations for \__fontscale_hook_selectfont:
% (1) Tries to avoid computing a floating point expression by using
% \fp_set_eq:NN when the font size equals the font size of any font size
% command from \tiny to \Huge.
% (2) Prefers the primitive \if_dim:w to \dim_compare:nNnTF or \dim_case:nnTF.
% (3) Tests the font sizes roughly in order of usage rate.
% (4) The case when the font size equals that of \normalsize is optimized
% further because it is by far the most common case.
% \f@size and \f@baselineskip are not used elsewhere in this package.
\cs_new_protected:Npn \__fontscale_hook_selectfont:
  {
    \dim_set:Nn \l_fontscale_size_dim { \f@size pt }
    \skip_set:Nn \l_fontscale_baselineskip_skip { \f@baselineskip }
    \if_dim:w \l_fontscale_size_dim = \l_fontscale_normalsize_size_dim
      \exp_after:wN \__fontscale_hook_selectfont_normalsize:
    \else:
      \exp_after:wN \__fontscale_hook_selectfont_auxi:
    \fi:
  }
\cs_new_protected:Npn \__fontscale_hook_selectfont_normalsize:
  {
    \fp_set_eq:NN \l_fontscale_step_fp  \c_fontscale_normalsize_step_fp
    \fp_set_eq:NN \l_fontscale_scale_fp \c_fontscale_normalsize_scale_fp
  }
\cs_new_protected:Npn \__fontscale_hook_selectfont_auxi:
  {
    \if_dim:w \l_fontscale_size_dim = \l_fontscale_small_size_dim
      \exp_after:wN \__fontscale_hook_selectfont_small:
    \fi:
    \if_dim:w \l_fontscale_size_dim = \l_fontscale_large_size_dim
      \exp_after:wN \__fontscale_hook_selectfont_large:
    \fi:
    \if_dim:w \l_fontscale_size_dim = \l_fontscale_footnotesize_size_dim
      \exp_after:wN \__fontscale_hook_selectfont_footnotesize:
    \fi:
    \if_dim:w \l_fontscale_size_dim = \l_fontscale_Large_size_dim
      \exp_after:wN \__fontscale_hook_selectfont_Large:
    \fi:
    \if_dim:w \l_fontscale_size_dim = \l_fontscale_scriptsize_size_dim
      \exp_after:wN \__fontscale_hook_selectfont_scriptsize:
    \fi:
    \if_dim:w \l_fontscale_size_dim = \l_fontscale_LARGE_size_dim
      \exp_after:wN \__fontscale_hook_selectfont_LARGE:
    \fi:
    \if_dim:w \l_fontscale_size_dim = \l_fontscale_tiny_size_dim
      \exp_after:wN \__fontscale_hook_selectfont_tiny:
    \fi:
    \if_dim:w \l_fontscale_size_dim = \l_fontscale_huge_size_dim
      \exp_after:wN \__fontscale_hook_selectfont_huge:
    \fi:
    \if_dim:w \l_fontscale_size_dim = \l_fontscale_Huge_size_dim
      \exp_after:wN \__fontscale_hook_selectfont_Huge:
    \fi:
    \__fontscale_hook_selectfont_auxii:
    \prg_break:
    \prg_break_point:
  }
\tl_map_inline:nn
  {
    {tiny} {scriptsize} {footnotesize} {small}
    {large} {Large} {LARGE} {huge} {Huge}
  }
  {
    \cs_new_protected:cpe { __fontscale_hook_selectfont_#1: }
      {
        \fp_set_eq:NN \exp_not:N \l_fontscale_step_fp
          \exp_not:c { c_fontscale_#1_step_fp }
        \fp_set_eq:NN \exp_not:N \l_fontscale_scale_fp
          \exp_not:c { l_fontscale_#1_scale_fp }
        \exp_not:N \prg_break:
      }
  }
\cs_new_protected:Npn \__fontscale_hook_selectfont_auxii:
  {
    \fp_set:Nn \l_fontscale_scale_fp
      {
        \dim_to_fp:n { \l_fontscale_size_dim } /
        \l__fontscale_normalsize_size_fp
      }
    \str_if_eq:VnTF \l__fontscale_typographic_scale_str { musical }
      {
        \fp_set:Nn \l_fontscale_step_fp
          {
            \l__fontscale_musical_selectfont_fp *
            ln
              (
                \dim_compare:nNnTF \l__fontscale_musical_base_dim =
                  \l_fontscale_normalsize_size_dim
                  { \l_fontscale_scale_fp }
                  {
                    \dim_to_fp:n { \l_fontscale_size_dim } /
                    \l__fontscale_musical_base_fp
                  }
              )
          }
      }
      { \fp_set_eq:NN \l_fontscale_step_fp \c_nan_fp }
  }
\hook_gput_code:nnn { selectfont } { fontscale }
  { \__fontscale_hook_selectfont: }

% Used only to speed up floating point computations.
\fp_new:N \l__fontscale_normalsize_size_fp
\fp_set:Nn \l__fontscale_normalsize_size_fp { 10 }
\fp_new:N \l__fontscale_musical_base_fp
\fp_new:N \l__fontscale_musical_notes_fp
\fp_new:N \l__fontscale_musical_selectfont_fp
\fp_const:Nn \c__fontscale_five_fp { 5 }

% Scratch variable
\fp_new:N \l__fontscale_tmp_fp

%%> \section{Some functions}

% Variants of l3kernel functions
\cs_generate_variant:Nn \msg_error:nnnn { nnnv }
% Similar to \fontsize + \selectfont, except that it:
% (1) Takes dimen and skip expressions as arguments without appending a default
% unit of 'pt'.
% (2) Avoids the issue where \f@size is set to the new font size before the
% second argument is expanded.
% (3) Issues a user-friendly error message if the font size is invalid.
\cs_new_protected:Npn \__fontscale_set_font_size:nn #1#2
  {
    \use:e
      {
        \__fontscale_set_font_size_aux:nn { \dim_eval:n {#1} }
          { \skip_eval:n {#2} }
      }
  }
\cs_new_protected:Npn \__fontscale_set_font_size_aux:nn #1#2
  {
    \dim_compare:nTF { \c_zero_dim < #1 < 2048pt }
      { \fontsize {#1} {#2} \selectfont }
      { \msg_error:nnn { fontscale } { font-size-invalid } {#1} }
  }
% Similar to \__fontscale_set_font_size:nn, except that it sets the font
% baselineskip equal to the new font size times
% \l__fontscale_baselineskip_size_ratio_fp.
\cs_new_protected:Npn \__fontscale_set_font_size:n #1
  { \exp_args:Ne \__fontscale_set_font_size_aux:n { \dim_eval:n {#1} } }
\cs_new_protected:Npn \__fontscale_set_font_size_aux:n #1
  {
    \dim_compare:nTF { \c_zero_dim < #1 < 2048pt }
      {
        \fontsize {#1}
          {
            \fp_to_dim:n
              { \dim_to_fp:n {#1} * \l__fontscale_baselineskip_size_ratio_fp }
          }
        \selectfont
      }
      { \msg_error:nnn { fontscale } { font-size-invalid } {#1} }
  }
\cs_new:Npn \__fontscale_fp_eval_round:n #1
  { \fp_eval:n { round ( #1 , \c__fontscale_five_fp ) } }

%%> \section{Define keys}

\str_new:N \l__fontscale_typographic_scale_str

\keys_define:nn { fontscale }
  {
      magscale .tl_set:N = \l__fontscale_magscale_tl
    , magscale .value_required:n = true

    , baselineskip-size-ratio .fp_set:N =
        \l__fontscale_baselineskip_size_ratio_fp
    , baselineskip-size-ratio .value_required:n = true

    , typographic-scale .choices:nn =
        {
            classic-10pt , classic-11pt , classic-12pt
          , classic-10bp , classic-11bp , classic-12bp
          , classic-10dd , classic-11dd , classic-12dd
          , classic-10nd , classic-11nd , classic-12nd , musical
        }
        { \str_set:Nn \l__fontscale_typographic_scale_str {#1} }
    , typographic-scale .value_required:n = true

    , classic-10pt .meta:n = { typographic-scale = classic-10pt }
    , classic-10pt .value_forbidden:n = true

    , classic-11pt .meta:n = { typographic-scale = classic-11pt }
    , classic-11pt .value_forbidden:n = true

    , classic-12pt .meta:n = { typographic-scale = classic-12pt }
    , classic-12pt .value_forbidden:n = true

    , classic-10bp .meta:n = { typographic-scale = classic-10bp }
    , classic-10bp .value_forbidden:n = true

    , classic-11bp .meta:n = { typographic-scale = classic-11bp }
    , classic-11bp .value_forbidden:n = true

    , classic-12bp .meta:n = { typographic-scale = classic-12bp }
    , classic-12bp .value_forbidden:n = true

    , classic-10dd .meta:n = { typographic-scale = classic-10dd }
    , classic-10dd .value_forbidden:n = true

    , classic-11dd .meta:n = { typographic-scale = classic-11dd }
    , classic-11dd .value_forbidden:n = true

    , classic-12dd .meta:n = { typographic-scale = classic-12dd }
    , classic-12dd .value_forbidden:n = true

    , classic-10nd .meta:n = { typographic-scale = classic-10nd }
    , classic-10nd .value_forbidden:n = true

    , classic-11nd .meta:n = { typographic-scale = classic-11nd }
    , classic-11nd .value_forbidden:n = true

    , classic-12nd .meta:n = { typographic-scale = classic-12nd }
    , classic-12nd .value_forbidden:n = true

    , musical .meta:n = { typographic-scale = musical }
    , musical .value_forbidden:n = true
  }
\keys_define:nn { fontscale / musical }
  {
      base .dim_set:N = \l__fontscale_musical_base_dim
    , base .value_required:n = true

    , ratio .fp_set:N = \l__fontscale_musical_ratio_fp
    , ratio .value_required:n = true

    , notes .int_set:N = \l__fontscale_musical_notes_int
    , notes .value_required:n = true
  }
\keys_define:nn { fontscale / normalsize }
  {
      size .tl_set:N = \l__fontscale_normalsize_size_tl
    , size .value_required:n = true

    , baselineskip-size-ratio .tl_set:N =
        \l__fontscale_normalsize_baselineskip_size_ratio_tl
    , baselineskip-size-ratio .value_required:n = true

    , baselineskip .tl_set:N = \l__fontscale_normalsize_baselineskip_tl
    , baselineskip .value_required:n = true
  }
\tl_map_inline:nn
  {
    {tiny} {scriptsize} {footnotesize} {small}
    {large} {Large} {LARGE} {huge} {Huge}
  }
  {
    \keys_define:nn { fontscale / #1 }
      {
          scale .tl_set:c = { l__fontscale_#1_scale_tl }
        , scale .value_required:n = true

        , size .tl_set:c = { l__fontscale_#1_size_tl }
        , size .value_required:n = true

        , baselineskip-size-ratio .tl_set:c =
            { l__fontscale_#1_baselineskip_size_ratio_tl }
        , baselineskip-size-ratio .value_required:n = true

        , baselineskip .tl_set:c = { l__fontscale_#1_baselineskip_tl }
        , baselineskip .value_required:n = true
      }
  }
\keys_define:nn { fontscale }
  {
      tiny .tl_set:N = \l__fontscale_tiny_size_tl
    , tiny .value_required:n = true

    , scriptsize .tl_set:N = \l__fontscale_scriptsize_size_tl
    , scriptsize .value_required:n = true

    , footnotesize .tl_set:N = \l__fontscale_footnotesize_size_tl
    , footnotesize .value_required:n = true

    , small .tl_set:N = \l__fontscale_small_size_tl
    , small .value_required:n = true

    , normalsize .tl_set:N = \l__fontscale_normalsize_size_tl
    , normalsize .value_required:n = true

    , large .tl_set:N = \l__fontscale_large_size_tl
    , large .value_required:n = true

    , Large .tl_set:N = \l__fontscale_Large_size_tl
    , Large .value_required:n = true

    , LARGE .tl_set:N = \l__fontscale_LARGE_size_tl
    , LARGE .value_required:n = true

    , huge .tl_set:N = \l__fontscale_huge_size_tl
    , huge .value_required:n = true

    , Huge .tl_set:N = \l__fontscale_Huge_size_tl
    , Huge .value_required:n = true
  }

%%> \section{Initialize and pre-compile keys}

% Pre-compiles the keys with their initial values and then sets the keys to
% their initial values. This is significantly faster than using .initial:n and
% \keys_precompile:nnN. The special value of \q_no_value is used to test if the
% key has been set by the user.
\cs_new_protected:Npn \__fontscale_keys_set_initial:
  {
    \str_set:Nn \l__fontscale_typographic_scale_str      { classic-10pt }
    \dim_set:Nn \l__fontscale_musical_base_dim           { 10pt }
    \fp_set:Nn  \l__fontscale_musical_ratio_fp           { 2 }
    \int_set:Nn \l__fontscale_musical_notes_int          { 5 }
    \fp_set:Nn  \l__fontscale_baselineskip_size_ratio_fp { 1.2 }

    \tl_map_inline:nn
      {
        \l__fontscale_tiny_scale_tl
        \l__fontscale_scriptsize_scale_tl
        \l__fontscale_footnotesize_scale_tl
        \l__fontscale_small_scale_tl
        \l__fontscale_large_scale_tl
        \l__fontscale_Large_scale_tl
        \l__fontscale_LARGE_scale_tl
        \l__fontscale_huge_scale_tl
        \l__fontscale_Huge_scale_tl

        \l__fontscale_tiny_size_tl
        \l__fontscale_scriptsize_size_tl
        \l__fontscale_footnotesize_size_tl
        \l__fontscale_small_size_tl
        \l__fontscale_normalsize_size_tl
        \l__fontscale_large_size_tl
        \l__fontscale_Large_size_tl
        \l__fontscale_LARGE_size_tl
        \l__fontscale_huge_size_tl
        \l__fontscale_Huge_size_tl

        \l__fontscale_tiny_baselineskip_size_ratio_tl
        \l__fontscale_scriptsize_baselineskip_size_ratio_tl
        \l__fontscale_footnotesize_baselineskip_size_ratio_tl
        \l__fontscale_small_baselineskip_size_ratio_tl
        \l__fontscale_normalsize_baselineskip_size_ratio_tl
        \l__fontscale_large_baselineskip_size_ratio_tl
        \l__fontscale_Large_baselineskip_size_ratio_tl
        \l__fontscale_LARGE_baselineskip_size_ratio_tl
        \l__fontscale_huge_baselineskip_size_ratio_tl
        \l__fontscale_Huge_baselineskip_size_ratio_tl

        \l__fontscale_tiny_baselineskip_tl
        \l__fontscale_scriptsize_baselineskip_tl
        \l__fontscale_footnotesize_baselineskip_tl
        \l__fontscale_small_baselineskip_tl
        \l__fontscale_normalsize_baselineskip_tl
        \l__fontscale_large_baselineskip_tl
        \l__fontscale_Large_baselineskip_tl
        \l__fontscale_LARGE_baselineskip_tl
        \l__fontscale_huge_baselineskip_tl
        \l__fontscale_Huge_baselineskip_tl

        \l__fontscale_magscale_tl
      }
      { \tl_set:Nn ##1 { \q_no_value } }
  }
\__fontscale_keys_set_initial:

%%> \section{Set and process keys}

\NewDocumentCommand \fontscalesetup { s m }
  {
    \mode_if_math:TF
      {
        \msg_warning:nne { fontscale } { math-mode-warning }
          { \token_to_str:N \fontscalesetup }
      }
      {
        \IfBooleanTF #1
          { \__fontscale_keys_reset:n {#2} }
          { \__fontscale_keys_set:n   {#2} }
      }
  }
\cs_new_protected:Npn \__fontscale_keys_reset:n
  { \__fontscale_keys_set_initial: \__fontscale_keys_set:n }
\cs_new_protected:Npn \__fontscale_keys_set:n #1
  {
    \keys_set:nn { fontscale } {#1}
    \__fontscale_keys_process:
    \normalsize
    \prg_break:
    \prg_break_point:
  }
\cs_new_protected:Npn \__fontscale_keys_process:
  {
    \__fontscale_keys_process_start:
    \__fontscale_keys_process_sizes_normalsize:
    \__fontscale_keys_process_sizes_other:
    \__fontscale_keys_process_sizes_check_order:
    \__fontscale_keys_process_baselineskips:
  }
\cs_new_protected:Npn \__fontscale_keys_process_start:
  {
    \dim_compare:nF { \c_zero_dim < \l__fontscale_musical_base_dim < 2048pt }
      {
        \msg_error:nnV { fontscale } { key-musical-base-invalid }
          \l__fontscale_musical_base_dim
        \prg_break:
      }
    \fp_compare:nNnF \l__fontscale_musical_ratio_fp > \c_one_fp
      {
        \msg_error:nne { fontscale } { key-musical-ratio-invalid }
          { \fp_use:N \l__fontscale_musical_ratio_fp }
        \prg_break:
      }
    \int_compare:nNnF \l__fontscale_musical_notes_int > 0
      {
        \msg_error:nnV { fontscale } { key-musical-notes-invalid }
          \l__fontscale_musical_notes_int
        \prg_break:
      }
    \str_if_eq:VnT \l__fontscale_typographic_scale_str { musical }
      {
        \fp_set:Nn \l__fontscale_musical_base_fp
          { \dim_to_fp:n { \l__fontscale_musical_base_dim } }
        \fp_set:Nn \l__fontscale_musical_notes_fp
          { \int_use:N \l__fontscale_musical_notes_int }
        \fp_set:Nn \l__fontscale_musical_selectfont_fp
          {
            \l__fontscale_musical_notes_fp /
            ln ( \l__fontscale_musical_ratio_fp )
          }
      }
    \quark_if_no_value:NF \l__fontscale_magscale_tl
      {
        \fp_set:Nn \l__fontscale_tmp_fp { \l__fontscale_magscale_tl }
        \tl_set:NV \l__fontscale_magscale_tl \l__fontscale_tmp_fp
        \fp_compare:nNnF \l__fontscale_tmp_fp > \c_zero_fp
          {
            \msg_error:nne { fontscale } { key-magscale-invalid }
              { \fp_use:N \l__fontscale_tmp_fp }
            \prg_break:
          }
      }
  }
\cs_new_protected:Npn \__fontscale_keys_process_sizes_normalsize:
  {
    \quark_if_no_value:NTF \l__fontscale_normalsize_size_tl
      {
        \dim_set:Nn \l_fontscale_normalsize_size_dim
          {
            \str_case:Vn \l__fontscale_typographic_scale_str
              {
                { classic-10pt } { 10pt }
                { classic-11pt } { 11pt }
                { classic-12pt } { 12pt }
                { classic-10bp } { 10bp }
                { classic-11bp } { 11bp }
                { classic-12bp } { 12bp }
                { classic-10dd } { 10dd }
                { classic-11dd } { 11dd }
                { classic-12dd } { 12dd }
                { classic-10nd } { 10nd }
                { classic-11nd } { 11nd }
                { classic-12nd } { 12nd }
                { musical } { \l__fontscale_musical_base_dim }
              }
          }
      }
      {
        \dim_set:Nn \l_fontscale_normalsize_size_dim
          { \l__fontscale_normalsize_size_tl }
        \tl_set:NV \l__fontscale_normalsize_size_tl
          \l_fontscale_normalsize_size_dim
      }
    \quark_if_no_value:NF \l__fontscale_magscale_tl
      {
        \dim_set:Nn \l_fontscale_normalsize_size_dim
          {
            \fp_to_dim:n
              {
                \dim_to_fp:n { \l_fontscale_normalsize_size_dim } *
                \l__fontscale_magscale_tl
              }
          }
      }
    \dim_compare:nF
      { \c_zero_dim < \l_fontscale_normalsize_size_dim < 2048pt }
      {
        \msg_error:nnnV { fontscale } { name-font-size-invalid }
          { normalsize } \l_fontscale_normalsize_size_dim
        \prg_break:
      }
    \fp_set:Nn \l__fontscale_normalsize_size_fp
      { \dim_to_fp:n { \l_fontscale_normalsize_size_dim } }
  }
\cs_new_protected:Npn \__fontscale_keys_process_sizes_other:
  {
    \cs_set_eq:Nc \__fontscale_keys_process_sizes_other_aux:n
      {
        __fontscale_keys_process_sizes_other_
        \l__fontscale_typographic_scale_str :n
      }
    \tl_map_function:nN
      {
        {tiny} {scriptsize} {footnotesize} {small}
        {large} {Large} {LARGE} {huge} {Huge}
      }
      \__fontscale_keys_process_sizes_other:n
  }
\tl_map_inline:nn { {pt} {bp} {dd} {nd} }
  {
    \cs_new_protected:cpn
      { __fontscale_keys_process_sizes_other_classic-10#1:n } ##1
      {
        \dim_set:cn { l_fontscale_##1_size_dim }
          {
            \str_case:nn {##1}
              {
                { tiny         } {  6 }
                { scriptsize   } {  7 }
                { footnotesize } {  8 }
                { small        } {  9 }
                { large        } { 11 }
                { Large        } { 12 }
                { LARGE        } { 14 }
                { huge         } { 16 }
                { Huge         } { 18 }
              }
            #1
          }
      }
    \cs_new_protected:cpn
      { __fontscale_keys_process_sizes_other_classic-11#1:n } ##1
      {
        \dim_set:cn { l_fontscale_##1_size_dim }
          {
            \str_case:nn {##1}
              {
                { tiny         } {  7 }
                { scriptsize   } {  8 }
                { footnotesize } {  9 }
                { small        } { 10 }
                { large        } { 12 }
                { Large        } { 14 }
                { LARGE        } { 16 }
                { huge         } { 18 }
                { Huge         } { 21 }
              }
            #1
          }
      }
    \cs_new_protected:cpn
      { __fontscale_keys_process_sizes_other_classic-12#1:n } ##1
      {
        \dim_set:cn { l_fontscale_##1_size_dim }
          {
            \str_case:nn {##1}
              {
                { tiny         } {  8 }
                { scriptsize   } {  9 }
                { footnotesize } { 10 }
                { small        } { 11 }
                { large        } { 14 }
                { Large        } { 16 }
                { LARGE        } { 18 }
                { huge         } { 21 }
                { Huge         } { 24 }
              }
            #1
          }
      }
  }
\cs_new_protected:Npn \__fontscale_keys_process_sizes_other_musical:n #1
  {
    \dim_set:cn { l_fontscale_#1_size_dim }
      {
        \fp_to_dim:n
          {
            \l__fontscale_musical_base_fp * \l__fontscale_musical_ratio_fp ^
            (
              \use:c { c_fontscale_#1_step_fp } /
              \l__fontscale_musical_notes_fp
            )
          }
      }
  }
\cs_new_protected:Npn \__fontscale_keys_process_sizes_other:n #1
  {
    \quark_if_no_value:cF { l__fontscale_#1_scale_tl }
      {
        \fp_set:Nn \l__fontscale_tmp_fp { \use:c { l__fontscale_#1_scale_tl } }
        \tl_set:cV { l__fontscale_#1_scale_tl } \l__fontscale_tmp_fp
        \dim_set:cn { l_fontscale_#1_size_dim }
          {
            \fp_to_dim:n
              { \l__fontscale_normalsize_size_fp * \l__fontscale_tmp_fp }
          }
        \quark_if_no_value:cT { l__fontscale_#1_size_tl } { \prg_break: }
      }
    \quark_if_no_value:cF { l__fontscale_#1_size_tl }
      {
        \dim_set:cn { l_fontscale_#1_size_dim }
          { \use:c { l__fontscale_#1_size_tl } }
        \tl_set:cv { l__fontscale_#1_size_tl } { l_fontscale_#1_size_dim }
        \quark_if_no_value:cF { l__fontscale_#1_scale_tl }
          {
            \msg_warning:nnn { fontscale } { key-name-scale-overwritten } {#1}
          }
        \quark_if_no_value:NF \l__fontscale_magscale_tl
          {
            \dim_set:cn { l_fontscale_#1_size_dim }
              {
                \fp_to_dim:n
                  {
                    \dim_to_fp:n { \use:c { l_fontscale_#1_size_dim } } *
                    \l__fontscale_magscale_tl
                  }
              }
          }
        \prg_break:
      }
    \__fontscale_keys_process_sizes_other_aux:n {#1}
    \quark_if_no_value:NF \l__fontscale_magscale_tl
      {
        \dim_set:cn { l_fontscale_#1_size_dim }
          {
            \fp_to_dim:n
              {
                \dim_to_fp:n { \use:c { l_fontscale_#1_size_dim } } *
                \l__fontscale_magscale_tl
              }
          }
      }
    \prg_break:
    \prg_break_point:
    \dim_compare:nF
      { \c_zero_dim < \use:c { l_fontscale_#1_size_dim } < 2048pt }
      {
        \tl_map_break:n
          {
            \msg_error:nnnv { fontscale } { name-font-size-invalid } {#1}
              { l_fontscale_#1_size_dim }
            \prg_break:
          }
      }
    \fp_set:cn { l_fontscale_#1_scale_fp }
      {
        \dim_to_fp:n { \use:c { l_fontscale_#1_size_dim } } /
        \l__fontscale_normalsize_size_fp
      }
  }
\cs_new_protected:Npn \__fontscale_keys_process_sizes_check_order:
  {
    \dim_compare:nF
      {
          \l_fontscale_tiny_size_dim
        < \l_fontscale_scriptsize_size_dim
        < \l_fontscale_footnotesize_size_dim
        < \l_fontscale_small_size_dim
        < \l_fontscale_normalsize_size_dim
        < \l_fontscale_large_size_dim
        < \l_fontscale_Large_size_dim
        < \l_fontscale_LARGE_size_dim
        < \l_fontscale_huge_size_dim
        < \l_fontscale_Huge_size_dim
      }
      { \msg_warning:nn { fontscale } { font-sizes-wrong-order } }
  }
\cs_new_protected:Npn \__fontscale_keys_process_baselineskips:
  {
    \tl_map_function:nN
      {
        {tiny} {scriptsize} {footnotesize} {small} {normalsize}
        {large} {Large} {LARGE} {huge} {Huge}
      }
      \__fontscale_keys_process_baselineskips:n
  }
\cs_new_protected:Npn \__fontscale_keys_process_baselineskips:n #1
  {
    \quark_if_no_value:cF { l__fontscale_#1_baselineskip_size_ratio_tl }
      {
        \fp_set:Nn \l__fontscale_tmp_fp
          { \use:c { l__fontscale_#1_baselineskip_size_ratio_tl } }
        \tl_set:cV { l__fontscale_#1_baselineskip_size_ratio_tl }
          \l__fontscale_tmp_fp
        \skip_set:cn { l_fontscale_#1_baselineskip_skip }
          {
            \fp_to_dim:n
              {
                \dim_to_fp:n { \use:c { l_fontscale_#1_size_dim } } *
                \l__fontscale_tmp_fp
              }
          }
        \quark_if_no_value:cT { l__fontscale_#1_baselineskip_tl }
          { \prg_break: }
      }
    \quark_if_no_value:cF { l__fontscale_#1_baselineskip_tl }
      {
        \skip_set:cn { l_fontscale_#1_baselineskip_skip }
          { \use:c { l__fontscale_#1_baselineskip_tl } }
        \tl_set:cv { l__fontscale_#1_baselineskip_tl }
          { l_fontscale_#1_baselineskip_skip }
        \quark_if_no_value:cF { l__fontscale_#1_baselineskip_size_ratio_tl }
          {
            \msg_warning:nnn { fontscale }
              { key-name-baselineskip-size-ratio-overwritten } {#1}
          }
        \quark_if_no_value:NF \l__fontscale_magscale_tl
          {
            \skip_set:cn { l_fontscale_#1_baselineskip_skip }
              {
                \fp_to_dim:n
                  {
                    \dim_to_fp:n
                      { \use:c { l_fontscale_#1_baselineskip_skip } }
                    * \l__fontscale_magscale_tl
                  }
              }
          }
        \prg_break:
      }
    \skip_set:cn { l_fontscale_#1_baselineskip_skip }
      {
        \fp_to_dim:n
          {
            \dim_to_fp:n { \use:c { l_fontscale_#1_size_dim } } *
            \l__fontscale_baselineskip_size_ratio_fp
          }
      }
    \prg_break:
    \prg_break_point:
  }

%%> \section{Document commands}

% Defines the standard LaTeX font size commands from \tiny to \Huge and
% initializes to \normalsize. Sets \@currsize only for compatibility. The
% internal functions are not used elsewhere for compatibility with hooks such
% as \AddToHook{cmd/<font size command>/after}{<code>}.
\tl_map_inline:nn
  {
    {tiny} {scriptsize} {footnotesize} {small} {normalsize}
    {large} {Large} {LARGE} {huge} {Huge}
  }
  {
    \use:e
      {
        \DeclareDocumentCommand \exp_not:c {#1} { }
          {
            \exp_not:N \mode_if_math:TF
              {
                \msg_warning:nnn { fontscale } { math-mode-warning }
                  { \token_to_str:c {#1} }
              }
              { \exp_not:c { __fontscale_#1: } }
          }
      }
    \cs_new_protected:cpe { __fontscale_#1: }
      {
        \cs_set_eq:NN \exp_not:N \@currsize \exp_not:c {#1}
        \exp_not:N \fontsize
          { \exp_not:N \dim_use:N \exp_not:c { l_fontscale_#1_size_dim } }
          {
            \exp_not:N \skip_use:N
            \exp_not:c { l_fontscale_#1_baselineskip_skip }
          }
        \exp_not:N \selectfont
      }
  }
\normalsize

\NewExpandableDocumentCommand \currentnormalsize { }
  { \__fontscale_normalsize_size: }
\cs_new:Npn \__fontscale_normalsize_size:
  { \dim_use:N \l_fontscale_normalsize_size_dim }

\NewExpandableDocumentCommand \currentfontstep { } { \__fontscale_step: }
\cs_new:Npn \__fontscale_step:
  { \fp_if_nan:nF { \l_fontscale_step_fp } { \fp_use:N \l_fontscale_step_fp } }

\NewExpandableDocumentCommand \currentfontscale { } { \__fontscale_scale: }
\cs_new:Npn \__fontscale_scale: { \fp_use:N \l_fontscale_scale_fp }

\NewExpandableDocumentCommand \currentfontsize { } { \__fontscale_size: }
\cs_new:Npn \__fontscale_size: { \dim_use:N \l_fontscale_size_dim }

\NewExpandableDocumentCommand \currentfontbaselineskip { }
  { \__fontscale_baselineskip: }
\cs_new:Npn \__fontscale_baselineskip:
  { \skip_use:N \l_fontscale_baselineskip_skip }

\NewDocumentCommand \setfontstep { s m }
  {
    \mode_if_math:TF
      {
        \msg_warning:nne { fontscale } { math-mode-warning }
          { \token_to_str:N \setfontstep }
      }
      {
        \IfBooleanTF #1
          { \__fontscale_add_font_step:n {#2} }
          { \__fontscale_set_font_step:n {#2} }
      }
  }
\cs_new_protected:Npn \__fontscale_add_font_step:n #1
  {
    \fp_if_nan:nTF { \l_fontscale_step_fp }
      { \msg_error:nn { fontscale } { current-font-step-invalid } }
      { \__fontscale_set_font_step:n { (#1) + \l_fontscale_step_fp } }
  }
\cs_new_protected:Npn \__fontscale_set_font_step:n #1
  {
    \fp_set:Nn \l__fontscale_tmp_fp {#1}
    \str_case:enF { \__fontscale_fp_eval_round:n { \l__fontscale_tmp_fp } }
      {
        { -4 } { \tiny         }
        { -3 } { \scriptsize   }
        { -2 } { \footnotesize }
        { -1 } { \small        }
        {  0 } { \normalsize   }
        {  1 } { \large        }
        {  2 } { \Large        }
        {  3 } { \LARGE        }
        {  4 } { \huge         }
        {  5 } { \Huge         }
      }
      {
        \str_if_eq:VnTF \l__fontscale_typographic_scale_str { musical }
          {
            \__fontscale_set_font_size:n
              {
                \fp_to_dim:n
                  {
                    \l__fontscale_musical_base_fp *
                    \l__fontscale_musical_ratio_fp ^
                    ( \l__fontscale_tmp_fp / \l__fontscale_musical_notes_fp )
                  }
              }
          }
          {
            \msg_error:nne { fontscale } { font-step-invalid }
              { \fp_use:N \l__fontscale_tmp_fp }
          }
      }
  }

\NewDocumentCommand \setfontscale { s m }
  {
    \mode_if_math:TF
      {
        \msg_warning:nne { fontscale } { math-mode-warning }
          { \token_to_str:N \setfontscale }
      }
      {
        \IfBooleanTF #1
          { \__fontscale_add_font_scale:n {#2} }
          { \__fontscale_set_font_scale:n {#2} }
      }
  }
\cs_new_protected:Npn \__fontscale_add_font_scale:n #1
  { \__fontscale_set_font_scale:n { (#1) + \l_fontscale_scale_fp } }
\cs_new_protected:Npn \__fontscale_set_font_scale:n #1
  {
    \__fontscale_set_font_size:n
      { \fp_to_dim:n { (#1) * \l__fontscale_normalsize_size_fp } }
  }

\NewDocumentCommand \setfontsize { s m }
  {
    \mode_if_math:TF
      {
        \msg_warning:nne { fontscale } { math-mode-warning }
          { \token_to_str:N \setfontsize }
      }
      {
        \IfBooleanTF #1
          { \__fontscale_add_font_size:n {#2} }
          { \__fontscale_set_font_size:n {#2} }
      }
  }
\cs_new_protected:Npn \__fontscale_add_font_size:n #1
  { \__fontscale_set_font_size:n { (#1) + \l_fontscale_size_dim } }

\NewDocumentCommand \scalefont { m }
  {
    \mode_if_math:TF
      {
        \msg_warning:nne { fontscale } { math-mode-warning }
          { \token_to_str:N \scalefont }
      }
      { \__fontscale_scale_font:n {#1} }
  }
\cs_new_protected:Npn \__fontscale_scale_font:n #1
  {
    \fp_set:Nn \l__fontscale_tmp_fp {#1}
    \__fontscale_set_font_size:nn
      {
        \fp_to_dim:n
          { \l__fontscale_tmp_fp * \dim_to_fp:n { \l_fontscale_size_dim } }
      }
      {
        \fp_to_dim:n
          {
            \l__fontscale_tmp_fp *
            \dim_to_fp:n { \l_fontscale_baselineskip_skip }
          }
      }
  }

\NewDocumentCommand \setfontsizebaselineskip { m m }
  {
    \mode_if_math:TF
      {
        \msg_warning:nne { fontscale } { math-mode-warning }
          { \token_to_str:N \setfontsizebaselineskip }
      }
      { \__fontscale_set_font_size:nn {#1} {#2} }
  }

\NewDocumentCommand \setfontbaselineskip { s m }
  {
    \mode_if_math:TF
      {
        \msg_warning:nne { fontscale } { math-mode-warning }
          { \token_to_str:N \setfontbaselineskip }
      }
      {
        \IfBooleanTF #1
          { \__fontscale_add_font_baselineskip:n {#2} }
          { \__fontscale_set_font_baselineskip:n {#2} }
      }
  }
\cs_new_protected:Npn \__fontscale_add_font_baselineskip:n #1
  {
    \__fontscale_set_font_baselineskip:n
      { (#1) + \l_fontscale_baselineskip_skip }
  }
\cs_new_protected:Npn \__fontscale_set_font_baselineskip:n
  { \__fontscale_set_font_size:nn { \l_fontscale_size_dim } }

\NewDocumentCommand \printfontsizeparameters { }
  { \__fontscale_print_font_size_parameters: }
\cs_new_protected:Npn \__fontscale_print_font_size_parameters:
  {
    step~ =~
    \fp_if_nan:nF { \l_fontscale_step_fp }
      { \__fontscale_fp_eval_round:n { \l_fontscale_step_fp } }
    ,~
    scale~ =~ \__fontscale_fp_eval_round:n { \l_fontscale_scale_fp } ,~
    size~ =~ \dim_use:N \l_fontscale_size_dim ,~
    baselineskip~ =~ \skip_use:N \l_fontscale_baselineskip_skip
  }

\NewDocumentCommand \printallfontsizeparameters { }
  {
    \mode_if_math:TF
      {
        \msg_error:nne { fontscale } { math-mode-error }
          { \token_to_str:N \printallfontsizeparameters }
      }
      { \__fontscale_print_all_font_size_parameters: }
  }
\cs_new_protected:Npn \__fontscale_print_all_font_size_parameters:
  {
    \tl_map_inline:nn
      {
        {tiny} {scriptsize} {footnotesize} {small} {normalsize}
        {large} {Large} {LARGE} {huge} {Huge}
      }
      {
        \token_to_str:c {##1} \@ \c_colon_str \c_space_tl
        step~ =~ \fp_use:c { c_fontscale_##1_step_fp } ,~
        scale~ =~
        \str_if_eq:nnTF {##1} { normalsize } { 1 }
          {
            \__fontscale_fp_eval_round:n
              { \use:c { l_fontscale_##1_scale_fp } }
          }
        ,~
        size~ =~ \dim_use:c { l_fontscale_##1_size_dim } ,~
        baselineskip~ =~ \skip_use:c { l_fontscale_##1_baselineskip_skip }
        \str_if_eq:nnF {##1} { Huge } { \newline }
      }
  }

\NewDocumentCommand \printsampletext { >{ \ReverseBoolean } s +m }
  {
    \mode_if_math:TF
      {
        \msg_error:nne { fontscale } { math-mode-error }
          { \token_to_str:N \printsampletext }
      }
      {
        \IfBooleanTF #1
          { \__fontscale_print_sample_text_ascending_order:n  {#2} }
          { \__fontscale_print_sample_text_descending_order:n {#2} }
      }
  }
\cs_new_protected:Npn \__fontscale_print_sample_text_ascending_order:n #1
  {
    \group_begin:
      \tl_map_inline:nn
        {
          \tiny \scriptsize \footnotesize \small \normalsize
          \large \Large \LARGE \huge \Huge
        }
        { ##1 #1 \par }
    \group_end:
  }
\cs_new_protected:Npn \__fontscale_print_sample_text_descending_order:n #1
  {
    \group_begin:
      \tl_map_inline:nn
        {
          \Huge \huge \LARGE \Large \large
          \normalsize \small \footnotesize \scriptsize \tiny
        }
        { ##1 #1 \par }
    \group_end:
  }

\NewDocumentCommand \printfontsizecommand { }
  {
    \mode_if_math:TF
      {
        \msg_error:nne { fontscale } { math-mode-error }
          { \token_to_str:N \printfontsizecommand }
      }
      { \__fontscale_print_name: }
  }
\cs_new_protected:Npn \__fontscale_print_name:
  {
    \token_to_str:c
      {
        \dim_case:nnF { \l_fontscale_size_dim }
          {
            { \l_fontscale_tiny_size_dim         } { tiny         }
            { \l_fontscale_scriptsize_size_dim   } { scriptsize   }
            { \l_fontscale_footnotesize_size_dim } { footnotesize }
            { \l_fontscale_small_size_dim        } { small        }
            { \l_fontscale_normalsize_size_dim   } { normalsize   }
            { \l_fontscale_large_size_dim        } { large        }
            { \l_fontscale_Large_size_dim        } { Large        }
            { \l_fontscale_LARGE_size_dim        } { LARGE        }
            { \l_fontscale_huge_size_dim         } { huge         }
            { \l_fontscale_Huge_size_dim         } { Huge         }
          }
          { undefined }
      }
    \@
  }

%%> \section{Purifying text}

\NewExpandableDocumentCommand \__fontscale_use_none_sm:w { s m } { }
\tl_map_inline:nn
  {
    \fontscalesetup
    \setfontstep
    \setfontscale
    \setfontsize
    \setfontbaselineskip
  }
  { \text_declare_purify_equivalent:Nn #1 { \__fontscale_use_none_sm:w } }
\text_declare_purify_equivalent:Nn \scalefont { \use_none:n }
\text_declare_purify_equivalent:Nn \setfontsizebaselineskip { \use_none:nn }