3

I'm writing my own letter class and I was wondering how I could deal with several attachments. Currently, I made myself a macro accepting one parameter that should be set in the preamble. The content - if there is any - is printed automatically by my letter-environment.

If there are several attachments, I would like to call the macro twice which would normally overwrite the first declaration (see my MWE). But as LaTeX reads the code from top to bottom, it first sees my first declaration and processes it. If I could store that value in an array it would be safe. If the second declaration comes along, I could append it's content to the first one. Then with a for loop one could print all the attachments in the list. This is very easy in other programming languages like PHP or Python, so I think LaTeX will also be able to handle this.


MWE

Class

\NeedsTeXFormat{LaTeX2e}
\ProvidesClass{customLetterClass}[2017/10/20 Custom Letter Class]
\LoadClass{article}

% %%%%%%%%%%%%%%%%
% Require Packages
% %%%%%%%%%%%%%%%%


\usepackage[english]{babel}
\usepackage{%
    parskip,%
    tabularx,%
    environ,%
    etoolbox%
}



% %%%%%%%%%%%%%%%%%
% Declare Variables
% %%%%%%%%%%%%%%%%%

% Attachment
\def\attachment#1{\gdef\@attachment{#1}}
\def\@attachment{}



% %%%%%%%%%%%%%%%%%%
% Define environment
% %%%%%%%%%%%%%%%%%%

\NewEnviron{letter}%
    {%
        % Letter text
        {\BODY}%
        %
        % Attachment (if set)
        \ifdefempty{\@attachment}%
        {}%
        {
            \\[5\baselineskip]
            \begin{tabularx}{\textwidth}{@{}lX@{}}
                Attachment: & \@attachment
            \end{tabularx}
        }%
    }%

Main

% %%%%%%%%%%%%%%
% Document Setup
% %%%%%%%%%%%%%%

\documentclass{customLetterClass}



% %%%%%%%%%%%%%%%%%%%
% Set Variable Values
% %%%%%%%%%%%%%%%%%%%

% Attachment
\attachment{Foo}
\attachment{Bar}

\begin{document}

    \begin{letter}
    Hallo
    \end{letter}

\end{document}

Sorry, to not deliver this in a single file, but if I change the documentclass to article and paste the content of the class inside my preamble, suddenly the testing \ifdefempty does not work correctly. I didn't find the issue, so here I am posting the two files.


Problem with Werner's approach

If I include the code supposed by Werner into the letter environment, I get a weird error message. This does not happen if I use the MWE provided by him. I checked the code multiple times, but I'm unable to find the issue. That's why I'm providing this MWE with the error-producing code, so everyone knows where we are at. Maybe we can also solve this last bit. :-)

Class

\NeedsTeXFormat{LaTeX2e}
\ProvidesClass{customLetterClass}[2017/10/20 Custom Letter Class]
\LoadClass{article}

% %%%%%%%%%%%%%%%%
% Require Packages
% %%%%%%%%%%%%%%%%


\RequirePackage[english]{babel}
\RequirePackage{%
    parskip,%
    tabularx,%
    environ,%
    etoolbox%
}



% %%%%%%%%%%%%%%%%%
% Declare Variables
% %%%%%%%%%%%%%%%%%

% Attachment
\newcounter{attachment}

\newcommand{\attachmentpar}{%
    \renewcommand{\attachmentpar}{\par}%
}

