7

Related: keyreader: How to save choice key value to a macro.

I’m using l3keys2e, but I suspect this might not be the ideal way to go about what I’m trying to do.

I’m building a private package with various tweaks to font setups:

\bool_if:nTF { \xetex_if_engine_p: || \luatex_if_engine_p: }
  { \clist_const:Nn \c_jcsfonts_clist { lmodern, fontin, constantia } }
  { \clist_const:Nn \c_jcsfonts_clist { cmodern, kpfonts } }

I’m trying to set things up so \usepackage[font = choice]{jcsfonts} will enable the correct batch of customizations, with the first entry in the list being the default. This doesn’t seem to work:

\cs_generate_variant:Nn \quark_new:N {c}
\clist_map_inline:Nn \c_jcsfonts_clist { \quark_new:c { q_jcsfonts_ #1 } }

\keys_define:nn { jcsfonts }
  {
    font .choice_code:n =
      { \cs_set_eq:Nc \q_jcsfonts { q_jcsfonts \tl_use:N \l_keys_choice_tl } } ,
    font .generate_choices:n = { \clist_use:Nnnn \c_jcsfonts_clist {,} {,} {,} } ,
  }

\prg_new_conditional:Npnn \jcsfonts_if:n #1 { p, T, F, TF }
  {
    \cs_if_eq:NcTF \q_jcsfonts { q_jcsfonts_ #1 }
      { \prg_return_true: } { \prg_return_false: }
  }

\ProcessKeysOptions { jcsfonts }

What is the preferred way to accomplish this?

7

I may be misunderstanding what you are trying to achieve, but here is a simple way to associate code to various possible values of a key. The \keys_define:nn construction defines the key font of module jcsfonts to be a "choice" key, which means that the value given for font decides what code is performed. There are two ways of defining the code for each possible choice: either by introducing keys font/choice-a, font/choice-b, etc, as I do here, or (with a version of expl3 perhaps newer than August 2013 —that's the future) using .choices:nn, which defines several choices in one go, with the same code.

\documentclass{article}
\usepackage{expl3, xparse}
\ExplSyntaxOn
\keys_define:nn { jcsfonts }
  {
    font .choice: ,
    font / lmodern .code:n = { \RequirePackage{lmodern} } ,
    font / kpfonts .code:n = { \RequirePackage{kpfonts} } ,
  }
\NewDocumentCommand { \JcsSetup } { m }
  {
    \keys_set:nn { jcsfonts }
      { font = {#1} }
  }
\ExplSyntaxOff
\JcsSetup{kpfonts}
\begin{document}
Hello world!
\end{document}

If you want the set of choices to depend on the engine, simply do something like

\keys_define:nn { jcsfonts } { font .choice: }
\cs_new_protected:Npn \jcs_fonts_new:nnnn #1#2#3#4
  {
    \pdftex_if_engine:T { \keys_define:nn { jcsfonts } { font / #1 = {#2} } }
    \xetex_if_engine:T  { \keys_define:nn { jcsfonts } { font / #1 = {#3} } }
    \luatex_if_engine:T { \keys_define:nn { jcsfonts } { font / #1 = {#4} } }
  }
\msg_new:nnn { jcsfonts } { font-bad-engine }
  { Cannot~use~font~'#1'~in~engine~'#2'. }
\jcs_fonts_new:nnnn { kpfonts }
  { \RequirePackage{kpfonts} } % code for pdfTeX
  { \RequirePackage{kpfonts} } % code for XeTeX
  {
    \msg_error:nnnn { jcsfonts } { font-bad-engine }
      { kpfonts } { LuaTeX }
  } % code for LuaTeX... that font works, but I needed an example

If you more generally want to associate some code to a string among some choice of strings, I would use a property list:

\prop_new:N \g__jcs_fonts_code_prop
\prop_gput:Nnn \g__jcs_fonts_code_prop
  { kpfonts }
  { RequirePackage{kpfonts} }
% ...
\tl_new:N \l__jcs_fonts_code_tl
\keys_define:nn { jcsfonts }
  {
    font .code:n =
      {
        \prop_get:NnNTF
          \g__jcs_fonts_code_prop {#1} \l__jcs_fonts_code_tl
          { \tl_use:N \l_jcs_fonts_code_tl }
          { \msg_error:nnn { jcsfonts } { cannot-use-font } {#1} }
      }
  }
\msg_new:nnn { jcsfonts } { cannot-use-font }
  { Cannot~use~font~'#1'. }

Once more, you can make the list of fonts depend on the engine by filling the property list differently according to the engine used.

  • It appears that \keys_define:nn { font / unknown .code:n = { <Recovery~code~receiving~unknown~choice~as~#1.> } } works to recover from unknown choices for a .choice: key. – Bruno Le Floch Jul 9 '13 at 3:31
4

I'm not sure about your usage of quarks; if you want to generate choice keys based on the value of the constant clist defined at startup, you can stretch a bit the available tools and define a .generate_choice:V property:

\documentclass{article}
\usepackage{xparse}
\ExplSyntaxOn

\tl_new:N \g_jcs_fonts_font_tl

\bool_if:nTF { \xetex_if_engine_p: || \luatex_if_engine_p: }
 { \clist_const:Nn \c_jcsfonts_clist { lmodern, fontin, constantia } }
 { \clist_const:Nn \c_jcsfonts_clist { lmodern, kpfonts } }

%%% The property .generate_choice:V doesn't exist, so we create it
\cs_generate_variant:Nn \__keys_choices_generate:n { V }
\cs_new_protected:cpn { \c__keys_props_root_tl .generate_choices:V } #1
  { \__keys_choices_generate:V {#1} }

\keys_define:nn { jcsfonts }
 {
  font .choice_code:n = \tl_gset:NV \g_jcsfonts_font_tl \l_keys_choice_tl,
  font .generate_choices:V = \c_jcsfonts_clist,
 }

\NewDocumentCommand{\try}{m}
 {
  \keys_set:nn { jcsfonts } { #1 }
  \tl_show:N \g_jcsfonts_font_tl
 }
\NewDocumentCommand{\compare}{m}
 {
  \str_if_eq:nVTF { #1 } \g_jcsfonts_font_tl
   { \typeout{The ~ chosen ~ font ~ is ~ #1} }
   { \typeout{The ~ chosen ~ font ~ is ~ not ~ #1} }
 }

\ExplSyntaxOff

\try{font=lmodern} % works both in latex and xelatex
\compare{lmodern}
\compare{xyz}

\try{font=fontin}  % fails in latex

\stop

I believe that .generate_choice:V would be a useful addition to the tools. You can make a feature request through the mailing list.


An alternative version using the .choices:nn property (but with a variant as above).

\documentclass{article}
\usepackage{xparse}
\ExplSyntaxOn

\tl_new:N \g_jcs_fonts_font_tl

\bool_if:nTF { \xetex_if_engine_p: || \luatex_if_engine_p: }
 { \clist_const:Nn \c_jcsfonts_clist { lmodern, fontin, constantia } }
 { \clist_const:Nn \c_jcsfonts_clist { lmodern, kpfonts } }

%%% The property .choices:Vn doesn't exist, so we create it
\cs_generate_variant:Nn \__keys_choices_make:nn { V }
\cs_new_protected:cpn { \c__keys_props_root_tl .choices:Vn } #1
  { \__keys_choices_make:Vn #1 }

\keys_define:nn { jcsfonts }
 {
  font .choices:Vn = \c_jcsfonts_clist
   {
    \tl_gset:NV \g_jcsfonts_font_tl \l_keys_choice_tl
   }
 }
\NewDocumentCommand{\try}{m}
 {
  \keys_set:nn { jcsfonts } { #1 }
  \tl_show:N \g_jcsfonts_font_tl
 }

\NewDocumentCommand{\compare}{m}
 {
  \str_if_eq:nVTF { #1 } \g_jcsfonts_font_tl
   { \typeout{The ~ chosen ~ font ~ is ~ #1} }
   { \typeout{The ~ chosen ~ font ~ is ~ not ~ #1} }
 }

\ExplSyntaxOff

\try{font=lmodern} % works both in latex and xelatex
\compare{lmodern}
\compare{xyz}

\try{font=fontin}  % fails in latex

\stop
  • How do I get the conditional out of this? Would something like \str_if_eq:VnTF \g_jcsfonts_font_tl {lmodern} {…} {…} work? – J. C. Salomon Jun 20 '13 at 19:51
  • @J.C.Salomon Yes. I'll add an example. I don't get any error except when I try with pdflatex because fontin is not a valid choice. – egreg Jun 20 '13 at 19:59

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.