3

I'm using LaTeX to typeset a drama script. Each character has a self-made command \<charactername>x that takes an argument and outputs that text as speech of that character. e.g. \tomx{Hi, I'm Tom} means that Tom says "Hi, I'm Tom".

At the beginning of each scene I need a list of characters occuring in this scene. Right now, I'm doing that manually, but it's an error-prone task to keep track which characters say something in that scene, especially if they get crowded. Often, someone is forgotten and I need to triple-check if I included everyone in the character list.

I wonder if it would be possible to tweak my custom speech commands a bit, so that each scene has a variable that keeps a list of characters occuring in that scene, and everytime one of that \<charactername>x commands are called, they append the name of the character to that list if it isn't in it already. How could one approach this issue? A MWE might look like this:

\documentclass{scrartcl}

\newcommand{\tomx}[1]{
% Alter this command (maybe?) to append "Tom" to the list of
% characters of the scene this command is used in if "Tom" is not yet present.
\textsc{Tom:} #1
}

\begin{document}
\section{In the supermarket}
% Call command here that lists characters in this scene, in this case only tom.
\tomx{Hi, I'm Tom!}
\end{document}

It doesn't need to be fast or fancy. Any help is greatly appreciated, Thanks in advance!

  • Well, you need some kind of a label mechanism, i.e. store where the name is referred and read the information to get information what will come up later on. – user31729 Mar 5 '17 at 18:03
  • How could I implement that? I haven't done much complex LaTeX coding yet. – LukeLR Mar 5 '17 at 18:04
5

The difficulty here is to know which persons will appear later on but the list of characters have to done before.

I am using the \label approach here and checking if the label is given, otherwise the person is not listed.

A new person is defined with \NewPerson{foo}, which automatically defines \foox and adds the code to check for label existence etc.

Now, the Command \DisplayPersons loops through all defined persons and stores the local ones, loops again through this smaller list of persons and applies \displayindividualperson which can be changed to personal preferences.

The macros \PostPersonlist and \personlistheader can be configured to provide other styles.

Note: As with any \label based approach at least two runs of compilations are necessary

\documentclass{scrartcl}

\usepackage{refcount}
\usepackage{xparse}

\ExplSyntaxOn
\seq_new:N \g_luke_listofpersons_seq
\seq_new:N \l_luke_listofpersons_seq 


\NewDocumentCommand{\addperson}{m}{%
  \seq_gput_right:Nn \g_luke_listofpersons_seq {#1}
  \seq_gremove_duplicates:N \g_luke_listofpersons_seq
}


\NewDocumentCommand{\addpersonlocal}{m}{%
  \seq_gput_right:Nn \l_luke_listofpersons_seq {#1}
}

\cs_new:Npn \IfPersonCalledAlreadyF #1#2 {%
  \seq_if_in:NnF \l_luke_listofpersons_seq {#1} {#2}
}

\NewDocumentCommand{\DisplayPersons}{}{%
  \seq_clear:N \l_luke_listofpersons_seq
  \group_begin:
  \seq_clear:N \l_tmpa_seq
  \seq_map_inline:Nn \g_luke_listofpersons_seq {%
    \IfRefUndefinedExpandable{##1\thesection}{}{
      \seq_put_right:Nn \l_tmpa_seq {\displayindividualperson{##1}}
     }
   }
   \seq_if_empty:NF \l_tmpa_seq {
     \PrePersonList
     \seq_use:Nn \l_tmpa_seq {,~} 
     \PostPersonList
   }
   \group_end:

}
\ExplSyntaxOff

\NewDocumentCommand{\PostPersonList}{}{%
  \bigskip%

}

\NewDocumentCommand{\displayindividualperson}{m}{%
  \textbf{#1}%
}

\NewDocumentCommand{\PrePersonList}{}{%
  {\large \bfseries Persons in Section \thesection}

}

\makeatletter
\NewDocumentCommand{\NewPerson}{m}{%
  % Add this person to the global list
  \addperson{#1}%
  % Now define the personal \...x command 
  \expandafter\NewDocumentCommand\csname #1x\endcsname{+m}{%
    %Check if the person has been called in the local section already
    \IfPersonCalledAlreadyF{#1}{%
      \addpersonlocal{#1}
      % Add the personal to the local list, i.e. per section
      % Check whether the label has been defined already
        \protected@edef\@currentlabel{\thesection.#1}\label{#1\thesection}
    }%
    \textsc{#1:} ##1%
  }% End of the \...x command
}% End of \NewPerson
\makeatother


\NewPerson{tom}
\NewPerson{frodo}
\NewPerson{Gandalf}



\begin{document}
\section{In the supermarket}
\DisplayPersons
\tomx{Hi, I'm Tom!}

\frodox{Hi, I'm Frodo!}

\section{At TeX.SE}

\DisplayPersons

\frodox{Hi, I'm Frodo!}

\frodox{I am going to Mordor}

\Gandalfx{Cast the ring into the fire!}

\tomx{Waiting for Godot}
\end{document}

enter image description here

Update with hightlighting

\documentclass{scrartcl}

\usepackage{xcolor}
\usepackage{refcount}
\usepackage{xparse}

\ExplSyntaxOn
\seq_new:N \g_luke_listofpersons_seq
\seq_new:N \l_luke_listofpersons_seq 


\NewDocumentCommand{\addperson}{m}{%
  \seq_gput_right:Nn \g_luke_listofpersons_seq {#1}
  \seq_gremove_duplicates:N \g_luke_listofpersons_seq
}


\NewDocumentCommand{\addpersonlocal}{m}{%
  \seq_gput_right:Nn \l_luke_listofpersons_seq {#1}
}

\cs_new:Npn \IfPersonCalledAlreadyF #1#2 {%
  \seq_if_in:NnF \l_luke_listofpersons_seq {#1} {#2}
}

\NewDocumentCommand{\DisplayPersons}{}{%
  \seq_clear:N \l_luke_listofpersons_seq
  \group_begin:
  \seq_clear:N \l_tmpa_seq
  \seq_map_inline:Nn \g_luke_listofpersons_seq {%
    \IfRefUndefinedExpandable{##1\thesection}{}{
      \seq_put_right:Nn \l_tmpa_seq {\use:c{##1h}}
     }
   }
   \seq_if_empty:NF \l_tmpa_seq {
     \PrePersonList
     \seq_use:Nn \l_tmpa_seq {,~} 
     \PostPersonList
   }
   \group_end:

}
\ExplSyntaxOff

\NewDocumentCommand{\PostPersonList}{}{%
  \bigskip%

}

\NewDocumentCommand{\displayindividualperson}{m}{%
  \textbf{#1}%
}

\NewDocumentCommand{\PrePersonList}{}{%
  {\large \bfseries Persons in Section \thesection}

}

\makeatletter
\NewDocumentCommand{\NewPerson}{m+m}{%
  % Add this person to the global list
  \addperson{#1}%
  % Now define the personal \...x command 
  \expandafter\NewDocumentCommand\csname #1x\endcsname{+m}{%
    %Check if the person has been called in the local section already
    \IfPersonCalledAlreadyF{#1}{%
      \addpersonlocal{#1}
      % Add the personal to the local list, i.e. per section
      % Check whether the label has been defined already
        \protected@edef\@currentlabel{\thesection.#1}\label{#1\thesection}
    }%
    \textsc{#1:} ##1%
  }% End of the \...x command
  \expandafter\NewDocumentCommand\csname #1h\endcsname{}{%
    #2{#1}%
  }
}% End of \NewPerson
\makeatother


\NewPerson{tom}{\colorbox{green}}
\NewPerson{frodo}{\colorbox{yellow}}
\NewPerson{Gandalf}{\fcolorbox{yellow}{yellow!60!blue}}



\begin{document}
\section{In the supermarket}
\DisplayPersons
\tomx{Hi, I'm Tom!}

\frodox{Hi, I'm Frodo!}

\section{At TeX.SE}

\DisplayPersons

\frodox{Hi, I'm Frodo!}

\frodox{I am going to Mordor}

\Gandalfx{Cast the ring into the fire!}

\tomx{Waiting for Godot}
\end{document}

enter image description here

  • Huge thanks! I'll give that a try! Wouldn't have known how to approach that... – LukeLR Mar 5 '17 at 18:35
  • I will update with a better version... – user31729 Mar 5 '17 at 18:36
  • New persons should be added before \begin{document}, in my point of view – user31729 Mar 5 '17 at 18:46
  • Yes, all Persons are added before \begin{document}, but it's nice that you added the ability to add persons inline :) When compiled with pdflatex -synctex=1 -interaction=nonstopmode, the character names only appear on every second compile time. Is that expected behaviour? – LukeLR Mar 5 '17 at 18:53
  • 1
    Thanks a lot! I think everything works now! Huge thanks for your efforts! – LukeLR Mar 5 '17 at 21:55

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.