\newcommand{\attachment}[1]{%
    \stepcounter{attachment}%
    \listadd{\attachments}{#1}%
}



% %%%%%%%%%%%%%%%%%%
% Define environment
% %%%%%%%%%%%%%%%%%%

\NewEnviron{letter}%
    {%
        % Letter text
        {\BODY}%
        %
        % Attachment (if set)
        \ifnum\value{attachment}>0 % No \attachment
            \\[5\baselineskip]
            \begin{tabularx}{\linewidth}{ @{} l X @{} }
                \ifnum\value{attachment}=1 % Only one \attachment
                    Attachment
                \else % Multiple \attachments
                    Attachments
                \fi &
                \renewcommand{\do}[1]{\attachmentpar #1}% How each item should be processed
                \dolistloop{\attachments}% Process list of \attachments
            \end{tabularx}
        \fi
    }%

Main

same as above

Error

\c@attachment=\count89

! Illegal parameter number in definition of \env@letter@process.

  • In a class or package file it should be \RequirePackage, not \usepackage really. You could use the csvlist facilities of etoolbox as well – user31729 Oct 24 '17 at 19:35
  • I'm also using \RequirePackage, seems that I forgot to modify it back when trying to make it work in a single file. – Sam Oct 24 '17 at 19:42
1

Since you're using etoolbox, you can use its list processing functionality. Store items using \listadd{<list csname>}{<item>} and process them using \dolistloop{<list csname>}.

enter image description here

customLetterClass.cls:

\NeedsTeXFormat{LaTeX2e}
\ProvidesClass{customLetterClass}[2017/10/20 Custom Letter Class]
\LoadClass{article}

% %%%%%%%%%%%%%%%%
% Require Packages
% %%%%%%%%%%%%%%%%


\RequirePackage[english]{babel}
\RequirePackage{%
  parskip,%
  tabularx,%
  environ,%
  etoolbox%
}



% %%%%%%%%%%%%%%%%%
% Declare Variables
% %%%%%%%%%%%%%%%%%

% Attachment
\newcounter{attachment}

\newcommand{\attachmentpar}{%
  \renewcommand{\attachmentpar}{\par}%
}

\newcommand{\attachment}[1]{%
  \stepcounter{attachment}%
  \listadd{\attachments}{#1}%
}



% %%%%%%%%%%%%%%%%%%
% Define environment
% %%%%%%%%%%%%%%%%%%

\NewEnviron{letter}%
  {%
    % Letter text
    {\BODY}%
    %
    % Attachment (if set)
    \ifnum\value{attachment}>0 % No \attachment
      \\[5\baselineskip]
      \begin{tabularx}{\linewidth}{ @{} l X @{} }
        \ifnum\value{attachment}=1 % Only one \attachment
          Attachment
        \else % Multiple \attachments
          Attachments
        \fi &
        \renewcommand{\do}[1]{\attachmentpar ##1}% How each item should be processed
        \dolistloop{\attachments}% Process list of \attachments
      \end{tabularx}
    \fi
  }%

main.tex:

% %%%%%%%%%%%%%%
% Document Setup
% %%%%%%%%%%%%%%

\documentclass{customLetterClass}



% %%%%%%%%%%%%%%%%%%%
% Set Variable Values
% %%%%%%%%%%%%%%%%%%%

% Attachment
\attachment{Foo}
\attachment{Bar}

\begin{document}

\begin{letter}
Hallo
\end{letter}

\end{document}

The \attachment macro was written to also count the number of attachments added. This allows one to condition on whether to print Attachment or Attachments (or whatever).

  • I get the error message Illegal parameter number in definition of \env@letter@process. when adding your code in the letter environment (in place of the \ifdefempty{}{}{}). – Sam Oct 24 '17 at 20:39
  • I updated my question to include an MWE of the problem. This should be more easy to reconstruct the issue. – Sam Oct 24 '17 at 22:50
  • @Sam: Since you nest the redefinition of \do as an argument to another macro - the redefinition of the Letter environment via environ - you need to double the #'s. I've updated my answer to reflect this using your style. – Werner Oct 24 '17 at 23:54
  • Oh, that's awesome, thanks for the reference too! I learned something new again. That's fantastic! – Sam Oct 25 '17 at 0:05
  • 1
    @Sam: Indeed. This is also another option, using some from here to check for an empty argument. – Werner Oct 25 '17 at 0:33
2

The \def\attachment should check whether \@attachment is empty first and define it then to be \@attachment, otherwise use an \xdef statement to expand \@attachment first, then glue the new attachment to the expanded variable and assign it again to \@attachment.

Main file:

% %%%%%%%%%%%%%%
% Document Setup
% %%%%%%%%%%%%%%

\documentclass{customLetterClass}



% %%%%%%%%%%%%%%%%%%%
% Set Variable Values
% %%%%%%%%%%%%%%%%%%%

% Attachment
\attachment{Foo}
\attachment{Bar}

\begin{document}

    \begin{letter}
    Hallo
    \end{letter}

\end{document}

Class file:

\NeedsTeXFormat{LaTeX2e}
\ProvidesClass{customLetterClass}[2017/10/20 Custom Letter Class]
\LoadClass{article}

% %%%%%%%%%%%%%%%%
% Require Packages
% %%%%%%%%%%%%%%%%


\RequirePackage[english]{babel}
\RequirePackage{%
    parskip,%
    tabularx,%
    environ,%
    etoolbox%
}



% %%%%%%%%%%%%%%%%%
% Declare Variables
% %%%%%%%%%%%%%%%%%

% Attachment
\def\attachment#1{\ifdefempty{\@attachment}{\gdef\@attachment{#1}}{\xdef\@attachment{\@attachment\par#1}}}

\def\@attachment{}



% %%%%%%%%%%%%%%%%%%
% Define environment
% %%%%%%%%%%%%%%%%%%

\NewEnviron{letter}%
    {%
        % Letter text
        {\BODY}%
        %
        % Attachment (if set)
        \ifdefempty{\@attachment}%
        {}%
        {
            \\[5\baselineskip]
            \begin{tabularx}{\textwidth}{@{}lX@{}}
                Attachment: & \@attachment
            \end{tabularx}
        }%
    }%

enter image description here

**Second version of class with Attachments/Attachment check:

\NeedsTeXFormat{LaTeX2e}
\ProvidesClass{customLetterClass}[2017/10/20 Custom Letter Class]
\LoadClass{article}

% %%%%%%%%%%%%%%%%
% Require Packages
% %%%%%%%%%%%%%%%%


\RequirePackage[english]{babel}
\RequirePackage{%
    parskip,%
    tabularx,%
    environ,%
    etoolbox%
}



% %%%%%%%%%%%%%%%%%
% Declare Variables
% %%%%%%%%%%%%%%%%%

% Attachment
\def\attachment#1{\ifdefempty{\@attachment}{\gdef\@attachment{#1}}{\xappto\@attachment{\@attachment,#1}}}

\def\@attachment{}



% %%%%%%%%%%%%%%%%%%
% Define environment
% %%%%%%%%%%%%%%%%%%

\newcounter{attachcntr}

\NewEnviron{letter}%
    {%
%      \def\@@attachment{}
      \setcounter{attachcntr}{0}
        % Letter text
        {\BODY}%
        %
        % Attachment (if set)
        \ifdefempty{\@attachment}%
        {}%
        {
            \\[5\baselineskip]
            \def\localattachmentname{%
              \gdef\@@attachment{}
              \@for\@splitter@:=\@attachment\do{%
                \stepcounter{attachcntr}%
                \xappto\@@attachment{\@@attachment\par\@splitter@}
              }
              \ifnum1<\c@attachcntr%
              \def\realattachmentname{Attachments}% Use appropriate \GetTranslation here
              \else
              \def\realattachmentname{Attachment}% Use appropriate \GetTranslation here
              \fi
            }
            \localattachmentname%
            \begin{tabularx}{\textwidth}{@{}lX@{}}
              \realattachmentname: & \@@attachment
            \end{tabularx}
        }%
    }%
  • This works great. Do you have an idea, how I could print a different text (Attachments instead of Attachment), if there are several attachments set? PS: In the real code I'm using \GetTranslation{attachment} because my class is multilingual, so I need to modify the key. I'll maybe change them to attachment-singular and attachment-plural. – Sam Oct 24 '17 at 19:46
  • @Sam: That's a change of question – user31729 Oct 24 '17 at 20:06
  • \xdef can be dangerous. Since etoolbox is loaded, I suggest \gappto – egreg Oct 24 '17 at 20:12
0

Here's an implementation with expl3; you can do similarly for other variables, using \tl_gset:Nn for those that are not “incremental”.

\NeedsTeXFormat{LaTeX2e}
\ProvidesClass{customLetterClass}[2017/10/20 Custom Letter Class]
\LoadClass{article}

% %%%%%%%%%%%%%%%%
% Require Packages
% %%%%%%%%%%%%%%%%


\RequirePackage[english]{babel}
\RequirePackage{
  parskip,
  tabularx,
  environ,
  xparse,
}

\ExplSyntaxOn

% allocate the variables
\tl_new:N \g_clc_address_tl
\seq_new:N \g_clc_attachment_seq

% user commands

\NewDocumentCommand{\address}{m}
 {
  \tl_gset:Nn \g_clc_address_tl { #1 }
 }

\NewDocumentCommand{\attachment}{m}
 {
  \seq_gput_right:Nn \g_clc_attachment_seq { #1 }
 }


% user environment

\NewEnviron{letter}
 {
  % address box (if set)
  \tl_if_empty:NF \g_clc_address_tl
   {
    \hspace*{\fill}
    \begin{tabular}{@{}l@{}}
    \tl_use:N \g_clc_address_tl
    \end{tabular}
    \vspace{3\baselineskip}
   }
  % letter text
  \BODY
  %
  % attachment (if set)
  \seq_if_empty:NF \g_clc_attachment_seq
   {
    \par\nopagebreak\vspace{5\baselineskip}
    \begin{tabularx}{\textwidth}{@{}lX@{}}
    Attachment: &
    \seq_use:Nn \g_clc_attachment_seq { \\ Attachment: & }
    \end{tabularx}
   }
 }

\ExplSyntaxOff

Here's a sample letter.

\documentclass{customLetterClass}

% %%%%%%%%%%%%%%%%%%%
% Set Variable Values
% %%%%%%%%%%%%%%%%%%%

% Address
\address{
  egreg \\
  \TeX.Stackexchange \\
  Web
}

% Attachment
\attachment{Foo}
\attachment{Bar}

\begin{document}

\begin{letter}

Hallo

\end{letter}

\end{document}

enter image description here

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.