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!

LukeLR
  • 641
  • 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. –  Mar 05 '17 at 18:03
  • How could I implement that? I haven't done much complex LaTeX coding yet. – LukeLR Mar 05 '17 at 18:04

1 Answers1

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 05 '17 at 18:35
  • I will update with a better version... –  Mar 05 '17 at 18:36
  • New persons should be added before \begin{document}, in my point of view –  Mar 05 '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 05 '17 at 18:53
  • @LukeLR: As with any label approach, you need compile twice. The labels are written to the .aux first, which is then read at the begin of the second call, then positions, names etc. are known document-wide –  Mar 05 '17 at 18:54
  • Yes, but that's not what I meant. When I compile the first time, the .aux gets written. It's clear that lists will be missing at the first compile time. At the second compile time, all lists appear as expected. But when compiled for the third time, even without having made any changes, the character lists disappear in the output again. This makes me wonder, as e.g. tableofcontents does not disappear on every odd compile time. – LukeLR Mar 05 '17 at 18:57
  • @LukeLR: My checks were too paranoid. Try the updated version –  Mar 05 '17 at 19:09
  • Yes, that works! Thanks a lot! One last question: Usually I use commands like \<charactername>h to output the characternames, for example to dynamically enable highlighting. I understand that the output of the character names is defined in displayindividualperson, but how can I tell the macro to use \<charactername>h to output the charactername? I see that you've done something similiar in \expandafter\NewDocumentCommand\csname #1x\endcsname{+m}{ to define the \<charactername>x command, but I don't how I can use that to call the \<charactername>h-command... – LukeLR Mar 05 '17 at 19:30
  • @LukeLR: I replaced \displayindividualperson with \use:c{##1h} which looks for \tomh or \frodoh etc. If is not present, nothing is shown. I added a parameter to \NewPerson which is used to define \fooh etc. and will grab the argument. It, your command to highlight must be a macro that expects the name as the last argument. See the examples –  Mar 05 '17 at 19:43
  • 1
    Thanks a lot! I think everything works now! Huge thanks for your efforts! – LukeLR Mar 05 '17 at 21:55
  • @LukeLR: You're welcome. Viele Grüße und viel Spaß beim TeXen ;-) –  Mar 05 '17 at 21:56
  • Vielen Dank! Viele Grüße zurück! ;-) (You're Welcome) – LukeLR Mar 08 '17 at 10:12