%%^^A%%  fontspec-code-keyval.dtx -- part of FONTSPEC <latex3.github.io/fontspec>

% \section{Font loading (\pkg{keyval}) definitions}
%
% \iffalse
%    \begin{macrocode}
%<*fontspec>
%    \end{macrocode}
% \fi
%
% This package uses a large number of keyval modules which operate sequentially on keyval
% input to ensure priority.
%    \begin{macrocode}
\clist_gset:Nn \g_@@_all_keyval_modules_clist
  {
    fontspec, fontspec-opentype, fontspec-aat,
    fontspec-preparse, fontspec-preparse-cfg, fontspec-preparse-external, fontspec-preparse-nested,
    fontspec-renderer
  }
%    \end{macrocode}
%
% Wrapper function to save some characters in the source:
%    \begin{macrocode}
\cs_new:Nn \@@_keys_define_code:nnn
  {
    \keys_define:nn {#1} { #2 .code:n = {#3} }
  }
%    \end{macrocode}
%
% For catching features that cannot be used in |\addfontfeatures|:
%    \begin{macrocode}
\cs_new:Nn \@@_aff_error:n
  {
    \@@_keys_define_code:nnn {fontspec-addfeatures} {#1}
      { \@@_error:nx {not-in-addfontfeatures} {#1} }
  }
%    \end{macrocode}
%
% \subsection{Pre-pre-parsing stages}
%
% These features are extracted from the font feature list before all others.
%
% \paragraph{Don't load font config file}
%    \begin{macrocode}
\@@_keys_define_code:nnn {fontspec-preparse-cfg} {IgnoreFontspecFile}
  {
    \bool_set_false:N \l_@@_fontcfg_bool
  }
\@@_keys_define_code:nnn {fontspec-preparse-external} {IgnoreFontspecFile}
  {
    \bool_set_false:N \l_@@_fontcfg_bool
  }
%    \end{macrocode}
%
% \begin{macro}{Path}
% For fonts that aren't installed in the system. If no argument is given, the font is located
% with |kpsewhich|; it's either in the current directory or the \TeX\ tree. Otherwise, the
% argument given defines the file path of the font.
%    \begin{macrocode}
\@@_keys_define_code:nnn {fontspec-preparse-external} {Path}
  {
    \bool_set_true:N \l_@@_nobf_bool
    \bool_set_true:N \l_@@_noit_bool
    \tl_set:Nn \l_@@_font_path_tl {#1}
    \@@_font_is_file:
%<*XE>
    \keys_set:nn {fontspec-renderer} {Renderer=OpenType}
%</XE>
  }
\aliasfontfeature{Path}{ExternalLocation}
\@@_keys_define_code:nnn {fontspec} {Path} {}
%    \end{macrocode}
% \end{macro}
%
% \paragraph{\feat{Extension}}
% For fonts that aren't installed in the system. Specifies the font extension
% to use.
%    \begin{macrocode}
\@@_keys_define_code:nnn {fontspec-preparse-external} {Extension}
  {
    \tl_set:Nn \l_@@_extension_tl {#1}
    \bool_if:NF \l_@@_external_bool
      {
        \keys_set:nn {fontspec-preparse-external} {Path}
      }
  }
\tl_clear:N \l_@@_extension_tl
\@@_keys_define_code:nnn {fontspec} {Extension} {}
%    \end{macrocode}
%
% \paragraph{\feat{KpseOnly}}
% If the font is specified by filename, only search for it through kpse.
% \XeTeX\ does not support finding system fonts by filename so this is always implicitly set
% there.
%    \begin{macrocode}
\@@_keys_define_code:nnn {fontspec-preparse-external} {KpseOnly}
  {
    \bool_set_true:N \l_@@_external_kpse_bool
    \bool_if:NT \l_@@_external_bool \@@_font_is_file:
  }
\@@_keys_define_code:nnn {fontspec} {KpseOnly} {}
%    \end{macrocode}
%
%
% \paragraph{\feat{Renderer}}
% This feature must be processed before all others (the other font shape and features
% options are also pre-parsed for convenience) because the renderer determines the format
% of the features and whether certain features are available.
%    \begin{macrocode}
%<*XE>
\keys_define:nn {fontspec-renderer}
  {
    Renderer .choices:nn =
      {AAT,ICU,OpenType,Graphite,Full,Basic,Node,Base,HarfBuzz,Harfbuzz}
      {
        \int_compare:nTF {\l_keys_choice_int <= 4}
          {
            \tl_set:Nx \l_@@_renderer_tl
              {
                \int_case:nn \l_keys_choice_int { 1 {/AAT} 2 {/OT} 3 {/OT} 4 {/GR} }
              }
%<debug>\typeout{Renderer:~ \l_@@_renderer_tl}
            \tl_gset:Nx \g_@@_single_feat_tl { \l_@@_renderer_tl }
          }
          {
            \@@_warning:nx {only-luatex-feature} {Renderer=Full/Basic/Node/Base/HarfBuzz}
          }
      }
  }
%</XE>
%<*LU>
\keys_define:nn {fontspec-renderer}
  {
    Renderer .choices:nn =
      {Full,Node,Basic,Base,HarfBuzz,Harfbuzz,OpenType,AAT,Graphite}
      {
        \int_compare:nT {\l_keys_choice_int >= 5} { \bool_set_true:N \l_@@_harfbuzz_bool }

        \tl_set:Nx \l_@@_mode_tl
          {
            \int_case:nn \l_keys_choice_int { 1 {node} 2 {node} 3 {base} 4 {base} 5 {harf} 6 {harf} 7 {harf} 8 {harf} 9 {harf}}
          }

        \tl_set:Nx \l_@@_shaper_tl
          {
            \int_case:nn \l_keys_choice_int { 1 {} 2 {} 3 {} 4 {} 5 {} 6 {} 7 {ot} 8 {coretext_aat} 9 {graphite2} }
          }

%<debug>\typeout{Mode:~"\l_@@_mode_tl"~/~Shaper:~"\l_@@_shaper_tl"}

        \tl_gset:Nx \g_@@_single_feat_tl
          {
            mode=\l_@@_mode_tl ;
            \tl_if_empty:NF \l_@@_shaper_tl { shaper=\l_@@_shaper_tl}
          }
      } ,

    Renderer unknown .code:n =
      {
        \bool_set_true:N \l_@@_harfbuzz_bool
        \@@_warning:nx {unknown-renderer} {#1}
        \tl_set:Nn \l_@@_mode_tl {harf}
        \tl_set:Nn \l_@@_shaper_tl {#1}
      } ,
  }
%</LU>
%    \end{macrocode}
%
% \subsection{Pre-parsed features}
%
% \paragraph{OpenType script/language}
% See later for the resolutions from \pkg{fontspec} features to OpenType definitions.
%    \begin{macrocode}
\@@_keys_define_code:nnn {fontspec-preparse} {Script}
  {
%<XE>    \tl_if_empty:NT \l_@@_renderer_tl { \keys_set:nn {fontspec-renderer} {Renderer=OpenType} }
    \tl_set:Nn \l_@@_script_name_tl {#1}
  }
%    \end{macrocode}
% Exactly the same:
%    \begin{macrocode}
\@@_keys_define_code:nnn {fontspec-preparse} {Language}
  {
%<XE>    \tl_if_empty:NT \l_@@_renderer_tl { \keys_set:nn {fontspec-renderer} {Renderer=OpenType} }
    \tl_set:Nn \l_@@_lang_name_tl {#1}
  }
%    \end{macrocode}
%
%
% \paragraph{TTC font index}
%    \begin{macrocode}
\@@_keys_define_code:nnn {fontspec-preparse} {FontIndex}
  {
    \str_if_eq:eeF { \str_lowercase:f {\l_@@_extension_tl} } {.ttc}
      { \@@_warning:n {font-index-needs-ttc} }
%<XE>  \tl_set:Nn \l_@@_ttc_index_tl {:#1}
%<LU>  \tl_set:Nn \l_@@_ttc_index_tl {(#1)}
  }
\@@_keys_define_code:nnn {fontspec} {FontIndex}
  {
%<XE>  \tl_set:Nn \l_@@_ttc_index_tl {:#1}
%<LU>  \tl_set:Nn \l_@@_ttc_index_tl {(#1)}
  }
%    \end{macrocode}
%
% \subsection{Font faces}
%
% \paragraph{Upright}
%    \begin{macrocode}
\@@_keys_define_code:nnn {fontspec-preparse-external} {UprightFont}
  {
    \fontspec_complete_fontname:Nn \l_@@_fontname_up_tl {#1}
  }
%    \end{macrocode}
%
% \paragraph{Italic and slanted}
%    \begin{macrocode}
\@@_keys_define_code:nnn {fontspec-preparse-external} {ItalicFont}
  {
    \tl_if_empty:nTF {#1}
      {
        \bool_set_true:N \l_@@_noit_bool
      }
      {
        \bool_set_false:N \l_@@_noit_bool
        \fontspec_complete_fontname:Nn \l_@@_fontname_it_tl {#1}
      }
  }
%    \end{macrocode}
%
%    \begin{macrocode}
\@@_keys_define_code:nnn {fontspec-preparse-external} {SlantedFont}
  {
    \fontspec_complete_fontname:Nn \l_@@_fontname_sl_tl {#1}
  }
%    \end{macrocode}
%
%    \begin{macrocode}
\@@_keys_define_code:nnn {fontspec-preparse-external} {SwashFont}
  {
    \fontspec_complete_fontname:Nn \l_@@_fontname_sw_tl {#1}
  }
%    \end{macrocode}
%
%
% \paragraph{Bold (NFSS) Series}
% By default, \pkg{fontspec} uses the default bold series, \cs{bfdefault}.
% We want to be able to make this extensible. This code is not yet functional!
%    \begin{macrocode}
%\@@_keys_define_code:nnn {fontspec-preparse-external} {BoldSeries}
%  {
%    \tl_gset:Nx \g_@@_curr_series_tl { #1 }
%    \seq_put_right:Nx \l_@@_bf_series_seq { #1 }
%  }
%    \end{macrocode}
%
% \paragraph{Bold}
% This contains some stubb code to allow more than one bold font to be loaded.
%    \begin{macrocode}
\@@_keys_define_code:nnn {fontspec-preparse-external} {BoldFont}
  {
    \tl_if_empty:nTF {#1}
      {
        \bool_set_true:N \l_@@_nobf_bool
      }
      {
        \bool_set_false:N \l_@@_nobf_bool
        \fontspec_complete_fontname:Nn \l_@@_curr_bfname_tl {#1}

        \seq_if_empty:NT \l_@@_bf_series_seq
          {
            \tl_gset:Nx \g_@@_curr_series_tl {\bfdefault}
            \seq_put_right:Nx \l_@@_bf_series_seq {\bfdefault}
          }

        \tl_if_eq:oeT \g_@@_curr_series_tl {\bfdefault}
          {
            \tl_set_eq:NN \l_@@_fontname_bf_tl \l_@@_curr_bfname_tl
          }

        \prop_put:NeV \l_@@_nfss_prop {BoldFont-\g_@@_curr_series_tl} \l_@@_curr_bfname_tl

%<debug>\typeout{Setting~bold~font~"\l_@@_curr_bfname_tl"~with~series~"\g_@@_curr_series_tl"}

      }
  }
%    \end{macrocode}
%
% \paragraph{Bold italic/slanted}
%    \begin{macrocode}
\@@_keys_define_code:nnn {fontspec-preparse-external} {BoldItalicFont}
  {
    \fontspec_complete_fontname:Nn \l_@@_fontname_bfit_tl {#1}
  }
%    \end{macrocode}
%
%    \begin{macrocode}
\@@_keys_define_code:nnn {fontspec-preparse-external} {BoldSlantedFont}
  {
    \fontspec_complete_fontname:Nn \l_@@_fontname_bfsl_tl {#1}
  }
%    \end{macrocode}
%
%    \begin{macrocode}
\@@_keys_define_code:nnn {fontspec-preparse-external} {BoldSwashFont}
  {
    \fontspec_complete_fontname:Nn \l_@@_fontname_bfsw_tl {#1}
  }
%    \end{macrocode}
%
% \paragraph{Small caps}
% Small caps isn't pre-parsed because it can vary with others above:
%    \begin{macrocode}
\@@_keys_define_code:nnn {fontspec} {SmallCapsFont}
  {
    \tl_if_empty:nTF {#1}
      {
        \bool_set_true:N \l_@@_nosc_bool
      }
      {
        \bool_set_false:N \l_@@_nosc_bool
        \fontspec_complete_fontname:Nn \l_@@_fontname_sc_tl {#1}
      }
  }
%    \end{macrocode}
%
%
% \subsubsection{Preparsed font features}
%
%    \begin{macrocode}
\@@_keys_define_code:nnn {fontspec-preparse} {UprightFeatures}
  {
    \clist_put_right:Nn \l_@@_fontfeat_up_clist {#1}
  }
\@@_keys_define_code:nnn {fontspec-preparse} {BoldFeatures}
  {
    \clist_put_right:Nn \l_@@_fontfeat_bf_clist {#1}
%  \prop_put:NeV \l_@@_nfss_prop
%     {BoldFont-\g_@@_curr_series_tl} \l_@@_curr_bfname_tl
  }
\@@_keys_define_code:nnn {fontspec-preparse} {ItalicFeatures}
  {
    \clist_put_right:Nn \l_@@_fontfeat_it_clist {#1}
  }
\@@_keys_define_code:nnn {fontspec-preparse} {BoldItalicFeatures}
  {
    \clist_put_right:Nn \l_@@_fontfeat_bfit_clist {#1}
  }
\@@_keys_define_code:nnn {fontspec-preparse} {SlantedFeatures}
  {
    \clist_put_right:Nn \l_@@_fontfeat_sl_clist {#1}
  }
\@@_keys_define_code:nnn {fontspec-preparse} {BoldSlantedFeatures}
  {
    \clist_put_right:Nn \l_@@_fontfeat_bfsl_clist {#1}
  }
\@@_keys_define_code:nnn {fontspec-preparse} {SwashFeatures}
  {
    \clist_put_right:Nn \l_@@_fontfeat_sw_clist {#1}
  }
\@@_keys_define_code:nnn {fontspec-preparse} {BoldSwashFeatures}
  {
    \clist_put_right:Nn \l_@@_fontfeat_bfsw_clist {#1}
  }
%    \end{macrocode}
% Note that small caps features can vary by shape, so these in fact \emph{aren't} pre-parsed.
%    \begin{macrocode}
\@@_keys_define_code:nnn {fontspec} {SmallCapsFeatures}
  {
    \bool_if:NF \l_@@_firsttime_bool
      {
        \clist_put_right:Nn \l_@@_fontfeat_sc_clist {#1}
      }
  }
%    \end{macrocode}
%
% \paragraph{Features varying by size}
%    \begin{macrocode}
\@@_keys_define_code:nnn {fontspec-preparse} {SizeFeatures}
  {
    \clist_set:Nn \l_@@_sizefeat_clist {#1}
    \clist_put_right:Nn \l_@@_fontfeat_up_clist { SizeFeatures = {#1} }
  }
\@@_keys_define_code:nnn {fontspec-preparse-nested} {SizeFeatures}
  {
    \clist_set:Nn \l_@@_sizefeat_clist {#1}
    \tl_if_empty:NT \l_@@_this_font_tl
      { \tl_set:Nn \l_@@_this_font_tl { -- } } % needs to be non-empty as a flag
  }
\@@_keys_define_code:nnn {fontspec-preparse-nested} {Font}
  {
    \tl_set:Nn \l_@@_this_font_tl {#1}
  }
\@@_keys_define_code:nnn {fontspec} {SizeFeatures}
  {
    % dummy
  }
\@@_keys_define_code:nnn {fontspec} {Font}
  {
    % dummy
  }
%    \end{macrocode}
%
%    \begin{macrocode}
\@@_keys_define_code:nnn {fontspec-sizing} {Size}
  {
    \tl_set:Nn \l_@@_size_tl {#1}
  }
\@@_keys_define_code:nnn {fontspec-sizing} {Font}
  {
    \fontspec_complete_fontname:Nn \l_@@_sizedfont_tl {#1}
  }
%    \end{macrocode}
%
% A hack to fix a test, needs to be investigated why necessary!
%    \begin{macrocode}
\@@_keys_define_code:nnn {fontspec-opentype} {UprightFont} {}
\@@_keys_define_code:nnn {fontspec-opentype} {ItalicFont} {}
\@@_keys_define_code:nnn {fontspec-opentype} {SlantedFont} {}
\@@_keys_define_code:nnn {fontspec-opentype} {BoldFont} {}
\@@_keys_define_code:nnn {fontspec-opentype} {BoldItalicFont} {}
\@@_keys_define_code:nnn {fontspec-opentype} {BoldSlantedFont} {}
%    \end{macrocode}
%
%
%
% \subsection{General font-independent features}
%
% These features can be applied to any font.
%
% \paragraph{NFSS encoding}
% For the very brave.
%    \begin{macrocode}
\@@_keys_define_code:nnn {fontspec-preparse} {NFSSEncoding}
  {
    \tl_gset:Nx \g_@@_nfss_enc_tl { #1 }
  }
%    \end{macrocode}
%
% \paragraph{NFSS family}
% Interactions with other packages will sometimes require setting the NFSS family explicitly.
% (By default \pkg{fontspec} auto-generates one based on the font name.)
%    \begin{macrocode}
\@@_keys_define_code:nnn {fontspec-preparse} {NFSSFamily}
  {
    \tl_set:Nx \l_@@_nfss_fam_tl { #1 }
  }
%    \end{macrocode}
%
% \paragraph{NFSS series/shape}
% This option looks similar in name but has a very different function.
%    \begin{macrocode}
\@@_keys_define_code:nnn {fontspec-preparse} {FontFace}
  {
    \tl_clear:N \l_@@_this_font_tl
    \clist_set:No \l_@@_arg_clist { \use_iii:nnn #1 }
    \clist_set_eq:NN \l_@@_this_feat_clist \l_@@_arg_clist
    \int_compare:nT { \clist_count:N \l_@@_arg_clist = 1 }
      {
%<debug>\typeout{FontFace~ parsing:~ one~ clist~ item}
        \tl_if_in:NnF \l_@@_arg_clist {=}
          {
%<debug>\typeout{FontFace~ parsing:~ no~ equals~ =>~ font~ name~ only}
            \tl_set_eq:NN \l_@@_this_font_tl \l_@@_arg_clist
            \tl_clear:N \l_@@_this_feat_clist
          }
      }

    \@@_add_nfssfont:nnnn
      {\use_i:nnn #1} {\use_ii:nnn #1} {\l_@@_this_font_tl} {\l_@@_this_feat_clist}
  }
%    \end{macrocode}
%
%
%
% \paragraph{Scale}
% If the input isn't one of the pre-defined string options, then
% it's gotta be numerical. \cs{fontspec_calc_scale:n} and \cs{fontspec_calc_scale:nn}
% do all the work in the auto-scaling cases.
%    \begin{macrocode}
\@@_keys_define_code:nnn {fontspec} {Scale}
  {
    \str_case:nnF {#1}
      {
        {MatchLowercase} { \@@_calc_scale:n {5} }
        {MatchUppercase} { \@@_calc_scale:n {8} }
        {MatchAveragecase} { \@@_calc_scale:nn {5} {8} }
      }
      { \tl_set:Nx \l_@@_scale_tl {#1} }
      \@@_info:n {set-scale}
  }
%    \end{macrocode}
%
% \paragraph{ScaleAgain}
%    \begin{macrocode}
\@@_keys_define_code:nnn {fontspec} {ScaleAgain}
  {
    \tl_if_empty:NT \l_@@_scale_tl { \tl_set:Nn \l_@@_scale_tl {1} }
    \tl_set:Nx \l_@@_scale_tl { \fp_eval:n { #1 * \l_@@_scale_tl } }
    \@@_info:n {set-scale}
  }
%    \end{macrocode}
%
% \begin{macro}{\@@_calc_scale:n}
% This macro calculates the amount of scaling between the default
% roman font and the (default shape of) the font being selected such
% that the font dimension that is input is equal for both. The only
% font dimensions that justify this are 5 (lowercase height)
% and 8 (uppercase height in \XeTeX).
%
% This script is executed for every extra shape, which seems wasteful,
% but allows alternate italic shapes from a separate font, say, to
% be loaded and to be auto-scaled correctly. Even if this would be ugly.
%
% To begin, change to \cs{rmfamily} but use internal commands in case cs{rmfamily}
% has been overwritten.
% (Note that changing \cs{rmfamily} with fontspec resets \cs{encodingdefault}
% appropriately.)
%    \begin{macrocode}
\cs_new:Nn \@@_calc_scale:n
  {
    \group_begin:

      \fontencoding { \encodingdefault }
      \fontfamily { \familydefault }
      \selectfont

      \@@_set_font_dimen:NnN \l_@@_tmpa_dim {#1} \font
      \@@_set_font_dimen:NnN \l_@@_tmpb_dim {#1} \l_@@_fontface_cs_tl

      \tl_set:Nx \l_@@_scale_tl
        {
          \fp_eval:n { \dim_to_fp:n {\l_@@_tmpa_dim} /
                       \dim_to_fp:n {\l_@@_tmpb_dim}   }
        }

      \exp_args:NNNx
    \group_end:
    \tl_set:Nx \l_@@_scale_tl { \l_@@_scale_tl }
  }
%    \end{macrocode}
% \end{macro}
%
%
% \begin{macro}{\@@_calc_scale:nn}
% This macro calls \cs{fontspec_calc_scale:n} twice
% and then sets the scale to the average of the two results.
%    \begin{macrocode}
\cs_new:Nn \@@_calc_scale:nn
{
    \group_begin:
      \__fontspec_calc_scale:n {#1}
      \tl_set_eq:NN \l_@@_tmp_tl \l_@@_scale_tl
      \__fontspec_calc_scale:n {#2}
      \tl_set:Nx \l_@@_scale_tl
        {
          \fp_eval:n { (\l_@@_tmp_tl + \l_@@_scale_tl)/2 }
        }
      \exp_args:NNNx
    \group_end:
    \tl_set:Nx \l_@@_scale_tl { \l_@@_scale_tl }
  }
%    \end{macrocode}
% \end{macro}
% \begin{macro}{\@@_set_font_dimen:NnN}
% This function sets the dimension |#1| (for font |#3|) to `fontdimen' |#2|
% for either font dimension 5 (x-height) or 8 (cap-height). If, for some
% reason, these return an incorrect `zero' value (as \cs{fontdimen8} might
% for a \texttt{.tfm} font), then we cheat and measure the height of a glyph.
% We assume in this case that the font contains either an `X' or an `x'.
%    \begin{macrocode}
\cs_new:Nn \@@_set_font_dimen:NnN
  {
    \dim_set:Nn #1 { \fontdimen #2 #3 }
    \dim_compare:nNnT #1 = {0pt}
      {
        \settoheight #1
          {
            \str_if_eq:nnTF {#3} {\font} \rmfamily #3
            \int_case:nnF #2
              {
                {5} {x} % x-height
                {8} {X} % cap-height
              } {?} % "else" clause; never reached.
          }
      }
  }
%    \end{macrocode}
% \end{macro}
%
%
% \paragraph{Inter-word space}
% These options set the relevant \cmd\fontdimen s for the
% font being loaded.
%    \begin{macrocode}
\@@_keys_define_code:nnn {fontspec} {WordSpace}
  {
    \bool_if:NF \l_@@_firsttime_bool
      { \_fontspec_parse_wordspace:w #1,,,\q_stop }
  }
\@@_aff_error:n {WordSpace}
%    \end{macrocode}
%
% \begin{macro}{\_fontspec_parse_wordspace:w}
% This macro determines if the input to \feat{WordSpace} is
% of the form |{X}| or |{X,Y,Z}| and executes the font scaling.
% If the former input, it executes |{X,X,X}|.
%    \begin{macrocode}
\cs_set:Npn \_fontspec_parse_wordspace:w #1,#2,#3,#4 \q_stop
  {
    \tl_if_empty:nTF {#4}
      {
        \tl_set:Nn \l_@@_wordspace_adjust_tl
          {
            \fontdimen 2 \font = #1 \fontdimen 2 \font
            \fontdimen 3 \font = #1 \fontdimen 3 \font
            \fontdimen 4 \font = #1 \fontdimen 4 \font
          }
      }
      {
        \tl_set:Nn \l_@@_wordspace_adjust_tl
          {
            \fontdimen 2 \font = #1 \fontdimen 2 \font
            \fontdimen 3 \font = #2 \fontdimen 3 \font
            \fontdimen 4 \font = #3 \fontdimen 4 \font
          }
      }
  }
%    \end{macrocode}
% \end{macro}
%
% \paragraph{Punctuation space}
% Scaling factor for the nominal \cmd\fontdimen \#7.
%    \begin{macrocode}
\@@_keys_define_code:nnn {fontspec} {PunctuationSpace}
  {
    \str_case_e:nnF {#1}
      {
        {WordSpace}
          {
            \tl_set:Nn \l_@@_punctspace_adjust_tl
              { \fontdimen 7 \font = 0 \fontdimen 2 \font }
          }
        {TwiceWordSpace}
        {
          \tl_set:Nn \l_@@_punctspace_adjust_tl
            { \fontdimen 7 \font = 1 \fontdimen 2 \font }
        }
      }
      {
        \tl_set:Nn \l_@@_punctspace_adjust_tl
          { \fontdimen 7 \font = #1 \fontdimen 7 \font }
      }
  }
\@@_aff_error:n {PunctuationSpace}
%    \end{macrocode}
%
% \paragraph{Secret hook into the font-adjustment code}
%
%    \begin{macrocode}
\@@_keys_define_code:nnn {fontspec} {FontAdjustment}
  {
    \tl_put_right:Nx \l_@@_postadjust_tl {#1}
  }
%    \end{macrocode}
%
% \paragraph{Letterspacing}
%    \begin{macrocode}
\@@_keys_define_code:nnn {fontspec} {LetterSpace}
  {
    \@@_update_featstr:n {letterspace=#1}
  }
%    \end{macrocode}
%
% \paragraph{Hyphenation character}
% This feature takes one of three arguments: `\opt{None}',
% \meta{glyph}, or \meta{slot}. If the input isn't the first,
% and it's one character, then it's the second; otherwise, it's
% the third.
%
% LuaTeX decouples hyphenation from font settings, so only \verb|HyphenChar=None| works
% for that engine.
%    \begin{macrocode}
\@@_keys_define_code:nnn {fontspec} {HyphenChar}
  {
    \str_if_eq:nnTF {#1} {None}
      {
        \tl_put_right:Nn \l_@@_postadjust_tl
          { \@@_primitive_font_set_hyphenchar:Nn \font {-1} }
      }
      {
%<LU>        \@@_warning:nx {only-xetex-feature} {HyphenChar}

        \tl_if_single:nTF {#1}
          { \tl_set:Nn \l_@@_hyphenchar_tl {`#1} }
          { \tl_set:Nn \l_@@_hyphenchar_tl { #1} }

        \exp_args:No \@@_primitive_font_glyph_if_exist:NnTF \l_@@_fontface_cs_tl {\l_@@_hyphenchar_tl}
          {
            \tl_put_right:Nn \l_@@_postadjust_tl
              { \@@_primitive_font_set_hyphenchar:Nn \font { \l_@@_hyphenchar_tl } }
          }
          { \@@_error:nxx {no-glyph}{\l_fontspec_fontname_tl}{#1} }

      }
  }
\@@_aff_error:n {HyphenChar}
%    \end{macrocode}
%
% \paragraph{Color}
% Test first if the color is a named l3color, then if it is a color from
% \pkg{xcolor}, which names its colours \texttt{\char`\\color@<name>}.
% If this fails the argument is assumed to be a hex color.
%
%    \begin{macrocode}
\@@_keys_define_code:nnn {fontspec} {Color}
  {
%<*XE>
    \color_if_exist:nTF {#1}
      {
        \color_export:nnN {#1} {HTML}\l_@@_hexcol_tl
      }
      {
        \cs_if_exist:cTF { \token_to_str:N \color@ #1 }        
          {
            \convertcolorspec{named}{#1}{HTML}\l_@@_hexcol_tl
          }
          {
            \int_compare:nTF { \tl_count:n {#1} == 6 }
              { \tl_set:Nn \l_@@_hexcol_tl {#1} }
              {
                \int_compare:nTF { \tl_count:n {#1} == 8 }
                  { \fontspec_parse_colour:viii #1 }
                  {
                    \bool_if:NF \l_@@_firsttime_bool
                      { \@@_warning:nx {bad-colour} {#1} }
                  }
              }
          }
      }
%</XE>
%<*LU>
    \color_if_exist:nTF {#1}
      {
        \tl_set:Nn \l_@@_hexcol_tl {#1}
      }
      {
        \cs_if_exist:cTF { \token_to_str:N \color@ #1 }        
          {
            \convertcolorspec{named}{#1}{HTML}\l_@@_hexcol_tl
          }
          {
            \int_compare:nTF { \tl_count:n {#1} == 6 }
              { \tl_set:Nn \l_@@_hexcol_tl {#1} }
              {
                \int_compare:nTF { \tl_count:n {#1} == 8 }
                  { \fontspec_parse_colour:viii #1 }
                  {
                    \bool_if:NF \l_@@_firsttime_bool
                      { \@@_warning:nx {bad-colour} {#1} }
                  }
              }
          }
      }      
%</LU>
  }
%    \end{macrocode}
%
%    \begin{macrocode}
\cs_set:Npn \fontspec_parse_colour:viii #1#2#3#4#5#6#7#8
  {
    \tl_set:Nn \l_@@_hexcol_tl {#1#2#3#4#5#6}
    \tl_if_eq:NNF \l_@@_opacity_tl \c_@@_opacity_tl
      {
        \bool_if:NF \l_@@_firsttime_bool
        { \@@_warning:nx {opa-twice-col} {#7#8} }
      }
    \tl_set:Nn \l_@@_opacity_tl {#7#8}
  }
\aliasfontfeature{Color}{Colour}
%    \end{macrocode}
%    \begin{macrocode}
\@@_keys_define_code:nnn {fontspec} {Opacity}
  {
    \int_set:Nn \l_@@_tmp_int {255}
    \@@_int_mult_truncate:Nn \l_@@_tmp_int { #1 }
    \tl_if_eq:NNF \l_@@_opacity_tl \c_@@_opacity_tl
      {
        \bool_if:NF \l_@@_firsttime_bool
        { \@@_warning:nx {opa-twice} {#1} }
      }
    \tl_set:Nx \l_@@_opacity_tl
      {
%<LU>      ,
        \int_compare:nT { \l_@@_tmp_int <= "F } {0} % zero pad
        \int_to_hex:n { \l_@@_tmp_int }
      }
  }
%    \end{macrocode}
%
%
% \paragraph{Mapping}
%    \begin{macrocode}
%<*XE>
\@@_keys_define_code:nnn {fontspec-aat} {Mapping}
  {
    \tl_set:Nn \l_@@_mapping_tl { #1 }
  }
\@@_keys_define_code:nnn {fontspec-opentype} {Mapping}
  {
    \tl_set:Nn \l_@@_mapping_tl { #1 }
  }
%</XE>
%<*LU>
\@@_keys_define_code:nnn {fontspec-opentype} {Mapping}
  {
    \str_if_eq:nnTF {#1} {tex-text}
      {
        \@@_warning:n {no-mapping-ligtex}
        \msg_redirect_name:nnn {fontspec} {no-mapping-ligtex} {none}
        \keys_set:nn {fontspec-opentype} { Ligatures=TeX }
      }
      { \@@_warning:n {no-mapping} }
  }
%</LU>
%    \end{macrocode}
%
%
% \subsubsection{Continuous font axes}
%
%    \begin{macrocode}
%<*XE>
\@@_keys_define_code:nnn {fontspec} {Weight}
  {
    \@@_update_featstr:n{weight=#1}
  }
%</XE>
%<LU>\@@_define_opentype_variation_axis:nn {Weight} {wght}
%<*XE>
\@@_keys_define_code:nnn {fontspec} {Width}
  {
    \@@_update_featstr:n{width=#1}
  }
%</XE>
%<LU>\@@_define_opentype_variation_axis:nn {Width} {wdth}
\@@_define_opentype_variation_axis:nn {Slant} {slnt}
\@@_keys_define_code:nnn {fontspec} {OpticalSize}
%<*XE>
  {
    \bool_if:NTF \l_@@_ot_bool
      {
        \tl_set:Nn \l_@@_optical_size_tl {/ S = #1}
      }
      {
        \bool_if:NT \l_@@_mm_bool
        {
          \@@_update_featstr:n { optical size = #1 }
        }
      }
    \bool_if:nT { !\l_@@_ot_bool && !\l_@@_mm_bool }
      {
        \bool_if:NT \l_@@_firsttime_bool
        { \@@_warning:nx {no-opticals} {\l_fontspec_fontname_tl} }
      }
  }
%</XE>
%<*LU>
  {
    \tl_set:Nn \l_@@_optical_size_tl {/ S = #1}
  }
%</LU>
%    \end{macrocode}
%
% For other potentially font specific variation axes, there is a raw setter available:
%    \begin{macrocode}
\@@_keys_define_code:nnn {fontspec-opentype} {RawAxis}
  {
    \prop_gput_from_keyval:Nn \g_@@_rawvariations_prop {#1}
  }
%    \end{macrocode}
%
% \subsubsection{Variation instances}
%
%    \begin{macrocode}
\@@_keys_define_code:nnn {fontspec-opentype} {Instance}
  {
    \tl_gset:Nn \g_@@_instance_tl {#1}
  }
%    \end{macrocode}
%
% \subsubsection{Font transformations}
% These are to be specified to apply directly to a font shape:
%    \begin{macrocode}
\keys_define:nn {fontspec}
  {
    FakeSlant .code:n =
      {
        \@@_update_featstr:n {slant=#1}
      },
    FakeSlant .default:n = {0.2}
 }
\keys_define:nn {fontspec}
  {
    FakeStretch .code:n =
      {
        \@@_update_featstr:n {extend=#1}
      },
    FakeStretch .default:n = {1.2}
  }
\keys_define:nn {fontspec}
  {
    FakeBold .code:n =
      {
        \@@_update_featstr:n {embolden=#1}
      },
    FakeBold .default:n = {1.5}
  }
%    \end{macrocode}
% These are to be given to a shape that has no real bold/italic
% to signal that \pkg{fontspec} should automatically create `fake' shapes.
%
% The behaviour is currently that only if both \opt{AutoFakeSlant} \emph{and}
% \opt{AutoFakeBold} are specified, the bold italic is also faked.
%
% These features presently \emph{override} real shapes found in the font;
% in the future I'd like these features to be ignored in this case, instead.
% (This is just a bit harder to program in the current design of
% \pkg{fontspec}.)
%    \begin{macrocode}
\keys_define:nn {fontspec}
  {
    AutoFakeSlant .code:n =
      {
        \bool_if:NT \l_@@_firsttime_bool
          {
            \tl_set:Nn \l_@@_fake_slant_tl {#1}
            \clist_put_right:Nn \l_@@_fontfeat_it_clist {FakeSlant=#1}
            \tl_set_eq:NN \l_@@_fontname_it_tl \l_fontspec_fontname_tl
            \bool_set_false:N \l_@@_noit_bool

            \tl_if_empty:NF \l_@@_fake_embolden_tl
              {
                \clist_put_right:Nx \l_@@_fontfeat_bfit_clist
                {FakeBold=\l_@@_fake_embolden_tl}
                \clist_put_right:Nx \l_@@_fontfeat_bfit_clist {FakeSlant=#1}
                \tl_set_eq:NN \l_@@_fontname_bfit_tl \l_fontspec_fontname_tl
              }
          }
      },
    AutoFakeSlant .default:n = {0.2}
  }
%    \end{macrocode}
% Same but reversed:
%    \begin{macrocode}
\keys_define:nn {fontspec}
  {
    AutoFakeBold .code:n =
      {
        \bool_if:NT \l_@@_firsttime_bool
          {
            \tl_set:Nn \l_@@_fake_embolden_tl {#1}
            \clist_put_right:Nn \l_@@_fontfeat_bf_clist {FakeBold=#1}
            \tl_set_eq:NN \l_@@_fontname_bf_tl \l_fontspec_fontname_tl
            \bool_set_false:N \l_@@_nobf_bool

            \tl_if_empty:NF \l_@@_fake_slant_tl
              {
                \clist_put_right:Nx \l_@@_fontfeat_bfit_clist
                {FakeSlant=\l_@@_fake_slant_tl}
                \clist_put_right:Nx \l_@@_fontfeat_bfit_clist {FakeBold=#1}
                \tl_set_eq:NN \l_@@_fontname_bfit_tl \l_fontspec_fontname_tl
              }
          }
      },
    AutoFakeBold .default:n = {1.5}
  }
%    \end{macrocode}
%
%
% \subsubsection{Raw feature string}
% This allows savvy \XeTeX-ers to input font features manually if they have
% already memorised the OpenType abbreviations and don't mind not having error checking.
%    \begin{macrocode}
\@@_keys_define_code:nnn {fontspec-opentype} {RawFeature}
  {
    \@@_update_featstr:n {#1}
  }
\@@_keys_define_code:nnn {fontspec-aat} {RawFeature}
  {
    \@@_update_featstr:n {#1}
  }
%    \end{macrocode}
%
%
%
%
% \iffalse
%    \begin{macrocode}
%</fontspec>
%    \end{macrocode}
% \fi


\endinput

% /©
% ------------------------------------------------
% The FONTSPEC package  <latex3.github.io/fontspec>
% ------------------------------------------------
% Copyright  2022-2024  The LaTeX project,  LPPL "maintainer"
% Copyright  2004-2022  Will Robertson
% Copyright  2009-2015  Khaled Hosny
% Copyright  2013       Philipp Gesang
% Copyright  2013-2016  Joseph Wright
% ------------------------------------------------
% This package is free software and may be redistributed and/or modified under
% the conditions of the LaTeX Project Public License, version 1.3c or higher
% (your choice): <http://www.latex-project.org/lppl/>.
% ------------------------------------------------
% ©/
