8

I'm trying to draw rounded rectangles with solid or dotted lines and a shadow around arbitrary text in LaTeX. I was hopeful that my answer was in How to draw rounded corners around box with shadow, but those don't work for me because both tcolorbox and mdframed want a new environment, and I want to keep the rounded rectangles inline like \fbox does.

Right now I'm using the following TikZ code:

\newcommand{\solidButton}[1]{\begin{tikzpicture}[baseline=(char.base)]
  \node(char)[draw,rounded corners=0.75mm,fill=white,
    shape=rectangle, inner ysep=3pt, inner xsep=2pt,
    drop shadow={opacity=.6,shadow xshift=2pt, shadow yshift=-1pt},
    minimum width=0.6cm, minimum height=0.35cm]
    {#1};
\end{tikzpicture}}

\newcommand{\dottedButton}[1]{\begin{tikzpicture}[baseline=(char.base)]
  \node(char)[draw,dotted,rounded corners=0.75mm,fill=white,
    shape=rectangle, inner ysep=3pt, inner xsep=2pt,
    drop shadow={opacity=.6,shadow xshift=2pt, shadow yshift=-1pt},
    minimum width=0.6cm, minimum height=0.35cm]
    {#1};
\end{tikzpicture}}

This looks the way I'd like it to:

Boilerplate text with inline rounded rectangles and dotted rounded rectangles around the word "Button"

... but all the TikZ (I use it 10-20 times/page) is making my document really slow to compile. Is there a more efficient way to do what I want?

4
  • 1
    For dotted lines, I don't know, but for dashed lines, you have the dashbox package. For rounded corners, try the \ovalbox or \Ovalbox commands from fancybox.
    – Bernard
    Nov 10, 2021 at 10:01
  • tcolorbox has the hbox option, which allows you to have the box inline.
    – user255043
    Nov 10, 2021 at 15:01
  • You could externalise the pictures. See the documentation on the external tikz library. Nov 11, 2021 at 17:31
  • Another option is using \renewcommand\SolidButton{\textbf} while you are writing the document, and then commenting it out in the final runs (maybe adding saving to mimic the final boxing) - you get the idea.
    – Rmano
    Nov 14, 2021 at 11:20

4 Answers 4

7

This solution is completely without TikZ, only pict2e, which is almost pure LaTeX picture mode.

If this is not speeder, there is no hope.

P.S. = David Carlisle will be proud of me.

\documentclass{book}
\usepackage{xcolor}
\usepackage{graphicx}
\usepackage{pict2e}

\newlength{\myww}
\newlength{\mystep}
\setlength{\mystep}{1.8pt}
\newcommand{\solidButton}[1]{%
    \settowidth{\myww}{#1}%
    \addtolength{\myww}{4pt}%
    \begin{picture}(\myww,14)%
      \def\mypath{\moveto(2,-3)
      \circlearc{2}{-1}{2}{-90}{-180}
      \lineto(0,8)
      \circlearc{2}{8}{2}{180}{90}
      \lineto(\myww-2,10)
      \circlearc{\myww-2}{8}{2}{90}{0}
      \lineto(\myww,0)
      \circlearc{\myww-2}{-1}{2}{0}{-90}
      \lineto(2,-3)}%
      \hspace{2pt}\raisebox{-1pt}{\color{lightgray}\mypath%
      \fillpath}%
      \hspace{-2pt}{\color{white}\mypath%
      \fillpath}%
      {\color{black}%
      \mypath\strokepath%
      \put(2,0){#1}}%   
    \end{picture}%
    }
\newcommand{\dottedButton}[1]{%
    \settowidth{\myww}{#1}%
    \addtolength{\myww}{4pt}%
    \begin{picture}(\myww,14)%
      \def\mypath{\moveto(2,-3)
      \circlearc{2}{-1}{2}{-90}{-180}
      \lineto(0,8)
      \circlearc{2}{8}{2}{180}{90}
      \lineto(\myww-2,10)
      \circlearc{\myww-2}{8}{2}{90}{0}
      \lineto(\myww,0)
      \circlearc{\myww-2}{-1}{2}{0}{-90}
      \lineto(2,-3)}%
      \hspace{2pt}\raisebox{-1pt}{\color{lightgray}\mypath%
      \fillpath}%
      \hspace{-2pt}{\color{white}\mypath%
      \fillpath}%
      \color{black}%
      \put(0.6,-2.4){\circle{.3}}
      \put(0.6,9.4){\circle{.3}}  
      \put(\myww-.6,-2.4){\circle{.3}}
      \put(\myww-.6,9.4){\circle{.3}}
      \multiput(0,-1)(0,\mystep){6}{\circle{.3}}
      \multiput(\myww,-1)(0,\mystep){6}{\circle{.3}}
      \multiput(2,10)(\mystep,0){\numexpr\myww/\mystep-1\relax}{\circle{.3}}
      \multiput(2,-3)(\mystep,0){\numexpr\myww/\mystep-1\relax}{\circle{.3}}
      \put(2,0){#1}%   
    \end{picture}%
    }
\begin{document}
This is a solid box
\solidButton{Solid Button}
This is a solid box
\solidButton{ppp}
This is a solid box 
\solidButton{vvv}
This is a solid box 
\solidButton{ddd}
This is a solid box 

This is a dotted box 
\dottedButton{Dotted Button}
This is a dotted box
\dottedButton{ppp}
This is a dotted box
\dottedButton{vvv}
This is a dotted box
\dottedButton{ddd}
This is a dotted box
\end{document}

enter image description here

5
  • 4
    of course I'm proud Nov 14, 2021 at 9:11
  • 4
    "Answer IV: A New Hope" Nov 14, 2021 at 9:16
  • 1
    This one rocketed along! My (almost) null test case takes 26.6 seconds, and this completes the job in 37.0 seconds! Thanks for spending the time to do this and introducing me to pict2e! Nov 14, 2021 at 23:30
  • For the sake of posterity, I should mention that I added a \definecolor{sixty-grey}{gray}{0.60} and substituted that for \lightgray to match my original color. Nov 14, 2021 at 23:54
  • @user3486184 Great news! And thank you for accepting my answer!
    – CarLaTeX
    Nov 15, 2021 at 6:50
6

You can use pgf. Since pgf is a sublayer of tikz, it will be faster.

\documentclass{book}
\usepackage{pgf}

\newcommand{\solidButton}[1]{%
  \setbox0\hbox{#1}%
  \begin{pgfpicture}
  \pgfset{inner ysep=3pt, inner xsep=2pt, minimum height = 0.35cm, minimum width=0.6cm }
  \pgfsetbaseline{0pt}
  \pgfsetcornersarced{\pgfpoint{0.75mm}{0.75mm}}
  \pgftransformshift{\pgfpoint{2pt}{-1pt}}
  \pgfsetfillcolor{gray!60}
  \pgfnode{rectangle}{base}{\copy0}{}{\pgfusepath{fill}}
  \pgftransformshift{\pgfpoint{-2pt}{1pt}}
  \pgfpathmoveto{\pgfpointorigin}
  \pgfsetfillcolor{white}
  \pgfnode{rectangle}{base}{\box0}{}{\pgfusepath{fill,stroke}}
  \end{pgfpicture}%
  }

\newcommand{\dottedButton}[1]{%
  \setbox0\hbox{#1}%
  \begin{pgfpicture}
  \pgfset{inner ysep=3pt, inner xsep=2pt, minimum height = 0.35cm, minimum width=0.6cm }
  \pgfsetbaseline{0pt}
  \pgfsetcornersarced{\pgfpoint{0.75mm}{0.75mm}}
  \pgftransformshift{\pgfpoint{2pt}{-1pt}}
  \pgfsetfillcolor{gray!60}
  \pgfnode{rectangle}{base}{\copy0}{}{\pgfusepath{fill}}
  \pgftransformshift{\pgfpoint{-2pt}{1pt}}
  \pgfpathmoveto{\pgfpointorigin}
  \pgfsetfillcolor{white}
  \pgfsetdash{{0.1mm}{0.2mm}}{0cm}% line added
  \pgfnode{rectangle}{base}{\box0}{}{\pgfusepath{fill,stroke}}
  \end{pgfpicture}%
  }


\begin{document}
This is a solid box
\solidButton{Solid Button}
This is a solid box
This is a solid box
This is a solid box
This is a solid box
This is a solid box 

This is a dotted box 
\dottedButton{Dotted Button}
This is a dotted box
This is a dotted box
This is a dotted box
This is a dotted box

\end{document}

Output of the above code

1
  • Thanks for this. I timed it and found it shaved a little bit off my job. When I don't typeset anything but the button label, my job takes 26.6 seconds. When I use my TikZ implementation, it's 100.2 seconds. When I use pgf it takes 97.7 seconds. Nov 13, 2021 at 2:25
4

With tcolorbox, you can define your own boxes.

Unfortunately, as user3486184 commented, this is not speeder than TikZ.

\documentclass{book}

\usepackage{tcolorbox}
\tcbuselibrary{skins}
\tcbset{    
    enhanced,
    tcbox raise base,
    nobeforeafter, 
    colback=white,
    top=0pt,
    bottom=0pt,
    left=0pt,
    right=0pt,
    arc=2pt,
    }
\newtcbox{\solidButton}{
    colframe=black,
    boxrule=.5pt,
    shadow={1.6pt}{-.8pt}{0pt}{lightgray},
    }
\newtcbox{\dottedButton}{
    colframe=white,
    boxrule=0pt,
    borderline={.6pt}{0pt}{black, dotted},
    shadow={1.8pt}{-.8pt}{0pt}{lightgray},
    }

\begin{document}
This is a solid box
\solidButton{Solid Button}
This is a solid box
This is a solid box
This is a solid box
This is a solid box
This is a solid box 

This is a dotted box 
\dottedButton{Dotted Button}
This is a dotted box
This is a dotted box
This is a dotted box
This is a dotted box

\end{document}

enter image description here

5
  • Correct me if I'm wrong, but doesn't tcolorbox use tikz under the hood? Nov 11, 2021 at 16:13
  • @AndrewStacey Yes, it does. In the documentation, they say: "The base package tcolorbox loads the packages pgf, verbatim, etoolbox, and environ." But I don't know if it uses only pgf and uses TikZ for some peculiar functionality. However, it seems to me that tcolorbox is usually speeder than TikZ, maybe it's only my feeling.
    – CarLaTeX
    Nov 11, 2021 at 16:23
  • Thanks for this. I timed it and was surprised to find it takes a little bit longer to use tcolorbox than plain TikZ. When I don't typeset anything but the button label, my job takes 26.6 seconds. When I use my TikZ implementation, it's 100.2 seconds. When I use tcolorbox it takes 149.7 seconds. Nov 13, 2021 at 2:23
  • @user3486184 So my sensation was wrong, sorry.
    – CarLaTeX
    Nov 13, 2021 at 7:24
  • @AndrewStacey See user3486184's comment ^^^
    – CarLaTeX
    Nov 13, 2021 at 7:26
2

This is more of an extended comment than an answer.

I can think of three ways to approach this.

  1. Simplify the path generation. You have a suggestion in another answer to use pgf code directly instead of tikz, but even that could be simplified further. You don't need the full structure of a node here, you just need to measure the hbox containing the text and use that to draw a path. Taking it to extremes, you could even have the code generate the PDF literals directly.

  2. Generate each path once and once only. There are a variety of ways to do this. At one extreme you could externalise each individual box (with its contents). At the other, you simply save each path once it has been created to the aux file (my spath3 library can do this), so you still have the expense of rendering it but none of the computation.

  3. Only render the boxes when you're done with the document. So you have a draft mode in place when you are writing the document in which the boxes aren't drawn, and only switch them on when the document itself is in place. This follows the TeX paradigm of separating style and content. It's probably the easiest solution and certainly if it is for only one document then simplicity is also a strong factor in this one.

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .