%%^^A%% fontspec-code-internal.dtx -- part of 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 { %\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 } %\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} %\@@_warning:nxx {defining-font} {#1} {#2} } { %\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} % \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: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 { % \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 { % \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: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} } %\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 { % " \@@_fontname_wrap:n { #1 #2 #3 } % " \@@_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:,\@@_font_is_kpse:} % 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'. % For Lua\TeX\ there are two kinds kinds of filename based loading supported: Regular filename lookups which include system fonts and lookups restricted to kpse. % \begin{macrocode} \cs_new:Nn \@@_font_is_name: { % \cs_set_eq:NN \@@_fontname_wrap:n \use:n % \cs_set:Npn \@@_fontname_wrap:n ##1 { name: ##1 } } % \end{macrocode} % % \begin{macrocode} \cs_new:Nn \@@_font_is_file: { \cs_set:Npn \@@_fontname_wrap:n ##1 { [ \l_@@_font_path_tl ##1 ] } } % \end{macrocode} % % \begin{macrocode} %<*LU> \cs_new:Nn \@@_font_is_kpse: { \cs_set:Npn \@@_fontname_wrap:n ##1 { kpse: ##1 } } % %\cs_new_eq:NN \@@_font_is_kpse: \@@_font_is_file: % \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: { % \typeout{:: _set_scriptlang:} \bool_if:NT \l_@@_firsttime_bool { \tl_if_empty:NF \l_@@_script_name_tl { % \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 { % \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 { % \typeout{::: Setting~ keys~ for~ OpenType~ font~ features:~"\l_@@_keys_leftover_clist"} \keys_set:nV {fontspec-opentype} \l_@@_keys_leftover_clist } { % \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 } } % %<*LU> % \typeout{::: Setting~ keys~ for~ OpenType~ font~ features:~"\l_@@_keys_leftover_clist"} \keys_set:nV {fontspec-opentype} \l_@@_keys_leftover_clist % \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 } } % { \@@_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 } { % \typeout{save~ family:~ #1} % \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 \@@_get_variations: } } \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} % \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 { % \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 % \typeout{:::: TFM} \bool_set_true:N \l_@@_tfm_bool \or % \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 % \typeout{:::: MM} \bool_set_true:N \l_@@_mm_bool \fi \or % \typeout{:::: OpenType} \bool_set_true:N \l_@@_ot_bool \tl_if_empty:NT \l_@@_renderer_tl { \tl_set:Nn \l_@@_renderer_tl {/OT} } \or % \typeout{:::: Graphite} \bool_set_true:N \l_@@_graphite_bool \tl_if_empty:NT \l_@@_renderer_tl { \tl_set:Nn \l_@@_renderer_tl {/GR} } \fi % % \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 % } % \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 } \cs_if_eq:NNTF \l_@@_tmpa_font \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 { % \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={{},{},{}}|.\par} % \begin{macrocode} \cs_new:Nn \@@_declare_shape:nnnn { %\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} } %\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} {} % \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: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 { %\typeout{====~Setup~NFSS~shape:~<\l_@@_size_tl>~\l_fontspec_fontname_tl} \@@_get_features:n { #2 , #3 , #4 } %\typeout{====~Gathered~features:~\g_@@_rawfeatures_sclist \@@_get_variations:} \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 \@@_get_variations: } } } % \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 { %\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 ; } } } % %<*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 ; } } % % \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_new:Nn \@@_make_smallcaps:TF %\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} } } } % % \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 { % \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 \g_@@_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 \g_@@_rawfeatures_sclist {##1;} } } \cs_generate_variant:Nn \@@_remove_clashing_featstr:n {x} % \end{macrocode} % \end{macro} % % \begin{macro}{\@@_get_variations:} % \cmd{\@@_get_variations:} builds the feature string representing the % current variation instance and/or axis settings. % \begin{macrocode} \cs_generate_variant:Nn \tl_tail:n { e } \cs_new:Nn \@@_format_axis:nn { , #1 = #2 } \cs_new:Nn \@@_get_variations: { \tl_if_empty:NF \g_@@_instance_tl { instance = { \g_@@_instance_tl }; } \prop_if_empty:NF \g_@@_rawvariations_prop { axis = { \tl_tail:e { \prop_map_function:NN \g_@@_rawvariations_prop \@@_format_axis:nn } }; } } % \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_@@_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 % } % \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 \prop_gclear:N \g_@@_rawvariations_prop \tl_gclear:N \g_@@_instance_tl \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} % % \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 { %\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} % % \end{macrocode} % \end{macro} % % % \iffalse % \begin{macrocode} % % \end{macrocode} % \fi \endinput % /© % ------------------------------------------------ % The FONTSPEC package % ------------------------------------------------ % Copyright 2022-2023 The LaTeX project % 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): . % ------------------------------------------------ % ©/