%%^^A%%  fontspec-code-internal.dtx -- part of FONTSPEC <wspr.io/fontspec>

% \section{Internals}
%
% \iffalse
%    \begin{macrocode}
%<*fontspec>
%    \end{macrocode}
% \fi
%
% \subsection{The main function for setting fonts}
%
% \begin{macro}{\@@_select_font_family:nn}
% This is the command that defines font families for use, the underlying
% procedure of all \cmd\fontspec-like commands. Given a
% list of font features (|#1|) for a requested font (|#2|),
% it will define an NFSS
% family for that font and put the family name (globally) into \cs{l_fontspec_family_tl}.
% The \TeX\ `\cs{font}' command is (globally) stored in \cs{l_fontspec_font}.
%
% This macro does its processing inside a group to attempt to restrict the scope of its internal processing.
% This works to some degree to insulate the internal commands from having to be manually cleared.
%
% Some often-used variables to know about:
% \begin{itemize}
% \item \cmd{\l_fontspec_fontname_tl} is used as the generic name of the font being defined.
% \item \cmd{\l_@@_fontid_tl} is the unique identifier of the font with all its features.
% \item \cmd{\l_@@_fontname_up_tl} is the font specifically to be used as the upright font.
% \item \cmd{\l_@@_basename_tl} is the (immutable) original argument used for |*|-replacing.
% \item \cmd{\l_fontspec_font} is the plain \TeX{} font of the upright font requested.
% \end{itemize}
%    \begin{macrocode}
\cs_new_protected:Nn \@@_select_font_family:nn
  {
%<debug>\typeout{^^J^^J::::::::::::::::::::::::::::::^^J:: fontspec_select:nn~ {#1}~ {#2} }
    \group_begin:
    \@@_font_suppress_not_found_error:
    \@@_init:

    \@@_sanitise_fontname:Nn \l_fontspec_fontname_tl    {#2}
    \@@_sanitise_fontname:Nn \l_@@_fontname_up_tl       {#2}
    \@@_sanitise_fontname:Nn \l_@@_basename_tl          {#2}

    \@@_if_detect_external:nT {#2}
     { \keys_set:nn {fontspec-preparse-external} {Path} }

    \keys_set_known:nn {fontspec-preparse-cfg} {#1}

    \@@_init_ttc:n {#2}
    \@@_load_external_fontoptions:N \l_fontspec_fontname_tl

    \@@_extract_all_features:n {#1}
    \tl_set:Nx \l_@@_fontid_tl { \tl_to_str:N \l_fontspec_fontname_tl-:-\tl_to_str:N \l_@@_all_features_clist }

%<debug>\typeout{fontid: \l_@@_fontid_tl}

    \@@_preparse_features:
    \@@_load_font:
    \@@_set_scriptlang:
    \@@_get_features:n {}
    \bool_set_false:N \l_@@_firsttime_bool

    \@@_save_family_needed:nTF {#2}
      {
        \@@_save_family:nn {#1} {#2}
%<debug>\@@_warning:nxx {defining-font} {#1} {#2}
      }
      {
%<debug>\typeout{Font~ family~ already~ defined.}
      }
    \group_end:

    \tl_set_eq:NN \l_fontspec_family_tl \g_@@_nfss_family_tl
  }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}{\fontspec_select:nn}
% This old name has been used by 3rd party packages so for compatibility:
%    \begin{macrocode}
\cs_set_eq:NN \fontspec_select:nn \@@_select_font_family:nn %% deprecated, for compatibility only
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}{\@@_sanitise_fontname:Nn}
% Assigns font name |#2| to token list variable |#1| and strips extension(s) from it in the case of an external font.
% We strip spaces for luatex for consistency with luaotfload, although I'm not sure this is necessary any more.
% At one stage this also lowercased the name, but this step has been removed unless someone can remind me why it was necessary.
%    \begin{macrocode}
\cs_new:Nn \@@_sanitise_fontname:Nn
  {
    \tl_set:Nx #1 {#2}
%<LU>  \tl_remove_all:Nn #1 {~}
    \clist_map_inline:Nn \l_@@_extensions_clist
      {
        \tl_if_in:NnT #1 {##1}
          {
            \tl_remove_once:Nn #1 {##1}
            \tl_set:Nn \l_@@_extension_tl {##1}
            \clist_map_break:
          }
      }
  }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}{\@@_if_detect_external:nT}
% Check if either the fontname ends with a known font extension.
%    \begin{macrocode}
\prg_new_conditional:Nnn \@@_if_detect_external:n {T}
  {
%<debug>  \typeout{:: @@_if_detect_external:n  { \exp_not:n {#1} } }
    \clist_map_inline:Nn \l_@@_extensions_clist
      {
        \bool_set_false:N \l_@@_tmpa_bool
        \exp_args:Nx % <- this should be handled earlier
        \tl_if_in:nnT {#1 <= end_of_string} {##1 <= end_of_string}
          { \bool_set_true:N \l_@@_tmpa_bool \clist_map_break: }
      }
    \bool_if:NTF \l_@@_tmpa_bool \prg_return_true: \prg_return_false:
  }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}{\@@_init_ttc:n}
% For TTC fonts we assume they will be loading the italic/bold fonts from the same file,
% so prepopulate the fontnames to avoid needing to do it manually.
%    \begin{macrocode}
\cs_new:Nn \@@_init_ttc:n
  {
    \str_if_eq:eeT { \str_lowercase:f {\l_@@_extension_tl} } {.ttc}
      {
        \@@_sanitise_fontname:Nn \l_@@_fontname_it_tl   {#1}
        \@@_sanitise_fontname:Nn \l_@@_fontname_bf_tl   {#1}
        \@@_sanitise_fontname:Nn \l_@@_fontname_bfit_tl {#1}
      }
  }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}{\@@_load_external_fontoptions:N}
% Load a possible \texttt{.fontspec} font configuration file.
% This file could set font-specific options for the font about to be loaded.
% The parameter should be a tokenlist containing a sanitised fontname.
%    \begin{macrocode}
\cs_new:Nn \@@_load_external_fontoptions:N
  {
    \bool_if:NT \l_@@_fontcfg_bool
      {
%<debug>  \typeout{:: @@_load_external_fontoptions:N \exp_not:N #1 }
        \tl_set:Nx \l_@@_ext_filename_tl {#1.fontspec}
        \tl_remove_all:Nn \l_@@_ext_filename_tl {~}
        \prop_if_in:NVF \g_@@_fontopts_prop #1
         {
          \exp_args:No \file_if_exist:nT { \l_@@_ext_filename_tl }
           { \file_input:n { \l_@@_ext_filename_tl } }
         }
      }
  }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}{\@@_extract_all_features:}
%    \begin{macrocode}
\cs_new:Nn \@@_extract_all_features:n
  {
%<debug>  \typeout{:: @@_extract_all_features:n { \unexpanded {#1} } }
    \bool_if:NTF \l_@@_disable_defaults_bool
      {
        \clist_set:Nx \l_@@_all_features_clist {#1}
      }
      {
        \prop_get:NVNF \g_@@_fontopts_prop \l_fontspec_fontname_tl \l_@@_fontopts_clist
          { \clist_clear:N \l_@@_fontopts_clist }

        \prop_get:NVNF \g_@@_fontopts_prop \l_@@_family_label_tl \l_@@_family_fontopts_clist
          { \clist_clear:N \l_@@_family_fontopts_clist }
        \tl_clear:N \l_@@_family_label_tl

        \clist_set:Nx \l_@@_all_features_clist
          {
            \g_@@_default_fontopts_clist,
            \l_@@_family_fontopts_clist,
            \l_@@_fontopts_clist,
            #1
          }
      }
  }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}{\@@_preparse_features:}
% \darg{feature options}
% \darg{font name}
% Perform the (multi-step) feature parsing process.
%
% Convert the requested features to font definition
% strings. First the features are parsed for information about font
% loading (whether it's a named font or external font, etc.), and then
% information is extracted for the names of the other shape fonts.
%    \begin{macrocode}
\cs_new:Nn \@@_preparse_features:
  {
%<debug>  \typeout{:: @@_preparse_features:}
%    \end{macrocode}
% Detect if external fonts are to be used, possibly automatically, and
% parse fontspec features for bold/italic fonts and their features.
%    \begin{macrocode}

    \@@_keys_set_known:nxN {fontspec-preparse-external}
      { \l_@@_all_features_clist }
      \l_@@_keys_leftover_clist

%    \end{macrocode}
% When \cmd{\l_fontspec_fontname_tl} is augmented with a prefix or whatever to create
% the name of the upright font (\cmd{\l_@@_fontname_up_tl}), this latter is the new `general
% font name' to use.
%    \begin{macrocode}
    \tl_set_eq:NN \l_fontspec_fontname_tl \l_@@_fontname_up_tl
    \@@_keys_set_known:nxN {fontspec-renderer} {\l_@@_keys_leftover_clist}
      \l_@@_keys_leftover_clist
    \@@_keys_set_known:nxN {fontspec-preparse} {\l_@@_keys_leftover_clist}
      \l_@@_fontfeat_clist
  }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}{\@@_load_font:}
