2

I would like to dynamically calculate the width of a tcolorbox. Is this possible using pgfmath?

This is what I have so far:

\documentclass{article}
\usepackage{tikz}
\usepackage{tcolorbox}

\begin{document}
\pgfmathsetlengthmacro{\y}{%
    floor(\linewidth / \baselineskip) * \baselineskip} 
y: \y

\begin{tcolorbox}[width={\pgfmathsetlengthmacro{\x}{%
    floor(\linewidth / \baselineskip) * \baselineskip} \x}]
This is my box.
\end{tcolorbox}

\end{document}

The problem is that it produces the error message: ! Missing number, treated as zero.. I guess the macro does not actually get evaluated before the width option looks for a number. Is there a way to achieve such a thing?

FlorianL
  • 1,659

3 Answers3

2

\pgfsetmathlengthmacro is a command that defines ('sets') another macro and can't be expandable this way, so width={\pgfsethmathlengthmacro{...}}\x will not work here.

It is possible to 'exploit' the code=... option however and use width=\x then.

I've shown the value of \kvtcb@width as well, in order to use this macro, \makeatletter...\makeatother is necessary:

\documentclass{article}
\usepackage{tikz}
\usepackage{tcolorbox}

\begin{document}
\pgfmathsetlengthmacro{\y}{%
    floor(\linewidth / \baselineskip) * \baselineskip} 
y: \y


\makeatletter
\begin{tcolorbox}[code={\pgfmathsetlengthmacro{\x}{%
    floor(\linewidth / \baselineskip) * \baselineskip}},width=\x]
This is my box and it has the width \kvtcb@width
\end{tcolorbox}

\begin{tcolorbox}[code={\pgfmathsetlengthmacro{\x}{%
    floor(\linewidth / \baselineskip) * \baselineskip}},width=336pt]
This is my box and it has the width \kvtcb@width
\end{tcolorbox}

\makeatother

\end{document}

enter image description here

2
\documentclass{article}
\usepackage{tcolorbox,expl3}
\ExplSyntaxOn\let\calcnum\fp_to_decimal:n\ExplSyntaxOff
\makeatletter\let\stripPT\strip@pt\makeatother
\def\getWidth{\calcnum {floor(\stripPT\linewidth/% 
      \stripPT\baselineskip)*\stripPT\baselineskip}pt}
\begin{document}

\getWidth

\begin{tcolorbox}[width=\getWidth]
This is my box and it has the internal linewidth \the\linewidth
\end{tcolorbox}

\begin{tcolorbox}[width=336pt]
This is my box and it has the internal linewidth \the\linewidth
\end{tcolorbox}

\end{document}

enter image description here

1

You can do this thanks to e-TeX \dimexpr/\numexpr. Unfortunately, division in \numexpr rounds so the formula is a bit complicated for floor function. (see Why does \numexpr integer division round rather than truncate?)

\documentclass[a4paper]{article}
\usepackage{tikz}
\usepackage{tcolorbox}

\begin{document}

\pgfmathsetlengthmacro{\y}{%
    floor(\linewidth / \baselineskip) * \baselineskip} 

y: \y

\the\dimexpr\numexpr(\linewidth+\baselineskip/2)/\baselineskip-1\relax\baselineskip\relax


linewidth: \the\linewidth

baselineskip: \the\baselineskip

\hrule

\begin{tcolorbox}[width={\dimexpr\numexpr(\linewidth+\baselineskip/2)/\baselineskip-1\relax\baselineskip\relax}]
This is my box and it has width \the\dimexpr\csname kvtcb@width\endcsname.

linewidth: \the\linewidth

baselineskip: \the\baselineskip

floor(linewidth/baselineskip): \the\numexpr(\linewidth+\baselineskip/2)/\baselineskip-1\relax

width: \the\dimexpr\numexpr(\linewidth+\baselineskip/2)/\baselineskip-1\relax\baselineskip\relax

\hrule

\end{tcolorbox}

\end{document}

enter image description here

This all being said, it is a little strange to understand the meaning of the ratio linewidth/baselineskip, and why you would want a width to be an integral multiple of baselineskip ???