3

As pointed out by the following MCE, an expl3' token list variable storing the result of a \seq_map_inline: used in \title's argument:

  • works like a charm with the article class,
  • doesn't work with the beamer class:

          ! Undefined control sequence.
          <argument> \LaTeX3 error: 
                                    A sequence was misused.
    

Do you know why and how to make it work with beamer's class as well?

% \documentclass{article}
\documentclass{beamer}
\usepackage{xparse}
\ExplSyntaxOn
\seq_gset_from_clist:Nn \g_tmpa_seq { foo, bar }
\tl_set:Nn \g_tmpa_tl
{
  \seq_map_inline:Nn \g_tmpa_seq
  {
    #1,\c_space_tl
  }
}
\title{\g_tmpa_tl}
\ExplSyntaxOff
\begin{document}
\maketitle
\end{document}

Edit: aim that led me to the current question (and its simplified MCE)

For a course I'm giving, I prepare some presentations with the beamer class (BTW, what follows could apply to any class). The content of the whole course is splitted in several subjects (one file per subject) that are \input. The subjects (titles)/corresponding files that may be treated are (say):

  • "Foo 1"/foo1.tex
  • "Foo 2"/foo2.tex
  • "Foo 3"/foo3.tex
  • "Foo 4"/foo4.tex
  • "Foo 5"/foo5.tex

The presentations' contents (chosen subjects) varies depending on the audience and/or on the course progress.

The point is, I want for a given presentation the chosen subjects' titles to appear in the presentation's \title.

Suppose the current presentation is about "Foo 2" and "Foo 4". I could do the following:

\documentclass{beamer}
...
\title{
  % Foo 1,
  Foo 2,
  % Foo 3,
  Foo 4
  % Foo 5,
}
\begin{document}
\maketitle
% \input{foo1}
\input{foo2}
% \input{foo3}
\input{foo4}
% \input{foo5}
\end{document}

but this way of doing is painful and error prone: whenever the list of \inputed files changes, I have to (not forget to) change the \title's content.

