6

expl3 has defeated me again so I'm relying on the kindness of strangers again.

I think the underlying problem is more about splitting input rather than the file reading.

Context: I want to load the parameters to the geometry package's \geometry{} command from a file using expl3. I have tried at least 4 ways of reading the file. But however I load the file, the entire contents is being passed to \geometry{} as a single token / string / block / thing because the geometry package complains the entire thing top=2cm,bottom=2cm is not a valid key Package keyval Error: top=2cm,bottom=2cm undefined. Yet if I put \geometry{top=2cm,bottom=2cm} in the document then that is processed correctly.

I have seen this answer which splits key-value pairs but I need all the key-value pairs at once in the input stream not one at a time.

Minimal working examples

This works:

\documentclass{article}

\ExplSyntaxOn % Also works without expl3 syntax \usepackage{geometry} \geometry{top=2cm,bottom=2cm} \ExplSyntaxOff

\begin{document} Document content. \end{document}

Minimal not working examples

(Not using the recommended expl3 naming conventions for brevity)

Token list

Token list fails resulting in Package keyval Error: top=2cm,bottom=2cm undefined.

\documentclass{article}

\ExplSyntaxOn \tl_clear_new:N \geotoks_tl \tl_set:Nn \geotoks_tl {top=2cm,bottom=2cm} % Also fails with {top=2cm, bottom=2cm}

\usepackage{geometry} \geometry{\geotoks_tl} % fail % \geometry{\tl_use:N \geotoks_tl} % fail \ExplSyntaxOff

\begin{document} Document content. \end{document}

String

String fails resulting in Package keyval Error: top=2cm,bottom=2cm undefined.

\documentclass{article}

\ExplSyntaxOn

\str_clear_new:N \geotoks_str \str_set:Nn \geotoks_str {top=2cm,bottom=2cm} % Also fails with {top=2cm, bottom=2cm}

\usepackage{geometry} \geometry{\geotoks_str} % fail % \geometry{\str_use:N \geotoks_str} % fail \ExplSyntaxOff

\begin{document} Document content. \end{document}

Clist

Clist fails resulting in Package keyval Error: top=2cm,bottom=2cm undefined.

\documentclass{article}

\usepackage{geometry} \ExplSyntaxOn \clist_clear_new:N \geotoks_clist \clist_set:Nn \geotoks_clist {top=2cm, bottom=2cm}

\usepackage{geometry} \geometry{\clist_use:Nn \geotoks_clist {,}} % Also fails with % \geometry{\clist_use:Nn \geotoks_clist {~}} % though error message is Package keyval Error: top=2cm bottom=2cm undefined. \ExplSyntaxOff

\begin{document} Document content. \end{document}

Seq

\documentclass{article}

\usepackage{geometry} \ExplSyntaxOn \clist_clear_new:N \geotoks_clist \clist_set:Nn \geotoks_clist {top=2cm, bottom=2cm} \seq_clear_new:N \geotoks_seq \seq_set_from_clist:NN \geotoks_seq \geotoks_clist

\usepackage{geometry} \geometry{\seq_use:Nn \geotoks_seq {,}} % Also fails with {~} as the separator \ExplSyntaxOff

\begin{document} Document content. \end{document}

I have also met the same fail with splitting into a sequence as described here.

File loading

I have met the same Package keyval Error: top=2cm,bottom=2cm undefined. fails by using the file loading methods in these answers:

In desperation I also tried \geometry{\file_input:n {geometryParams.txt}} which at least gave me a different error Missing \endcsname inserted. Same error with \geometry{\input{params.txt}} inside or outside ExplSyntax.

Doc Octal
  • 369
  • 1
  • 10
  • 3
    As \geometry doesn't expands its argument nothing that you do inside will help, you need something like \ExpandArgs{o}\geometry{\geotoks_str} – Ulrike Fischer Sep 05 '23 at 12:04
  • @Ulrike Fischer That works. Please make it an answer so I can accept. I don't think I've seen \ExpandArgs before. – Doc Octal Sep 05 '23 at 12:32

2 Answers2

8

\geometry doesn't expand it argument, it wants a key list as argument, not a key list hidden in some command. So nothing what you do inside the argument will work. Assuming that your key list is in a simple command \mykeylist you can expand it from the outside. Either with the old

\expandafter\geometry\expandafter{\mykeylist}

or with the new LaTeX command \ExpandArgs:

\ExpandArgs{o}\geometry{\mykeylist}

Unrelated but you are not following the expl3 naming scheme. Variables should start with a prefix showing if they are a global or local variable, followed by the module name and then something showing their use.

So

\geotoks_str  -> \l_geotoks_keys_str
Skillmon
  • 60,462
Ulrike Fischer
  • 327,261
  • 1
    Thanks :) I promise I am following the naming scheme in my real code. I just couldn't be bothered with it for the question. – Doc Octal Sep 05 '23 at 12:52
6

From the question's title I guess you want to load the options from a file.

\begin{filecontents*}{\jobname.geo}
top=2cm,
bottom=2cm
left=2cm,a4paper
\end{filecontents*}

\documentclass{article} \usepackage[showframe]{geometry}

\ExplSyntaxOn \NewDocumentCommand{\geometryfromfile}{m} { \dococtal_geometry_fromfile:n { #1 } }

\ior_new:N \g_dococtal_geometry_file_ior \clist_new:N \g_dococtal_geometry_options_clist

\cs_new_protected:Nn \dococtal_geometry_fromfile:n { \ior_open:Nn \g_dococtal_geometry_file_ior { #1 } \clist_gclear:N \g_dococtal_geometry_options_clist \ior_map_inline:Nn \g_dococtal_geometry_file_ior { \clist_gput_right:Nn \g_dococtal_geometry_options_clist { ##1 } } \exp_args:NV \geometry \g_dococtal_geometry_options_clist }

\ExplSyntaxOff

\geometryfromfile{\jobname.geo}

\begin{document}

Test

\end{document}

You see that the file is typed quite lazily, but the command above works without glitches, because we read the file line by line and \clist_gput_right:Nn does the rest. Indeed, the contents of the clist is

The comma list \g_dococtal_geometry_options_clist contains the items (without
outer braces):
>  {top=2cm}
>  {bottom=2cm}
>  {left=2cm}
>  {a4paper}.
egreg
  • 1,121,712
  • Thanks @egreg this is neat. Is it more flexible / efficient / robust to input than the \file_get:nnNTF {filename} {} \token_list {\ExpandArgs{o}\geometry\token_list} {} I'm currently using? I had tried and failed with something similar to your answer. I was missing the \exp_args:NV and I had braces around the clist \geometry{\options_clist} which may be why it didn't work. – Doc Octal Sep 05 '23 at 13:25
  • 1
    @DocOctal With \file_get:nnnTF you can't be lazy in typing in your settings file. – egreg Sep 05 '23 at 13:58