% \section{Internals} % % \iffalse % \begin{macrocode} %<*fontspec&(xetexx|luatex)> % \end{macrocode} % \fi % % \subsection{The main function for setting fonts} % \label{sec:codeinternal} % % \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 { %\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} } \@@_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 } %\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} % \@@_warning:nxx {defining-font} {#1} {#2} } { % \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 % \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} % \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} { % \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 { % \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 { % \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: { % \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: { %\typeout{:: @@_load_font} %\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: %\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 { " \@@_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 ] } % \cs_set:Npn \@@_fontname_wrap:n ##1 { file:\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 { % \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 { % \typeout{::: Setting~ keys~ for~ OpenType~ font~ features:~"\l_@@_keys_leftover_clist"} \keys_set:nx {fontspec-opentype} {\l_@@_keys_leftover_clist} } { % \typeout{::: Setting~ keys~ for~ AAT~ font~ features:~"\l_@@_keys_leftover_clist"} \bool_if:NT \l_@@_atsui_bool { \keys_set:nx {fontspec-aat} {\l_@@_keys_leftover_clist} } } % %<*luatex> % \typeout{::: Setting~ keys~ for~ OpenType~ font~ features:~"\l_@@_keys_leftover_clist"} \keys_set:nx {fontspec-opentype} {\l_@@_keys_leftover_clist} % \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} { % \typeout{save~ family:~ #1} % \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} % \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: { % \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} } } } % %<*luatex> \bool_set_true:N \l_@@_ot_bool % } % \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 { % \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={{},{},{}}|.\par} % \begin{macrocode} \cs_new:Nn \@@_declare_shape:nnnn { %\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} } %\typeout{==~ size:~\l_@@_size_tl} % "normal" \@@_load_fontname:n {\l_@@_sizedfont_tl} \@@_setup_nfss:Nnnn \l_@@_nfss_tl {#1} {\l_@@_sizing_leftover_clist} {} % \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 { %\typeout{====~Small~ caps~ found.} \clist_put_left:Nn \l_@@_fontfeat_curr_clist {Letters=SmallCaps} } { %\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 { %\typeout{====~Setup~NFSS~shape:~<\l_@@_size_tl>~\l_fontspec_fontname_tl} \@@_get_features:Nn \l_@@_rawfeatures_sclist { #2 , #3 , #4 } %\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 { %\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} \clist_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 ; } } } % %<*luatex> { mode = \l_fontspec_mode_tl ; \tl_if_empty:NF \l_fontspec_script_tl { script = \l_fontspec_script_tl ; language = \l_fontspec_lang_tl ; } } % % \end{macrocode} % \end{macro} % % % \begin{macro}{\@@_make_ot_smallcaps:TF} % \label{mac:makesmallcaps} % This macro checks if the font contains small caps. % \begin{macrocode} %\cs_set:Nn \@@_make_smallcaps:TF %\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} } } } % % \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 { % \typeout{:::: @@_update_featstr:n {#1}} \bool_if:NF \l_@@_firsttime_bool { \tl_gset:Nx \g_@@_single_feat_tl { #1 } % \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 { % \typeout{:::: @@_remove_clashing_featstr:n {#1}} \clist_map_inline:nn {#1} { % \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: { % \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 % } % \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. % \begin{macrocode} \cs_set:Nn \@@_iv_str_to_num:Nn { \@@_iv_str_to_num:w #1 \q_nil #2 \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} % % \end{macrocode} % \fi