Note having the subjects' titles in the corresponding files is useless: they would be too late read to be in the presentation's \title (okay, another way of doing would be to write them in the .aux file and read them at the next compilation when the \title has to be filled, but that's another story).

So, what I am aiming, is to have :

  • a fixed property list (say \g_subjects_prop) containing the whole list of the subjects with, for each of them:
    • as key: the name of the corresponding file,
    • as value: its title. This list could be populated in a config file thanks to a document command (say SubjectFileTitle{⟨filename⟩}{⟨title⟩}),
  • a sequence (say \g_chosen_subjects_seq) containing the keys of the chosen subjects. This sequence could be populated in the preamble thanks to a document command (say ChosenSubjects{⟨comma separated list of keys (files)⟩}),
  • a token list (say \g_title_tl, instead of \l_tmpa_tl(?)) containing the values corresponding to the chosen keys above to be passed as \title's argument,
  • a document command (say \InputChosenSubjects) which, where used, inserts the sequence of the \inputed chosen files.

Thanks to egreg's answer, I managed with the following code:

\begin{filecontents*}{foo2}
\begin{frame}
  \frametitle{Foo 2 subject: quite interesting!}
  ...
\end{frame}
\end{filecontents*}

\documentclass{beamer}
\usepackage{xparse}

\ExplSyntaxOn

\prop_new:N \g_subjects_prop
\seq_new:N \g_chosen_subjects_seq
\tl_new:N \g_title_tl

\NewDocumentCommand{\SubjectFileTitle}{mm}
{
  \prop_gput:Nnn \g_subjects_prop {#1} {#2}
}

\cs_new_protected:Npn \__chosen_subjects:n #1
{
  \seq_gset_from_clist:Nn \g_chosen_subjects_seq {
    #1
  }
  \seq_map_inline:Nn \g_chosen_subjects_seq
  {
    \prop_get:NnN \g_subjects_prop {##1} \l_tmpa_tl
    \tl_if_empty:NF \l_tmpa_tl {
      \seq_put_right:Nn \g_tmpa_seq {\prop_item:Nn \g_subjects_prop {##1}}
    }
  }
  \tl_gset:Nx \g_title_tl
  {
    \seq_use:Nn \g_tmpa_seq {,~}
  }
  \title{\g_title_tl}
}

\NewDocumentCommand{\ChosenSubjects}{m}
{
  \__chosen_subjects:n {#1}
}

\cs_new_protected:Npn \__input_chosen_subjects_files:
{
  \seq_map_inline:Nn \g_chosen_subjects_seq
  {
    \par
    \file_if_exist_input:nF {##1}{%
      \begin{frame}
        \frametitle{\alert{File~\texttt{##1.tex}~not~found!}}
      \end{frame}
    }
  }
}

\NewDocumentCommand{\InputChosenSubjects}{}{
  \__input_chosen_subjects_files:
}

\ExplSyntaxOff

\SubjectFileTitle{foo1}{Foo 1}
\SubjectFileTitle{foo2}{Foo 2}
\SubjectFileTitle{foo3}{Foo 3}
\SubjectFileTitle{foo4}{Foo 4}
\SubjectFileTitle{foo5}{Foo 5}

\ChosenSubjects{
  % foo1,
  foo2,
  % foo3,
  foo4,
  % foo5,
}

\begin{document}
\maketitle{}
\InputChosenSubjects
\end{document}
  • In beamer the title argument is being stored in \inserttitle, but that value is also being fed to \hypersetup{pdftitle={...} could that be the culprit? Emulating storing in a macro and using it, does not seem to cause problems, but I haven't tested hypersetup – daleif May 7 '19 at 8:04
  • 1
    It IS a hyperref error, in a article variant add hyperref and \newcommand\test[1]{\def\inserttest{#1}}\test{\g_tmpa_tl} and try \hypersetup{pdftitle=\inserttest}, this gies the same error – daleif May 7 '19 at 8:17
3

You are asking TeX do perform \seq_map_inline:Nn while processing \maketitle. More likely, you want \maketitle to print the list of words separated by “comma and space”. In order to do this, use \tl_gset:Nx and \seq_use:Nn.

\documentclass{beamer}
\usepackage{xparse}

\ExplSyntaxOn
\seq_gset_from_clist:Nn \g_tmpa_seq { foo, bar }
\tl_gset:Nx \g_tmpa_tl
{
  \seq_use:Nn \g_tmpa_seq {,~}
}
\exp_args:NV \title \g_tmpa_tl
\ExplSyntaxOff

\begin{document}
\maketitle
\end{document}

I'd note that \g_tmpa_tl is not the proper variable to use, but I believe it is just for the sake of the example.


Here's an edited version of your code. I renamed functions and variables giving a common prefix. Most importantly, I fixed a glitch: if a property doesn't exist in a prop variable, then

\prop_get:NnN <prop variable> {<property>} <tl variable>

sets the token list variable to \q_no_value, rather than to “empty”. So the test to use is \quark_if_no_value:NF.

Also, instead of calling \title{\g_pres_title_tl}, I'd prefer

\exp_args:NV \title \g_pres_title_tl

so the contents of the variable is passed to \title, rather than the container.

\begin{filecontents*}{foo2}
\begin{frame}
  \frametitle{Foo 2 subject: quite interesting!}
  ...
\end{frame}
\end{filecontents*}

\documentclass{beamer}
\usepackage{xparse}

\ExplSyntaxOn

%% variables
\prop_new:N \g_pres_subjects_prop
\seq_new:N \g_pres_subjects_chosen_seq
\tl_new:N \g_pres_title_tl

%% User level commands
\NewDocumentCommand{\SubjectFileTitle}{mm}
 {
  \prop_gput:Nnn \g_pres_subjects_prop {#1} {#2}
 }

\NewDocumentCommand{\ChosenSubjects}{m}
 {
  \pres_subjects_choose:n {#1}
 }

\NewDocumentCommand{\InputChosenSubjects}{}
 {
  \pres_input_chosen_subjects_files:
 }

%% functions
\cs_new_protected:Npn \pres_subjects_choose:n #1
 {
  \seq_gset_from_clist:Nn \g_pres_subjects_chosen_seq { #1 }
  \seq_map_inline:Nn \g_pres_subjects_chosen_seq
   {
    \prop_get:NnN \g_pres_subjects_prop {##1} \l_tmpa_tl
    \quark_if_no_value:NF \l_tmpa_tl
     {
      \seq_put_right:NV \l_tmpa_seq \l_tmpa_tl
     }
   }
  \tl_gset:Nx \g_pres_title_tl
   {
    \seq_use:Nn \l_tmpa_seq {,~}
   }
  \exp_args:NV \title \g_pres_title_tl
 }

\cs_new_protected:Npn \pres_input_chosen_subjects_files:
 {
  \seq_map_inline:Nn \g_pres_subjects_chosen_seq
   {
    \par
    \file_if_exist_input:nF {##1}
     {
      \begin{frame}
      \frametitle{\alert{File~\texttt{##1.tex}~not~found!}}
      \end{frame}
     }
   }
 }
\ExplSyntaxOff

\SubjectFileTitle{foo1}{Foo 1}
\SubjectFileTitle{foo2}{Foo 2}
\SubjectFileTitle{foo3}{Foo 3}
\SubjectFileTitle{foo4}{Foo 4}
\SubjectFileTitle{foo5}{Foo 5}

\ChosenSubjects{
  % foo1,
  foo2,
  % foo3,
  foo4,
  % foo5,
  blurb % this doesn't exist among the defined files
}

\begin{document}
\maketitle{}
\InputChosenSubjects
\end{document}
| improve this answer | |
  • Indeed, I wanted "TeX to perform \seq_map_inline:Nn while processing \maketitle": I'm a bit lost about what is possible and what is not in expl3 :) About \g_tmpa_tl: what would you advise instead? – Denis Bitouzé May 7 '19 at 11:23
  • @DenisBitouzé I can't understand from the example code what your aim is. – egreg May 7 '19 at 11:25
  • I detailed my aim in the question. – Denis Bitouzé May 8 '19 at 20:22
  • 1
    @DenisBitouzé I added an edited version. – egreg May 8 '19 at 20:47
  • 1
    @DenisBitouzé Try with blurb as in my code. – egreg May 8 '19 at 20:59
2

Too long for comment, this gives the same error without beamer

\documentclass{article}
\usepackage{hyperref}
\usepackage{xparse}
\ExplSyntaxOn
\seq_gset_from_clist:Nn \g_tmpa_seq { foo, bar }
\tl_set:Nn \g_tmpa_tl
{
  \seq_map_inline:Nn \g_tmpa_seq
  {
    #1,\c_space_tl
  }
}

\newcommand\test[1]{
  \def\inserttest{#1}
}
\test{\g_tmpa_tl}
\ExplSyntaxOff
\begin{document}
%\maketitle

\hypersetup{pdftitle=\inserttest}

test

\end{document}
| improve this answer | |
  • Good catch! Now, isn't it "normal" since, following egreg's advise, that works with \seq_use:Nn instead of \seq_map_inline:Nn. – Denis Bitouzé May 7 '19 at 11:28
  • @DenisBitouzé isn't the issue that egregs solution is fully expandable where as yours is not (I can never understand the various ltx3 macros). Though, not at all sure why we get that particular error? We know that hyperref does a lot of parsing of the PDF data it uses. – daleif May 7 '19 at 11:33
2

beamer issues (in beamerbasetitle.sty)

\hypersetup{pdftitle={\inserttitle\ifx\insertsubtitle\@empty\else\ - \insertsubtitle\fi}}

This means that \inserttitle should be something that hyperref can handle in a bookmark -- when expl3 commands are involved: Stick to commands with a star in the documentation.

You can avoid that beamer uses the title as pdftitle by using

\documentclass[usepdftitle=false,hyperref={pdftitle=somethingsafe}]{beamer}

This will also affect author pdf-fields, so they would need to be set manually too.

| improve this answer | |

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.