# Non-invasive replacement for \fbox?

I am trying to create a macro that improves on \fbox by working correctly in all cases and by being minimally invasive with respect to padding. What I've got so far works fairly well, but I'm having trouble measuring text width, height, and depth in certain cases. My question is How do I calculate these correctly?

Why I don't like \fbox:

1. Notice below how \fbox gets the bounding boxes correct on the ‘,’ and the ‘l’, but completely messes up the fraction size when invoked as $\frac{\fbox{1}}{\fbox{2}}$ and the superscript size when invoked as $x^{\fbox{2}}$.

2. \fbox draws a rule around the bounding box rather than inside it. Thus, no matter how thin the rule, it still alters the dimensions of the text being boxed. In other words, \fbox is not minimally invasive. Below, the padding and line thickness are set with \setlength\fboxsep{0pt} and \setlength\fboxrule{0.001pt}. Setting \fboxrule to 0pt causes the rule to disappear altogether.

First attempt at a non-invasive box (using \vrule):

My first attempt used a \vrule. This was good, but it obscured text when the bounding box overlapped another character, for example the ‘r’ below. Worse yet, I didn't see how I could draw a dark rule around the box. So I threw out that idea.

Second attempt (using TikZ):

Next I used TikZ to draw a translucent filled box. The edge is not a stroked path around the box but rather is four very thin filled rectangles inside the bounding box.

Now this is getting somewhere. The problem, however, is in how I’m measuring the width, height, and depth of the bounding box. I'm using \settowidth, \settoheight, and \settodepth and giving these the value #1 passed to the macro. This approach works great for regular text, but once something is in a superscript, subscript, a fraction, or a stacked relation—or in any other font or size for any reason—it breaks down.

Problems:

As you can see, the widths, heights, and depths of certain elements below have been calculated incorrectly. The most egregious of is the \cdot, whose bounding box appears to its left because my macro doesn't know that it is a binary relational operator needing extra space. I'm not sure if that is fixable, but is there at least some way to fix the superscripts and subscripts? Because although $v_{\fillbox{x}}$ comes out with undisturbed spacing, it's measuring the size of x’s bounding box based on #1 being x (normal size and style) and not _x (subscripted math size and style).

Finally, why does the stacked relation disturb the horizontal spacing?

Minimal working example:

\documentclass{article}

\usepackage{tikz}

