diff options
Diffstat (limited to 'Master/texmf-dist/source/latex/fontspec/fontspec-internal.dtx')
-rw-r--r-- | Master/texmf-dist/source/latex/fontspec/fontspec-internal.dtx | 1183 |
1 files changed, 1183 insertions, 0 deletions
diff --git a/Master/texmf-dist/source/latex/fontspec/fontspec-internal.dtx b/Master/texmf-dist/source/latex/fontspec/fontspec-internal.dtx new file mode 100644 index 00000000000..467ccb77f71 --- /dev/null +++ b/Master/texmf-dist/source/latex/fontspec/fontspec-internal.dtx @@ -0,0 +1,1183 @@ +% \section{Internals} +% +% \iffalse +% \begin{macrocode} +%<*fontspec&(xetexx|luatex)> +% \end{macrocode} +% \fi +% +% \subsection{Internal macros} +% \label{sec:codeinternal} +% +% The macros from here in are used internally by all those defined above. +% They are not designed to remain consistent between versions. +% +% +% +% +% \begin{macro}{\fontspec_select: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_fontspec_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_set:Nn \fontspec_select:nn + { + \group_begin: + \@@_font_suppress_not_found_error: + \@@_init: + + \tl_set:Nx \l_fontspec_fontname_tl {#2} + \tl_set:Nx \l_fontspec_fontname_up_tl {#2} + \tl_set:Nx \l_@@_basename_tl {#2} + + \@@_load_external_fontoptions:Nn \l_fontspec_fontname_tl {#2} + \@@_extract_all_features:n {#1} + \@@_preparse_features: + + \@@_load_font: + \@@_set_scriptlang: + \@@_get_features:Nn \l_@@_rawfeatures_sclist {} + \bool_set_false:N \l_@@_firsttime_bool + + \@@_save_family:nTF {#2} + { + \@@_save_fontinfo: + \@@_find_autofonts: + \DeclareFontFamily{\l_@@_nfss_enc_tl}{\l_fontspec_family_tl}{} + \@@_set_faces: + \@@_info:nxx {defining-font} {#1} {#2} +%<*debug> + \typeout{"\l_@@_fontid_tl"~ defined.} + \@@_warning:nxx {defining-font} {#1} {#2} +%</debug> + } + { +%<*debug> + \typeout{"\l_@@_fontid_tl"~ already~ defined~ apparently.} +%</debug> + } + \group_end: + } +% \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 + { + \@@_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_features:} +% \begin{macrocode} +\cs_new:Nn \@@_extract_all_features:n + { + \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 + } + } + \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} +%</debug> + } +% \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: + { +% \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} + \@@_if_detect_external:VT \l_@@_basename_tl + { \keys_set:nn {fontspec-preparse-external} {ExternalLocation} } + + \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_fontspec_fontname_up_tl}), this latter is the new `general +% font name' to use. +% \begin{macrocode} + \tl_set_eq:NN \l_fontspec_fontname_tl \l_fontspec_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: + { + \@@_font_set:Nnn \l_fontspec_font + { \@@_fullname:n {\l_fontspec_fontname_up_tl} } {\f@size pt} + \@@_font_if_null:NT \l_fontspec_font { \@@_error:nx {font-not-found} {\l_fontspec_fontname_up_tl} } + \@@_set_font_type: + \@@_font_gset:Nnn \l_fontspec_font + { \@@_fullname:n {\l_fontspec_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}{\@@_if_detect_external:nnT} +% Check if either the fontname ends with a known font extension. +% \begin{macrocode} +\prg_new_conditional:Nnn \@@_if_detect_external:n {T} + { + \clist_map_inline:Nn \l_@@_extensions_clist + { + \bool_set_false:N \l_@@_tmpa_bool + \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: + } +\cs_generate_variant:Nn \@@_if_detect_external:nT {V} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\@@_fullname:n} +% Constructs the complete font name based on a common piece of info. +% \begin{macrocode} +\cs_set:Nn \@@_fullname:n + { + \@@_namewrap:n { #1 \l_@@_extension_tl } + \l_fontspec_renderer_tl + \l_@@_optical_size_tl + } +% \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 + { + \fontspec_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} {Script=\l_@@_script_name_tl} + \keys_set:nx {fontspec} {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} {Script=\l_@@_script_name_tl} + \keys_set:nx {fontspec} {Language=\l_@@_lang_name_tl} + } + } + } +% \end{macrocode} +% \end{macro} +% +% +% \begin{macro}{\@@_save_family: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:n {TF} + { +%<debug>\typeout{save~ family:~ #1} + \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_fontinfo:nn} +% Saves the relevant font information for future processing. +% \begin{macrocode} +\cs_new:Nn \@@_save_fontinfo: + { + \prop_new:c {g_@@_ \l_fontspec_family_tl _prop} + \prop_gput:cnx {g_@@_ \l_fontspec_family_tl _prop} {fontname} { \l_@@_basename_tl } + \prop_gput:cnx {g_@@_ \l_fontspec_family_tl _prop} {options} { \l_@@_all_features_clist } + \prop_gput:cnx {g_@@_ \l_fontspec_family_tl _prop} {fontdef} + { + \@@_fullname:n {\l_fontspec_fontname_tl} : + \l_@@_pre_feat_sclist \l_@@_rawfeatures_sclist + } + \prop_gput:cnV {g_@@_ \l_fontspec_family_tl _prop} {script-num} \l_fontspec_script_int + \prop_gput:cnV {g_@@_ \l_fontspec_family_tl _prop} {lang-num} \l_fontspec_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} +% +% \subsubsection{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_fontspec_fontname_bfit_tl {\l_fontspec_fontname_it_tl} {/B} + \@@_set_autofont:Nnn \l_fontspec_fontname_bfit_tl {\l_fontspec_fontname_bf_tl} {/I} + \@@_set_autofont:Nnn \l_fontspec_fontname_bfit_tl {\l_fontspec_fontname_tl} {/BI} + } + + \bool_if:NF \l_@@_nobf_bool + { + \@@_set_autofont:Nnn \l_fontspec_fontname_bf_tl {\l_fontspec_fontname_tl} {/B} + } + + \bool_if:NF \l_@@_noit_bool + { + \@@_set_autofont:Nnn \l_fontspec_fontname_it_tl {\l_fontspec_fontname_tl} {/I} + } + + \@@_set_autofont:Nnn \l_fontspec_fontname_bfsl_tl {\l_fontspec_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_fontspec_fontname_bf_tl \l_@@_fontfeat_bf_clist + \@@_add_nfssfont:nnnn \mddefault \itdefault \l_fontspec_fontname_it_tl \l_@@_fontfeat_it_clist + \@@_add_nfssfont:nnnn \mddefault \sldefault \l_fontspec_fontname_sl_tl \l_@@_fontfeat_sl_clist + \@@_add_nfssfont:nnnn \bfdefault \itdefault \l_fontspec_fontname_bfit_tl \l_@@_fontfeat_bfit_clist + \@@_add_nfssfont:nnnn \bfdefault \sldefault \l_fontspec_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} +% +% +% +% \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: +%<*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_generate_variant:Nn \tl_if_empty:nF {x} +\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} + { + \@@_font_set:Nnn \l_tmpa_font { \@@_fullname:n {#1} } {\f@size pt} + \@@_font_set:Nnn \l_tmpb_font { \@@_fullname:n {#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 + { + \@@_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 } + \@@_font_set:Nnn \l_fontspec_font {\@@_fullname:n {\l_fontspec_fontname_tl}} {\f@size pt} + \@@_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 + { + \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} + { + \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 ##1 } + \l_@@_sizing_leftover_clist + \tl_if_empty:NT \l_@@_size_tl { \@@_error:n {no-size-info} } + + % "normal" + \@@_load_fontname:n {\l_@@_sizedfont_tl} + \@@_setup_nfss:Nnn \l_@@_nfss_tl {#3} {} + + % 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_fontspec_fontname_sc_tl + { +%<debug> \typeout{Attempting~ small~ caps?} + \@@_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_fontspec_fontname_sc_tl} }% local for each size + } + + \bool_if:NF \l_@@_nosc_bool + { + \@@_setup_nfss:Nnn \l_@@_nfss_sc_tl {#3} {\l_@@_fontfeat_curr_clist} + } + + } + + \@@_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} + +\cs_new:Nn \@@_setup_nfss:Nnn + { + \@@_get_features:Nn \l_@@_rawfeatures_sclist + { #2 , \l_@@_sizing_leftover_clist , #3 } + + \tl_put_right:Nx #1 + { + <\l_@@_size_tl> \l_@@_scale_tl + \@@_fontwrap:n + { + \@@_fullname:n { \l_fontspec_fontname_tl } + : \l_@@_pre_feat_sclist \l_@@_rawfeatures_sclist + } + } + } + +\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} + } + +\cs_new:Nn \@@_declare_shapes_smcaps:nn + { + \bool_if:NF \l_@@_nosc_bool + { + \@@_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 } + } + +\cs_new:Nn \@@_DeclareFontShape:nnnnnn + { + \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} +% 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} +% 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 \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 + } + } + } +\cs_generate_variant:Nn \tl_if_empty:nF {f} +% \end{macrocode} +% Maybe |\str_if_eq_x:nnF| would be better? +% \end{macro} +% +% \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 ; + } + } + } +%</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} +% +% +% +% +% +% \subsubsection{Features} +% +% \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. +% \begin{macrocode} +\cs_set:Nn \@@_get_features:Nn + { + \sclist_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 + + \keys_set_known:nxN {fontspec-renderer} {\l_@@_fontfeat_clist,#2} + \l_@@_keys_leftover_clist + \keys_set:nx {fontspec} {\l_@@_keys_leftover_clist} +% \end{macrocode} +% Finish the colour specification. +% Do not set the colour if not explicitly spec'd else \verb|\color| (using +% specials) will not work. +% \begin{macrocode} + \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}{\@@_init:} +% Initialisations that either need to occur globally: +% (all setting of these variables is done locally inside a group) +% \begin{macrocode} +\tl_clear:N \l_@@_family_label_tl +\tl_clear:N \l_fontspec_fontname_bf_tl +\tl_clear:N \l_fontspec_fontname_it_tl +\tl_clear:N \l_fontspec_fake_slant_tl +\tl_clear:N \l_fontspec_fake_embolden_tl +\tl_clear:N \l_fontspec_fontname_bfit_tl +\tl_clear:N \l_fontspec_fontname_sl_tl +\tl_clear:N \l_fontspec_fontname_bfsl_tl +\tl_clear:N \l_fontspec_fontname_sc_tl +\tl_clear:N \l_@@_fontfeat_up_clist +\tl_clear:N \l_@@_fontfeat_bf_clist +\tl_clear:N \l_@@_fontfeat_it_clist +\tl_clear:N \l_@@_fontfeat_bfit_clist +\tl_clear:N \l_@@_fontfeat_sl_clist +\tl_clear:N \l_@@_fontfeat_bfsl_clist +\tl_clear:N \l_@@_fontfeat_sc_clist +\tl_clear:N \l_@@_script_name_tl +\tl_clear:N \l_fontspec_script_tl +\tl_clear:N \l_@@_lang_name_tl +\tl_clear:N \l_fontspec_lang_tl +\tl_set:Nn \g_@@_postadjust_tl { \l_@@_wordspace_adjust_tl \l_@@_punctspace_adjust_tl } + +\clist_set:Nn \l_@@_sizefeat_clist {Size={-}} +\tl_new:N \g_@@_hexcol_tl +\tl_new:N \g_@@_opacity_tl +\tl_set:Nn \g_@@_hexcol_tl {000000} +\tl_set:Nn \g_@@_opacity_tl {FF~} +% \end{macrocode} +% Or 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: + { + \bool_set_false:N \l_@@_ot_bool + \bool_set_true:N \l_@@_firsttime_bool + \cs_set:Npn \@@_namewrap:n ##1 { ##1 } + \tl_clear:N \l_@@_optical_size_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 + + % This is for detecting font families when assigning default features. + % Replace defaults for the standard families because they're not set in the usual way: + \exp_args:NV \str_case:nnF {\l_@@_family_label_tl} + { + {\rmdefault} { \tl_set:Nn \l_@@_family_label_tl {\g_@@_rmfamily_family} } + {\sfdefault} { \tl_set:Nn \l_@@_family_label_tl {\g_@@_sffamily_family} } + {\ttdefault} { \tl_set:Nn \l_@@_family_label_tl {\g_@@_ttfamily_family} } + }{} + +%<*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}{\@@_make_smallcaps:TF} +% \label{mac:makesmallcaps} +% This macro checks if the font contains small caps. +% \begin{macrocode} +\cs_set:Nn \fontspec_make_ot_smallcaps:TF + { + \fontspec_check_ot_feat:nTF {+smcp} {#1} {#2} + } +%<*xetexx> +\cs_set:Nn \@@_make_smallcaps:TF + { + \bool_if:NTF \l_@@_ot_bool + { \fontspec_make_ot_smallcaps:TF {#1} {#2} } + { + \bool_if:NT \l_@@_atsui_bool + { \fontspec_make_AAT_feature_string:nnTF {3}{3} {#1} {#2} } + } + } +%</xetexx> +%<*luatex> +\cs_set_eq:NN \@@_make_smallcaps:TF \fontspec_make_ot_smallcaps:TF +%</luatex> +% \end{macrocode} +% \end{macro} +% +% +% +% \begin{macro}{\sclist_put_right:Nn} +% I'm hardly going to write an `sclist' module but a couple of functions are +% useful. Here, items in semi-colon lists are always followed by a semi-colon +% (as opposed to the s.-c's being placed between elements) so we can append +% sclists without worrying about it. +% \begin{macrocode} +\cs_set_eq:NN \sclist_clear:N \tl_clear:N +\cs_new:Nn \sclist_gput_right:Nn + { \tl_gput_right:Nn #1 {#2;} } +\cs_generate_variant:Nn \sclist_gput_right:Nn {Nx} +% \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 + { + \bool_if:NF \l_@@_firsttime_bool + { + \sclist_gput_right:Nx \l_@@_rawfeatures_sclist {#1} + } + } +% \end{macrocode} +% \end{macro} +% +% +% +% \begin{macro}{\fontspec_make_feature:nnn} +% This macro is called by each feature key selected, and runs +% according to which type of font is selected. +% \begin{macrocode} +\cs_new:Nn \fontspec_make_feature:nnn +%<*xetexx> + { + \bool_if:NTF \l_@@_ot_bool + { \fontspec_make_OT_feature:n {#3} } + { + \bool_if:NT \l_@@_atsui_bool + { \fontspec_make_AAT_feature:nn {#1}{#2} } + } + } +%</xetexx> +%<*luatex> + { \fontspec_make_OT_feature:n {#3} } +%</luatex> +\cs_generate_variant:Nn \fontspec_make_feature:nnn {nnx} +% \end{macrocode} +% +% \begin{macrocode} +\cs_new:Nn \fontspec_make_AAT_feature:nn + { + \tl_if_empty:nTF {#1} + { \@@_warning:n {aat-feature-not-exist} } + { + \fontspec_make_AAT_feature_string:nnTF {#1}{#2} + { + \@@_update_featstr:n {\l_fontspec_feature_string_tl} + } + { \@@_warning:nx {aat-feature-not-exist-in-font} {#1,#2} } + } + } +\cs_new:Nn \fontspec_make_OT_feature:n + { + \tl_if_empty:nTF {#1} + { \@@_warning:n {icu-feature-not-exist} } + { + \fontspec_check_ot_feat:nTF {#1} + { + \@@_update_featstr:n {#1} + } + { \@@_warning:nx {icu-feature-not-exist-in-font} {#1} } + } + } +\cs_new_protected:Nn \fontspec_make_numbered_feature:nn + { + \fontspec_check_ot_feat:nTF {#1} + { + \@@_update_featstr:n { #1 = #2 } + } + { \@@_warning:nx {icu-feature-not-exist-in-font} {#1} } + } +\cs_generate_variant:Nn \fontspec_make_numbered_feature:nn {xn} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\@@_define_font_feature:n} +% \begin{macro}{\@@_define_feature_option:nnnnn} +% \begin{macro}{\fontspec_define_numbered_feat:nnnn} +% These macros are used in order to simplify font feature definition later on. +% \begin{macrocode} +\cs_new:Nn \@@_define_font_feature:n + { + \keys_define:nn {fontspec} { #1 .multichoice: } + } +\cs_new:Nn \@@_define_feature_option:nnnnn + { + \keys_define:nn {fontspec} + { + #1/#2 .code:n = { \fontspec_make_feature:nnn{#3}{#4}{#5} } + } + } +\cs_new:Nn \fontspec_define_numbered_feat:nnnn + { + \keys_define:nn {fontspec} + { + #1/#2 .code:n = + { \fontspec_make_numbered_feature:nn {#3}{#4} } + } + } +% \end{macrocode} +% \end{macro} +% \end{macro} +% \end{macro} +% +% \begin{macro}{\fontspec_make_AAT_feature_string:nnTF} +% This macro takes the numerical codes for a font feature and +% creates a specified macro containing the string required in the +% font definition to turn that feature on or off. Used primarily in +% [...], but also used to check if small caps +% exists in the requested font (see page~\pageref{mac:makesmallcaps}). +% +% For exclusive selectors, it's easy; just grab the string: +% For \emph{non}-exclusive selectors, it's a little more complex. +% If the selector is even, it corresponds to switching the feature on. +% If the selector is \emph{odd}, it corresponds to switching the feature off. +% But \XeTeX\ doesn't return a selector string for this number, since the +% feature is defined for the `switching on' value. So we need to check the +% selector of the previous number, and then prefix the feature string with |!| +% to denote the switch. +% +% Finally, save out the complete feature string in \cmd\l_fontspec_feature_string_tl. +% \begin{macrocode} +\prg_new_conditional:Nnn \fontspec_make_AAT_feature_string:nn {TF,T,F} + { + \tl_set:Nx \l_tmpa_tl { \XeTeXfeaturename \l_fontspec_font #1 } + \tl_if_empty:NTF \l_tmpa_tl + { \prg_return_false: } + { + \int_compare:nTF { \XeTeXisexclusivefeature\l_fontspec_font #1 > 0 } + { + \tl_set:Nx \l_tmpb_tl {\XeTeXselectorname\l_fontspec_font #1\space #2} + } + { + \int_if_even:nTF {#2} + { + \tl_set:Nx \l_tmpb_tl {\XeTeXselectorname\l_fontspec_font #1\space #2} + } + { + \tl_set:Nx \l_tmpb_tl + { + \XeTeXselectorname\l_fontspec_font #1\space \numexpr#2-1\relax + } + \tl_if_empty:NF \l_tmpb_tl { \tl_put_left:Nn \l_tmpb_tl {!} } + } + } + \tl_if_empty:NTF \l_tmpb_tl + { \prg_return_false: } + { + \tl_set:Nx \l_fontspec_feature_string_tl { \l_tmpa_tl = \l_tmpb_tl } + \prg_return_true: + } + } + } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\fontspec_iv_str_to_num:Nn} +% \begin{macro}{\fontspec_v_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 \cmd\l_fontspec_strnum_int. +% +% 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. +% +% The variant \cmd\fontspec_v_str_to_num:n\ is used when looking at features, which are passed around +% with prepended plus and minus signs (\eg, \texttt{+liga}, \texttt{-dlig}); it +% simply strips off the first char of the input before calling the normal \cmd\fontspec_iv_str_to_num:n. +% \begin{macrocode} +\cs_set:Nn \fontspec_iv_str_to_num:Nn + { + \fontspec_iv_str_to_num:w #1 \q_nil #2 \c_empty_tl \c_empty_tl \q_nil + } +\cs_set:Npn \fontspec_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 \fontspec_iv_str_to_num:Nn {No} +\cs_set:Nn \fontspec_v_str_to_num:Nn + { + \bool_if:nTF + { + \tl_if_head_eq_charcode_p:nN {#2} {+} || + \tl_if_head_eq_charcode_p:nN {#2} {-} + } + { \fontspec_iv_str_to_num:No #1 { \use_none:n #2 } } + { \fontspec_iv_str_to_num:Nn #1 {#2} } + } +% \end{macrocode} +% \end{macro} +% \end{macro} +% +% +% \begin{macro}{\fontspec_check_script:nTF} +% This macro takes an OpenType script tag and checks if it exists in the current +% font. The output boolean is \cmd\@tempswatrue. \cmd\l_fontspec_strnum_int\ is used to store +% the number corresponding to the script tag string. +% \begin{macrocode} +\prg_new_conditional:Nnn \fontspec_check_script:n {TF} +%<*xetexx> + { + \fontspec_iv_str_to_num:Nn \l_fontspec_strnum_int {#1} + \int_set:Nn \l_tmpb_int { \XeTeXOTcountscripts \l_fontspec_font } + \int_zero:N \l_tmpa_int + \bool_set_false:N \l__fontspec_check_bool + \bool_until_do:nn { \int_compare_p:nNn \l_tmpa_int = \l_tmpb_int } + { + \ifnum \XeTeXOTscripttag\l_fontspec_font \l_tmpa_int = \l_fontspec_strnum_int + \bool_set_true:N \l__fontspec_check_bool + \int_set:Nn \l_tmpa_int {\l_tmpb_int} + \else + \int_incr:N \l_tmpa_int + \fi + } + \bool_if:NTF \l__fontspec_check_bool \prg_return_true: \prg_return_false: + } +%</xetexx> +%<*luatex> + { + \directlua{fontspec.check_ot_script("l_fontspec_font", "#1")} + \bool_if:NTF \l__fontspec_check_bool \prg_return_true: \prg_return_false: + } +%</luatex> +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\fontspec_check_lang:nTF} +% This macro takes an OpenType language tag and checks if it exists in the current +% font/script. The output boolean is \cmd\@tempswatrue. \cmd\l_fontspec_strnum_int\ is used to store +% the number corresponding to the language tag string. +% The script used is whatever's held in \cmd\l_fontspec_script_int. By default, that's the +% number corresponding to `|latn|'. +% \begin{macrocode} +\prg_new_conditional:Nnn \fontspec_check_lang:n {TF} +%<*xetexx> + { + \fontspec_iv_str_to_num:Nn \l_fontspec_strnum_int {#1} + \int_set:Nn \l_tmpb_int + { \XeTeXOTcountlanguages \l_fontspec_font \l_fontspec_script_int } + \int_zero:N \l_tmpa_int + \bool_set_false:N \l__fontspec_check_bool + \bool_until_do:nn { \int_compare_p:nNn \l_tmpa_int = \l_tmpb_int } + { + \ifnum\XeTeXOTlanguagetag\l_fontspec_font\l_fontspec_script_int \l_tmpa_int =\l_fontspec_strnum_int + \bool_set_true:N \l__fontspec_check_bool + \int_set:Nn \l_tmpa_int {\l_tmpb_int} + \else + \int_incr:N \l_tmpa_int + \fi + } + \bool_if:NTF \l__fontspec_check_bool \prg_return_true: \prg_return_false: + } +%</xetexx> +%<*luatex> + { + \directlua + { + fontspec.check_ot_lang( "l_fontspec_font", "#1", "\l_fontspec_script_tl" ) + } + \bool_if:NTF \l__fontspec_check_bool \prg_return_true: \prg_return_false: + } +%</luatex> +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\fontspec_check_ot_feat:nTF} +% \begin{macro}{\fontspec_check_ot_feat:nT} +% This macro takes an OpenType feature tag and checks if it exists in the current +% font/script/language. +% \cmd\l_fontspec_strnum_int\ is used to store the number corresponding to the feature tag string. +% The script used is whatever's held in \cmd\l_fontspec_script_int. By default, that's the +% number corresponding to `|latn|'. The language used is \cmd\l_fontspec_language_int, +% by default |0|, the `default language'. +% \begin{macrocode} +\prg_new_conditional:Nnn \fontspec_check_ot_feat:n {TF,T} +%<*xetexx> + { + \int_set:Nn \l_tmpb_int + { + \XeTeXOTcountfeatures \l_fontspec_font + \l_fontspec_script_int + \l_fontspec_language_int + } + \fontspec_v_str_to_num:Nn \l_fontspec_strnum_int {#1} + \int_zero:N \l_tmpa_int + \bool_set_false:N \l__fontspec_check_bool + \bool_until_do:nn { \int_compare_p:nNn \l_tmpa_int = \l_tmpb_int } + { + \ifnum\XeTeXOTfeaturetag\l_fontspec_font\l_fontspec_script_int\l_fontspec_language_int + \l_tmpa_int =\l_fontspec_strnum_int + \bool_set_true:N \l__fontspec_check_bool + \int_set:Nn \l_tmpa_int {\l_tmpb_int} + \else + \int_incr:N \l_tmpa_int + \fi + } + \bool_if:NTF \l__fontspec_check_bool \prg_return_true: \prg_return_false: + } +%</xetexx> +%<*luatex> + { + \directlua + { + fontspec.check_ot_feat( + "l_fontspec_font", "#1", + "\l_fontspec_lang_tl", "\l_fontspec_script_tl" + ) + } + \bool_if:NTF \l__fontspec_check_bool \prg_return_true: \prg_return_false: + } +%</luatex> +% \end{macrocode} +% \end{macro} +% \end{macro} +% +% \iffalse +% \begin{macrocode} +%</fontspec&(xetexx|luatex)> +% \end{macrocode} +% \fi |