25

I want to put some small boxes around certain words in a paragraph. The boxes will never contain more than one word.

The \ovalbox command described in the fancybox documentation is close to what I need. However, I found some problems:

  • The rounded corners do not line up with the sides perfectly.
  • Text to the left and right in the paragraph are pushed away.
  • The spacing between lines in the paragraph is increased vertically.

I would like to create boxes which:

  • Adjust horizontally to fit the width of the text inside.
  • Have rounded or beveled corners.
  • Can be filled with a solid color.
  • Will not cause any text inside or around the box to move.
  • Will not impact the vertical spacing of lines.
lockstep
  • 250,273
Village
  • 13,603
  • 23
  • 116
  • 219

4 Answers4

36

You can use TikZ with the overlay option and a correct anchor. That would look like this:

\documentclass{article}
\usepackage{tikz}
\newcommand\mybox[2][]{\tikz[overlay]\node[fill=blue!20,inner sep=2pt, anchor=text, rectangle, rounded corners=1mm,#1] {#2};\phantom{#2}}
\begin{document}
   \noindent
   this is some text \mybox[fill=blue!20]{box} text\\
   this is some text box text
\end{document}

You can specify extra options (like I have done here for the color). By using the overlay option and the text anchor we ensure correct placement and no influence on spacing. The phantom is added to get the normal spacing for the content of the box. This is the result of the example code:

TikZ text box

Edit: To show that the vertical spacing is not affected either, consider the following:

\documentclass{article}
\usepackage{tikz}
\newcommand\mybox[2][]{\tikz[overlay]\node[fill=blue!20,inner sep=2pt, anchor=text, rectangle, rounded corners=1mm,#1] {#2};\phantom{#2}}
\begin{document}
  \begin{minipage}{0.4\textwidth}
    \noindent
    this is some text \mybox[fill=blue!20]{box} text\\
    this is some text box text
  \end{minipage}
  \begin{minipage}{0.4\textwidth}
    \noindent
    this is some text box text\\
    this is some text box text
  \end{minipage}    
\end{document}

Which results in:

TikZ text box vertical spacing

David Carlisle
  • 757,742
Roelof Spijker
  • 17,663
  • 5
  • 55
  • 63
  • 2
    \def\mybox[#1]#2 would force the "optional" argument, so \newcommand\mybox[2][] would be better. – Martin Scharrer Nov 28 '11 at 14:27
  • @MartinScharrer: You are absolutely correct. I have modified the answer to reflect this. The [fill=blue!20] is now of course also optional on the \mybox command. – Roelof Spijker Nov 28 '11 at 14:33
  • @wh1t3 And what happens if the boxed word is at the end of line with some cesura ? I think you have a vertically problem and some text are pushed horizontally ! – Alain Matthes Nov 28 '11 at 14:51
  • You can try : \begin{minipage}{4cm} \noindent this is a word boxed \mybox[fill=blue!20]{problem}. \end{minipage} – Alain Matthes Nov 28 '11 at 14:56
  • Another problem appears if inner sep is too greater. The box can erase some parts of character like por if two boxed word are one above the other. In a normal case, you can remove inner sep=2pt and rectangle because these values are given by default. I think it's very difficult to keep a text very clean (in terms of typography) when some words are boxed. – Alain Matthes Nov 28 '11 at 15:16
  • @Altermundus: You are right in concluding that the boxing breaks hyphenation. I don't see a nice way around this though and I don't think the OP was expecting this. I don't know exactly what you mean with your last comment. The OP simply shouldn't make inner sep much larger, since it will start to overlap with preceding and succeeding words. Again, this is by design, the OP clearly states he does not want horizontal or vertical spacing to be affected. I can't fit a huge box in a tiny gap. rectangle is default, I believe inner sep is greater than 2pt by default though. – Roelof Spijker Nov 28 '11 at 15:27
  • the vertical spacing is affected. Try it with a contents like "fg" –  Nov 28 '11 at 15:49
  • 1
    @Herbert: With vertical spacing I was referring to the vertical space between lines as the OP mentions. The vertical size of the box is indeed dependent on what is inside. If this is unwanted you could add text depth=0pt as an option to the node. This does not fix it entirely though. There are multiple options, the easiest is to set text height=1.5ex. This way you will have uniform box height. – Roelof Spijker Nov 28 '11 at 16:07
  • @RoelofSpijker Thanks for your answer. If I had \usepackage[french]{babel} it is not working anymore!!! error : `Runaway argument? \node [fill=blue!20,inner sep=2pt, anchor=text, rectangle, rounded co\ETC. ! File ended while scanning use of \tikz@next. \par <*> test.tex` – Colas Nov 05 '13 at 11:46
  • Solved by adding after \usepackage{tike} the following : \tikzset{ every picture/.prefix style={ execute at begin picture=\shorthandoff{;} } } – Colas Nov 05 '13 at 11:52
  • Can you explain again what the phantom is used for? Thanks. Great answer! – Dr. Manuel Kuehner Nov 30 '15 at 23:39
  • 1
    Like it says in the answer: "The phantom is added to get the normal spacing for the content of the box". Basically, because the node is placed in 'overlay mode', it has 0 size when being placed. But we don't want the next word to overlap, so we add a phantom to take up the space that the word would normally take up without actually printing the word. Try removing the phantom and see what happens. (It's been a while since I used any of this, just saw your comment. So I'm really hoping this is actually true... Pretty sure it is :)) – Roelof Spijker Dec 01 '15 at 09:41
  • @RoelofSpijker , could you please explain to me what is the role of #1 ? – Mohamed Vall Jan 24 '16 at 18:45
  • 1
    @MedVall, the #1 takes the value of the optional arguments. In the example I use fill=blue!20, which is superfluous because I already set it in the node command. Anyway, this value for #1 is added in the optional part of the node command. So you can give it a set of comma-separated arguments that the node command understands. – Roelof Spijker Feb 10 '16 at 12:50
12

My solution needs no special macros. No TikZ, no PSTricks. Only \pdfliteral primitive is used. And you can define \def\pdfliteral#1{\special{pdf:literal #1}} if \pdfliteral isn't available (for example in XeTeX).

\def\mybox#1{\leavevmode \setbox0=\hbox{#1}%
   \dimen0=\wd0 \edef\posxA{\expandafter\ignorept\the\dimen0 \space}%
   \hbox{\kern3pt\pdfliteral{q .8 .8 1 rg .8 .8 1 RG .9963 0 0 .9963 0 0 cm 1 j 1 J 6 w
                             0 0 m 0 5 l \posxA 5 l \posxA 0 l 0 0 l B Q}%
         \box0 \kern3pt}%
}
{\lccode`\?=`\p \lccode`\!=`\t  \lowercase{\gdef\ignorept#1?!{#1}}}

This is a \mybox{test} of my \mybox{box}.

\bye

roundbox

wipet
  • 74,238
  • Where/How do you define the colors? For example, what would be the settings for light gray? – JPMD Sep 27 '21 at 13:04
  • The color is defined in RGB color model in my example: 0.8 red, 0.8 green, 1 blue. For fills (rg) and the same for strokes (RG), see ".8 .8 1 rg .8 .8 1 RG". If you want light gray, then you can use GRAY color model (g for fills, G for strokes), for example ".9 g .9 G". – wipet Sep 28 '21 at 18:13
  • Cool! thank you! – JPMD Sep 29 '21 at 15:26
10

This kind of boxes can be done with extrude option from tcolorbox. A little example adapted from tcolorbox documentation:

\documentclass{article}
\usepackage[most]{tcolorbox}

\newtcbox{\mybox}[1][]{enhanced, colframe=blue, colback=blue!15, 
       frame style={opacity=0.25}, interior style={opacity=0.25}, 
       nobeforeafter, tcbox raise base, shrink tight, extrude by=1mm, #1}

\begin{document}
Lorem ipsum dolor sit amet, consectetuer adipiscing elit.
Ut \mybox[extrude bottom by=2cm]{purus} elit, vestibulum ut, \mybox{placerat} ac, 
\mybox[extrude top by=2mm]{adipiscing} vitae, \mybox[extrude left by=5mm, extrude 
bottom by=2mm]{felis}. Curabitur dictum gravida mauris. \mybox{Nam} arcu libero,    
nonummy eget, consectetuer id, vulputate a, magna.
\end{document}

enter image description here

Ignasi
  • 136,588
4

Basically similar to the solution given by Roelof Spijker. I suggest handling the optional argument differently, and use a \strut to ensure consistent box size while not changing the line spacing:

\documentclass{article}

\usepackage{tikz}

\newcommand\mmybox[2][fill=blue!20]{%
    \tikz[baseline]\node[%
        inner ysep=0pt, 
        inner xsep=2pt, 
        anchor=text, 
        rectangle, 
        rounded corners=1mm,
        #1] {\strut#2};%
}

\begin{document}
   \noindent
   this is some text \mmybox{Box} text\\
   this is some text box text
\end{document}

The results:

Sample of boxed text

sgmoye
  • 8,586