%    \begin{macrocode}
\cs_new:Nn \@@_load_font:
  {
%<debug>\typeout{:: @@_load_font}

%<debug>\typeout{Set~ base~ font~ for~ preliminary~ analysis: \@@_construct_font_call:nn { \l_@@_fontname_up_tl } {} }
    \@@_primitive_font_set:NnnF \l_@@_test_font
      { \@@_construct_font_call:nn { \l_@@_fontname_up_tl } { \l_@@_pre_feat_sclist } }
      { \f@size pt - 2sp }
      { \@@_error:nx {font-not-found} {\l_@@_fontname_up_tl} }

%<debug>\typeout{Set~ base~ font~ properly: \@@_construct_font_call:nn { \l_@@_fontname_up_tl } {} }
    \@@_set_font_type:N \l_@@_test_font
    \@@_primitive_font_gset:Onn \l_@@_fontface_cs_tl
      {  \@@_construct_font_call:nn { \l_@@_fontname_up_tl } { \l_@@_pre_feat_sclist } }
      { \f@size pt + 2sp }

    \l_@@_fontface_cs_tl % this is necessary for LuaLaTeX to check the scripts properly

  }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}{\@@_construct_font_call:nn}
% Constructs the complete font invocation.
% \darg{Base name}
% \darg{Extension}
% \darg{TTC Index}
% \darg{Renderer}
% \darg{Optical size}
% \darg{Font features}
% We check if \meta{Font features} are empty and if so don't add in the separator colon.
%    \begin{macrocode}
\cs_new:Nn \@@_construct_font_call:nnnnnn
  {
%<XE>  " \@@_fontname_wrap:n { #1 #2 #3 }
%<LU>  " \@@_fontname_wrap:n { #1 #2 } #3
    #4 #5
    \str_if_eq:eeF {#6}{} {:#6} "
  }
%    \end{macrocode}
% In practice, we don't use the six-argument version, since most arguments are constructed on-the-fly:
%    \begin{macrocode}
\cs_new:Nn \@@_construct_font_call:nn
  {
    \@@_construct_font_call:nnnnnn
      {#1}
      \l_@@_extension_tl
      \l_@@_ttc_index_tl
      \l_@@_renderer_tl
      \l_@@_optical_size_tl
      {#2}
  }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}{\@@_font_is_file:,\@@_font_is_name:}
% The \cs{@@_fontname_wrap:n} command takes the font name and either passes it through unchanged or wraps it in the syntax for loading a font `by filename'.
% \XeTeX's syntax is followed since \pkg{luaotfload} provides compatibility.
%    \begin{macrocode}
\cs_new:Nn \@@_font_is_name:
  {
    \cs_set_eq:NN \@@_fontname_wrap:n \use:n
  }
%    \end{macrocode}
%
%    \begin{macrocode}
\cs_new:Nn \@@_font_is_file:
  {
    \cs_set:Npn \@@_fontname_wrap:n ##1 { [ \l_@@_font_path_tl ##1 ] }
  }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}{\@@_set_scriptlang:}
% Only necessary for OpenType fonts.
% First check if the font supports scripts, then apply defaults if
% none are explicitly requested. Similarly with the language settings.
%    \begin{macrocode}
\cs_new:Nn \@@_set_scriptlang:
  {
%<debug>  \typeout{:: _set_scriptlang:}
    \bool_if:NT \l_@@_firsttime_bool
      {
        \tl_if_empty:NF \l_@@_script_name_tl
          {
%<debug>  \typeout{:::: Script=\l_@@_script_name_tl, Language=\l_@@_lang_name_tl}
            \keys_set:nx {fontspec-opentype} {Script=\l_@@_script_name_tl}
            \keys_set:nx {fontspec-opentype} {Language=\l_@@_lang_name_tl}
          }
      }
  }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}{\@@_get_features:Nn}
%   This macro is a wrapper for |\keys_set:nn| which expands and adds a
%   default specification to the original passed options. It begins by
%   initialising the commands used to hold font-feature specific
%   strings.
%   Its argument is any additional features to prepend to the default.
%
% Do not set the colour if not explicitly spec'd else \verb|\color| (using
% specials) will not work.
%    \begin{macrocode}
\cs_new:Nn \@@_get_features:n
  {
%<debug>  \typeout{:: @@_get_features:Nn { \exp_not:n {#1} } }
    \@@_init_fontface:
    \@@_keys_set_known:nxN {fontspec-renderer} {\l_@@_fontfeat_clist,#1}
      \l_@@_keys_leftover_clist
    \@@_keys_set_known:nxN {fontspec} {\l_@@_keys_leftover_clist} \l_@@_keys_leftover_clist
%<*XE>
    \bool_if:NTF \l_@@_ot_bool
      {
%<debug>  \typeout{::: Setting~ keys~ for~ OpenType~ font~ features:~"\l_@@_keys_leftover_clist"}
        \keys_set:nV {fontspec-opentype} \l_@@_keys_leftover_clist
      }
      {
%<debug>  \typeout{::: Setting~ keys~ for~ AAT/Graphite~ font~ features:~"\l_@@_keys_leftover_clist"}
        \bool_if:nT { \l_@@_atsui_bool || \l_@@_graphite_bool }
          { \keys_set:nV {fontspec-aat} \l_@@_keys_leftover_clist }
      }
%</XE>
%<*LU>
%<debug>  \typeout{::: Setting~ keys~ for~ OpenType~ font~ features:~"\l_@@_keys_leftover_clist"}
    \keys_set:nV {fontspec-opentype} \l_@@_keys_leftover_clist
%</LU>

    \tl_if_empty:NF \l_@@_mapping_tl
      { \@@_update_featstr:n { mapping = \l_@@_mapping_tl } }

    \str_if_eq:eeF { \l_@@_hexcol_tl \l_@@_opacity_tl }
                     { \c_@@_hexcol_tl \c_@@_opacity_tl }
      { \@@_update_featstr:n { color = \l_@@_hexcol_tl\l_@@_opacity_tl } }
  }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}{\@@_save_family_needed:nTF}
% Check if the family is unique and, if so, save its information.
% (\cs{addfontfeature} and other macros use this data.)
% Then the font family and its shapes are defined in the NFSS.
%
% Now we have a unique (in fact, too unique!) string that contains
% the family name and every option in abbreviated form. This is used
% with a counter to create a simple NFSS family name for the font we're
% selecting.
%
%    \begin{macrocode}
\prg_new_conditional:Nnn \@@_save_family_needed:n { TF }
  {

%<debug>  \typeout{save~ family:~ #1}
%<debug>  \typeout{== fontid_tl: "\l_@@_fontid_tl".}

  \tl_if_empty:NTF \l_@@_nfss_fam_tl
    {
      \prop_get:NVNTF \g_@@_fontid_family_prop \l_@@_fontid_tl \l_@@_tmp_tl
        {
          \tl_gset_eq:NN \g_@@_nfss_family_tl \l_@@_tmp_tl
          \prg_return_false:
        }
        {
          \tl_set:Nx \l_@@_tmp_tl {#1}
          \tl_remove_all:Nn \l_@@_tmp_tl { ~ }
          \@@_save_fontid_family:VV \l_@@_fontid_tl \l_@@_tmp_tl
          \prg_return_true:
        }
    }
    {
      \tl_gset_eq:NN \g_@@_nfss_family_tl \l_@@_nfss_fam_tl
      \cs_undefine:c { g_@@_fontinfo_ \g_@@_nfss_family_tl _prop }
      \prg_return_true:
    }
  }
%    \end{macrocode}
%    \begin{macrocode}
\cs_new:Nn \@@_save_fontid_family:nn
  {
    \prop_get:NnNTF \g_@@_family_int_prop {#2} \l_@@_tmp_tl
      {
        \tl_set:Nx \l_@@_tmp_tl
          { \int_eval:n { \l_@@_tmp_tl + 1 } }
      }
      { \tl_set:Nn \l_@@_tmp_tl { 0 } }
    \prop_gput:NnV \g_@@_family_int_prop {#2} \l_@@_tmp_tl
    \tl_gset:Nx \g_@@_nfss_family_tl { #2 ( \l_@@_tmp_tl ) }
    \prop_gput:NnV \g_@@_fontid_family_prop {#1} \g_@@_nfss_family_tl
  }
\cs_generate_variant:Nn \@@_save_fontid_family:nn { VV }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}{\@@_save_family:nn}
% Saves the relevant font information for future processing.
%    \begin{macrocode}
\cs_new:Nn \@@_save_family:nn
  {
    \@@_save_fontinfo:n {#2}
    \@@_find_autofonts:
    \DeclareFontFamily{\g_@@_nfss_enc_tl}{\g_@@_nfss_family_tl}{}
    \@@_set_faces:
    \@@_info:nxx {defining-font} {#1} {#2}
  }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}{\@@_save_fontinfo:n}
% Saves the relevant font information for future processing.
%    \begin{macrocode}
\cs_new:Nn \@@_save_fontinfo:n
  {
    \prop_new:c    {g_@@_fontinfo_ \g_@@_nfss_family_tl _prop}
    \prop_gput:cnx {g_@@_fontinfo_ \g_@@_nfss_family_tl _prop} {fontname} { #1 }
    \prop_gput:cnx {g_@@_fontinfo_ \g_@@_nfss_family_tl _prop} {options}  { \l_@@_all_features_clist }
    \prop_gput:cnx {g_@@_fontinfo_ \g_@@_nfss_family_tl _prop} {fontdef}
      {
        \@@_construct_font_call:nn {\l_fontspec_fontname_tl}
          { \l_@@_pre_feat_sclist \g_@@_rawfeatures_sclist }
      }
    \prop_gput:cnV {g_@@_fontinfo_ \g_@@_nfss_family_tl _prop} {script-num} \l_@@_script_int
    \prop_gput:cnV {g_@@_fontinfo_ \g_@@_nfss_family_tl _prop} {lang-num}   \l_@@_language_int
    \prop_gput:cnV {g_@@_fontinfo_ \g_@@_nfss_family_tl _prop} {script-tag} \l_@@_script_tl
    \prop_gput:cnV {g_@@_fontinfo_ \g_@@_nfss_family_tl _prop} {lang-tag}   \l_@@_lang_tl
  }
%    \end{macrocode}
% \end{macro}
%
% \subsection{Setting font shapes in a family}
%
% All NFSS specifications take their default values, so if any of them
% are redefined, the shapes will be selected to fit in with the
% current state. For example, if \cmd\bfdefault\ is redefined to |b|, all
% bold shapes defined by this package will also be assigned to |b|.
%
% The combination shapes are searched first because they use information that may be redefined in the single cases.
% E.g., if no bold font is specified then |set_autofont| will attempt to set it.
% This has subtle/small ramifications on the logic of choosing the bold italic font.
%
% \begin{macro}{\@@_find_autofonts:}
%    \begin{macrocode}
\cs_new:Nn \@@_find_autofonts:
  {
    \bool_if:nF {\l_@@_noit_bool || \l_@@_nobf_bool}
      {
        \@@_set_autofont:Nnn \l_@@_fontname_bfit_tl {\l_@@_fontname_it_tl} {/B}
        \@@_set_autofont:Nnn \l_@@_fontname_bfit_tl {\l_@@_fontname_bf_tl} {/I}
        \@@_set_autofont:Nnn \l_@@_fontname_bfit_tl {\l_fontspec_fontname_tl} {/BI}
      }

    \bool_if:NF \l_@@_nobf_bool
      {
        \@@_set_autofont:Nnn \l_@@_fontname_bf_tl {\l_fontspec_fontname_tl} {/B}
      }

    \bool_if:NF \l_@@_noit_bool
      {
        \@@_set_autofont:Nnn \l_@@_fontname_it_tl {\l_fontspec_fontname_tl} {/I}
      }

    \@@_set_autofont:Nnn \l_@@_fontname_bfsl_tl {\l_@@_fontname_sl_tl} {/B}
  }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}{\@@_set_faces:}
%    \begin{macrocode}
\cs_new:Nn \@@_set_faces:
  {
    \@@_add_nfssfont:nnnn \mddefault \shapedefault \l_fontspec_fontname_tl \l_@@_fontfeat_up_clist
    \@@_add_nfssfont:nnnn \bfdefault \shapedefault \l_@@_fontname_bf_tl    \l_@@_fontfeat_bf_clist
    \@@_add_nfssfont:nnnn \mddefault \itdefault    \l_@@_fontname_it_tl    \l_@@_fontfeat_it_clist
    \@@_add_nfssfont:nnnn \mddefault \sldefault    \l_@@_fontname_sl_tl    \l_@@_fontfeat_sl_clist
    \@@_add_nfssfont:nnnn \mddefault \swdefault    \l_@@_fontname_sw_tl    \l_@@_fontfeat_sw_clist
    \@@_add_nfssfont:nnnn \bfdefault \itdefault    \l_@@_fontname_bfit_tl  \l_@@_fontfeat_bfit_clist
    \@@_add_nfssfont:nnnn \bfdefault \sldefault    \l_@@_fontname_bfsl_tl  \l_@@_fontfeat_bfsl_clist
    \@@_add_nfssfont:nnnn \bfdefault \swdefault    \l_@@_fontname_bfsw_tl  \l_@@_fontfeat_bfsw_clist
    \prop_map_inline:Nn \l_@@_nfssfont_prop { \@@_set_faces_aux:nnnnn ##2 }
  }
%    \end{macrocode}
%
%    \begin{macrocode}
\cs_new:Nn \@@_set_faces_aux:nnnnn
  {
    \fontspec_complete_fontname:Nn \l_@@_curr_fontname_tl {#3}
    \@@_make_font_shapes:Nnnnn \l_@@_curr_fontname_tl {#1} {#2} {#4} {#5}
  }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}{\fontspec_complete_fontname:Nn}
% This macro defines |#1| as the input with any |*| tokens of its input
% replaced by the font name. This lets us define supplementary fonts in full
% (``\texttt{Baskerville Semibold}'') or in abbreviation (``\texttt{* Semibold}'').
%    \begin{macrocode}
\cs_new:Nn \fontspec_complete_fontname:Nn
  {
    \tl_set:Nx #1 {#2}
    \tl_replace_all:Nnx #1 {*} {\l_@@_basename_tl}
%<LU>  \tl_remove_all:Nn #1 {~}
  }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}{\@@_add_nfssfont:nnnn}
% \darg{series}
% \darg{shape}
% \darg{fontname}
% \darg{fontspec features}
%    \begin{macrocode}
\cs_new:Nn \@@_add_nfssfont:nnnn
  {
    \tl_set:Nx \l_@@_this_font_tl {#3}

    \tl_if_empty:xTF {#4}
      { \clist_set:Nn \l_@@_sizefeat_clist {Size={-}} }
      { \@@_keys_set_known:nxN {fontspec-preparse-nested} {#4} \l_@@_tmp_tl }

    \tl_if_empty:NF \l_@@_this_font_tl
      {
        \prop_put:Nxx \l_@@_nfssfont_prop {#1/#2}
          { {#1}{#2}{\l_@@_this_font_tl}{#4}{\l_@@_sizefeat_clist} }
      }
  }
%    \end{macrocode}
% \end{macro}
%
%
% \subsubsection{Fonts}
%
% \begin{macro}{\@@_set_font_type:N}
% Now check if the font is to be rendered with \ATSUI\ or Harfbuzz. This will either
% be automatic (based on the font type), or specified by the user via a font feature.
%
% This macro sets booleans
% accordingly depending if the font in \cmd\l_fontspec_test_font\ is an \AAT\
% font or an OpenType font or a font with feature axes (either \AAT\ or
% Multiple Master), respectively.
%    \begin{macrocode}
\cs_new:Nn \@@_set_font_type:N
  {
%<debug>  \typeout{:: @@_set_font_type:}
%<*XE>
		\bool_set_false:N \l_@@_tfm_bool
		\bool_set_false:N \l_@@_atsui_bool
		\bool_set_false:N \l_@@_ot_bool
		\bool_set_false:N \l_@@_mm_bool
		\bool_set_false:N \l_@@_graphite_bool
		\ifcase\XeTeXfonttype #1
%<debug>  \typeout{:::: TFM}
			\bool_set_true:N \l_@@_tfm_bool
		\or
%<debug>  \typeout{:::: AAT}
			\bool_set_true:N \l_@@_atsui_bool
			\tl_if_empty:NT \l_@@_renderer_tl { \tl_set:Nn \l_@@_renderer_tl {/AAT} }
			\ifnum\XeTeXcountvariations #1 > 0\relax
%<debug>  \typeout{:::: MM}
				\bool_set_true:N \l_@@_mm_bool
			\fi
		\or
%<debug>  \typeout{:::: OpenType}
			\bool_set_true:N \l_@@_ot_bool
			\tl_if_empty:NT \l_@@_renderer_tl { \tl_set:Nn \l_@@_renderer_tl {/OT} }
		\or
%<debug>  \typeout{:::: Graphite}
			\bool_set_true:N \l_@@_graphite_bool
			\tl_if_empty:NT \l_@@_renderer_tl { \tl_set:Nn \l_@@_renderer_tl {/GR} }
		\fi
%</XE>
%    \end{macrocode}
% If automatic, the \cmd{\l_@@_renderer_tl} token list will still be
% empty (other suffices that could be added will be later in the feature
% processing), and if it is indeed still empty, assign it a value so that the
% other weights of the font are specifically loaded with the same renderer.
%
% LuaTeX only supports one:
%    \begin{macrocode}
%<*LU>
    \bool_set_true:N \l_@@_ot_bool
%</LU>
  }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}{\@@_set_autofont:Nnn}
% \darg{Font name tl}
% \darg{Base font name}
% \darg{Font name modifier}
%
% This function looks for font with \meta{name} and \meta{modifier} |#2#3|, and if found (i.e., different to font with name |#2|) stores it in tl |#1|. A modifier is something like |/B| to look for a bold font, for example.
%
% We can't match external fonts in this way (in \XeTeX\ anyway; todo: test with LuaTeX).
% If \meta{font name tl} is not empty, then it's already been specified by the user so abort.
% If \meta{Base font name} is not given, we also abort for obvious reasons.
%
% If \meta{font name tl} is empty, then proceed.
% If not found, \meta{font name tl} remains empty.
% Otherwise, we have a match.
%    \begin{macrocode}
\cs_new:Nn \@@_set_autofont:Nnn
  {
    \bool_if:NF \l_@@_external_bool
      {
        \tl_if_empty:xF {#2}
          {
            \tl_if_empty:NT #1
              {
                \@@_if_autofont:nnTF {#2} {#3}
                  { \tl_set:Nx #1 {#2#3} }
                  { \@@_info:nx {no-font-shape} {#2#3} }
              }
          }
      }
  }
%    \end{macrocode}
%
%    \begin{macrocode}
\prg_new_conditional:Nnn \@@_if_autofont:nn {T,TF}
  {
    \group_begin:
    \@@_primitive_font_set:Nnn \l_@@_tmpa_font { \@@_construct_font_call:nn {#1}   { \l_@@_pre_feat_sclist } } { \f@size pt + 1sp }
    \@@_primitive_font_set:Nnn \l_@@_tmpb_font { \@@_construct_font_call:nn {#1#2} { \l_@@_pre_feat_sclist } } { \f@size pt + 1sp }
    \str_if_eq:eeTF { \@@_primitive_font_get_name:N \l_@@_tmpa_font } { \@@_primitive_font_get_name:N \l_@@_tmpb_font }
      { \group_end: \prg_return_false: }
      { \group_end: \prg_return_true: }
  }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}{\@@_make_font_shapes:Nnnnn}
%  \darg{Font name}
%  \darg{Font series}
%  \darg{Font shape}
%  \darg{Font features}
%  \darg{Size features}
%   This macro eventually uses \cs{DeclareFontShape} to define the font shape in
%   question.
%    \begin{macrocode}
\cs_new:Nn \@@_make_font_shapes:Nnnnn
  {
    \group_begin:
      \@@_keys_set_known:nxN {fontspec-preparse-external} { #4 } \l_@@_leftover_clist
      \@@_load_fontname:Nn \l_fontspec_fontname_tl {#1}
      \@@_declare_shape:nnxx {#2} {#3} { \l_@@_fontopts_clist, \l_@@_leftover_clist } {#5}
    \group_end:
  }
%    \end{macrocode}
%
%    \begin{macrocode}
\cs_new:Nn \@@_load_fontname:Nn
  {
%<debug>    \typeout{:: @@_load_fontname:Nn \exp_not:N #1 (#1) {#2} }
    \@@_sanitise_fontname:Nn #1 {#2}
    \@@_load_external_fontoptions:N #1
    \prop_get:NVNF \g_@@_fontopts_prop #1 \l_@@_fontopts_clist
      { \clist_clear:N \l_@@_fontopts_clist }
    \keys_set_groups:nnV {fontspec/fontname} {getfontname} \l_@@_fontopts_clist
    \@@_primitive_font_set:OnnF \l_@@_fontface_cs_tl
      { \@@_construct_font_call:nn {#1} { \l_@@_pre_feat_sclist } } { \f@size pt + 2sp }
      { \@@_error:nx {font-not-found} {#2} }
  }
%    \end{macrocode}
%
%    \begin{macrocode}
\keys_define:nn {fontspec/fontname}
  {
    Font .tl_set:N = \l_fontspec_fontname_tl ,
    Font .groups:n = {getfontname} ,
  }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}{\@@_declare_shape:nnnn}
% \darg{Font series}
% \darg{Font shape}
% \darg{Font features}
% \darg{Size features}
% Wrapper for \cmd\DeclareFontShape.
% And finally the actual font shape declaration using \cmd\l_@@_nfss_tl\ defined above.
% \cmd\l_@@_postadjust_tl\ is defined in various places to deal with things like the hyphenation
% character and interword spacing.
%
% The main part is to loop through \feat{SizeFeatures} arguments, which are of the form
% {\par\centering |SizeFeatures={{<one>},{<two>},{<three>}}|.\par}
%    \begin{macrocode}
\cs_new:Nn \@@_declare_shape:nnnn
  {
%<debug>\typeout{=~ declare_shape:~{\l_fontspec_fontname_tl}~{#1}~{#2}}
    \tl_build_begin:N \l_@@_nfss_tl
    \tl_build_begin:N \l_@@_nfss_sc_tl
    \tl_set_eq:NN \l_@@_saved_fontname_tl \l_fontspec_fontname_tl

    \exp_args:Nx \clist_map_inline:nn {#4} { \@@_setup_single_size:nn {#3} {##1} }

    \tl_build_end:N \l_@@_nfss_tl
    \tl_build_end:N \l_@@_nfss_sc_tl

    \@@_declare_shapes_normal:nn {#1} {#2}
    \@@_declare_shapes_smcaps:nn {#1} {#2}
    \@@_declare_shape_slanted:nn {#1} {#2}
    \@@_declare_shapes_bx:nn     {#1} {#2}
    \@@_declare_shape_loginfo:nn {#1} {#2}
  }
%    \end{macrocode}
%
%    \begin{macrocode}
\cs_generate_variant:Nn \@@_declare_shape:nnnn {nnxx}
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}{\@@_setup_single_size:nn}
%    \begin{macrocode}
\cs_new:Nn \@@_setup_single_size:nn
  {
    \tl_clear:N \l_@@_size_tl
    \tl_set_eq:NN \l_@@_sizedfont_tl \l_@@_saved_fontname_tl % in case not spec'ed

    \keys_set_known:nxN {fontspec-sizing} { \exp_after:wN \use:n #2 }
      \l_@@_sizing_leftover_clist
    \tl_if_empty:NT \l_@@_size_tl { \@@_error:n {no-size-info} }
%<debug>\typeout{==~ size:~\l_@@_size_tl}

    % "normal"
    \@@_load_fontname:Nn \l_fontspec_fontname_tl {\l_@@_sizedfont_tl}
    \@@_setup_nfss:Nnnn \l_@@_nfss_tl {#1} {\l_@@_sizing_leftover_clist} {}
%<debug>    \typeout{===~ sized~ font:~ \l_@@_sizedfont_tl}

    % small caps
    \clist_set_eq:NN \l_@@_fontfeat_curr_clist \l_@@_fontfeat_sc_clist

    \bool_if:NF \l_@@_nosc_bool
      {
        \tl_if_empty:NTF \l_@@_fontname_sc_tl
          {
            \@@_make_smallcaps:TF
              {
%<debug>\typeout{====~Small~ caps~ found.}
                \clist_put_left:Nn \l_@@_fontfeat_curr_clist {Letters=SmallCaps}
              }
              {
%<debug>\typeout{====~Small~ caps~ not~ found.}
                \bool_set_true:N \l_@@_nosc_bool
              }
          }
          { \@@_load_fontname:Nn \l_fontspec_fontname_tl {\l_@@_fontname_sc_tl} }% local for each size
      }

    \bool_if:NF \l_@@_nosc_bool
      {
        \@@_setup_nfss:Nnnn \l_@@_nfss_sc_tl
          {#1} {\l_@@_sizing_leftover_clist} {\l_@@_fontfeat_curr_clist}
      }
  }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}{\@@_setup_nfss:Nnnn}
%    \begin{macrocode}
\cs_new:Nn \@@_setup_nfss:Nnnn
  {
%<debug>\typeout{====~Setup~NFSS~shape:~<\l_@@_size_tl>~\l_fontspec_fontname_tl}

    \@@_get_features:n { #2 , #3 , #4 }
%<debug>\typeout{====~Gathered~features:~\g_@@_rawfeatures_sclist}

    \tl_if_empty:NF \l_@@_scale_tl
      {
        \tl_set:Nx \l_@@_scale_tl { s*[\l_@@_scale_tl] }
      }

    \tl_build_put_right:Nx #1
      {
        <\l_@@_size_tl> \l_@@_scale_tl
        \@@_construct_font_call:nn { \l_fontspec_fontname_tl }
          { \l_@@_pre_feat_sclist \g_@@_rawfeatures_sclist }
      }
  }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}{\@@_declare_shapes_normal:nn}
%    \begin{macrocode}
\cs_new:Nn \@@_declare_shapes_normal:nn
  {
    \@@_DeclareFontShape:xxxxxx {\g_@@_nfss_enc_tl} {\g_@@_nfss_family_tl}
      {#1} {#2} {\l_@@_nfss_tl}{\l_@@_postadjust_tl}
  }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}{\@@_declare_shapes_smcaps:nn}
%    \begin{macrocode}
\cs_new:Nn \@@_declare_shapes_smcaps:nn
  {
    \tl_if_empty:NF \l_@@_nfss_sc_tl
     {
      \@@_DeclareFontShape:xxxxxx {\g_@@_nfss_enc_tl} {\g_@@_nfss_family_tl} {#1}
        { \@@_combo_sc_shape:n {#2} } {\l_@@_nfss_sc_tl} {\l_@@_postadjust_tl}
     }
  }
%    \end{macrocode}
%
%    \begin{macrocode}
\cs_new:Nn \@@_combo_sc_shape:n
  {
    \tl_if_exist:cTF { \@@_shape_merge:nn {#1} {\scdefault} }
         { \tl_use:c { \@@_shape_merge:nn {#1} {\scdefault} } }
         { \scdefault#1 }
  }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}{\@@_DeclareFontShape:nnnnnn}
%    \begin{macrocode}
\cs_new:Nn \@@_DeclareFontShape:nnnnnn
  {
%<debug>\typeout{DeclareFontShape:~{#1}{#2}{#3}{#4}...}
		\group_begin:
			\normalsize
			\cs_undefine:c {#1/#2/#3/#4/\f@size}
		\group_end:
		\DeclareFontShape{#1}{#2}{#3}{#4}{#5}{#6}
  }
\cs_generate_variant:Nn \@@_DeclareFontShape:nnnnnn {xxxxxx}
%    \end{macrocode}
%
% \begin{macro}{\@@_declare_shape_slanted:nn}
% This extra stuff for the slanted shape substitution is a little bit awkward.
% We define the slanted shape to be a synonym for it when (a)~we're defining an italic font, but also (b)~when the default slanted shape isn't `it'.
% (Presumably this turned up once in a test and I realised it caused problems. I doubt this would happen much.)
%
% We should test when a slanted font has been specified and not run this code if so, but the \verb|\@@_set_slanted:| code will overwrite this anyway if necessary.
%    \begin{macrocode}
\cs_new:Nn \@@_declare_shape_slanted:nn
  {
    \bool_if:nT
      {
          \str_if_eq_p:ee {#2} {\itdefault}  &&
        !(\str_if_eq_p:ee {\itdefault} {\sldefault})
      }
      {
        \@@_DeclareFontShape:xxxxxx {\g_@@_nfss_enc_tl}{\g_@@_nfss_family_tl}{#1}{\sldefault}
          {<->ssub*\g_@@_nfss_family_tl/#1/\itdefault}{\l_@@_postadjust_tl}
      }
  }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}{\@@_declare_shapes_bx:nn}
% Similar processing for setting up b/bx substitutions.
%    \begin{macrocode}
\cs_new:Nn \@@_declare_shapes_bx:nn
  {
    \bool_if:nT
      {
          \str_if_eq_p:ee {#1} {\bfdefault}  &&
        !(\str_if_eq_p:ee {\bfdefault} {bx})
      }
      {
        % bx/?
        \@@_DeclareFontShape:xxxxxx {\g_@@_nfss_enc_tl} {\g_@@_nfss_family_tl}
          {bx} {#2}
          { <->ssub*\g_@@_nfss_family_tl/\bfdefault/#2 }
          { \l_@@_postadjust_tl }

        % bx/sc -> b/sc
        \tl_if_empty:NF \l_@@_nfss_sc_tl
          {
            \@@_DeclareFontShape:xxxxxx {\g_@@_nfss_enc_tl} {\g_@@_nfss_family_tl}
              {bx} { \@@_combo_sc_shape:n {#2} }
              { <->ssub*\g_@@_nfss_family_tl/\bfdefault/#2 }
              { \l_@@_postadjust_tl }
          }

        % bx/sl -> bx/it
        \bool_if:nT
          {
              \str_if_eq_p:ee {#2} {\itdefault}  &&
            !(\str_if_eq_p:ee {\itdefault} {\sldefault})
          }
          {
            \@@_DeclareFontShape:xxxxxx {\g_@@_nfss_enc_tl} {\g_@@_nfss_family_tl}
              {bx} {\sldefault}
              { <->ssub*\g_@@_nfss_family_tl/bx/\itdefault }
              { \l_@@_postadjust_tl }
          }

      }
  }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}{\@@_declare_shape_loginfo:nn}
% Lastly some informative messaging.
%    \begin{macrocode}
\cs_new:Nn \@@_declare_shape_loginfo:nn
  {
    \tl_gput_right:Nx \g_@@_defined_shapes_tl
      {
        \exp_not:n { \\ }
        -~ \exp_not:N \str_case:nn {#1/#2}
        {
          {\mddefault/\shapedefault} {'normal'~}
          {\bfdefault/\shapedefault} {'bold'~}
          {\mddefault/\itdefault} {'italic'~}
          {\mddefault/\sldefault} {'slanted'~}
          {\mddefault/\swdefault} {'swash'~}
          {\bfdefault/\itdefault} {'bold~ italic'~}
          {\bfdefault/\sldefault} {'bold~ slanted'~}
          {\bfdefault/\swdefault} {'bold~ swash'~}
        } (#1/#2)~
        with~ NFSS~ spec.:~
        \l_@@_nfss_tl
        \exp_not:n { \\ }
        -~ \exp_not:N \str_case:nn { #1 / \@@_combo_sc_shape:n {#2} }
        {
          {\mddefault/\scdefault} {'small~ caps'~}
          {\bfdefault/\scdefault} {'bold~ small~ caps'~}
          {\mddefault/\scitdefault} {'italic~ small~ caps'~}
          {\bfdefault/\scitdefault} {'bold~ italic~ small~ caps'~}
          {\mddefault/\scsldefault} {'slanted~ small~ caps'~}
          {\bfdefault/\scsldefault} {'bold~ slanted~ small~ caps'~}
        }~( #1 / \@@_combo_sc_shape:n {#2} )~
        with~ NFSS~ spec.:~
        \l_@@_nfss_sc_tl
        \tl_if_empty:fF {\l_@@_postadjust_tl}
          {
          \exp_not:N \\ and~ font~ adjustment~ code:
          \exp_not:N \\ \l_@@_postadjust_tl
          }
      }
  }
%    \end{macrocode}
% Maybe |\str_if_eq:eeF| would be better?
% \end{macro}
%
%
% \subsubsection{Features}
%
% \begin{macro}{\l_@@_pre_feat_sclist}
% These are the features always applied to a font selection before other
% features.
%    \begin{macrocode}
\tl_set:Nn \l_@@_pre_feat_sclist
%<*XE>
  {
    \bool_if:NT \l_@@_ot_bool
      {
        \tl_if_empty:NF \l_@@_script_tl { script   = \l_@@_script_tl ; }
        \tl_if_empty:NF \l_@@_lang_tl   { language = \l_@@_lang_tl   ; }
      }
  }
%</XE>
%<*LU>
  {
    mode     = \l_@@_mode_tl   ;
    \tl_if_empty:NF \l_@@_shaper_tl { shaper = \l_@@_shaper_tl   ; }
    \tl_if_empty:NF \l_@@_script_tl { script   = \l_@@_script_tl ; }
    \tl_if_empty:NF \l_@@_lang_tl   { language = \l_@@_lang_tl   ; }
  }
%</LU>
%    \end{macrocode}
% \end{macro}
%
%
% \begin{macro}{\@@_make_ot_smallcaps:TF}
% \label{mac:makesmallcaps}
% This macro checks if the font contains small caps.
%    \begin{macrocode}
%<LU>\cs_new:Nn \@@_make_smallcaps:TF
%<XE>\cs_new:Nn \@@_make_ot_smallcaps:TF
  {
    \exp_args:No \@@_check_ot_feat:NnTF \l_@@_fontface_cs_tl {smcp} {#1} {#2}
  }
%<*XE>
\cs_new:Nn \@@_make_smallcaps:TF
  {
    \bool_if:NTF \l_@@_ot_bool
      { \@@_make_ot_smallcaps:TF {#1} {#2} }
      {
        \bool_if:NT \l_@@_atsui_bool
          {
            \exp_args:No \@@_make_AAT_feature_string:NnnTF
              \l_@@_fontface_cs_tl {3} {3} {#1} {#2}
          }
      }
  }
%</XE>
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}{\@@_update_featstr:n}
% \cmd{\g_@@_rawfeatures_sclist} is the string used to define the list of specific
% font features. Each time another font feature is requested, this
% macro is used to add that feature to the list. Font features are
% separated by semicolons.
%    \begin{macrocode}
\cs_new:Nn \@@_update_featstr:n
  {
%<debug>            \typeout{:::: @@_update_featstr:n {#1}}
    \bool_if:NF \l_@@_firsttime_bool
      {
        \tl_gset:Nx \g_@@_single_feat_tl { #1 }
%<debug>            \typeout{::::~ Adding~ feature.}
        \tl_gput_right:Nx  \g_@@_rawfeatures_sclist {#1;}
      }
  }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}{\@@_remove_clashing_featstr:n}
%    \begin{macrocode}
\cs_new:Nn \@@_remove_clashing_featstr:n
  {
%<debug>    \typeout{:::: @@_remove_clashing_featstr:n {#1}}
    \clist_map_inline:nn {#1}
      {
%<debug>        \typeout{::::~ Removing~ feature~ "##1;"}
        \tl_gremove_all:Nn \g_@@_rawfeatures_sclist {##1;}
      }
  }
\cs_generate_variant:Nn \@@_remove_clashing_featstr:n {x}
%    \end{macrocode}
% \end{macro}
%
% \subsection{Initialisation}
%
% \begin{macro}{\@@_init:}
% Initialisations that need to occur once per fontspec font invocation.
% (Some of these may be redundant.
% Check whether they're assigned to globally or not.)
%    \begin{macrocode}
\cs_set:Npn \@@_init:
  {
%<debug>  \typeout{:: @@_init:}
    \bool_set_false:N \l_@@_ot_bool
    \bool_set_true:N \l_@@_firsttime_bool
    \@@_font_is_name:
    \tl_clear:N \l_@@_font_path_tl
    \tl_clear:N \l_@@_optical_size_tl
    \tl_clear:N \l_@@_ttc_index_tl
    \tl_clear:N \l_@@_renderer_tl
    \tl_gclear:N \g_@@_defined_shapes_tl
    \tl_gclear:N \g_@@_curr_series_tl
    \tl_gset_eq:NN \g_@@_nfss_enc_tl \g_fontspec_encoding_tl
%<*LU>
    \tl_set:Nn \l_@@_mode_tl {node}
    \int_set:Nn \prehyphenchar { `\- } % fixme
    \int_zero:N \posthyphenchar        % fixme
    \int_zero:N \preexhyphenchar       % fixme
    \int_zero:N \postexhyphenchar      % fixme
%</LU>
  }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}{\@@_init_fontface:}
% Executed in \cs{@@_get_features:Nn}.
%    \begin{macrocode}
\cs_new:Nn \@@_init_fontface:
  {
    \tl_gclear:N \g_@@_rawfeatures_sclist
    \tl_clear:N \l_@@_scale_tl
    \tl_set_eq:NN \l_@@_opacity_tl \c_@@_opacity_tl
    \tl_set_eq:NN \l_@@_hexcol_tl \c_@@_hexcol_tl
    \tl_set_eq:NN \l_@@_postadjust_tl \c_@@_postadjust_tl
    \tl_clear:N \l_@@_wordspace_adjust_tl
    \tl_clear:N \l_@@_punctspace_adjust_tl
  }
%    \end{macrocode}
% \end{macro}
%
%
% \subsection{Miscellaneous}
%
% \begin{macro}{\@@_ot_validate_tag:n}
% This macro takes an OpenType tag and validates it.
%    \begin{macrocode}
%<*LU>
%    \end{macrocode}
%    \begin{macrocode}
\cs_new_protected:Nn \@@_ot_validate_tag:n
  {
    \@@_ot_validate_tag:w #1 \q_nil
  }
\cs_generate_variant:Nn \@@_ot_validate_tag:n {x}
%    \end{macrocode}
%
%    \begin{macrocode}
\cs_set:Npn \@@_ot_validate_tag:w #1 #2 \q_nil
  {
    \bool_if:nTF { \str_if_eq_p:nn {#1} {+} || \str_if_eq_p:nn {#1} {-} }
      { \@@_ot_validate_tag_aux:w #2   \c_empty_tl \c_empty_tl \q_nil }
      { \@@_ot_validate_tag_aux:w #1#2 \c_empty_tl \c_empty_tl \q_nil }
  }
%    \end{macrocode}
%
%    \begin{macrocode}
\cs_set:Npn \@@_ot_validate_tag_aux:w #1#2#3#4#5 \q_nil
  {
    \int_compare:nT { \tl_count:n {#5} > 2 }
      { \@@_error:nx {ot-tag-too-long} {#1#2#3#4#5} }
  }
%    \end{macrocode}
%    \begin{macrocode}
%</LU>
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}{\@@_iv_str_to_num:Nn}
% This macro takes a four character string and converts it to the
% numerical representation required for \XeTeX\ OpenType script/language/feature
% purposes. The output is stored in |#1|.
%
% This code is not used in Lua\TeX, as the checking for that engine is done via Lua code
% provided by \pkg{luaotfload}.
%    \begin{macrocode}
%<*XE>
%    \end{macrocode}
%
%    \begin{macrocode}
\cs_new:Nn \@@_iv_str_to_num:Nn
  {
%<debug>\typeout{_iv_str_to_num:~#1~/~#2}
    \@@_strip_leading_sign:Nw #1#2 \q_nil
  }
\cs_generate_variant:Nn \@@_iv_str_to_num:Nn {Nx}
%    \end{macrocode}
%
% The input can be of the form of any of these:
% `|abcd|', `|abc|', `|abc |', `|ab|', `|ab  |', \etc.
% (It is assumed the first two chars are \emph{always} not spaces.) So this macro
% reads in the string padded with \cmd\@empty s,
% and anything beyond four chars is snipped. The \cmd\@empty s then are used to reconstruct
% the spaces in the string to number calculation.
%
% For backwards compatibility this code also strips a leading |+| or |-|.
%
%    \begin{macrocode}
\cs_set:Npn \@@_strip_leading_sign:Nw #1#2#3 \q_nil
  {
    \bool_if:nTF { \str_if_eq_p:nn {#2} {+} || \str_if_eq_p:nn {#2} {-} }
      { \@@_iv_str_to_num:w #1 \q_nil #3   \c_empty_tl \c_empty_tl \q_nil }
      { \@@_iv_str_to_num:w #1 \q_nil #2#3 \c_empty_tl \c_empty_tl \q_nil }
  }
%    \end{macrocode}
% If input string (after sign is stripped) is more than 4 chars, \verb|#6| will contain
% `\meta{excess}\cs{c_empty_tl}\cs{c_empty_tl}'. Therefore use \verb|#6| to verify string length.
%    \begin{macrocode}
\cs_set:Npn \@@_iv_str_to_num:w #1 \q_nil #2#3#4#5#6 \q_nil
  {
    \int_compare:nT { \tl_count:n {#6} > 2 }
      { \@@_error:nx {ot-tag-too-long} {#2#3#4#5#6} }

    \int_set:Nn #1
      {
          `#2 * "1000000
        + `#3 * "10000
        + \ifx \c_empty_tl #4 32 \else `#4 \fi * "100
        + \ifx \c_empty_tl #5 32 \else `#5 \fi
      }
  }
%    \end{macrocode}
%
%    \begin{macrocode}
%</XE>
%    \end{macrocode}
% \end{macro}
%
%
% \iffalse
%    \begin{macrocode}
%</fontspec>
%    \end{macrocode}
% \fi


\endinput

% /©
% ------------------------------------------------
% The FONTSPEC package  <wspr.io/fontspec>
% ------------------------------------------------
% Copyright  2004-2022  Will Robertson, LPPL "maintainer"
% 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/>.
% ------------------------------------------------
% ©/
