summaryrefslogtreecommitdiff
path: root/macros/latex/contrib/l3kernel/l3text.dtx
diff options
context:
space:
mode:
Diffstat (limited to 'macros/latex/contrib/l3kernel/l3text.dtx')
-rw-r--r--macros/latex/contrib/l3kernel/l3text.dtx178
1 files changed, 177 insertions, 1 deletions
diff --git a/macros/latex/contrib/l3kernel/l3text.dtx b/macros/latex/contrib/l3kernel/l3text.dtx
index df4f2fcde5..3e89563a5e 100644
--- a/macros/latex/contrib/l3kernel/l3text.dtx
+++ b/macros/latex/contrib/l3kernel/l3text.dtx
@@ -43,7 +43,7 @@
% }^^A
% }
%
-% \date{Released 2022-08-23}
+% \date{Released 2022-08-30}
%
% \maketitle
%
@@ -184,6 +184,8 @@
% combining dot is removed when uppercasing in these cases. Note that
% \emph{only} the accents used in Lithuanian are covered: the behaviour
% of other accents are not modified.
+% \item Medieval Latin (\texttt{la-xmedieval}).
+% The characters |u| and |V| are interchanged on case changing.
% \item Dutch (\texttt{nl}).
% Capitalisation of \texttt{ij} at the beginning of titlecased
% input produces \texttt{IJ} rather than \texttt{Ij}. The output
@@ -299,6 +301,62 @@
% \texttt{true}.
% \end{variable}
%
+% \section{Mapping to graphemes}
+%
+% Grapheme splitting is implemented using the algorithm described in Unicode
+% Standard Annex \#29. This includes support for extended grapheme clusters.
+% Text starting with a line feed or carriage return character will drop this
+% due to standard \TeX{} processing. At present extended pictograms are
+% not supported: these may be added in a future release.
+%
+% \begin{function}[rEXP, added = 2022-08-04]{\text_map_function:nN}
+% \begin{syntax}
+% \cs{text_map_function:nN} \meta{text} \Arg{function}
+% \end{syntax}
+% Takes user input \meta{text} and expands as described for
+% \cs{text_expand:n}, then maps over the \emph{graphemes} within the
+% result, passing each grapheme to the \meta{function}.
+% Broadly a grapheme is a \enquote{user perceived character}:
+% the Unicode Consortium describe the decomposition of input to
+% graphemes in depth, and the approach used here implements that
+% algorithm. The \meta{function} should accept one argument as \meta{balanced
+% text}: this may be comprise codepoints or may be a control sequence.
+% With $8$-bit engines, the codepoint(s) themselves may of course be
+% made up of multiple bytes: the mapping will pass the correct codepoints
+% independent of the engine in use.
+% See also \cs{text_map_inline:nn}.
+% \end{function}
+%
+% \begin{function}[added = 2022-08-04]{\text_map_inline:nn}
+% \begin{syntax}
+% \cs{text_map_inline:nn} \meta{text} \Arg{inline function}
+% \end{syntax}
+% Takes user input \meta{text} and expands as described for
+% \cs{text_expand:n}, then maps over the \emph{graphemes} within the
+% result, passing each grapheme to the \meta{inline function}.
+% Broadly a grapheme is a \enquote{user perceived character}:
+% the Unicode Consortium describe the decomposition of input to
+% graphemes in depth, and the approach used here implements that
+% algorithm. The \meta{inline function} should consist of code which
+% receives the grapheme as \meta{balanced
+% text}: this may be comprise codepoints or may be a control sequence.
+% With $8$-bit engines, the codepoint(s) themselves may of course be
+% made up of multiple bytes: the mapping will pass the correct codepoints
+% independent of the engine in use.
+% See also \cs{text_map_function:nN}.
+% \end{function}
+%
+% \begin{function}[rEXP, added = 2022-08-04]
+% {\text_map_break:, \text_map_break:n}
+% \begin{syntax}
+% \cs{text_map_break:}
+% \cs{text_map_break:n} \Arg{code}
+% \end{syntax}
+% Used to terminate a \cs[no-index]{text_map_\ldots} function before all
+% entries in the \meta{text} have been processed. This
+% normally takes place within a conditional statement.
+% \end{function}
+%
% \end{documentation}
%
% \begin{implementation}
@@ -357,11 +415,14 @@
% \end{macro}
%
% \begin{macro}[EXP]{\@@_if_q_recursion_tail_stop_do:Nn}
+% \begin{macro}[EXP]{\@@_if_q_recursion_tail_stop_do:nn}
% Functions to query recursion quarks.
% \begin{macrocode}
\__kernel_quark_new_test:N \@@_if_q_recursion_tail_stop_do:Nn
+\__kernel_quark_new_test:N \@@_if_q_recursion_tail_stop_do:nn
% \end{macrocode}
% \end{macro}
+% \end{macro}
%
% \begin{variable}{\s_@@_recursion_tail,\s_@@_recursion_stop}
% Internal scan marks quarks.
@@ -581,6 +642,121 @@
% \end{macrocode}
% \end{macro}
%
+% \subsection{Codepoint utilities}
+%
+% For working with codepoints in an engine-neutral way.
+%
+% \begin{macro}[EXP]{\@@_codepoint_process:nN}
+% \begin{macro}[EXP]{\@@_codepoint_process:nNN}
+% \begin{macro}[EXP]{\@@_codepoint_process:nNNN}
+% \begin{macro}[EXP]{\@@_codepoint_process:nNNNN}
+% Grab a codepoint and apply some code to it: here |#1| should expect one
+% following \emph{balanced text}.
+% \begin{macrocode}
+\bool_lazy_or:nnTF
+ { \sys_if_engine_luatex_p: }
+ { \sys_if_engine_xetex_p: }
+ {
+ \cs_new:Npn \@@_codepoint_process:nN #1#2 { #1 {#2} }
+ }
+ {
+ \cs_new:Npn \@@_codepoint_process:nN #1#2
+ {
+ \int_compare:nNnTF { `#2 } > { "80 }
+ {
+ \int_compare:nNnTF { `#2 } < { "E0 }
+ { \@@_codepoint_process:nNN }
+ {
+ \int_compare:nNnTF { `#2 } < { "F0 }
+ { \@@_codepoint_process:nNNN }
+ { \@@_codepoint_process:nNNNN }
+ }
+ }
+ { \use:n }
+ {#1} #2
+ }
+ \cs_new:Npn \@@_codepoint_process:nNN #1#2#3
+ { #1 {#2#3} }
+ \cs_new:Npn \@@_codepoint_process:nNNN #1#2#3#4
+ { #1 {#2#3#4} }
+ \cs_new:Npn \@@_codepoint_process:nNNNN #1#2#3#4#5
+ { #1 {#2#3#4#5} }
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}[EXP, pTF]{\@@_codepoint_compare:nNn}
+% \begin{macro}[EXP]{\@@_codepoint_compare:N, \@@_codepoint_compare_aux:N}
+% \begin{macro}[EXP]{\@@_codepoint_compare:NN}
+% \begin{macro}[EXP]{\@@_codepoint_compare:NNN}
+% \begin{macro}[EXP]{\@@_codepoint_compare:NNNN}
+% Allows comparison for all engines using a first \enquote{character} followed
+% by a codepoint.
+% \begin{macrocode}
+\bool_lazy_or:nnTF
+ { \sys_if_engine_luatex_p: }
+ { \sys_if_engine_xetex_p: }
+ {
+ \prg_new_conditional:Npnn
+ \@@_codepoint_compare:nNn #1#2#3 { TF , p }
+ {
+ \int_compare:nNnTF { `#1 } #2 {#3}
+ \prg_return_true: \prg_return_false:
+ }
+ }
+ {
+ \prg_new_conditional:Npnn
+ \@@_codepoint_compare:nNn #1#2#3 { TF , p }
+ {
+ \int_compare:nNnTF { \@@_codepoint_compare:N #1 }
+ #2 {#3}
+ \prg_return_true: \prg_return_false:
+ }
+ \cs_new:Npn \@@_codepoint_compare:N #1
+ {
+ \if_int_compare:w `#1 > "80 \exp_stop_f:
+ \if_int_compare:w `#1 < "E0 \exp_stop_f:
+ \exp_after:wN \exp_after:wN \exp_after:wN
+ \@@_codepoint_compare:NN
+ \else:
+ \if_int_compare:w `#1 < "F0 \exp_stop_f:
+ \exp_after:wN \exp_after:wN \exp_after:wN
+ \exp_after:wN \exp_after:wN \exp_after:wN
+ \exp_after:wN \@@_codepoint_compare:NNN
+ \else:
+ \exp_after:wN \exp_after:wN \exp_after:wN
+ \exp_after:wN \exp_after:wN \exp_after:wN
+ \exp_after:wN \@@_codepoint_compare:NNNN
+ \fi:
+ \fi:
+ \else:
+ \exp_after:wN \@@_codepoint_compare_aux:N
+ \fi:
+ #1
+ }
+ \cs_new:Npn \@@_codepoint_compare_aux:N #1 { `#1 }
+ \cs_new:Npn \@@_codepoint_compare:NN #1#2
+ { (`#1 - "C0) * "40 + `#2 - "80 }
+ \cs_new:Npn \@@_codepoint_compare:NNN #1#2#3
+ { (`#1 - "E0) * "1000 + (`#2 - "80) * "40 + `#3 - "80 }
+ \cs_new:Npn \@@_codepoint_compare:NNNN #1#2#3#4
+ {
+ (`#1 - "F0) * "40000
+ + (`#2 - "80) * "1000
+ + (`#3 - "80) * "40
+ + `#4 - "80
+ }
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+%
% \subsection{Configuration variables}
%
% \begin{variable}{\l_text_accents_tl, \l_text_letterlike_tl}