\makeatletter
\newlength\fillbox@height%
\newlength\fillbox@depth%
\newlength\fillbox@width%
\newcommand{\fillbox}[1]{%
\ifmmode%
\settoheight{\fillbox@height}{$#1$}%
\settodepth{\fillbox@depth}{$#1$}%
\settowidth{\fillbox@width}{$#1$}%
\else%
\settoheight{\fillbox@height}{#1}%
\settodepth{\fillbox@depth}{#1}%
\settowidth{\fillbox@width}{#1}%
\fi%
\raisebox{-\fillbox@depth}{%
\begin{tikzpicture}[scale=1]
\pgfsetfillcolor{yellow!90!red}
\pgfsetfillopacity{0.5}
\pgfsetrectcap
\fill (0,-\fillbox@depth) rectangle (\fillbox@width,\fillbox@height);
\pgfsetfillcolor{yellow!50!black}
\pgfsetfillopacity{1}
\fill (0,-\fillbox@depth)
rectangle (\fillbox@width,-\fillbox@depth+.1pt);
\fill (0,\fillbox@height)
rectangle (\fillbox@width,\fillbox@height-.1pt);
\fill (0,-\fillbox@depth)
rectangle (.1pt,\fillbox@height);
\fill (\fillbox@width,-\fillbox@depth)
rectangle (\fillbox@width-.1pt,\fillbox@height);
\end{tikzpicture}%
\kern-\fillbox@width%
}%
#1%
}
\makeatother

\begin{document}

\vskip1em
$\fillbox{p}_{\fillbox{x}} \mathrel{\stackrel{\fillbox{_{~+}}}{\fillbox{\leftarrow}}} \fillbox{(}\fillbox{\frac{\fillbox{1}}{\fillbox{2}}}\fillbox{\cdot} \fillbox{a}_{\fillbox{x}}\fillbox{\cdot}% \fillbox{\Delta}\fillbox{t}^{\fillbox{2}}\fillbox{)} \fillbox{+} \fillbox{(}\fillbox{v}_{\fillbox{x}}\fillbox{\cdot}% \fillbox{\Delta}\fillbox{t}\fillbox{)}$\fillbox{;}\par
$\fillbox{v}_{\fillbox{x}} \mathrel{\stackrel{\fillbox{_{~+}}}{\fillbox{\leftarrow}}} \fillbox{a}_{\fillbox{x}}\fillbox{\cdot}% \fillbox{\Delta}\fillbox{t}$\fillbox{;}\par

\vskip1em
$p_x \mathrel{\stackrel{_{~+}}{\leftarrow}} (\frac{1}{2}\cdot a_x\cdot\Delta t^2) + (v_x\cdot\Delta t)$;\par
$v_x \mathrel{\stackrel{_{~+}}{\leftarrow}} a_x\cdot\Delta t$;\par

\end{document}


(Note: I didn't tag this question with tikz because even though it uses TikZ, the question is really about spacing and boxing; TikZ just happens to be used.)

-

Any box commands in math mode need to be set in all styles so TeX can pick the right one later; the \mathchoice command is what you need here. See my rant against \over on this site the other day:-) If you just use \hbox{$...$} you are starting a fresh math list at text size even in subscript position.

You can re-assert the correct spacing around your construct using \mathbin \mathop and friends, This could be done automatically by looking up the\mathcode of the character you are boxing. Inspecting bm.sty might be helpful, which does this to re-assert the correct math class after making things bold, but the issues are similar.

It's a bit late to re-create bm interrogation of \mathcode so this just looks at special cases \cdot and + but otherwise does something plausible I suspect

UPDATE At the end I place a version that does automatically pick up the math class from character tokens, things like \cdot defined by \mathchar and things like \log with explicit \mathop. I will leave the original version in place as the math class detection probably obscures the logic of the measuring which was perhaps the main part of the question.

\documentclass{article}

\usepackage{amsmath,color}

\makeatletter

\def\fb@cdot{\cdot}
\def\fb@p{+}
\def\@mfbox#1{%
\def\fb@{#1}%
\ifx\fb@\fb@cdot\mathbin\fi
\ifx\fb@\fb@p\mathbin\fi
{\text{\fboxsep\z@\colorbox{yellow}{$\m@th#1$}}}}
\def\@tfbox#1{{\fboxsep\z@\colorbox{yellow}{#1}}}
\def\fillbox#1{\ifmmode\expandafter\@mfbox\else\expandafter\@tfbox\fi{#1}}

\begin{document}

\vskip1em
$\fillbox{p}_{\fillbox{x}} \mathrel{\stackrel{\fillbox{_{~+}}}{\fillbox{\leftarrow}}} \fillbox{(}\fillbox{\frac{\fillbox{1}}{\fillbox{2}}}\fillbox{\cdot} \fillbox{a}_{\fillbox{x}}\fillbox{\cdot}% \fillbox{\Delta}\fillbox{t}^{\fillbox{2}}\fillbox{)} \fillbox{+} \fillbox{(}\fillbox{v}_{\fillbox{x}}\fillbox{\cdot}% \fillbox{\Delta}\fillbox{t}\fillbox{)}$\fillbox{;}\par
$\fillbox{v}_{\fillbox{x}} \mathrel{\stackrel{\fillbox{_{~+}}}{\fillbox{\leftarrow}}} \fillbox{a}_{\fillbox{x}}\fillbox{\cdot}% \fillbox{\Delta}\fillbox{t}$\fillbox{;}\par

\vskip1em
$p_x \mathrel{\stackrel{_{~+}}{\leftarrow}} (\frac{1}{2}\cdot a_x\cdot\Delta t^2) + (v_x\cdot\Delta t)$;\par
$v_x \mathrel{\stackrel{_{~+}}{\leftarrow}} a_x\cdot\Delta t$;\par

\end{document}


Version using bm-style math class detection.

\documentclass{article}

\usepackage{amsmath,color}

\makeatletter

\def\fb@eat#1#2#3#4#5{\futurelet\fb@let@token\fb@eat@}
\def\fb@eat@#1\fb@eat{%
\ifx\fb@let@token\bgroup
\else\ifx\fb@let@token\mathop
\mathop
\else\ifx\fb@let@token\mathbin
\mathbin
\else\ifx\fb@let@token\mathrel
\mathrel
\else\ifx\fb@let@token\mathopen
\mathopen
\else\ifx\fb@let@token\mathop
\mathop
\else\ifx\fb@let@token\mathpunct
\mathpunct
\else\ifcat.\ifcat a\noexpand\fb@let@token.\else\noexpand\fb@let@token\fi
\afterassignment\fb@mathchar\count@\mathcode#1\relax\fb@eat
\else\ifx\fb@let@token\mathchar
\afterassignment\fb@mathchar\expandafter\count@\@gobble#1\relax\fb@eat
\else
\xdef\meaning@{\meaning\fb@let@token}%
\expandafter\fb@mchar@test\meaning@""\@nil
\fi\fi\fi\fi\fi\fi\fi\fi\fi
}

\def\@mfbox#1{%
\begingroup
\let\protect\empty
\expandafter\fb@eat\romannumeral\Q#1\relax\fb@eat
\ifcase\count@
\or
\mathop\or
\mathbin\or
\mathrel\or
\mathopen\or
\mathclose\or
\mathpunct\or
\fi
{\text{\fboxsep\z@\colorbox{yellow}{$\m@th#1$}}}%
\endgroup}

\edef\fb@mchar@{\meaning\mathchar}

\def\fb@mchar@test#1"#2"#3\@nil{%
\xdef\meaning@{#1}%
\ifx\meaning@\fb@mchar@
\count@"#2\relax
\fb@mathchar\fb@eat
\fi
}

\def\fb@mathchar#1\fb@eat{%
\divide\count@"1000 }

\def\@tfbox#1{{\fboxsep\z@\colorbox{yellow}{#1}}}
\def\fillbox#1{\ifmmode\expandafter\@mfbox\else\expandafter\@tfbox\fi{#1}}

\begin{document}

$a-b$

$a{-}b$

$a\fillbox{-}b$

$\log x + \mathrm{log}x$

$\fillbox{\log} \fillbox{x} \fillbox{+} \fillbox{\mathrm{log}}\fillbox{x}$

$\fillbox{0}$

$a \cdot b {\cdot} c$

$a \fillbox{\cdot} b \fillbox{{\cdot}}c$

$a \fillbox{\mathchar"2201} b \fillbox{{\mathchar"2201}} c$

$a - b < \alpha$

$\fillbox{a} \fillbox{-} \fillbox{b} \fillbox{<} \fillbox{\alpha}$

$\fillbox{a+b}$

$a = \sqrt{h}$

$a = \fillbox{\sqrt{h}}$

\vskip1em
$\fillbox{p}_{\fillbox{x}} \mathrel{\stackrel{\fillbox{_{~+}}}{\fillbox{\leftarrow}}} \fillbox{(}\fillbox{\frac{\fillbox{1}}{\fillbox{2}}}\fillbox{\cdot} \fillbox{a}_{\fillbox{x}}\fillbox{\cdot}% \fillbox{\Delta}\fillbox{t}^{\fillbox{2}}\fillbox{)} \fillbox{+} \fillbox{(}\fillbox{v}_{\fillbox{x}}\fillbox{\cdot}% \fillbox{\Delta}\fillbox{t}\fillbox{)}$\fillbox{;}\par
$\fillbox{v}_{\fillbox{x}} \mathrel{\stackrel{\fillbox{_{~+}}}{\fillbox{\leftarrow}}} \fillbox{a}_{\fillbox{x}}\fillbox{\cdot}% \fillbox{\Delta}\fillbox{t}$\fillbox{;}\par

\vskip1em
$p_x \mathrel{\stackrel{_{~+}}{\leftarrow}} (\frac{1}{2}\cdot a_x\cdot\Delta t^2) + (v_x\cdot\Delta t)$;\par
$v_x \mathrel{\stackrel{_{~+}}{\leftarrow}} a_x\cdot\Delta t$;\par

\end{document}


Producing

-
This is starting to look really good! Is it possible to make the box color translucent so that nesting is visible? And with a thin border on the interior of the box? –  Todd Lehman Feb 9 '12 at 21:16
I imagine that xcolor or one of the newer packages can do translucent color, If you know the syntax feel free to edit the answer instead of the simple "yellow" . I've not actually written any TeX for a decade so I'm sticking to what I know this week, hence just original color package syntax. Inner border of the box is doable but a bit of a pain as I don't think \fcolorbox works with negative fboxsep (as it's basically \fbox) so I'd have to implement the rules from scratch, but probably the definition of \fcolorbox can be copied more or less.. –  David Carlisle Feb 9 '12 at 21:23

In response to the questioner's desire to fix the box issue for sub and superscripts, the scalerel package has the means to carry the current math style into places where it would otherwise be lost. The syntax is \ThisStyle{...\SavedStyle...} which will import the math style in force at location \ThisStyle to the spot where \SavedStyle is invoked.

\documentclass{article}
\usepackage{scalerel}
\fboxsep=0pt
\newcommand\boxit[1]{\ThisStyle{\fbox{$\SavedStyle#1$}}}
\begin{document}
$y = x^{\boxit{2}}$
\end{document}


-