2

This is a follow up to this post. Initially the problem was to store some function's options in some container. Examples of such functions: \includepdf, or \newwatermark. This can be useful if the options are many, and \function[options] is to be used repeatedly. If the options are stored directly in l3seq (first method), it works. But I wanted to store the options in l3prop because it takes care of duplicates (say if I update options at the point of expansion). So, then, an additional step is needed at the point of expansion: transfer the options from l3prop to l3seq and then expand with \seq_use:Nn. This defines the second method. It works for \includepdf, but not \newwatermark. However, Someone pointed out the l3prop method does not preserve the catcode, specifically changes that of the keys to an l3str. The proposed solution (code below) came with this caption: "The first part of that code corresponds to your programmation and the result is FALSE. In the second case, keys are yet strings by construction and the result is TRUE." It identifies where the code fails, but does not solve the problem. Hence the challenge in the subject line: be able to store key-val pairs in l3prop, and be able to recover the catcodes with which they were supplied. In particular, that their expansion work for \newwatermark[options].

PS: someone suggested dropping xwatermark altogether. I pointed out that I ran into problems with background.

\documentclass{article} \begin{document} \ExplSyntaxOn { \seq_set_from_clist:Nn \l_tmpa_seq { key = smth } \prop_set_from_keyval:Nn \l_tmpa_prop { key = smth } \prop_map_inline:Nn \l_tmpa_prop { \seq_put_right:Nn \l_tmpb_seq { #1 = #2 } } \tl_set:Nf \l_tmpa_tl { \seq_use:Nn \l_tmpa_seq { } } \tl_set:Nf \l_tmpb_tl { \seq_use:Nn \l_tmpb_seq { } } \tl_if_eq:NNTF \l_tmpa_tl \l_tmpb_tl { TRUE } { FALSE } % return FALSE } -- { \str_set:Nn \l_tmpa_str { key } \exp_args:NNx \seq_set_from_clist:Nn \l_tmpa_seq { \l_tmpa_str = smth } \exp_args:NNx \prop_set_from_keyval:Nn \l_tmpa_prop { \l_tmpa_str = smth } \prop_map_inline:Nn \l_tmpa_prop { \seq_put_right:Nn \l_tmpb_seq { #1 = #2 } } \tl_set:Nf \l_tmpa_tl { \seq_use:Nn \l_tmpa_seq { } } \tl_set:Nf \l_tmpb_tl { \seq_use:Nn \l_tmpb_seq { } } \tl_if_eq:NNTF \l_tmpa_tl \l_tmpb_tl { TRUE } { FALSE } % return TRUE } \ExplSyntaxOff \end{document} 
2
  • Drop xwatermark. If you had a new LaTeX I would suggest the new shipout/background hook as replacement, with older system I used eso-pic. Commented Feb 24, 2021 at 7:16
  • 2
    That's an impossible task, you'd have to change the internals of l3prop to achieve this, there is no viable way of storing inside an l3prop without the stringification, and once a token list has been turned into a string there is no way of knowing the original category codes. What you could do is store inside an l3prop both the name and the value, so something like \prop_put:Nnn \l_tmpa_prop { <name> } { { <name> } { <value> } }. But honestly, I agree with the others who already told you: Drop xwatermark. Commented Feb 24, 2021 at 9:04

2 Answers 2

3

The following defines a set of functions for an alternative prop structure (which is based on l3prop's type) that stores the keys twice, once as the normal prop key, and once in an undetokenized form inside the prop's value. The functions are all named \erwannprop_..., the variables ..._eprop. These functions are just thin wrappers around l3prop and l3seq functions.

\documentclass[]{article} \ExplSyntaxOn \cs_new_protected:Npn \erwannprop_new:N #1 { \prop_new:N #1 } \msg_new:nnn { erwannprop } { no-value } { Missing~ '='~ in~ '#1'~ (in~ ..._keyval:Nn) } \cs_new_protected:Npn \erwannprop_set_from_keyval:Nn #1#2 { \prop_clear:N #1 \keyval_parse:nnn { \msg_error:nnn { erwannprop } { no-value } } { \erwannprop_put:Nnn #1 } {#2} } \cs_new_protected:Npn \erwannprop_put:Nnn #1#2#3 { \prop_put:Nnn #1 {#2} { {#2} {#3} } } \cs_new_protected:Npn \erwannprop_to_seq:NN #1#2 { \seq_clear:N #2 \prop_map_tokens:Nn #1 { \__erwannprop_to_seq:Nnn #2 } } \cs_new_protected:Npn \__erwannprop_to_seq:Nnn #1#2#3 { \__erwannprop_to_seq_aux:Nnn #1 #3 } \cs_new_protected:Npn \__erwannprop_to_seq_aux:Nnn #1#2#3 { \seq_put_right:Nn #1 { #2 = #3 } } \ExplSyntaxOff \begin{document} \ExplSyntaxOn \erwannprop_new:N \l_tmpa_eprop \seq_set_from_clist:Nn \l_tmpa_seq { key = smth } \erwannprop_set_from_keyval:Nn \l_tmpa_eprop { key = smth } \erwannprop_to_seq:NN \l_tmpa_eprop \l_tmpb_seq \tl_set:Nf \l_tmpa_tl { \seq_use:Nn \l_tmpa_seq { } } \tl_set:Nf \l_tmpb_tl { \seq_use:Nn \l_tmpb_seq { } } \tl_if_eq:NNTF \l_tmpa_tl \l_tmpb_tl { TRUE } { FALSE } \ExplSyntaxOff \end{document} 
1

It's possible to retokenize with \tl_set_rescan:Nnn of expl3.

However, this is not recommended at all. As said in the documentation (interface3.pdf):

Whilst this functionality is supported, it is often preferable to find alternative approaches to achieving outcomes rather than rescanning tokens.

I write this answer only for general information.

\documentclass{article} \begin{document} \ExplSyntaxOn { \seq_set_from_clist:Nn \l_tmpa_seq { key = smth } \prop_set_from_keyval:Nn \l_tmpa_prop { key = smth } \prop_map_inline:Nn \l_tmpa_prop { \seq_put_right:Nn \l_tmpb_seq { #1 = #2 } } \tl_set:Nf \l_tmpa_tl { \seq_use:Nn \l_tmpa_seq { } } \tl_set:Nf \l_tmpb_tl { \seq_use:Nn \l_tmpb_seq { } } \tl_set_rescan:Nno \l_tmpb_tl { } \l_tmpb_tl \tl_if_eq:NNTF \l_tmpa_tl \l_tmpb_tl { TRUE } { FALSE } % return TRUE because of the previous line } -- { \str_set:Nn \l_tmpa_str { key } \exp_args:NNx \seq_set_from_clist:Nn \l_tmpa_seq { \l_tmpa_str = smth } \exp_args:NNx \prop_set_from_keyval:Nn \l_tmpa_prop { \l_tmpa_str = smth } \prop_map_inline:Nn \l_tmpa_prop { \seq_put_right:Nn \l_tmpb_seq { #1 = #2 } } \tl_set:Nf \l_tmpa_tl { \seq_use:Nn \l_tmpa_seq { } } \tl_set:Nf \l_tmpb_tl { \seq_use:Nn \l_tmpb_seq { } } \tl_if_eq:NNTF \l_tmpa_tl \l_tmpb_tl { TRUE } { FALSE } % return TRUE } \ExplSyntaxOff \end{document} 
5
  • It is worth mentioning that this doesn't restore the old catcodes, rather it assigns new ones based on the currently active category regime (which might be the same thing most of the times, but isn't necessarily). Commented Feb 24, 2021 at 11:03
  • @Skillmon : You are right. Commented Feb 24, 2021 at 13:36
  • @skillmon, what are the catcodes, in this case? Commented Mar 20, 2021 at 4:19
  • 1
    @Erwann in TeX every token that isn't a control sequence is assigned a category code (which basically defines how it can be used). E.g., \ is assigned category code 0 usually, meaning it's the escape char, { is 1 (group-begin), } is 2 (group-end), $ is 3 (math), & is 4 (alignment), and so on. Basically each special functionality of a character is defined by its category code. What happens if you stringify a sequence (\detokenize it) is that everything gets category 12 (other), and there is no way to know afterwards what exactly was which, you can only guess (with \scantokens). Commented Mar 20, 2021 at 9:33
  • @Erwann and that's what this answer does. It guesses with \scantokens (well, with expl3's abstraction layer \tl_set_rescan:Nnn) what should be what, but I can easily build lists in which this guessing fails. For the most part it'll work out, though. Still the best tip you became on this thread is: Drop xwatermark! Commented Mar 20, 2021 at 9:37

You must log in to answer this question.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.