12

I just realized that when I use \par as a macro parameter delimiter the parameter then contains a trailing space.

MWE:

\def\test #1\par{\def\param{#1}}
\test test

\hbox{\vrule\param\vrule}     % prints "|test |"

\bye

Is it a bug or standard behaviour, and if the latter, why is it so? Thx.

In reply to wipet's answer
@wipet: Thx, but I need more complex solution and I am not sure I would be able to adapt your suggestion for that.

Here is what I am doing. I have a file with a lot of macros, among others those to define and to place pictures and their descriptions, looking as follows.

% picture definition
% #1 - reference name (must be unique within a book or article, not necessarily within a volume or entire collection)
% #2 - base file name (without suffix)
% #3 - picture title
\def\picdef #1 #2 #3\par{%
    \expandafter\ifx\csname picfile-#1\endcsname\relax\else
        \errmessage{Picture definition duplicity: reference #1 already defined!}\fi
    \expandafter\def\csname picfile-#1\endcsname{\localPath/imgs/#2}%
    \expandafter\def\csname pictitle-#1\endcsname{#3\unskip}%
}


% picture description definition
% #1 - reference name
% #2 - picture description
\long\def\picdesc #1 #2\descend{%
    \expandafter\ifx\csname picfile-#1\endcsname\relax
        \errmessage{Define picture first before defining its description}%
    \else
        \expandafter\expandafter\expandafter\long\expandafter\def\csname picdesc-#1\endcsname{#2\unskip}%
    \fi
}


