Why is the space in text mode bigger than in math mode

I defined a command to draw a box around a text and, optionally, put another character inside this box.

The command is this (along with MWE):

\documentclass{minimal}

\newlength{\xboxh} % Sets a macro for the height
\newlength{\xboxw} % and for the width

\newcommand{\putbox}[2][]{
\settoheight{\xboxh}{#2}% Gets the height
\settowidth{\xboxw}{#2}% and width of the input
\frame{\phantom{\rule{\xboxw}{\xboxh}}}% And draws a frame around it
\setbox0=\hbox{#1\unskip}\ifdim\wd0=0pt% And if the optional argument is not empty
\relax
\else
\hspace{-\xboxw}#1% Prints it too
\fi\relax
}

\begin{document}
Some text to \putbox{Z} fill up space \par

Some text to \putbox[Z]{Z} fill up space \par

Some text to $$\putbox{Z}$$ fill up space

Some text to $$\putbox[Z]{Z}$$ fill up space
\end{document}


The command is supposed to have the same behavior in text and math mode. But that's not what happens. When used in text mode the command outputs an unwanted space before and after the box, and also misplaces the content of the box (if any):

How do I fix the spacing issue with the command?

You have a spurious space in your definition of \putbox:

\documentclass{article}

\newlength{\xboxh} % Sets a macro for the height
\newlength{\xboxw} % and for the width

\newcommand{\putbox}[2][]{% <------------------- spurious space
\settoheight{\xboxh}{#2}% Gets the height
\settowidth{\xboxw}{#2}% and width of the input
\frame{\phantom{\rule{\xboxw}{\xboxh}}}% And draws a frame around it
\setbox0=\hbox{#1\unskip}\ifdim\wd0=0pt% And if the optional argument is not empty
\relax
\else
\hspace{-\xboxw}#1% Prints it too
\fi\relax
}

\begin{document}

Some text to \putbox{Z} fill up space

Some text to \putbox[Z]{Z} fill up space

Some text to $$\putbox{Z}$$ fill up space

Some text to $$\putbox[Z]{Z}$$ fill up space

Width of Z: \setbox0=\hbox{Z}\the\wd0

Width of $Z$: \setbox0=\hbox{$Z$}\the\wd0

\end{document}


Note that \hbox sets its content in text mode.

• Well, that was fast! Thank you very much! About your note on the \hbox, I'm just using it to check if the optional argument is empty (didn't find a better way to do that). Or are you saying that this is the reason that in math mode the Z has a little tip out of the box? – Phelype Oleinik Nov 6 '17 at 18:09
• @PhelypeOleinik: You can check for an empty #1 using \if\relax\detokenize{#1}\relax <empty> \else <not empty> \fi. See How to check if a macro value is empty or will not create text with plain TeX conditionals? – Werner Nov 6 '17 at 18:11

You need no measuring; besides, you're forgetting the depth.

\documentclass{article}
\usepackage{amsmath}
\usepackage{xparse}

\makeatletter
\NewDocumentCommand{\putbox}{sm}{%
\begingroup
\ifmmode\@tempswatrue\else\@tempswafalse\fi
\setlength{\fboxsep}{-\fboxrule}%
\fbox{\IfBooleanTF{#1}{\po@math{#2}}{\po@math{\phantom{#2}}}}%
\endgroup
}
\newcommand{\po@math}[1]{\if@tempswa\ensuremath{#1}\else#1\fi}
\makeatother

\begin{document}

Some text to \putbox{Z} fill up space

Some text to \putbox*{Z} fill up space

Some text to \putbox*{g} fill up space

Some text to $$\putbox{Z}$$ fill up space

Some text to $$\putbox*{Z}$$ fill up space

Some text to $$\putbox*{g}$$ fill up space

\end{document}


As you see, if the macro is called in math mode, the argument is typeset in math mode as well. The *-variant prints the symbol.