Here's how I would write it.
\makeatletter
\newcommand{\DoIfNoText}[1]{%
\begingroup
\sbox0{#1}%
\ifdim\wd0=\z@
\endgroup
\expandafter\@gobble
\else
\endgroup
\expandafter\@firstofone
\fi}
\makeatother
\newcommand{\MandatoryName}{}
\newcommand{\SetName}[1]{\renewcommand{\MandatoryName}{#1}}
\newcommand{\OptionalAddress}{}
\newcommand{\SetAddress}[1]{%
\DoIfNoText{#1}
{\renewcommand{\OptionalAddress}{\ignorespaces#1}}%
}
\newcommand{\ShowNameAndAddress}{%
\par\noindent\textbf{Name:}~\MandatoryName
\DoIfNoText{\OptionalAddress}
{\par\noindent\textbf{Address:}~\OptionalAddress}%
}
Probably
\def\OptionalAddress{}
should be added to \ShowNameAndAddress
if you want to avoid the meaning of \OptionalAddress
to be carried over to the next address.
Blank lines in the argument of \DoIfNoText
are irrelevant, as the box is built in restricted horizontal mode, where \par
commands are ignored.
Note I've added a \begingroup
-\endgroup
pair to keep the assignment to \box0
local, thus avoiding possible conflicts in some situations (see Missing Item number in enumerate and Frank Mittelbach's answer).
Trimming all of the explicit spaces around input is certainly doable. There are a number of approaches about this problem: I would go with the one Bruno Le Floch wrote for expl3
as \tl_trim_spaces:n
. That can be used by doing
\usepackage{expl3}
\ExplSyntaxOn
\cs_new_eq:NN \trimspaces \tl_trim_spaces:n
\ExplSyntaxOff
Alternatively, the implementation can be included directly in the source and thus avoid any dependency:
\documentclass{article}
\makeatletter
\long\def\trim@spaces#1{%
\@@trim@spaces{\q@mark#1}%
}
\def\@tempa#1{%
\long\def\@@trim@spaces##1{%
\@@trim@spaces@i##1\q@nil\q@mark#1{}\q@mark
\@@trim@spaces@ii
\@@trim@spaces@iii
#1\q@nil
\@@trim@spaces@iv
\q@stop
}%
\long\def\@@trim@spaces@i##1\q@mark#1##2\q@mark##3{%
##3%
\@@trim@spaces@i
\q@mark
##2%
\q@mark#1{##1}%
}%
\long\def\@@trim@spaces@ii\@@trim@spaces@i\q@mark\q@mark##1{%
\@@trim@spaces@iii
##1%
}%
\long\def\@@trim@spaces@iii##1#1\q@nil##2{%
##2%
##1\q@nil
\@@trim@spaces@iii
}%
\long\def\@@trim@spaces@iv##1\q@nil##2\q@stop{%
\unexpanded\expandafter{\@gobble##1}%
}%
}
\@tempa{ }
\def\test{ foo }
\edef\test{\expandafter\trim@spaces\expandafter{\test}}
\show\test
This will remove all of the spaces from the ends of the input, even is you do something tricky like \edef\test{ \space foo \space}
to start with (so there are multiple spaces at both ends). (If you are happy to limit yourself to this case, then xparse
offers the \TrimSpaces
post-processor for arguments using this method.)
The way the above works is that there are two loops: one for spaces at the start of the input (\@@trim@spaces@i
), a second for those as the end (\@@trim@spaces@iii
). First, \@@trim@spaces
sets things up such that the correct markers are in place. In the 'leading' step, \@@trim@spaces@i
matches an argument consisting of \q@mark
followed by a space (the space itself is discarded). If there are more spaces then #1
and #3
will be empty and #2
will be the remaining input, meaning that \@@trim@spaces@i
will be called again with the remaining input. On the other hand, if there are no spaces left in the input then #2
matches the empty input set up by \@@trim@spaces
, #1
is the user input with all leading spaces removed and #3
is \@@trim@spaces@ii
. The latter stops the loop and hands off to \@@trim@spaces@iii
(a \q@mark
is left on the front of the user input to prevent any loss of braces: see later). In this second loop, and spaces at the end of the input will appear just before \q@nil
. This pattern is matched by the argument to \@@trim@spaces@iii
. If there was a trailing space in the input then #1
is the user input with the space removed (but still with a leading \q@mark
) and #2
is \@@trim@spaces@iii
, leading to a loop. However, when the trailing spaces are exhausted, #2
is \@@trim@spaces@iv
and #1
is the \q@mark <user input>\q@nil\@@trim@spaces@iii
. The \q@nil\@@trim@spaces@iii
is removed by the argument patter for \@@trim@spaces@iv
before the leading \q@amrk
is stripped off by \@gobble
(with the \unexpanded
preventing further expansion).
Note that the above uses e-TeX to allow it to prevent further expansion inside an \edef
or similar. If the extensions are not available, change the last auxiliary to
\long\def\@@trim@spaces@iv##1\q@nil##2\q@stop{%
\@gobble##1%
}%
with the proviso that this will mean that you do have to be cautious what is passed through.
A second thing to note is that there are some 'special' tokens in the above, for example \q@nil
, that are used to match the macro argument patterns and so can't be in the input. That really should be okay with 'text', but you could use something even more obscure like \catcode`\Q=3
then Q
(math shift catcode) if you wanted to.
Removing the other items requested would mean searching for all of them separately. That sounds quite tricky in the case of \hspace
/\hskip
as presumably the spacing could be given in any valid units, even before we worry about things like
\def\foo{10 pt }
\hskip\foo
As you may know, dealing with group tokens is tricky at the best of times, so finding an empty group could also be hard. (I guess you'd need to use a loop: grab each token in the input, see if it's empty and if it's not add it to the 'keep' pile.)
Moreover, I think that this sort of input is pretty unlikely in real input. Trimming explicit spaces make sense, but I am not convinced about the other items (unless there is some particular case here where there is a good chance of picking up the other items).
Best Answer
The problem with the
\phantom
macro is that it places its content into restricted horizontal mode, i.e. in a horizontal box which isn't broken. Therefore it can't include line breaks or paragraphs. Normally you could overcome this issue by placing the content into aminipage
environment first, which allows multiple paragraphs. In order to not have to specify the width you can use the similarvarwidth
environment from thevarwidth
package instead. However\phantom
is unfortunatly not defined as a long macro and therefore you can't have paragraph breaks in it. You would need to box the content yourself first then use\phantom
on it or execute the underlying code yourself. The first method is pretty easy using theadjustbox
package.Your idea with
\hide
and\show
is more complicated. It is possible to write some code which does this, but handling page breaks is difficult.Some basic code which uses a vertical box instead would be:
However it does not support page breaks and does not add 100% the same height as the normal text, because of the missing line skip between the
\vbox
and the surrounding paragraphs. But it is very close. Page break support could be added by e.g. checking the height against\pagetotal
and\pagegoal
.