summaryrefslogtreecommitdiff
path: root/macros/latex/contrib/l3kernel/l3str-convert.dtx
diff options
context:
space:
mode:
Diffstat (limited to 'macros/latex/contrib/l3kernel/l3str-convert.dtx')
-rw-r--r--macros/latex/contrib/l3kernel/l3str-convert.dtx134
1 files changed, 103 insertions, 31 deletions
diff --git a/macros/latex/contrib/l3kernel/l3str-convert.dtx b/macros/latex/contrib/l3kernel/l3str-convert.dtx
index 5836bea27b..4000a2d1f7 100644
--- a/macros/latex/contrib/l3kernel/l3str-convert.dtx
+++ b/macros/latex/contrib/l3kernel/l3str-convert.dtx
@@ -44,7 +44,7 @@
% }^^A
% }
%
-% \date{Released 2020-05-15}
+% \date{Released 2020-06-03}
%
% \maketitle
%
@@ -186,6 +186,19 @@
% encoding. Instead, the \meta{false code} is performed.
% \end{function}
%
+% \section{Conversion by expansion (for PDF contexts)}
+%
+% A small number of expandable functions are provided for use in PDF string/name
+% contexts. These \emph{assume UTF-8} and \emph{no escaping} in the input.
+%
+% \begin{function}[EXP]{\str_convert_pdfname:n}
+% \begin{syntax}
+% \cs{str_convert_pdfname:n} \meta{string}
+% \end{syntax}
+% As \cs{str_set_convert:Nnnn}, converts the \meta{string} on a byte-by-byte
+% basis with non-ASCII codepoints escaped using hashes.
+% \end{function}
+%
% \section[Creating 8-bit mappings]{Creating $8$-bit mappings}
%
% \begin{function}{\str_declare_eight_bit_encoding:nnn}
@@ -345,39 +358,41 @@
%
% \subsection{String conditionals}
%
-% \begin{macro}[EXP]{\@@_if_contains_char:NNT, \@@_if_contains_char:NNTF}
-% \begin{macro}[EXP]{\@@_if_contains_char:nNTF}
-% \begin{macro}[EXP]{\@@_if_contains_char_aux:NN}
+% \begin{macro}[EXP]{\@@_if_contains_char:NnT, \@@_if_contains_char:NnTF}
+% \begin{macro}[EXP]{\@@_if_contains_char:nnTF}
+% \begin{macro}[EXP]{\@@_if_contains_char_aux:nn,\@@_if_contains_char_auxi:nN}
% \begin{macro}[EXP]{\@@_if_contains_char_true:}
% \begin{syntax}
-% \cs{@@_if_contains_char:nNTF} \Arg{token list} \meta{char}
+% \cs{@@_if_contains_char:nnTF} \Arg{token list} \meta{char}
% \end{syntax}
% Expects the \meta{token list} to be an \meta{other string}: the
% caller is responsible for ensuring that no (too-)special catcodes
-% remain. Spaces with catcode $10$ are ignored.
+% remain.
% Loop over the characters of the string, comparing character codes.
% The loop is broken if character codes match. Otherwise we return
% \enquote{false}.
% \begin{macrocode}
-\prg_new_conditional:Npnn \@@_if_contains_char:NN #1#2 { T , TF }
+\prg_new_conditional:Npnn \@@_if_contains_char:Nn #1#2 { T , TF }
{
- \exp_after:wN \@@_if_contains_char_aux:NN \exp_after:wN #2
- #1 { \prg_break:n { ? \fi: } }
+ \exp_after:wN \@@_if_contains_char_aux:nn \exp_after:wN {#1} {#2}
+ { \prg_break:n { ? \fi: } }
\prg_break_point:
\prg_return_false:
}
-\prg_new_conditional:Npnn \@@_if_contains_char:nN #1#2 { TF }
+\cs_new:Npn \@@_if_contains_char_aux:nn #1#2
+ { \@@_if_contains_char_auxi:nN {#2} #1 }
+\prg_new_conditional:Npnn \@@_if_contains_char:nn #1#2 { TF }
{
- \@@_if_contains_char_aux:NN #2 #1 { \prg_break:n { ? \fi: } }
+ \@@_if_contains_char_auxi:nN {#2} #1 { \prg_break:n { ? \fi: } }
\prg_break_point:
\prg_return_false:
}
-\cs_new:Npn \@@_if_contains_char_aux:NN #1#2
+\cs_new:Npn \@@_if_contains_char_auxi:nN #1#2
{
\if_charcode:w #1 #2
\exp_after:wN \@@_if_contains_char_true:
\fi:
- \@@_if_contains_char_aux:NN #1
+ \@@_if_contains_char_auxi:nN {#1}
}
\cs_new:Npn \@@_if_contains_char_true:
{ \prg_break:n { \prg_return_true: \use_none:n } }
@@ -1595,8 +1610,8 @@
% \end{macro}
%
% \begin{macro}{\@@_convert_escape_name:}
-% \begin{macro}[rEXP]{\@@_escape_name_char:N}
-% \begin{macro}[rEXP]{\@@_if_escape_name:NTF}
+% \begin{macro}[rEXP]{\@@_escape_name_char:n}
+% \begin{macro}[rEXP]{\@@_if_escape_name:nTF}
% \begin{variable}{\c_@@_escape_name_str}
% \begin{variable}{\c_@@_escape_name_not_str}
% For each byte, test whether it should be output as is, or be
@@ -1609,22 +1624,22 @@
\str_const:Nn \c_@@_escape_name_not_str { ! " $ & ' } %$
\str_const:Nn \c_@@_escape_name_str { {}/<>[] }
\cs_new_protected:Npn \@@_convert_escape_name:
- { \@@_convert_gmap:N \@@_escape_name_char:N }
-\cs_new:Npn \@@_escape_name_char:N #1
+ { \@@_convert_gmap:N \@@_escape_name_char:n }
+\cs_new:Npn \@@_escape_name_char:n #1
{
- \@@_if_escape_name:NTF #1 {#1}
+ \@@_if_escape_name:nTF {#1} {#1}
{ \c_hash_str \@@_output_hexadecimal:n {`#1} }
}
-\prg_new_conditional:Npnn \@@_if_escape_name:N #1 { TF }
+\prg_new_conditional:Npnn \@@_if_escape_name:n #1 { TF }
{
\if_int_compare:w `#1 < "2A \exp_stop_f:
- \@@_if_contains_char:NNTF \c_@@_escape_name_not_str #1
+ \@@_if_contains_char:NnTF \c_@@_escape_name_not_str {#1}
\prg_return_true: \prg_return_false:
\else:
\if_int_compare:w `#1 > "7E \exp_stop_f:
\prg_return_false:
\else:
- \@@_if_contains_char:NNTF \c_@@_escape_name_str #1
+ \@@_if_contains_char:NnTF \c_@@_escape_name_str {#1}
\prg_return_false: \prg_return_true:
\fi:
\fi:
@@ -1652,8 +1667,8 @@
{
\@@_if_escape_string:NTF #1
{
- \@@_if_contains_char:NNT
- \c_@@_escape_string_str #1
+ \@@_if_contains_char:NnT
+ \c_@@_escape_string_str {#1}
{ \c_backslash_str }
#1
}
@@ -1683,28 +1698,28 @@
% \end{macro}
%
% \begin{macro}{\@@_convert_escape_url:}
-% \begin{macro}[rEXP]{\@@_escape_url_char:N}
-% \begin{macro}[rEXP]{\@@_if_escape_url:NTF}
+% \begin{macro}[rEXP]{\@@_escape_url_char:n}
+% \begin{macro}[rEXP]{\@@_if_escape_url:nTF}
% This function is similar to \cs{@@_convert_escape_name:}, escaping
% different characters.
% \begin{macrocode}
\cs_new_protected:Npn \@@_convert_escape_url:
- { \@@_convert_gmap:N \@@_escape_url_char:N }
-\cs_new:Npn \@@_escape_url_char:N #1
+ { \@@_convert_gmap:N \@@_escape_url_char:n }
+\cs_new:Npn \@@_escape_url_char:n #1
{
- \@@_if_escape_url:NTF #1 {#1}
+ \@@_if_escape_url:nTF {#1} {#1}
{ \c_percent_str \@@_output_hexadecimal:n { `#1 } }
}
-\prg_new_conditional:Npnn \@@_if_escape_url:N #1 { TF }
+\prg_new_conditional:Npnn \@@_if_escape_url:n #1 { TF }
{
\if_int_compare:w `#1 < "41 \exp_stop_f:
- \@@_if_contains_char:nNTF { "-.<> } #1
+ \@@_if_contains_char:nnTF { "-.<> } {#1}
\prg_return_true: \prg_return_false:
\else:
\if_int_compare:w `#1 > "7E \exp_stop_f:
\prg_return_false:
\else:
- \@@_if_contains_char:nNTF { [ ] } #1
+ \@@_if_contains_char:nnTF { [ ] } {#1}
\prg_return_false: \prg_return_true:
\fi:
\fi:
@@ -2581,6 +2596,63 @@
\group_end:
% \end{macrocode}
%
+% \subsection{PDF names and strings by expansion}
+%
+% \begin{macro}[EXP]{\str_convert_pdfname:n}
+% \begin{macro}[EXP]{\@@_convert_pdfname:n}
+% \begin{macro}[EXP]
+% {\@@_convert_pdfname_bytes:n, \@@_convert_pdfname_bytes_aux:n}
+% \begin{macro}[EXP]{\@@_convert_pdfname_bytes_aux:nnn}
+% To convert to PDF names by expansion, we work purely on UTF-8 input. The
+% first step is to make a string with \enquote{other} spaces,
+% after which we use a simple token-by-token approach. In Unicode
+% engines, we break down everything before one-byte codepoints, but for
+% $8$-bit engines there is no need to worry. Actual escaping is covered
+% by the same code as used in the non-expandable route.
+% \begin{macrocode}
+\cs_new:Npn \str_convert_pdfname:n #1
+ {
+ \exp_args:Ne \tl_to_str:n
+ { \str_map_function:nN {#1} \@@_convert_pdfname:n }
+ }
+\bool_lazy_or:nnTF
+ { \sys_if_engine_luatex_p: }
+ { \sys_if_engine_xetex_p: }
+ {
+ \cs_new:Npn \@@_convert_pdfname:n #1
+ {
+ \int_compare:nNnTF { `#1 } > { "7F }
+ { \@@_convert_pdfname_bytes:n {#1} }
+ { \@@_escape_name_char:n {#1} }
+ }
+ \cs_new:Npn \@@_convert_pdfname_bytes:n #1
+ {
+ \exp_args:Ne \@@_convert_pdfname_bytes_aux:n
+ { \char_to_utfviii_bytes:n {`#1} }
+ }
+ \cs_new:Npn \@@_convert_pdfname_bytes_aux:n #1
+ { \@@_convert_pdfname_bytes_aux:nnnn #1 }
+ \cs_new:Npx \@@_convert_pdfname_bytes_aux:nnnn #1#2#3#4
+ {
+ \c_hash_str \exp_not:N \@@_output_hexadecimal:n {#1}
+ \c_hash_str \exp_not:N \@@_output_hexadecimal:n {#2}
+ \exp_not:N \tl_if_blank:nF {#3}
+ {
+ \c_hash_str \exp_not:N \@@_output_hexadecimal:n {#3}
+ \exp_not:N \tl_if_blank:nF {#4}
+ {
+ \c_hash_str \exp_not:N \@@_output_hexadecimal:n {#4}
+ }
+ }
+ }
+ }
+ { \cs_new_eq:NN \@@_convert_pdfname:n \@@_escape_name_char:n }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+%
% \begin{macrocode}
%</initex|package>
% \end{macrocode}