% \section{Internals}
%
% \iffalse
%    \begin{macrocode}
%<*fontspec&(xetexx|luatex)>
%    \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:Nn \l_fontspec_fontname_tl {#2}

  \@@_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:Nn \l_@@_rawfeatures_sclist {}
  \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:
 }
%    \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}
%<luatex>  \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_x:nnT { \str_lower_case: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:Nn}
% Load a possible \texttt{.fontspec} font configuration file.
% This file could set font-specific options for the font about to be loaded.
%    \begin{macrocode}
\cs_new:Nn \@@_load_external_fontoptions:Nn
  {
    \bool_if:NT \l_@@_fontcfg_bool
      {
%<debug>  \typeout{:: @@_load_external_fontoptions:Nn \exp_not:N #1 {#2} }
        \@@_sanitise_fontname:Nn #1 {#2}
        \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:Nnn \l_fontspec_font
     { \@@_construct_font_call:nn { \l_@@_fontname_up_tl } {} } {\f@size pt}
  \@@_primitive_font_if_null:NT \l_fontspec_font { \@@_error:nx {font-not-found} {\l_@@_fontname_up_tl} }
  \@@_set_font_type:
%<debug>\typeout{Set~ base~ font~ properly: \@@_construct_font_call:nn { \l_@@_fontname_up_tl } {} }
  \@@_primitive_font_gset:Nnn \l_fontspec_font
     { \@@_construct_font_call:nn { \l_@@_fontname_up_tl } {} } {\f@size pt}
  \l_fontspec_font % 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_set:Nn \@@_construct_font_call:nnnnnn
 {
%<xetexx>  " \@@_fontname_wrap:n { #1 #2 #3 }
%<luatex>  " \@@_fontname_wrap:n { #1 #2 } #3
    #4 #5
    \str_if_eq_x:nnF {#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_set:Nn \@@_construct_font_call:nn
 {
  \@@_construct_font_call:nnnnnn
    {#1}
    \l_@@_extension_tl
    \l_@@_ttc_index_tl
    \l_fontspec_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
  }
\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:
 {
  \bool_if:NT \l_@@_firsttime_bool
   {
    \tl_if_empty:NTF \l_@@_script_name_tl
     {
      \@@_check_script:nTF {latn}
       {
        \tl_set:Nn \l_@@_script_name_tl {Latin}
        \tl_if_empty:NT \l_@@_lang_name_tl
         {
          \tl_set:Nn \l_@@_lang_name_tl {Default}
         }
        \keys_set:nx {fontspec-opentype} {Script=\l_@@_script_name_tl}
        \keys_set:nx {fontspec-opentype} {Language=\l_@@_lang_name_tl}
       }
       {
        \@@_info:n {no-scripts}
       }
     }
     {
      \tl_if_empty:NT \l_@@_lang_name_tl
       {
        \tl_set:Nn \l_@@_lang_name_tl {Default}
       }
      \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_set:Nn \@@_get_features:Nn
 {
%<debug>  \typeout{:: @@_get_features:Nn \exp_not:N #1 { \exp_not:n {#2} } }
  \@@_init_fontface:
  \@@_keys_set_known:nxN {fontspec-renderer} {\l_@@_fontfeat_clist,#2}
    \l_@@_keys_leftover_clist
  \@@_keys_set_known:nxN {fontspec} {\l_@@_keys_leftover_clist} \l_@@_keys_leftover_clist
%<*xetexx>
  \bool_if:NTF \l_@@_ot_bool
    {
%<debug>  \typeout{::: Setting~ keys~ for~ OpenType~ font~ features:~"\l_@@_keys_leftover_clist"}
    %  \tracingall
      \keys_set:nV {fontspec-opentype} \l_@@_keys_leftover_clist
    %  \EROROR
    }
    {
%<debug>  \typeout{::: Setting~ keys~ for~ AAT~ font~ features:~"\l_@@_keys_leftover_clist"}
      \bool_if:NT \l_@@_atsui_bool
        { \keys_set:nV {fontspec-aat} \l_@@_keys_leftover_clist }
    }
%</xetexx>
%<*luatex>
%<debug>  \typeout{::: Setting~ keys~ for~ OpenType~ font~ features:~"\l_@@_keys_leftover_clist"}
  \keys_set:nV {fontspec-opentype} \l_@@_keys_leftover_clist
%</luatex>

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

  \str_if_eq_x:nnF { \l_@@_hexcol_tl \l_@@_opacity_tl }
                   { \g_@@_hexcol_tl \g_@@_opacity_tl }
    { \@@_update_featstr:n { color = \l_@@_hexcol_tl\l_@@_opacity_tl } }

  \tl_set_eq:NN #1 \l_@@_rawfeatures_sclist
 }
%    \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".}

  \cs_if_exist:NT \l_@@_nfss_fam_tl
   {
    \cs_set_eq:cN {g_@@_UID_\l_@@_fontid_tl} \l_@@_nfss_fam_tl
   }
  \cs_if_exist:cF {g_@@_UID_\l_@@_fontid_tl}
   {
    % The font name is fully expanded, in case it's defined in terms of macros, before having its spaces zapped:
    \tl_set:Nx \l_@@_tmp_tl {#1}
    \tl_remove_all:Nn \l_@@_tmp_tl {~}

    \cs_if_exist:cTF {g_@@_family_ \l_@@_tmp_tl _int}
     { \int_gincr:c  {g_@@_family_ \l_@@_tmp_tl _int} }
     { \int_new:c    {g_@@_family_ \l_@@_tmp_tl _int} }

    \tl_gset:cx {g_@@_UID_\l_@@_fontid_tl}
     {
      \l_@@_tmp_tl ( \int_use:c {g_@@_family_ \l_@@_tmp_tl _int} )
     }
   }
  \tl_gset:Nv \l_fontspec_family_tl {g_@@_UID_\l_@@_fontid_tl}
  \cs_if_exist:cTF {g_@@_ \l_fontspec_family_tl _prop}
    \prg_return_false: \prg_return_true:
 }
%    \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{\l_@@_nfss_enc_tl}{\l_fontspec_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_@@_ \l_fontspec_family_tl _prop}
  \prop_gput:cnx {g_@@_ \l_fontspec_family_tl _prop} {fontname} { #1 }
  \prop_gput:cnx {g_@@_ \l_fontspec_family_tl _prop} {options}  { \l_@@_all_features_clist }
  \prop_gput:cnx {g_@@_ \l_fontspec_family_tl _prop} {fontdef}
   {
    \@@_construct_font_call:nn {\l_fontspec_fontname_tl}
      { \l_@@_pre_feat_sclist \l_@@_rawfeatures_sclist }
   }
  \prop_gput:cnV {g_@@_ \l_fontspec_family_tl _prop} {script-num} \l_@@_script_int
  \prop_gput:cnV {g_@@_ \l_fontspec_family_tl _prop} {lang-num} \l_@@_language_int
  \prop_gput:cnV {g_@@_ \l_fontspec_family_tl _prop} {script-tag} \l_fontspec_script_tl
  \prop_gput:cnV {g_@@_ \l_fontspec_family_tl _prop} {lang-tag} \l_fontspec_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 \updefault \l_fontspec_fontname_tl      \l_@@_fontfeat_up_clist
  \@@_add_nfssfont:nnnn \bfdefault \updefault \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 \bfdefault \itdefault \l_@@_fontname_bfit_tl \l_@@_fontfeat_bfit_clist
  \@@_add_nfssfont:nnnn \bfdefault \sldefault \l_@@_fontname_bfsl_tl \l_@@_fontfeat_bfsl_clist

  \prop_map_inline:Nn \l_@@_nfssfont_prop { \@@_set_faces_aux:nnnnn ##2 }
 }
\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_set:Nn \fontspec_complete_fontname:Nn
 {
  \tl_set:Nx #1 {#2}
  \tl_replace_all:Nnx #1 {*} {\l_@@_basename_tl}
%<luatex>  \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:}
% 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_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:
 {
%<debug>  \typeout{:: @@_set_font_type:}
%<*xetexx>
  \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\l_fontspec_font
    \bool_set_true:N \l_@@_tfm_bool
  \or
    \bool_set_true:N \l_@@_atsui_bool
    \ifnum\XeTeXcountvariations\l_fontspec_font > \c_zero
      \bool_set_true:N \l_@@_mm_bool
    \fi
  \or
    \bool_set_true:N \l_@@_ot_bool
  \fi
%    \end{macrocode}
% If automatic, the \cmd{\l_fontspec_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.
%    \begin{macrocode}
  \tl_if_empty:NT \l_fontspec_renderer_tl
   {
    \bool_if:NTF \l_@@_atsui_bool
     { \tl_set:Nn \l_fontspec_renderer_tl {/AAT} }
     {
       \bool_if:NT \l_@@_ot_bool
        { \tl_set:Nn \l_fontspec_renderer_tl {/OT} }
     }
   }
%</xetexx>
%<*luatex>
  \bool_set_true:N \l_@@_ot_bool
%</luatex>
 }
%    \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} }
     }
   }
   }
 }

\prg_new_conditional:Nnn \@@_if_autofont:nn {T,TF}
 {
  \@@_primitive_font_set:Nnn \l_tmpa_font { \@@_construct_font_call:nn {#1} {}  } {\f@size pt}
  \@@_primitive_font_set:Nnn \l_tmpb_font { \@@_construct_font_call:nn {#1#2} {} } {\f@size pt}
  \str_if_eq_x:nnTF { \fontname \l_tmpa_font } { \fontname \l_tmpb_font }
   { \prg_return_false: }
   { \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:n {#1}
    \@@_declare_shape:nnxx {#2} {#3} { \l_@@_fontopts_clist, \l_@@_leftover_clist } {#5}
  \group_end:
 }

\cs_new:Nn \@@_load_fontname:n
 {
%<debug>    \typeout{:: @@_load_fontname:n {#1} }
    \@@_load_external_fontoptions:Nn \l_fontspec_fontname_tl {#1}
    \prop_get:NVNF \g_@@_fontopts_prop \l_fontspec_fontname_tl \l_@@_fontopts_clist
     { \clist_clear:N \l_@@_fontopts_clist }
    \@@_primitive_font_set:Nnn \l_fontspec_font { \@@_construct_font_call:nn {\l_fontspec_fontname_tl} {} } {\f@size pt}
    \@@_primitive_font_if_null:NT \l_fontspec_font { \@@_error:nx {font-not-found} {#1} }
 }
%    \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_clear:N \l_@@_nfss_tl
  \tl_clear: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} }

  \@@_declare_shapes_normal:nn {#1} {#2}
  \@@_declare_shapes_smcaps:nn {#1} {#2}
  \@@_declare_shape_slanted:nn {#1} {#2}
  \@@_declare_shape_loginfo:nn {#1} {#2}
 }
\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:n {\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:n {\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:Nn \l_@@_rawfeatures_sclist { #2 , #3 , #4 }
%<debug>\typeout{====~Gathered~features:~\l_@@_rawfeatures_sclist}

  \tl_put_right:Nx #1
   {
    <\l_@@_size_tl> \l_@@_scale_tl
      \@@_construct_font_call:nn { \l_fontspec_fontname_tl }
        { \l_@@_pre_feat_sclist \l_@@_rawfeatures_sclist }
   }
 }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}{\@@_declare_shapes_normal:nn}
%    \begin{macrocode}
\cs_new:Nn \@@_declare_shapes_normal:nn
  {
    \@@_DeclareFontShape:xxxxxx {\l_@@_nfss_enc_tl} {\l_fontspec_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 {\l_@@_nfss_enc_tl} {\l_fontspec_family_tl} {#1}
        { \@@_combo_sc_shape:n {#2} } {\l_@@_nfss_sc_tl} {\l_@@_postadjust_tl}
     }
  }

\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 }
  }
%    \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_x_p:nn {#2} {\itdefault}  &&
    !(\str_if_eq_x_p:nn {\itdefault} {\sldefault})
   }
   {
    \@@_DeclareFontShape:xxxxxx {\l_@@_nfss_enc_tl}{\l_fontspec_family_tl}{#1}{\sldefault}
      {<->ssub*\l_fontspec_family_tl/#1/\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 \l_fontspec_defined_shapes_tl
   {
    \exp_not:n { \\ }
    -~ \exp_not:N \str_case:nn {#1/#2}
     {
       {\mddefault/\updefault} {'normal'~}
       {\bfdefault/\updefault} {'bold'~}
       {\mddefault/\itdefault} {'italic'~}
       {\mddefault/\sldefault} {'slanted'~}
       {\bfdefault/\itdefault} {'bold~ italic'~}
       {\bfdefault/\sldefault} {'bold~ slanted'~}
     } (#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/\itscdefault} {'italic~ small~ caps'~}
       {\bfdefault/\itscdefault} {'bold~ italic~ small~ caps'~}
       {\mddefault/\slscdefault} {'slanted~ small~ caps'~}
       {\bfdefault/\slscdefault} {'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_x:nnF| 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
%<*xetexx>
 {
  \bool_if:NT \l_@@_ot_bool
   {
    \tl_if_empty:NF \l_fontspec_script_tl
     {
      script   = \l_fontspec_script_tl ;
      language = \l_fontspec_lang_tl   ;
     }
   }
 }
%</xetexx>
%<*luatex>
 {
  mode     = \l_fontspec_mode_tl   ;
  \tl_if_empty:NF \l_fontspec_script_tl
   {
    script   = \l_fontspec_script_tl ;
    language = \l_fontspec_lang_tl   ;
   }
 }
%</luatex>
%    \end{macrocode}
% \end{macro}
%
%
% \begin{macro}{\@@_make_ot_smallcaps:TF}
% \label{mac:makesmallcaps}
% This macro checks if the font contains small caps.
%    \begin{macrocode}
%<luatex>\cs_set:Nn \@@_make_smallcaps:TF
%<xetexx>\cs_set:Nn \@@_make_ot_smallcaps:TF
 {
  \@@_check_ot_feat:nTF {smcp} {#1} {#2}
 }
%<*xetexx>
\cs_set:Nn \@@_make_smallcaps:TF
 {
  \bool_if:NTF \l_@@_ot_bool
   { \@@_make_ot_smallcaps:TF {#1} {#2} }
   {
     \bool_if:NT \l_@@_atsui_bool
      { \@@_make_AAT_feature_string:nnTF {3}{3} {#1} {#2} }
   }
 }
%</xetexx>
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}{\@@_update_featstr:n}
% \cmd{\l_@@_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  \l_@@_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 \l_@@_rawfeatures_sclist {##1;}
      }
  }
%    \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_fontspec_renderer_tl
  \tl_clear:N \l_fontspec_defined_shapes_tl
  \tl_clear:N \g_@@_curr_series_tl
  \tl_gset_eq:NN \l_@@_nfss_enc_tl \g_fontspec_encoding_tl

%<*luatex>
  \tl_set:Nn \l_fontspec_mode_tl {node}
  \int_set:Nn \luatex_prehyphenchar:D { `\- } % fixme
  \int_zero:N \luatex_posthyphenchar:D        % fixme
  \int_zero:N \luatex_preexhyphenchar:D       % fixme
  \int_zero:N \luatex_postexhyphenchar:D      % fixme
%</luatex>
 }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}{\@@_init_fontface:}
% Executed in \cs{@@_get_features:Nn}.
%    \begin{macrocode}
\cs_new:Nn \@@_init_fontface:
  {
    \tl_clear:N \l_@@_rawfeatures_sclist
    \tl_clear:N \l_@@_scale_tl
    \tl_set_eq:NN \l_@@_opacity_tl \g_@@_opacity_tl
    \tl_set_eq:NN \l_@@_hexcol_tl \g_@@_hexcol_tl
    \tl_set_eq:NN \l_@@_postadjust_tl \g_@@_postadjust_tl
    \tl_clear:N \l_@@_wordspace_adjust_tl
    \tl_clear:N \l_@@_punctspace_adjust_tl
  }
%    \end{macrocode}
% \end{macro}
%
%
% \subsection{Miscellaneous}
%
% \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|.
%
% The reason it's ugly is because 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, delimited by a space; this input is 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:Nn \@@_iv_str_to_num:Nn
  {
    \@@_strip_leading_sign:Nw #1#2 \q_nil
  }
\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 }
  }
\cs_set:Npn \@@_iv_str_to_num:w #1 \q_nil #2#3#4#5#6 \q_nil
 {
  \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
   }
 }
\cs_generate_variant:Nn \@@_iv_str_to_num:Nn {No}
%    \end{macrocode}
% \end{macro}
%
%
% \iffalse
%    \begin{macrocode}
%</fontspec&(xetexx|luatex)>
%    \end{macrocode}
% \fi
