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?

2 Answers 2

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.

1
  • 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. Commented Jul 9, 2013 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
3
  • How do I get the conditional out of this? Would something like \str_if_eq:VnTF \g_jcsfonts_font_tl {lmodern} {…} {…} work? Commented Jun 20, 2013 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
    Commented Jun 20, 2013 at 19:59

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .