%% autotype.sty %% Copyright 2020-2026 Stephan Hennig and Keno Wehr % % This work may be distributed and/or modified under the % conditions of the LaTeX Project Public License, either version 1.3 % of this license or (at your option) any later version. % The latest version of this license is in % http://www.latex-project.org/lppl.txt % and version 1.3 or later is part of all distributions of LaTeX % version 2005/12/01 or later. \ProvidesExplPackage {autotype} {2026-04-27} {0.6} {automatic language-specific typography} % Variables \str_new:N \l_autotype_lang_str % for storing a language name \str_new:N \l_autotype_font_str % font name given by the user % Error messages \msg_new:nnn {autotype} {bad-engine} { LuaTeX~engine~required.~You~could~try~with~the~'lualatex'~command. } \msg_new:nnn {autotype} {undefined-language} {Language~'#1'~is~not~loaded.} \msg_new:nnn {autotype} {unsupported-hyphenation} {#1~hyphenation~is~not~supported~for~language~'#2'.} \msg_new:nnn {autotype} {outdated-option} { The~option~'#1'~is~outdated~and~will~be~removed~from~the~package.~Please~use~'#2'~instead~\msg_line_context:. } \msg_new:nnn {autotype} {outdated-command} { The~command~\c_backslash_str #1~is~outdated~and~will~be~removed~from~the~package.~Please~use~\c_backslash_str #2~instead~\msg_line_context:. } \msg_new:nnn {autotype} {strange-keepligatures} { The~option~'ligatures=selective'~is~not~active~for~\languagename.~ The~\keepligatures~command~is~useless~\msg_line_context: . } % Test for the LuaTeX engine \sys_if_engine_luatex:F { \msg_fatal:nn {autotype} {bad-engine} } % Load autotype's Lua module \directlua { autotype = require('autotype') } % To be able to switch the node list manipulation on and off even within a % paragraph, we define a LuaTeX attribute for every language and autotype % option requested be the user. E.g., for suppressing ligatures in language % 'german' we create an attribute \autotype_german_lig_supp_attr. % Check if a LuaTeX attribute exists % #1: language name, #2: autotype option \prg_new_conditional:Npnn \autotype_attribute_if_exist:nn #1#2 {T,F,TF} { \cs_if_exist:cTF {autotype_#1_#2_attr} {\prg_return_true:} {\prg_return_false:} } \cs_generate_variant:Nn \autotype_attribute_if_exist:nnT {V} \cs_generate_variant:Nn \autotype_attribute_if_exist:nnF {V} % Command for creating LuaTeX attributes % #1: language name, #2: autotype option \cs_new:Npn \autotype_new_attribute:nn #1#2 { \exp_after:wN \newattribute \cs:w autotype_#1_#2_attr \cs_end: } \cs_generate_variant:Nn \autotype_new_attribute:nn {Vn} % Command for setting LuaTeX attributes % #1: language name, #2: autotype option \cs_new:Npn \autotype_set_attribute:nn #1#2 { \exp_after:wN \setattribute \cs:w autotype_#1_#2_attr \cs_end: {1} } \cs_generate_variant:Nn \autotype_set_attribute:nn {Vn} % Command for unsetting LuaTeX attributes % #1: language name, #2: autotype option \cs_new:Npn \autotype_unset_attribute:nn #1#2 { \exp_after:wN \unsetattribute \cs:w autotype_#1_#2_attr \cs_end: } \cs_generate_variant:Nn \autotype_unset_attribute:nn {Vn} % Check if a LuaTeX attribute is set % #1: language name, #2: autotype option \prg_new_conditional:Npnn \autotype_attribute_if_set:nn #1#2 {TF} { \int_compare:nNnTF { \cs:w autotype_#1_#2_attr \cs_end: } = {1} { \prg_return_true: } { \prg_return_false: } } % #1: language name, #2: Lua function name \cs_new:Npn \autotype_activate_lang_option:nn #1#2 { \IfPackageLoadedTF {polyglossia} { \begin {#1} % invoke polyglossia language environment \directlua { autotype.#2 ('#1', tex.language) } \end {#1} } { % With babel the language numbers may not have their final values in the preamble. % See https://codeberg.org/jspitz/babel-german/issues/31 \ifx \@onlypreamble \@notprerr % we are not in the preamble \directlua { autotype.#2 ('#1', luatexbase.registernumber('l@'..'#1')) } \else % we are still in the preamble \AddToHook {begindocument} [autotype-activate-lang-option] { \directlua { autotype.#2 ('#1', luatexbase.registernumber('l@'..'#1')) } } \fi } } \cs_generate_variant:Nn \autotype_activate_lang_option:nn {Vn} % #1: attribute name \cs_new:Npn \autotype_activate_lang_option_with_attribute:n #1 { % Create attribute if not already existing \autotype_attribute_if_exist:VnF \l_autotype_lang_str {#1} { \autotype_new_attribute:Vn \l_autotype_lang_str {#1} % Register callback \autotype_activate_lang_option:Vn \l_autotype_lang_str {#1} } % Set attribute \autotype_set_attribute:Vn \l_autotype_lang_str {#1} } % #1: attribute name \cs_new:Npn \autotype_deactivate_lang_option_with_attribute:n #1 { % Unset attribute if existing \autotype_attribute_if_exist:VnT \l_autotype_lang_str {#1} { \autotype_unset_attribute:Vn \l_autotype_lang_str {#1} } } % Command for executing language options % #1: language name, #2: option \cs_new:Npn \autotype_execute_lang_option:nn #1#2 { \str_set:Nn \l_autotype_lang_str {#1} \keys_set:nn {autotype-lang} {#2} } \keys_define:nn {autotype-lang} { hyphenation .choices:nn = {default, primary, special, weighted} { \str_case:nn {#1} { {default} { \autotype_activate_lang_option:Vn \l_autotype_lang_str {default_hyph} } {primary} { \autotype_activate_lang_option:Vn \l_autotype_lang_str {primary_hyph} } {special} { \autotype_activate_lang_option:Vn \l_autotype_lang_str {special_hyph} } {weighted} { \autotype_activate_lang_option:Vn \l_autotype_lang_str {weighted_hyph} } } } , hyphenation .value_required:n = true , mark-hyph. choices:nn = {on, off} { \str_case:nn {#1} { {on} { \autotype_activate_lang_option_with_attribute:n {mark_hyph} } {off} { \autotype_deactivate_lang_option_with_attribute:n {mark_hyph} } } } , mark-hyph .default:n = {on} , ligatures .choices:nn = {selective, default} { \str_case:nn {#1} { {selective} { \autotype_activate_lang_option_with_attribute:n {lig_supp} } {default} { \autotype_deactivate_lang_option_with_attribute:n {lig_supp} } } } , ligatures .default:n = {selective} , ligbreak .choices:nn = {on, off} { \str_case:nn {#1} { {on} { \msg_warning:nnnn {autotype} {outdated-option} {ligbreak=on} {ligatures=selective} \autotype_activate_lang_option_with_attribute:n {lig_supp} } {off} { \msg_warning:nnnn {autotype} {outdated-option} {ligbreak=off} {ligatures=default} \autotype_deactivate_lang_option_with_attribute:n {lig_supp} } } } , ligbreak .default:n = {on} , long-s .choices:nn = {on, off} { \str_case:nn {#1} { {on} { \autotype_activate_lang_option_with_attribute:n {long_s} } {off} { \autotype_deactivate_lang_option_with_attribute:n {long_s} } } } , long-s .default:n = {on} } \keys_define:nn {autotype-font} { long-s-codepoint .code:n = { \directlua { autotype.set_long_s_codepoint ( "\l_autotype_font_str" , #1 ) } } , round-s-codepoint .code:n = { \directlua { autotype.set_round_s_codepoint ( "\l_autotype_font_str" , #1 ) } } , final-round-s-codepoint .code:n = { \directlua { autotype.set_final_round_s_codepoint ( "\l_autotype_font_str" , #1 ) } } } % Command for executing font options % #1: font name, #2: option \cs_new:Npn \autotype_execute_font_option:nn #1#2 { \str_set:Nn \l_autotype_font_str {#1} \keys_set:nn {autotype-font} {#2} } %%%%% Document commands % Command for setting autotype language options % #1: language name, #2: a comma-separated list of options \NewDocumentCommand \autotypelangoptions {mm} { % Test if language #1 is defined and iterate over options. \IfPackageLoadedTF {polyglossia} { \iflanguageloaded {#1} { \clist_map_inline:nn {#2} { \autotype_execute_lang_option:nn {#1} {##1} } } { \msg_error:nnn {autotype} {undefined-language} {#1} } } { \cs_if_exist:cTF {l@#1} { \clist_map_inline:nn {#2} { \autotype_execute_lang_option:nn {#1} {##1} } } { \msg_error:nnn {autotype} {undefined-language} {#1} } } } % Command for setting autotype font options % #1: font name, #2: a comma-separated list of options \NewDocumentCommand \autotypefontoptions {mm} { \clist_map_inline:nn {#2} { \autotype_execute_font_option:nn {#1} {##1} } } % Command for temporarily switching off the suppression of ligatures % #1: text for which no ligatures are suppressed % Note: This command may fail, if the language is changed within the argument. \NewDocumentCommand \keepligatures {m} { % Check if the current language's lig_supp attribute exists \autotype_attribute_if_exist:nnTF {\languagename} {lig_supp} { % Check if the current language's lig_supp attribute is set \autotype_attribute_if_set:nnTF {\languagename} {lig_supp} { % Store the current language name (just in case it is changed % within the command argument) \str_set_eq:NN \l_autotype_lang_str \languagename % Unset the attribute \autotype_unset_attribute:nn {\languagename} {lig_supp} % Insert the argument #1 % Reset the attribute \autotype_set_attribute:nn {\l_autotype_lang_str} {lig_supp} } { % Issue a warning \msg_warning:nn {autotype} {strange-keepligatures} % Insert the argument #1 } } { % Issue a warning \msg_warning:nn {autotype} {strange-keepligatures} % Insert the argument #1 } } \NewDocumentCommand \noligbreak {m} { \msg_warning:nnnn {autotype} {outdated-command} {noligbreak} {keepligatures} \keepligatures {#1} } % Command for inserting a long s \NewDocumentCommand \autotypelongs { } { % Check if the current language's long_s attribute exists \autotype_attribute_if_exist:nnTF {\languagename} {long_s} { % Check if the current language's long_s attribute is set \autotype_attribute_if_set:nnTF {\languagename} {long_s} { % Unset the attribute \autotype_unset_attribute:nn {\languagename} {long_s} % Insert a long s \symbol { \directlua { tex.write(autotype.get_current_long_s_codepoint()) } } % Reset the attribute \autotype_set_attribute:nn {\languagename} {long_s} } { % Insert a long s \symbol { \directlua { tex.write(autotype.get_current_long_s_codepoint()) } } } } { % Insert a long s \symbol { \directlua { tex.write(autotype.get_current_long_s_codepoint()) } } } } % Command for inserting a round s \NewDocumentCommand \autotyperounds { } { % Check if the current language's long_s attribute exists \autotype_attribute_if_exist:nnTF {\languagename} {long_s} { % Check if the current language's long_s attribute is set \autotype_attribute_if_set:nnTF {\languagename} {long_s} { % Unset the attribute \autotype_unset_attribute:nn {\languagename} {long_s} % Insert a round s \symbol { \directlua { tex.write(autotype.get_current_round_s_codepoint()) } } % Reset the attribute \autotype_set_attribute:nn {\languagename} {long_s} } { % Insert a long s \symbol { \directlua { tex.write(autotype.get_current_round_s_codepoint()) } } } } { % Insert a long s \symbol { \directlua { tex.write(autotype.get_current_round_s_codepoint()) } } } }