% user macros to actually place the picture or its description
\def\obrazek #1 {%
    \expandafter\ifx\csname picfile-#1\endcsname\relax
        \message{Warning: unknown picture reference #1 (typo or not yet defined?)!}%
    \else
        \ifhmode\par\fi
        \begin{figure}[t]
            \centerline{\includegraphics[width=0.9\textwidth]{\csname picfile-#1\endcsname}}
            \vskip 14pt
            \centerline{\large \bfseries {\csname pictitle-#1\endcsname}}
        \end{figure}
    \fi
}


\def\obrazekPopis #1 {%
    \expandafter\ifx\csname picfile-#1\endcsname\relax
        \message{Warning: unknown picture reference #1 (typo or not yet defined?)!}%
    \else
        \ifhmode\par\fi
        \csname pictitle-#1\endcsname: \csname picdesc-#1\endcsname\par
    \fi
}

Then for a particular work within a volume there is a file with picture definitions like this:

\picdef
    logo-s              % small logo
    logo_min            % logo_min.pdf
    Obr. 1              % Czech for Pic. 1

\picdesc logo-s
    This is what a~small logo is supposed to look like.
\descend

\endinput

And finally within an actual text pictures are placed like this:

Some text
and text

\obrazek logo-s

Some other text
etc.

\obrazekPopis logo-s

And text continues on
9

It is that way just because that's the way it is. Essentially the first end of line turns into a space as usual, the second newline turns into \par. The normal \par behaviour does an \unskip to remove a final glue node (usually from this space) before breaking the lines..

A couple of possibilities below.

\def\test #1\par{\def\param{#1}}
\test test

\hbox{\vrule\param\vrule}     % prints "|test |"

\def\testB #1 \par{\def\param{#1}}
\testB test

\hbox{\vrule\param\vrule}     % prints "|test|"

\def\testC #1\par{\def\param{#1\unskip}}
\testC test

\hbox{\vrule\param\vrule}     % prints "|test|"

\bye

Note that if you use an actual explicit \par in the use as in

\test test\par

then the additional space is not there, so your original would not have an extra space. \testB would have an error as it would be looking for a \par preceded by a space.

  • It is not good idea to use the variant of type \testB in general because the usage \testB about \TeX (end line), (empty line) causes an error. – wipet Mar 8 '16 at 10:12
  • @David: Thx. So the testC seems to be the correct solution as it works correctly both with and without explicit \par delimiter and also it follows the way TeX does it internally. Does it have any drawbacks I am overlooking? – Marcel S. Mar 8 '16 at 10:14
  • @wipet yes as I note above. (well I noted a slightly different case but anyway) – David Carlisle Mar 8 '16 at 10:15
  • 1
    @MarcelS. not necessarily, the \unskip removes the space if you are just typesetting the saved paragraph text but if, for example you are writing it to a file, or parsing a special file format such as a comma separate value line, it will write as \unskip which may not be what you want, in which case you need to do a second pass and explicitly test for and remove a trailing space token before saving the text. – David Carlisle Mar 8 '16 at 10:17
2

There is another possibility. To create a macro, which reads its parameter to the end of line. This solution is inspired from OPmac trick 0121. Macro programmer can use

\eoldef\macro#1{here is a parameter "#1"}

and then \macro text<end of line> causes that "#1" is text. The \eoldef macro can be defined by:

\gdef\eoldef#1{\def#1{\begingroup \catcode`\^^M=12 \eoldefA#1}%
   \expandafter\def\csname\string#1:G\endcsname}
{\catcode`\^^M=12 %
 \gdef\eoldefA#1#2^^M{\endgroup\csname\string#1:G\endcsname{#2}}}
  • See my own “answer” (not real one, but I cannot place proper formatting in a comment and also it is too long for a comment). – Marcel S. Mar 8 '16 at 11:26
1

You can use xparse and expl3:

\documentclass{article}
\usepackage{xparse}

\ExplSyntaxOn
\NewDocumentCommand{\test}{u{\par}}
 {
  \tl_set:Nx \param { \tl_trim_spaces:n { #1 } }
 }
\ExplSyntaxOff

\begin{document}

\test test

\mbox{\vrule\param\vrule}     % prints "|test|"

\end{document}

It can be done also with plain TeX, of course, but only for pdftex.

enter image description here

You can also get your more complicated macros with just some more work.

\documentclass{article}
\usepackage[demo]{graphicx}
\usepackage{xparse}

\ExplSyntaxOn
% first and second argument delimited by spaces
\NewDocumentCommand{\picdef}{u{~}u{~}u{\par}}
 {
  \marcels_pic_def:nnn { #1 } { #2 } { #3 }
 }
\NewDocumentCommand{\picdesc}{u{~}u{\par}}
 {
  \marcels_pic_desc:nn { #1 } { #2 }
 }
\NewDocumentCommand{\obrazek}{u{~}}
 {
  \marcels_pic_obrazek:n { #1 }
 }
\NewDocumentCommand{\obrazekPopis}{u{~}}
 {
  \marcels_pic_popis:n { #1 }
 }

\seq_new:N \g_marcels_pic_list_seq

\cs_new_protected:Nn \marcels_pic_def:nnn
 {
  \seq_if_in:NnTF \g_marcels_pic_list_seq { #1 }
   {
    \msg_error:nnn { marcels/pic } { duplicate } { #1 }
   }
   {
    \__marcels_pic_defnew:nnn { #1 } { #2 } { #3 }
   }
 }

\cs_new_protected:Nn \__marcels_pic_defnew:nnn
 {
  \seq_gput_right:Nn \g_marcels_pic_list_seq { #1 }
  \prop_gclear_new:c { g_marcels_pic_#1_prop }
  \prop_gput:cnn { g_marcels_pic_#1_prop } { image } { \LocalPath/imgs/#2 }
  \prop_gput:cnx { g_marcels_pic_#1_prop } { title } { \tl_trim_spaces:n { #3 } }
 }

\cs_new_protected:Nn \marcels_pic_desc:nn
 {
  \seq_if_in:NnTF \g_marcels_pic_list_seq { #1 }
   {
    \prop_gput:cnn { g_marcels_pic_#1_prop } { desc } { #2 }
   }
   {
    \msg_warning:nnn { marcels/pic } { early } { #1 }
   }
 }

\cs_new_protected:Nn \marcels_pic_obrazek:n
 {
  \seq_if_in:NnTF \g_marcels_pic_list_seq { #1 }
   {
    \__marcels_pic_obrazek_out:n { #1 }
   }
   {
    \msg_warning:nnn { marcels/pic } { undefined } { #1 }
   }
 }

\cs_new_protected:Nn \__marcels_pic_obrazek_out:n
 {
  \par
  \begin{figure}[tp]
  \centering
  \includegraphics[width=0.9\textwidth]
   {
    \prop_item:cn { g_marcels_pic_#1_prop } { image }
   }
  \par\vspace{14pt}
  {\large\bfseries \prop_item:cn { g_marcels_pic_#1_prop } { title }}
  \end{figure}
 }

\cs_new_protected:Nn \marcels_pic_popis:n
 {
  \seq_if_in:NnTF \g_marcels_pic_list_seq { #1 }
   {
    \__marcels_pic_popis_out:n { #1 }
   }
   {
    \msg_warning:nnn { marcels/pic } { undefined } { #1 }
   }
 }

\cs_new_protected:Nn \__marcels_pic_popis_out:n
 {
  \par
  \prop_item:cn { g_marcels_pic_#1_prop } { title }:~
  \prop_item:cn { g_marcels_pic_#1_prop } { desc }
  \par
 }

\msg_new:nnnn { marcels/pic } { duplicate }
 {
  Picture~name~'#1'~defined
 }
 {
  The~name~'#1'~is~already~assigned;~I'll~ignore~the~new~definition
 }

\msg_new:nnn { marcels/pic } { early }
 {
  Description~before~definition~of~'#1'
 }

\msg_new:nnn { marcels/pic } { undefined }
 {
  Undefined~picture~'#1'
 }

\ExplSyntaxOff

\begin{document}

\picdef
    logo-s              % small logo
    logo_min            % logo_min.pdf
    Obr. 1              % Czech for Pic. 1

\picdesc logo-s
    This is what a~small logo is supposed to look like.

Some text
and text

\obrazek logo-s

Some other text
etc.

\obrazekPopis logo-s

And text continues on

\end{document}

enter image description here

A different implementation with the same output. A key-value interface is, in my opinion, much clearer.

\documentclass{article}
\usepackage[demo]{graphicx}
\usepackage{xparse}

\ExplSyntaxOn
\NewDocumentCommand{\picdef}{mm}
 {%#1 = name, #2 = key-value pairs
  \marcels_pic_def:nn { #1 } { #2 }
 }
\NewDocumentCommand{\obrazek}{m}
 {
  \marcels_pic_obrazek:n { #1 }
 }
\NewDocumentCommand{\obrazekPopis}{m}
 {
  \marcels_pic_popis:n { #1 }
 }

\keys_define:nn { marcels/pic }
 {
  image .code:n = \prop_put:Nnn \l_marcels_pic_tmp_prop { image } { \LocalPath/imgs/#1 },
  title .code:n = \prop_put:Nnn \l_marcels_pic_tmp_prop { title } { #1 },
  desc  .code:n = \prop_put:Nnn \l_marcels_pic_tmp_prop { desc } { #1 },
 }

\seq_new:N \g_marcels_pic_list_seq
\prop_new:N \l_marcels_pic_tmp_prop

\cs_new_protected:Nn \marcels_pic_def:nn
 {
  \seq_if_in:NnTF \g_marcels_pic_list_seq { #1 }
   {
    \msg_error:nnn { marcels/pic } { duplicate } { #1 }
   }
   {
    \__marcels_pic_defnew:nn { #1 } { #2 }
   }
 }

\cs_new_protected:Nn \__marcels_pic_defnew:nn
 {
  \seq_gput_right:Nn \g_marcels_pic_list_seq { #1 }
  \prop_clear:N \l_marcels_pic_tmp_prop
  \keys_set:nn { marcels/pic } { #2 }
  \prop_new:c { g_marcels_pic_#1_prop }
  \prop_gset_eq:cN { g_marcels_pic_#1_prop } \l_marcels_pic_tmp_prop
 }

\cs_new_protected:Nn \marcels_pic_desc:nn
 {
  \seq_if_in:NnTF \g_marcels_pic_list_seq { #1 }
   {
    \prop_gput:cnn { g_marcels_pic_#1_prop } { desc } { #2 }
   }
   {
    \msg_warning:nnn { marcels/pic } { early } { #1 }
   }
 }

\cs_new_protected:Nn \marcels_pic_obrazek:n
 {
  \seq_if_in:NnTF \g_marcels_pic_list_seq { #1 }
   {
    \__marcels_pic_obrazek_out:n { #1 }
   }
   {
    \msg_warning:nnn { marcels/pic } { undefined } { #1 }
   }
 }

\cs_new_protected:Nn \__marcels_pic_obrazek_out:n
 {
  \par
  \begin{figure}[tp]
  \centering
  \includegraphics[width=0.9\textwidth]
   {
    \prop_item:cn { g_marcels_pic_#1_prop } { image }
   }
  \par\vspace{14pt}
  {\large\bfseries \prop_item:cn { g_marcels_pic_#1_prop } { title }}
  \end{figure}
 }

\cs_new_protected:Nn \marcels_pic_popis:n
 {
  \seq_if_in:NnTF \g_marcels_pic_list_seq { #1 }
   {
    \__marcels_pic_popis_out:n { #1 }
   }
   {
    \msg_warning:nnn { marcels/pic } { undefined } { #1 }
   }
 }

\cs_new_protected:Nn \__marcels_pic_popis_out:n
 {
  \par
  \prop_item:cn { g_marcels_pic_#1_prop } { title }:~
  \prop_item:cn { g_marcels_pic_#1_prop } { desc }
  \par
 }

\msg_new:nnnn { marcels/pic } { duplicate }
 {
  Picture~name~'#1'~defined
 }
 {
  The~name~'#1'~is~already~assigned;~I'll~ignore~the~new~definition
 }

\msg_new:nnn { marcels/pic } { early }
 {
  Description~before~definition~of~'#1'
 }

\msg_new:nnn { marcels/pic } { undefined }
 {
  Undefined~picture~'#1'
 }

\ExplSyntaxOff

\begin{document}

\picdef{logo-s} % small logo
 {
  image=logo_min, % logo_min.pdf
  title=Obr. 1,   % Czech for Pic. 1
  desc=This is what a~small logo is supposed to look like.
 }

Some text
and text

\obrazek{logo-s}

Some other text
etc.

\obrazekPopis{logo-s}

And text continues on

\end{document}

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.