From b3225c4301e7724de481f410444fef5463799b9d Mon Sep 17 00:00:00 2001 From: Norbert Preining Date: Tue, 14 Jan 2020 03:00:43 +0000 Subject: CTAN sync 202001140300 --- macros/latex/contrib/l3kernel/l3text-case.dtx | 1421 +++++++++++++++++++++++++ 1 file changed, 1421 insertions(+) create mode 100644 macros/latex/contrib/l3kernel/l3text-case.dtx (limited to 'macros/latex/contrib/l3kernel/l3text-case.dtx') diff --git a/macros/latex/contrib/l3kernel/l3text-case.dtx b/macros/latex/contrib/l3kernel/l3text-case.dtx new file mode 100644 index 0000000000..3c608eacb9 --- /dev/null +++ b/macros/latex/contrib/l3kernel/l3text-case.dtx @@ -0,0 +1,1421 @@ +% \iffalse meta-comment +% +%% File: l3text-case.dtx +% +% Copyright (C) 2020 The LaTeX3 Project +% +% It may be distributed and/or modified under the conditions of the +% LaTeX Project Public License (LPPL), either version 1.3c of this +% license or (at your option) any later version. The latest version +% of this license is in the file +% +% https://www.latex-project.org/lppl.txt +% +% This file is part of the "l3kernel bundle" (The Work in LPPL) +% and all files in that bundle must be distributed together. +% +% ----------------------------------------------------------------------- +% +% The development version of the bundle can be found at +% +% https://github.com/latex3/latex3 +% +% for those people who are interested. +% +%<*driver> +\documentclass[full,kernel]{l3doc} +\begin{document} + \DocInput{\jobname.dtx} +\end{document} +% +% \fi +% +% \title{^^A +% The \textsf{l3text-case} package: text processing (case changing)^^A +% } +% +% \author{^^A +% The \LaTeX3 Project\thanks +% {^^A +% E-mail: +% \href{mailto:latex-team@latex-project.org} +% {latex-team@latex-project.org}^^A +% }^^A +% } +% +% \date{Released 2020-01-12} +% +% \maketitle +% +% \begin{documentation} +% +% \end{documentation} +% +% \begin{implementation} +% +% \section{\pkg{l3text-case} implementation} +% +% \begin{macrocode} +%<*initex|package> +% \end{macrocode} +% +% \begin{macrocode} +%<@@=text> +% \end{macrocode} +% +% \subsection{Case changing} +% +% \begin{macro}[EXP] +% { +% \text_lowercase:n, +% \text_uppercase:n, +% \text_titlecase:n, +% \text_titlecase_first:n +% } +% \begin{macro}[EXP] +% { +% \text_lowercase:nn, +% \text_uppercase:nn, +% \text_titlecase:nn, +% \text_titlecase_first:nn +% } +% The user level functions here are all wrappers around the internal +% functions for case changing. +% \begin{macrocode} +\cs_new:Npn \text_lowercase:n #1 + { \@@_change_case:nnn { lower } { } {#1} } +\cs_new:Npn \text_uppercase:n #1 + { \@@_change_case:nnn { upper } { } {#1} } +\cs_new:Npn \text_titlecase:n #1 + { \@@_change_case:nnn { title } { } {#1} } +\cs_new:Npn \text_titlecase_first:n #1 + { \@@_change_case:nnn { titleonly } { } {#1} } +\cs_new:Npn \text_lowercase:nn #1#2 + { \@@_change_case:nnn { lower } {#1} {#2} } +\cs_new:Npn \text_uppercase:nn #1#2 + { \@@_change_case:nnn { upper } {#1} {#2} } +\cs_new:Npn \text_titlecase:nn #1#2 + { \@@_change_case:nnn { title } {#1} {#2} } +\cs_new:Npn \text_titlecase_first:nn #1#2 + { \@@_change_case:nnn { titleonly } {#1} {#2} } +% \end{macrocode} +% \end{macro} +% \end{macro} +% +% \begin{macro}[EXP]{\@@_change_case:nnn, \@@_change_case_aux:nnn} +% \begin{macro}[EXP]{\@@_change_case_store:n} +% \begin{macro}[EXP]{\@@_change_case_store:nw} +% \begin{macro}[EXP]{\@@_change_case_result:n} +% \begin{macro}[EXP]{\@@_change_case_end:w} +% \begin{macro}[EXP]{\@@_change_case_loop:nnw} +% \begin{macro}[EXP]{\@@_change_case_break:w} +% \begin{macro}[EXP] +% { +% \@@_change_case_group_lower:nnn , +% \@@_change_case_group_upper:nnn , +% \@@_change_case_group_title:nnn , +% \@@_change_case_group_titleonly:nnn +% } +% \begin{macro}[EXP]{\@@_change_case_space:nnw} +% \begin{macro}[EXP]{\@@_change_case_N_type:nnN, \@@_change_case_N_type_aux:nnN} +% \begin{macro}[EXP]{\@@_change_case_N_type:nnnN} +% \begin{macro}[EXP]{\@@_change_case_math_search:nnNNN} +% \begin{macro}[EXP]{\@@_change_case_math_loop:nnNw} +% \begin{macro}[EXP]{\@@_change_case_math_N_type:nnNN} +% \begin{macro}[EXP]{\@@_change_case_math_group:nnNn} +% \begin{macro}[EXP]{\@@_change_case_math_space:nnNw} +% \begin{macro}[EXP]{\@@_change_case_cs_check:nnN} +% \begin{macro}[EXP]{\@@_change_case_exclude:nnN} +% \begin{macro}[EXP]{\@@_change_case_exclude:nnnN} +% \begin{macro}[EXP]{\@@_change_case_exclude:nnNN} +% \begin{macro}[EXP]{\@@_change_case_exclude:nnNn} +% \begin{macro}[EXP] +% { +% \@@_change_case_letterlike_lower:nnN , +% \@@_change_case_letterlike_upper:nnN , +% \@@_change_case_letterlike_title:nnN , +% \@@_change_case_letterlike_titleonly:nnN +% } +% \begin{macro}[EXP]{\@@_change_case_letterlike:nnnnN} +% \begin{macro}[EXP] +% {\@@_change_case_char_lower:nnN, \@@_change_case_char_upper:nnN} +% \begin{macro}[EXP]{\@@_change_case_lower_sigma:nnnN} +% \begin{macro}[EXP]{\@@_change_case_lower_sigma:nnNw} +% \begin{macro}[EXP]{\@@_change_case_lower_sigma:NnnN} +% \begin{macro}[EXP] +% {\@@_change_case_char_title:nnN, \@@_change_case_char_titleonly:nnN} +% \begin{macro}[EXP] +% {\@@_change_case_char_title:nN, \@@_change_case_char_titleonly:nN} +% \begin{macro}[EXP]{\@@_change_case_char_title:nnnN} +% \begin{macro}[EXP]{\@@_change_case_char_char:nnnN} +% \begin{macro}[EXP]{\@@_change_case_char_UTFviii:nnnNN} +% \begin{macro}[EXP]{\@@_change_case_char_UTFviii:nnnNNN} +% \begin{macro}[EXP]{\@@_change_case_char_UTFviii:nnnNNNNN} +% \begin{macro}[EXP]{\@@_change_case_char_UTFviii:nnnn} +% \begin{macro}[EXP] +% { +% \@@_change_case_char_next_lower:nn , +% \@@_change_case_char_next_upper:nn , +% \@@_change_case_char_next_title:nn , +% \@@_change_case_char_next_titleonly:nn +% } +% \begin{macro}[EXP]{\@@_change_case_char_next_end:nn} +% As for the expansion code, the business end of case changing is the +% handling of \texttt{N}-type tokens. First, we expand the input fully +% (so the loops here don't need to worry about awkward look-aheads and the +% like). Then we split into the different paths. +% +% The code here needs to be \texttt{f}-type expandable to deal with the +% situation where case changing is applied in running text. There, we +% might have case changing as a document command and the text containing +% other non-expandable document commands. +% \begin{verbatim} +% \cs_set_eq:NN \MakeLowercase \text_lowercase +% ... +% \MakeLowercase{\enquote*{A} text} +% \end{verbatim} +% If we use an \texttt{e}-type expansion and wrap each token in +% \cs{exp_not:n}, that would explode: the document command grabs +% \cs{exp_not:n} as an argument, and things go badly wrong. So we have to +% wrap the entire result in exactly one \cs{exp_not:n}, or rather in the +% kernel version. +% \begin{macrocode} +\cs_new:Npn \@@_change_case:nnn #1#2#3 + { + \__kernel_exp_not:w \exp_after:wN + { + \exp:w + \exp_args:Ne \@@_change_case_aux:nnn + { \text_expand:n {#3} } + {#1} {#2} + } + } +\cs_new:Npn \@@_change_case_aux:nnn #1#2#3 + { + \group_align_safe_begin: + \@@_change_case_loop:nnw {#2} {#3} #1 + \q_recursion_tail \q_recursion_stop + \@@_change_case_result:n { } + } +% \end{macrocode} +% As for expansion, collect up the tokens for future use. +% \begin{macrocode} +\cs_new:Npn \@@_change_case_store:n #1 + { \@@_change_case_store:nw {#1} } +\cs_generate_variant:Nn \@@_change_case_store:n { o , e , V , v } +\cs_new:Npn \@@_change_case_store:nw #1#2 \@@_change_case_result:n #3 + { #2 \@@_change_case_result:n { #3 #1 } } +\cs_new:Npn \@@_change_case_end:w #1 \@@_change_case_result:n #2 + { + \group_align_safe_end: + \exp_end: + #2 + } +% \end{macrocode} +% The main loop is the standard \texttt{tl action} type. +% \begin{macrocode} +\cs_new:Npn \@@_change_case_loop:nnw #1#2#3 \q_recursion_stop + { + \tl_if_head_is_N_type:nTF {#3} + { \@@_change_case_N_type:nnN } + { + \tl_if_head_is_group:nTF {#3} + { \use:c { @@_change_case_group_ #1 :nnn } } + { \@@_change_case_space:nnw } + } + {#1} {#2} #3 \q_recursion_stop + } +\cs_new:Npn \@@_change_case_break:w #1 \q_recursion_tail \q_recursion_stop + { + \@@_change_case_store:n {#1} + \@@_change_case_end:w + } +% \end{macrocode} +% For a group, we \emph{could} worry about whether this contains a character +% or not. However, that would make life very complex for little gain: exactly +% what a first character is is rather weakly-defined anyway. So if there is +% a group, we simply assume that a character has been seen, and for title +% case we switch to the \enquote{rest of the tokens} situation. To avoid +% having too much testing, we use a two-step process here to allow the +% titlecase functions to be separate. +% \begin{macrocode} +\cs_new:Npn \@@_change_case_group_lower:nnn #1#2#3 + { + \@@_change_case_store:o + { + \exp_after:wN + { + \exp:w + \@@_change_case_aux:nnn {#3} {#1} {#2} + } + } + \@@_change_case_loop:nnw {#1} {#2} + } +\cs_new_eq:NN \@@_change_case_group_upper:nnn + \@@_change_case_group_lower:nnn +\cs_new:Npn \@@_change_case_group_title:nnn #1#2#3 + { + \@@_change_case_store:o + { + \exp_after:wN + { + \exp:w + \@@_change_case_aux:nnn {#3} {#1} {#2} + } + } + \@@_change_case_loop:nnw { lower } {#2} + } +\cs_new:Npn \@@_change_case_group_titleonly:nnn #1#2#3 + { + \@@_change_case_store:o + { + \exp_after:wN + { + \exp:w + \@@_change_case_aux:nnn {#3} {#1} {#2} + } + } + \@@_change_case_break:w + } +\use:x + { + \cs_new:Npn \exp_not:N \@@_change_case_space:nnw ##1##2 \c_space_tl + } + { + \@@_change_case_store:n { ~ } + \@@_change_case_loop:nnw {#1} {#2} + } +% \end{macrocode} +% The first step of handling \texttt{N}-type tokens is to filter out the +% end-of-loop. That has to be done separately from the first real step +% as otherwise we pick up the wrong delimiter. The loop here is the same +% as the \texttt{expand} one, just passing the additional data long. If no +% close-math token is found then the final clean-up is forced +% (i.e.~there is no assumption of \enquote{well-behaved} input in terms of +% math mode). +% \begin{macrocode} +\cs_new:Npn \@@_change_case_N_type:nnN #1#2#3 + { + \quark_if_recursion_tail_stop_do:Nn #3 + { \@@_change_case_end:w } + \@@_change_case_N_type_aux:nnN {#1} {#2} #3 + } +\cs_new:Npn \@@_change_case_N_type_aux:nnN #1#2#3 + { + \exp_args:NV \@@_change_case_N_type:nnnN + \l_text_math_delims_tl {#1} {#2} #3 + } +\cs_new:Npn \@@_change_case_N_type:nnnN #1#2#3#4 + { + \@@_change_case_math_search:nnNNN {#2} {#3} #4 #1 + \q_recursion_tail \q_recursion_tail + \q_recursion_stop + } +\cs_new:Npn \@@_change_case_math_search:nnNNN #1#2#3#4#5 + { + \quark_if_recursion_tail_stop_do:Nn #4 + { \@@_change_case_cs_check:nnN {#1} {#2} #3 } + \token_if_eq_meaning:NNTF #3 #4 + { + \use_i_delimit_by_q_recursion_stop:nw + { + \@@_change_case_store:n {#3} + \@@_change_case_math_loop:nnNw {#1} {#2} #5 + } + } + { \@@_change_case_math_search:nnNNN {#1} {#2} #3 } + } +\cs_new:Npn \@@_change_case_math_loop:nnNw #1#2#3#4 \q_recursion_stop + { + \tl_if_head_is_N_type:nTF {#4} + { \@@_change_case_math_N_type:nnNN } + { + \tl_if_head_is_group:nTF {#4} + { \@@_change_case_math_group:nnNn } + { \@@_change_case_math_space:nnNw } + } + {#1} {#2} #3 #4 \q_recursion_stop + } +\cs_new:Npn \@@_change_case_math_N_type:nnNN #1#2#3#4 + { + \quark_if_recursion_tail_stop_do:Nn #4 + { \@@_change_case_end:w } + \@@_change_case_store:n {#4} + \token_if_eq_meaning:NNTF #4 #3 + { \@@_change_case_loop:nnw {#1} {#2} } + { \@@_change_case_math_loop:nnNw {#1} {#2} #3 } + } +\cs_new:Npn \@@_change_case_math_group:nnNn #1#2#3#4 + { + \@@_change_case_store:n { {#4} } + \@@_change_case_math_loop:nnNw {#1} {#2} #3 + } +\use:x + { + \cs_new:Npn \exp_not:N \@@_change_case_math_space:nnNw ##1##2##3 + \c_space_tl + } + { + \@@_change_case_store:n { ~ } + \@@_change_case_math_loop:nnNw {#1} {#2} #3 + } +% \end{macrocode} +% Once potential math-mode cases are filtered out the next stage is to +% test if the token grabbed is a control sequence: the two routes the code +% may take are then very different. +% \begin{macrocode} +\cs_new:Npn \@@_change_case_cs_check:nnN #1#2#3 + { + \token_if_cs:NTF #3 + { \@@_change_case_exclude:nnN } + { \use:c { @@_change_case_char_ #1 :nnN } } + {#1} {#2} #3 + } +% \end{macrocode} +% To deal with a control sequence there is first a need to test if it is +% on the list which indicate that case changing should be skipped. That's +% done using a loop as for the other special cases. If a hit is found then +% the argument is grabbed and passed through as-is. +% \begin{macrocode} +\cs_new:Npn \@@_change_case_exclude:nnN #1#2#3 + { + \exp_args:Ne \@@_change_case_exclude:nnnN + { + \exp_not:V \l_text_math_arg_tl + \exp_not:V \l_text_case_exclude_arg_tl + } + {#1} {#2} #3 + } +\cs_new:Npn \@@_change_case_exclude:nnnN #1#2#3#4 + { + \@@_change_case_exclude:nnNN {#2} {#3} #4 #1 + \q_recursion_tail \q_recursion_stop + } +\cs_new:Npn \@@_change_case_exclude:nnNN #1#2#3#4 + { + \quark_if_recursion_tail_stop_do:Nn #4 + { \use:c { @@_change_case_letterlike_ #1 :nnN } {#1} {#2} #3 } + \cs_if_eq:NNTF #3 #4 + { + \use_i_delimit_by_q_recursion_stop:nw + { \@@_change_case_exclude:nnNn {#1} {#2} #3 } + } + { \@@_change_case_exclude:nnNN {#1} {#2} #3 } + } +\cs_new:Npn \@@_change_case_exclude:nnNn #1#2#3#4 + { + \@@_change_case_store:n { #3 {#4} } + \@@_change_case_loop:nnw {#1} {#2} + } +% \end{macrocode} +% Letter-like commands may still be present: they are set up using a simple +% lookup approach, so can easily be handled with no loop. If there is no +% hit, we are at the end of the process: we loop around. Letter-like chars +% are all available only in upper- and lowercase, so titlecasing maps to the +% uppercase version. +% \begin{macrocode} +\cs_new:Npn \@@_change_case_letterlike_lower:nnN #1#2#3 + { \@@_change_case_letterlike:nnnnN {#1} {#1} {#1} {#2} #3 } +\cs_new_eq:NN \@@_change_case_letterlike_upper:nnN + \@@_change_case_letterlike_lower:nnN +\cs_new:Npn \@@_change_case_letterlike_title:nnN #1#2#3 + { \@@_change_case_letterlike:nnnnN { upper } { lower } {#1} {#2} #3 } +\cs_new:Npn \@@_change_case_letterlike_titleonly:nnN #1#2#3 + { \@@_change_case_letterlike:nnnnN { upper } { end } {#1} {#2} #3 } +\cs_new:Npn \@@_change_case_letterlike:nnnnN #1#2#3#4#5 + { + \cs_if_exist:cTF { c_@@_ #1 case_ \token_to_str:N #5 _tl } + { + \@@_change_case_store:v + { c_@@_ #1 case_ \token_to_str:N #5 _tl } + \use:c { @@_change_case_char_next_ #2 :nn } {#2} {#4} + } + { + \@@_change_case_store:n {#5} + \cs_if_exist:cTF + { + c_@@_ + \str_if_eq:nnTF {#1} { lower } { upper } { lower } + case_ \token_to_str:N #5 _tl + } + { \use:c { @@_change_case_char_next_ #2 :nn } {#2} {#4} } + { \@@_change_case_loop:nnw {#3} {#4} } + } + } +% \end{macrocode} +% For upper- and lowercase changes, once we get to this stage there are only +% a couple of questions remaining: is there a language-specific mapping and +% is there the special case of a terminal sigma. If not, then we pass to +% a simple character mapping. +% \begin{macrocode} +\cs_new:Npx \@@_change_case_char_lower:nnN #1#2#3 + { + \exp_not:N \cs_if_exist_use:cF { @@_change_case_lower_ #2 :nnnN } + { + \bool_lazy_or:nnTF + { \sys_if_engine_luatex_p: } + { \sys_if_engine_xetex_p: } + { \exp_not:N \@@_change_case_lower_sigma:nnnN } + { \exp_not:N \@@_change_case_char:nnnN } + } + {#1} {#1} {#2} #3 + } +\cs_new:Npn \@@_change_case_char_upper:nnN #1#2#3 + { + \cs_if_exist_use:cF { @@_change_case_upper_ #2 :nnnN } + { \@@_change_case_char:nnnN } + {#1} {#1} {#2} #3 + } +% \end{macrocode} +% If the current character is an uppercase sigma, the a check is made on the +% next item in the input. If it is \texttt{N}-type and not a control sequence +% then there is a look-ahead phase: the logic here is simply based on letters. +% The one exception is Dutch: see below. +% \begin{macrocode} +\bool_lazy_or:nnT + { \sys_if_engine_luatex_p: } + { \sys_if_engine_xetex_p: } + { + \cs_new:Npn \@@_change_case_lower_sigma:nnnN #1#2#3#4 + { + \int_compare:nNnTF { `#4 } = { "03A3 } + { \@@_change_case_lower_sigma:nnNw {#2} {#3} #4 } + { \@@_change_case_char:nnnN {#1} {#2} {#3} #4 } + } + \cs_new:Npn \@@_change_case_lower_sigma:nnNw #1#2#3#4 \q_recursion_stop + { + \tl_if_head_is_N_type:nTF {#4} + { \@@_change_case_lower_sigma:NnnN #3 } + { + \@@_change_case_store:e + { \char_generate:nn { "03C2 } { \@@_char_catcode:N #3 } } + \@@_change_case_loop:nnw + } + {#1} {#2} #4 \q_recursion_stop + } + \cs_new:Npn \@@_change_case_lower_sigma:NnnN #1#2#3#4 + { + \@@_change_case_store:e + { + \token_if_letter:NTF #4 + { \char_generate:nn { "03C3 } { \@@_char_catcode:N #1 } } + { \char_generate:nn { "03C2 } { \@@_char_catcode:N #1 } } + } + \@@_change_case_loop:nnw {#2} {#3} #4 + } + } +% \end{macrocode} +% For titlecasing, we need to fully expand the new character to see if it +% is a letter (or active) But that means looking ahead in the $8$-bit case, so +% we have to grab the required tokens up-front. Life is a lot easier for Unicode +% \TeX{}'s, where we just have one token to worry about. The one wrinkle here +% is that for look-ahead we'd get into trouble: luckily, only Dutch has that +% issue. +% \begin{macrocode} +\cs_new:Npx \@@_change_case_char_title:nnN #1#2#3 + { + \bool_lazy_or:nnTF + { \sys_if_engine_luatex_p: } + { \sys_if_engine_xetex_p: } + { \exp_not:N \token_if_letter:NTF #3 } + { + \exp_not:N \bool_lazy_or:nnTF + { \exp_not:N \token_if_letter_p:N #3 } + { \exp_not:N \token_if_active_p:N #3 } + } + { \exp_not:N \use:c { @@_change_case_char_ #1 :nN } } + { \exp_not:N \@@_change_case_char_title:nnnN { title } {#1} } + {#2} #3 + } +\cs_new_eq:NN \@@_change_case_char_titleonly:nnN + \@@_change_case_char_title:nnN +\cs_new:Npn \@@_change_case_char_title:nN #1#2 + { \@@_change_case_char_title:nnnN { title } { lower } {#1} #2 } +\cs_new:Npn \@@_change_case_char_titleonly:nN #1#2 + { \@@_change_case_char_title:nnnN { title } { end } {#1} #2 } +\cs_new:Npn \@@_change_case_char_title:nnnN #1#2#3#4 + { + \cs_if_exist_use:cF { @@_change_case_title_ #3 :nnnN } + { + \cs_if_exist_use:cF { @@_change_case_upper_ #3 :nnnN } + { \@@_change_case_char:nnnN } + } + {#1} {#2} {#3} #4 + } +% \end{macrocode} +% For Unicode engines we can handle all characters directly. However, for +% the $8$-bit engines the aim is to deal with (a subset of) Unicode (UTF-8) +% input. They deal with that by making the upper half of the range active, +% so we look for that and if found work out how many UTF-8 octets there +% are to deal with. Those can then be grabbed to reconstruct the full +% Unicode character, which is then used in a lookup. (As will become +% obvious below, there is no intention here of covering all of Unicode.) +% \begin{macrocode} +\bool_lazy_or:nnTF + { \sys_if_engine_luatex_p: } + { \sys_if_engine_xetex_p: } + { + \cs_new:Npn \@@_change_case_char:nnnN #1#2#3#4 + { + \@@_change_case_store:e + { \use:c { char_ #1 case :N } #4 } + \use:c { @@_change_case_char_next_ #2 :nn } {#2} {#3} + } + } + { + \cs_new:Npn \@@_change_case_char:nnnN #1#2#3#4 + { + \int_compare:nNnTF { `#4 } > { "80 } + { + \int_compare:nNnTF { `#4 } < { "E0 } + { \@@_change_case_char_UTFviii:nnnNN } + { + \int_compare:nNnTF { `#4 } < { "F0 } + { \@@_change_case_char_UTFviii:nnnNNN } + { \@@_change_case_char_UTFviii:nnnNNNN } + } + {#1} {#2} {#3} #4 + } + { + \@@_change_case_store:e{ \use:c { char_ #1 case :N } #4 } + \use:c { @@_change_case_char_next_ #2 :nn } {#2} {#3} + } + } + \cs_new:Npn \@@_change_case_char_UTFviii:nnnNN #1#2#3#4#5 + { \@@_change_case_char_UTFviii:nnnn {#1} {#2} {#3} {#4#5} } + \cs_new:Npn \@@_change_case_char_UTFviii:nnnNNN #1#2#3#4#5#6 + { \@@_change_case_char_UTFviii:nnnn {#1} {#2} {#3} {#4#5#6} } + \cs_new:Npn \@@_change_case_char_UTFviii:nnnNNNNN #1#2#3#4#5#6#7 + { \@@_change_case_char_UTFviii:nnnn {#1} {#2} {#3} {#4#5#6#7} } + \cs_new:Npn \@@_change_case_char_UTFviii:nnnn #1#2#3#4 + { + \cs_if_exist:cTF { c_@@_ #1 case_ \tl_to_str:n {#4} _tl } + { + \@@_change_case_store:v + { c_@@_ #1 case_ \tl_to_str:n {#4} _tl } + } + { \@@_change_case_store:n {#4} } + \use:c { @@_change_case_char_next_ #2 :nn } {#2} {#3} + } + } +\cs_new:Npn \@@_change_case_char_next_lower:nn #1#2 + { \@@_change_case_loop:nnw {#1} {#2} } +\cs_new_eq:NN \@@_change_case_char_next_upper:nn + \@@_change_case_char_next_lower:nn +\cs_new_eq:NN \@@_change_case_char_next_title:nn + \@@_change_case_char_next_lower:nn +\cs_new_eq:NN \@@_change_case_char_next_titleonly:nn + \@@_change_case_char_next_lower:nn +\cs_new:Npn \@@_change_case_char_next_end:nn #1#2 + { \@@_change_case_break:w } +% \end{macrocode} +% \end{macro} +% \end{macro} +% \end{macro} +% \end{macro} +% \end{macro} +% \end{macro} +% \end{macro} +% \end{macro} +% \end{macro} +% \end{macro} +% \end{macro} +% \end{macro} +% \end{macro} +% \end{macro} +% \end{macro} +% \end{macro} +% \end{macro} +% \end{macro} +% \end{macro} +% \end{macro} +% \end{macro} +% \end{macro} +% \end{macro} +% \end{macro} +% \end{macro} +% \end{macro} +% \end{macro} +% \end{macro} +% \end{macro} +% \end{macro} +% \end{macro} +% \end{macro} +% \end{macro} +% \end{macro} +% \end{macro} +% \end{macro} +% \end{macro} +% +% \begin{macro}[EXP]{\@@_change_case_upper_de-alt:nnnN} +% A simple alternative version for German. +% \begin{macrocode} +\bool_lazy_or:nnT + { \sys_if_engine_luatex_p: } + { \sys_if_engine_xetex_p: } + { + \cs_new:cpn { @@_change_case_upper_de-alt:nnnN } #1#2#3#4 + { + \int_compare:nNnTF { `#4 } = { "00DF } + { + \@@_change_case_store:e + { \char_generate:nn { "1E9E } { \@@_char_catcode:N #4 } } + \use:c { @@_change_case_char_next_ #2 :nn } + {#2} {#3} + } + { \@@_change_case_char:nnnN {#1} {#2} {#3} #4 } + } + } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}[EXP]{\@@_change_case_upper_el:nnnN} +% \begin{macro}[EXP]{\@@_change_case_upper_el:nnnn} +% \begin{macro}[EXP]{\@@_change_case_upper_el_aux:nnnN} +% \begin{macro}[EXP]{\@@_change_case_upper_el_loop:nnw} +% \begin{macro}[EXP]{\@@_change_case_upper_el:nnN} +% \begin{macro}[EXP]{\@@_change_case_if_greek:nTF} +% For Greek uppercasing, we need to know if characters \emph{in the Greek +% range} have accents. That means doing a \textsc{nfd} conversion first, then +% starting a search. As described by the Unicode \textsc{cldr}, Greek accents +% need to be found \emph{after} any U+0308 (diaeresis) and are done in two +% groups to allow for the canonical ordering. +% \begin{macrocode} +\bool_lazy_or:nnT + { \sys_if_engine_luatex_p: } + { \sys_if_engine_xetex_p: } + { + \cs_new:Npn \@@_change_case_upper_el:nnnN #1#2#3#4 + { + \@@_change_case_if_greek:nTF { `#4 } + { + \exp_args:Ne \@@_change_case_upper_el:nnnn + { \char_to_nfd:N #4 } {#1} {#2} {#3} + } + { \@@_change_case_char:nnnN {#1} {#2} {#3} #4 } + } + \cs_new:Npn \@@_change_case_upper_el:nnnn #1#2#3#4 + { \@@_change_case_upper_el_aux:nnnN {#2} {#3} {#4} #1 } + \cs_new:Npn \@@_change_case_upper_el_aux:nnnN #1#2#3#4 + { + \@@_change_case_store:e { \use:c { char_ #1 case:N } #4 } + \@@_change_case_upper_el_loop:nnw {#2} {#3} + } + \cs_new:Npn \@@_change_case_upper_el_loop:nnw + #1#2#3 \q_recursion_stop + { + \tl_if_head_is_N_type:nTF {#3} + { \@@_change_case_upper_el:nnN } + { \@@_change_case_loop:nnw } + {#1} {#2} #3 \q_recursion_stop + } +% \end{macrocode} +% In addition to the Greek accents, we list three cases here where an +% accent outside the Greek range has a \text{nfd} that would make it +% equivalent. That includes U+0344, which has to insert U+0308. +% \begin{macrocode} + \cs_new:Npn \@@_change_case_upper_el:nnN #1#2#3 + { + \token_if_cs:NTF #3 + { \@@_change_case_loop:nnw {#1} {#2} #3 } + { + \int_compare:nNnTF { `#3 } = { "0308 } + { + \@@_change_case_store:n {#3} + \@@_change_case_upper_el_loop:nnw {#1} {#2} + } + { + \bool_lazy_any:nTF + { + { \int_compare_p:nNn { `#3 } = { "0300 } } + { \int_compare_p:nNn { `#3 } = { "0301 } } + { \int_compare_p:nNn { `#3 } = { "0304 } } + { \int_compare_p:nNn { `#3 } = { "0306 } } + { \int_compare_p:nNn { `#3 } = { "0308 } } + { \int_compare_p:nNn { `#3 } = { "0313 } } + { \int_compare_p:nNn { `#3 } = { "0314 } } + { \int_compare_p:nNn { `#3 } = { "0342 } } + { \int_compare_p:nNn { `#3 } = { "0340 } } + { \int_compare_p:nNn { `#3 } = { "0341 } } + { \int_compare_p:nNn { `#3 } = { "0343 } } + } + { \@@_change_case_upper_el_loop:nnw {#1} {#2} } + { + \int_compare:nNnTF { `#3 } = { "0344 } + { + \@@_change_case_store:e + { + \char_generate:nn { "0308 } + { \@@_char_catcode:N #3 } + } + \@@_change_case_upper_el_loop:nnw {#1} {#2} + } + { + \int_compare:nNnTF { `#3 } = { "0345 } + { \@@_change_case_loop:nnw {#1} {#2} } + { \@@_change_case_loop:nnw {#1} {#2} #3 } + } + } + } + } + } + \prg_new_conditional:Npnn \@@_change_case_if_greek:n #1 { TF } + { + \if_int_compare:w #1 < "0370 \exp_stop_f: + \prg_return_false: + \else: + \if_int_compare:w #1 > "03FF \exp_stop_f: + \if_int_compare:w #1 < "1F00 \exp_stop_f: + \prg_return_false: + \else: + \if_int_compare:w #1 > "1FFF \exp_stop_f: + \prg_return_false: + \else: + \prg_return_true: + \fi: + \fi: + \else: + \prg_return_true: + \fi: + \fi: + } + } +% \end{macrocode} +% \end{macro} +% \end{macro} +% \end{macro} +% \end{macro} +% \end{macro} +% \end{macro} +% \begin{macro}[EXP]{\@@_change_case_title_el:nnnN} +% Titlecasing retains accents, but to prevent the uppercasing code +% from kicking in, there has to be an explicit function here. +% \begin{macrocode} +\bool_lazy_or:nnT + { \sys_if_engine_luatex_p: } + { \sys_if_engine_xetex_p: } + { + \cs_new:Npn \@@_change_case_title_el:nnnN #1#2#3#4 + { \@@_change_case_char:nnnN {#1} {#2} {#3} #4 } + } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}[EXP] +% { +% \@@_change_cases_lower_lt:nnnN , +% \@@_change_cases_lower_lt_auxi:nnnN , +% \@@_change_cases_lower_lt_auxii:nnnN +% } +% \begin{macro}[rEXP]{\@@_change_case_lower_lt:nnw} +% \begin{macro}[rEXP]{\@@_change_case_lower_lt:nnN} +% For Lithuanian, the issue to be dealt with is dots over lower case +% letters: these should be present if there is another accent. The first step +% is a simple match attempt: look for the three uppercase accented letters +% which should gain a dot-above char in their lowercase form. +% \begin{macrocode} +\bool_lazy_or:nnT + { \sys_if_engine_luatex_p: } + { \sys_if_engine_xetex_p: } + { + \cs_new:Npn \@@_change_case_lower_lt:nnnN #1#2#3#4 + { + \exp_args:Ne \@@_change_case_lower_lt_auxi:nnnN + { + \int_case:nn { `#4 } + { + { "00CC } { "0300 } + { "00CD } { "0301 } + { "0128 } { "0303 } + } + } + {#2} {#3} #4 + } +% \end{macrocode} +% If there was a hit, output the result with the dot-above and move on. +% Otherwise, look for one of the three letters that can take a combining +% accent: I, J and I-ogonek. +% \begin{macrocode} + \cs_new:Npn \@@_change_case_lower_lt_auxi:nnnN #1#2#3#4 + { + \tl_if_blank:nTF {#1} + { + \exp_args:Ne \@@_change_case_lower_lt_auxii:nnnN + { + \int_case:nn { `#4 } + { + { "0049 } { "0069 } + { "004A } { "006A } + { "012E } { "012F } + } + } + {#2} {#3} #4 + } + { + \@@_change_case_store:e + { + \char_generate:nn { "0069 } { \@@_char_catcode:N #4 } + \char_generate:nn { "0307 } { \@@_char_catcode:N #4 } + \char_generate:nn {#1} { \@@_char_catcode:N #4 } + } + \@@_change_case_loop:nnw {#2} {#3} + } + } +% \end{macrocode} +% Again, branch depending on a hit. If there is one, we output the character +% then need to look for a combining accent: as usual, we need to be aware of +% the loop situation. +% \begin{macrocode} + \cs_new:Npn \@@_change_case_lower_lt_auxii:nnnN #1#2#3#4 + { + \tl_if_blank:nTF {#1} + { \@@_change_case_lower_sigma:nnnN {#2} {#2} {#3} #4 } + { + \@@_change_case_store:e + { \char_generate:nn {#1} { \@@_char_catcode:N #4 } } + \@@_change_case_lower_lt:nnw {#2} {#3} + } + } + \cs_new:Npn \@@_change_case_lower_lt:nnw #1#2#3 \q_recursion_stop + { + \tl_if_head_is_N_type:nTF {#3} + { \@@_change_case_lower_lt:nnN } + { \@@_change_case_loop:nnw } + {#1} {#2} #3 \q_recursion_stop + } + \cs_new:Npn \@@_change_case_lower_lt:nnN #1#2#3 + { + \bool_lazy_and:nnT + { ! \token_if_cs_p:N #3 } + { + \bool_lazy_any_p:n + { + { \int_compare_p:nNn { `#3 } = { "0300 } } + { \int_compare_p:nNn { `#3 } = { "0301 } } + { \int_compare_p:nNn { `#3 } = { "0303 } } + } + } + { + \@@_change_case_store:e + { \char_generate:nn { "0307 } { \@@_char_catcode:N #3 } } + } + \@@_change_case_loop:nnw {#1} {#2} #3 + } + } +% \end{macrocode} +% \end{macro} +% \end{macro} +% \end{macro} +% \begin{macro}[EXP] +% { +% \@@_change_cases_upper_lt:nnnN , +% \@@_change_cases_upper_lt_aux:nnnN +% } +% \begin{macro}[rEXP]{\@@_change_case_upper_lt:nnw} +% \begin{macro}[rEXP]{\@@_change_case_upper_lt:nnN} +% The uppercasing version: first find i/j/i-ogonek, then look for the +% combining char: drop it if present. +% \begin{macrocode} +\bool_lazy_or:nnT + { \sys_if_engine_luatex_p: } + { \sys_if_engine_xetex_p: } + { + \cs_new:Npn \@@_change_case_upper_lt:nnnN #1#2#3#4 + { + \exp_args:Ne \@@_change_case_upper_lt_aux:nnnN + { + \int_case:nn { `#4 } + { + { "0069 } { "0049 } + { "006A } { "004A } + { "012F } { "012E } + } + } + {#2} {#3} #4 + } + \cs_new:Npn \@@_change_case_upper_lt_aux:nnnN #1#2#3#4 + { + \tl_if_blank:nTF {#1} + { \@@_change_case_char:nnnN { upper } {#2} {#3} #4 } + { + \@@_change_case_store:e + { \char_generate:nn {#1} { \@@_char_catcode:N #4 } } + \@@_change_case_upper_lt:nnw {#2} {#3} + } + } + \cs_new:Npn \@@_change_case_upper_lt:nnw #1#2#3 \q_recursion_stop + { + \tl_if_head_is_N_type:nTF {#3} + { \@@_change_case_upper_lt:nnN } + { \use:c { @@_change_case_char_next_ #1 :nn } } + {#1} {#2} #3 \q_recursion_stop + } + \cs_new:Npn \@@_change_case_upper_lt:nnN #1#2#3 + { + \bool_lazy_and:nnTF + { ! \token_if_cs_p:N #3 } + { \int_compare_p:nNn { `#3 } = { "0307 } } + { \use:c { @@_change_case_char_next_ #1 :nn } {#1} {#2} } + { \use:c { @@_change_case_char_next_ #1 :nn } {#1} {#2} #3 } + } + } +% \end{macrocode} +% \end{macro} +% \end{macro} +% \end{macro} +% +% \begin{macro}[EXP]{\@@_change_case_title_nl:nnnN} +% \begin{macro}[EXP]{\@@_change_case_title_nl:nnw} +% \begin{macro}[EXP]{\@@_change_case_title_nl:nnN} +% For Dutch, there is a single look-ahead test for \texttt{ij} when +% title casing. If the appropriate letters are found, produce \texttt{IJ} +% and gobble the \texttt{j}/\texttt{J}. +% \begin{macrocode} +\cs_new:Npn \@@_change_case_title_nl:nnnN #1#2#3#4 + { + \bool_lazy_or:nnTF + { \int_compare_p:nNn { `#4 } = { "0049 } } + { \int_compare_p:nNn { `#4 } = { "0069 } } + { + \@@_change_case_store:e + { \char_generate:nn { "0049 } { \@@_char_catcode:N #4 } } + \@@_change_case_title_nl:nnw {#2} {#3} + } + { \@@_change_case_char:nnnN {#1} {#2} {#3} #4 } + } +\cs_new:Npn \@@_change_case_title_nl:nnw #1#2#3 \q_recursion_stop + { + \tl_if_head_is_N_type:nTF {#3} + { \@@_change_case_title_nl:nnN } + { \use:c { @@_change_case_char_next_ #1 :nn } } + {#1} {#2} #3 \q_recursion_stop + } +\cs_new:Npn \@@_change_case_title_nl:nnN #1#2#3 + { + \bool_lazy_and:nnTF + { ! \token_if_cs_p:N #3 } + { + \bool_lazy_or_p:nn + { \int_compare_p:nNn { `#3 } = { "004A } } + { \int_compare_p:nNn { `#3 } = { "006A } } + } + { + \@@_change_case_store:e + { \char_generate:nn { "004A } { \@@_char_catcode:N #3 } } + \use:c { @@_change_case_char_next_ #1 :nn } {#1} {#2} + } + { \use:c { @@_change_case_char_next_ #1 :nn } {#1} {#2} #3 } + } +% \end{macrocode} +% \end{macro} +% \end{macro} +% \end{macro} +% +% \begin{macro}[EXP]{\@@_change_case_lower_tr:nnnN} +% \begin{macro}[EXP]{\@@_change_case_lower_tr:nnNw} +% \begin{macro}[EXP]{\@@_change_case_lower_tr:nnN} +% \begin{macro}[EXP]{\@@_change_case_lower_tr:nnnNN} +% The Turkic languages need special treatment for dotted-i and dotless-i. +% The lower casing rule can be expressed in terms of searching first for +% either a dotless-I or a dotted-I. In the latter case the mapping is +% easy, but in the former there is a second stage search. +% \begin{macrocode} +\bool_lazy_or:nnTF + { \sys_if_engine_luatex_p: } + { \sys_if_engine_xetex_p: } + { + \cs_new:Npn \@@_change_case_lower_tr:nnnN #1#2#3#4 + { + \int_compare:nNnTF { `#4 } = { "0049 } + { \@@_change_case_lower_tr:nnNw {#1} {#3} #4 } + { + \int_compare:nNnTF { `#4 } = { "0130 } + { + \@@_change_case_store:e + { \char_generate:nn { "0069 } { \@@_char_catcode:N #4 } } + \@@_change_case_loop:nnw {#1} {#3} + } + { \@@_change_case_lower_sigma:nnnN {#1} {#2} {#3} #4 } + } + } +% \end{macrocode} +% After a dotless-I there may be a dot-above character. If there is then +% a dotted-i should be produced, otherwise output a dotless-i. When the +% combination is found both the dotless-I and the dot-above char have to +% be removed from the input. +% \begin{macrocode} + \cs_new:Npn \@@_change_case_lower_tr:nnNw #1#2#3#4 \q_recursion_stop + { + \tl_if_head_is_N_type:nTF {#4} + { \@@_change_case_lower_tr:nnN } + { + \@@_change_case_store:e + { \char_generate:nn { "0131 } { \@@_char_catcode:N #3 } } + \@@_change_case_loop:nnw + } + {#1} {#2} #4 \q_recursion_stop + } + \cs_new:Npn \@@_change_case_lower_tr:nnN #1#2#3 + { + \bool_lazy_or:nnTF + { \token_if_cs_p:N #3 } + { ! \int_compare_p:nNn { `#3 } = { "0307 } } + { + \@@_change_case_store:e + { \char_generate:nn { "0131 } { \@@_char_catcode:N #3 } } + \@@_change_case_loop:nnw {#1} {#2} #3 + } + { + \@@_change_case_store:e + { \char_generate:nn { "0069 } { \@@_char_catcode:N #3 } } + \@@_change_case_loop:nnw {#1} {#2} + } + } + } +% \end{macrocode} +% For $8$-bit engines, dot-above is not available so there is a simple +% test for an upper-case I. Then we can look for the UTF-8 representation of +% an upper case dotted-I without the combining char. If it's not there, +% preserve the UTF-8 sequence as-is. With $8$bit engines, we cannot +% completely preserve category codes, so we have to make some assumptions: +% output a \enquote{normal} \texttt{i} for the dotted case. As the original +% character here is catcode-$13$, we have to make a choice about handling +% of |i|: generate a \enquote{normal} one. +% \begin{macrocode} + { + \cs_new:Npn \@@_change_case_lower_tr:nnnN #1#2#3#4 + { + \int_compare:nNnTF { `#4 } = { "0049 } + { + \@@_change_case_store:V \c_@@_dotless_i_tl + \@@_change_case_loop:nnw {#1} {#3} + } + { + \int_compare:nNnTF { `#4 } = { "00C4 } + { \@@_change_case_lower_tr:nnnNN {#1} {#2} {#3} #4 } + { \@@_change_case_char:nnnN {#1} {#2} {#3} #4 } + } + } + \cs_new:Npn \@@_change_case_lower_tr:nnnNN #1#2#3#4#5 + { + \int_compare:nNnTF { `#5 } = { "00B0 } + { + \@@_change_case_store:e + { + \char_generate:nn { "0069 } + { \char_value_catcode:n { "0069 } } + } + \@@_change_case_loop:nnw {#1} {#3} + } + { \@@_change_case_char:nnnN {#1} {#2} {#3} #4#5 } + } + } +% \end{macrocode} +% \end{macro} +% \end{macro} +% \end{macro} +% \end{macro} +% \begin{macro}[EXP]{\@@_change_case_upper_tr:nnnN} +% Uppercasing is easier: just one exception with no context. +% \begin{macrocode} +\cs_new:Npx \@@_change_case_upper_tr:nnnN #1#2#3#4 + { + \exp_not:N \int_compare:nNnTF { `#4 } = { "0069 } + { + \bool_lazy_or:nnTF + { \sys_if_engine_luatex_p: } + { \sys_if_engine_xetex_p: } + { + \exp_not:N \@@_change_case_store:e + { + \exp_not:N \char_generate:nn { "0130 } + { \exp_not:N \@@_char_catcode:N #4 } + } + } + { + \exp_not:N \@@_change_case_store:V + \exp_not:N \c_@@_dotted_I_tl + } + \exp_not:N \use:c { @@_change_case_char_next_ #2 :nn } {#2} {#3} + } + { \exp_not:N \@@_change_case_char:nnnN {#1} {#2} {#3} #4 } + } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}[EXP] +% {\@@_change_case_lower_az:nnnN, \@@_change_case_upper_az:nnnN} +% Straight copies. +% \begin{macrocode} +\cs_new_eq:NN \@@_change_case_lower_az:nnnN + \@@_change_case_lower_tr:nnnN +\cs_new_eq:NN \@@_change_case_upper_az:nnnN + \@@_change_case_upper_tr:nnnN +% \end{macrocode} +% \end{macro} +% +% \subsection{Case changing data for $8$-bit engines} +% +% \begin{variable} +% { +% \c_@@_dotless_i_tl , +% \c_@@_dotted_I_tl , +% \c_@@_i_ogonek_tl , +% \c_@@_I_ogonek_tl +% } +% For cases where there is an $8$-bit option in the |T1| font set up, +% a variant is provided in both cases. +% \begin{macrocode} +\group_begin: + \bool_lazy_or:nnF + { \sys_if_engine_luatex_p: } + { \sys_if_engine_xetex_p: } + { + \cs_set_protected:Npn \@@_tmp:w #1#2 + { + \group_begin: + \cs_set_protected:Npn \@@_tmp:w ##1##2##3##4 + { + \tl_const:Nx #1 + { + \exp_after:wN \exp_after:wN \exp_after:wN + \exp_not:N \char_generate:nn {##1} { 13 } + \exp_after:wN \exp_after:wN \exp_after:wN + \exp_not:N \char_generate:nn {##2} { 13 } + } + } + \use:x + { \@@_tmp:w \char_to_utfviii_bytes:n { "#2 } } + \group_end: + } + \@@_tmp:w \c_@@_dotless_i_tl { 0131 } + \@@_tmp:w \c_@@_dotted_I_tl { 0130 } + \@@_tmp:w \c_@@_i_ogonek_tl { 012F } + \@@_tmp:w \c_@@_I_ogonek_tl { 012E } + } +\group_end: +% \end{macrocode} +% \end{variable} +% +% For $8$-bit engines we now need to define the case-change data for +% the multi-octet mappings. These need a list of what code points are +% doable in |T1| so the list is hard coded (there's no saving in loading +% the mappings dynamically). All of the straight-forward ones have two +% octets, so that is taken as read. +% \begin{macrocode} +\group_begin: + \bool_lazy_or:nnF + { \sys_if_engine_luatex_p: } + { \sys_if_engine_xetex_p: } + { + \cs_set_protected:Npn \@@_loop:nn #1#2 + { + \quark_if_recursion_tail_stop:n {#1} + \use:x + { + \@@_tmp:w + \char_to_utfviii_bytes:n { "#1 } + \char_to_utfviii_bytes:n { "#2 } + } + \@@_loop:nn + } + \cs_set_protected:Npn \@@_tmp:nnnn #1#2#3#4#5 + { + \tl_const:cx + { + c_@@_ #1 case_ + \char_generate:nn {#2} { 12 } + \char_generate:nn {#3} { 12 } + _tl + } + { + \exp_after:wN \exp_after:wN \exp_after:wN + \exp_not:N \char_generate:nn {#4} { 13 } + \exp_after:wN \exp_after:wN \exp_after:wN + \exp_not:N \char_generate:nn {#5} { 13 } + } + } + \cs_set_protected:Npn \@@_tmp:w #1#2#3#4#5#6#7#8 + { + \tl_const:cx + { + c_@@_lowercase_ + \char_generate:nn {#1} { 12 } + \char_generate:nn {#2} { 12 } + _tl + } + { + \exp_after:wN \exp_after:wN \exp_after:wN + \exp_not:N \char_generate:nn {#5} { 13 } + \exp_after:wN \exp_after:wN \exp_after:wN + \exp_not:N \char_generate:nn {#6} { 13 } + } + \@@_tmp:nnnn { upper } {#5} {#6} {#1} {#2} + \@@_tmp:nnnn { title } {#5} {#6} {#1} {#2} + } + \@@_loop:nn + { 00C0 } { 00E0 } + { 00C1 } { 00E1 } + { 00C2 } { 00E2 } + { 00C3 } { 00E3 } + { 00C4 } { 00E4 } + { 00C5 } { 00E5 } + { 00C6 } { 00E6 } + { 00C7 } { 00E7 } + { 00C8 } { 00E8 } + { 00C9 } { 00E9 } + { 00CA } { 00EA } + { 00CB } { 00EB } + { 00CC } { 00EC } + { 00CD } { 00ED } + { 00CE } { 00EE } + { 00CF } { 00EF } + { 00D0 } { 00F0 } + { 00D1 } { 00F1 } + { 00D2 } { 00F2 } + { 00D3 } { 00F3 } + { 00D4 } { 00F4 } + { 00D5 } { 00F5 } + { 00D6 } { 00F6 } + { 00D8 } { 00F8 } + { 00D9 } { 00F9 } + { 00DA } { 00FA } + { 00DB } { 00FB } + { 00DC } { 00FC } + { 00DD } { 00FD } + { 00DE } { 00FE } + { 0100 } { 0101 } + { 0102 } { 0103 } + { 0104 } { 0105 } + { 0106 } { 0107 } + { 0108 } { 0109 } + { 010A } { 010B } + { 010C } { 010D } + { 010E } { 010F } + { 0110 } { 0111 } + { 0112 } { 0113 } + { 0114 } { 0115 } + { 0116 } { 0117 } + { 0118 } { 0119 } + { 011A } { 011B } + { 011C } { 011D } + { 011E } { 011F } + { 0120 } { 0121 } + { 0122 } { 0123 } + { 0124 } { 0125 } + { 0128 } { 0129 } + { 012A } { 012B } + { 012C } { 012D } + { 012E } { 012F } + { 0132 } { 0133 } + { 0134 } { 0135 } + { 0136 } { 0137 } + { 0139 } { 013A } + { 013B } { 013C } + { 013E } { 013F } + { 0141 } { 0142 } + { 0143 } { 0144 } + { 0145 } { 0146 } + { 0147 } { 0148 } + { 014A } { 014B } + { 014C } { 014D } + { 014E } { 014F } + { 0150 } { 0151 } + { 0152 } { 0153 } + { 0154 } { 0155 } + { 0156 } { 0157 } + { 0158 } { 0159 } + { 015A } { 015B } + { 015C } { 015D } + { 015E } { 015F } + { 0160 } { 0161 } + { 0162 } { 0163 } + { 0164 } { 0165 } + { 0168 } { 0169 } + { 016A } { 016B } + { 016C } { 016D } + { 016E } { 016F } + { 0170 } { 0171 } + { 0172 } { 0173 } + { 0174 } { 0175 } + { 0176 } { 0177 } + { 0178 } { 00FF } + { 0179 } { 017A } + { 017B } { 017C } + { 017D } { 017E } + { 01CD } { 01CE } + { 01CF } { 01D0 } + { 01D1 } { 01D2 } + { 01D3 } { 01D4 } + { 01E2 } { 01E3 } + { 01E6 } { 01E7 } + { 01E8 } { 01E9 } + { 01EA } { 01EB } + { 01F4 } { 01F5 } + { 0218 } { 0219 } + { 021A } { 021B } + \q_recursion_tail ? + \q_recursion_stop + \cs_set_protected:Npn \@@_tmp:w #1#2#3 + { + \group_begin: + \cs_set_protected:Npn \@@_tmp:w ##1##2##3##4 + { + \tl_const:cx + { + c_@@_ #3 case_ + \char_generate:nn {##1} { 12 } + \char_generate:nn {##2} { 12 } + _tl + } + {#2} + } + \use:x + { \@@_tmp:w \char_to_utfviii_bytes:n { "#1 } } + \group_end: + } + \@@_tmp:w { 00DF } { SS } { upper } + \@@_tmp:w { 00DF } { Ss } { title } + \@@_tmp:w { 0131 } { I } { upper } + } + \group_end: +% \end{macrocode} +% +% The (fixed) look-up mappings for letter-like control sequences. +% \begin{macrocode} +\group_begin: + \cs_set_protected:Npn \@@_change_case_setup:NN #1#2 + { + \quark_if_recursion_tail_stop:N #1 + \tl_const:cn { c_@@_lowercase_ \token_to_str:N #1 _tl } + { #2 } + \tl_const:cn { c_@@_uppercase_ \token_to_str:N #2 _tl } + { #1 } + \@@_change_case_setup:NN + } + \@@_change_case_setup:NN + \AA \aa + \AE \ae + \DH \dh + \DJ \dj + \IJ \ij + \L \l + \NG \ng + \O \o + \OE \oe + \SS \ss + \TH \th + \q_recursion_tail ? + \q_recursion_stop + \tl_const:cn { c_@@_uppercase_ \token_to_str:N \i _tl } { I } + \tl_const:cn { c_@@_uppercase_ \token_to_str:N \j _tl } { J } +\group_end: +% \end{macrocode} +% +% \begin{macrocode} +% +% \end{macrocode} +% +% \end{implementation} +% +% \PrintIndex -- cgit v1.2.3