%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
% Documentation for the panda package
% A package to estimate the blackness of fonts
% Maintained by samcarter
%
% Project repository and bug tracker:
% https://github.com/samcarter/panda
%
% Released under the LaTeX Project Public License v1.3c or later
% See https://www.latex-project.org/lppl.txt
%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% arara: latexmk
\documentclass{scrartcl}

\usepackage[
  themecolor=samdgray
]{\jobname-settings}

% meta %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\title{The panda package}
\subtitle{Estimating the blackness of fonts}
\author{%
  \texorpdfstring{
   \begin{tikzpicture}
     \colorlet{black}{samdgray}
     \panda
   \end{tikzpicture}\\[0.8em]
   \texttt{samcarter}\\
   \url{https://github.com/samcarter/panda}\\
   \url{https://ctan.org/pkg/panda}
  }{samcarter}}
\date{Version v0.3 \textendash{} 2026/02/12}
\packagename{panda}

\usepackage{panda}
\usepackage{tikzlings}
\usepackage{tabularray}
\UseTblrLibrary{counter}
\newlength{\IHeight}
\NewTblrTheme{naked}{
  \DefTblrTemplate{foot}{empty}{}
  \SetTblrTemplate{foot}{empty}
  \DefTblrTemplate{head}{empty}{}
  \SetTblrTemplate{head}{empty}
}

\newcommand{\badbamboo}[2]{%
  \rule{#1}{#2}\,\rule{#1}{#2}\,\rule{#1}{#2}%
}

\newcommand{\cleverbamboo}{%
  \rule{ \CleverPanda{} }{ \fontcharht\font`I }%
}

\newcommand{\busybamboo}{%
  \rule{ \BusyPanda{0.125}\fontcharht\font`I }{ \fontcharht\font`I }%
}

\newcommand{\tikzbamboo}{%
  \setlength{\IHeight}{\fontcharht\font`I}%
  \CleverPandaSet
  \begin{tikzpicture}[baseline]
    \draw[line width=\CleverPandaGet{}] (0,0) -- (0,\IHeight);
  \end{tikzpicture}%
}

\begin{document}
\maketitle

\section{Introduction}
\label{intro}

Imagine the following problem:
there is a hungry panda sitting next to you and just today, you happen to not have any bamboo with you.
Now you are a \LaTeX\ user and the only limits are your own imagination, so of course you try to draw a couple of bamboo sticks with \LaTeX.
Here dear panda, have three bamboo sticks: \badbamboo{0.8pt}{\fontcharht\font`I}\,.
The panda likes them and wants to make more.
{\fontfamily{ugq}\selectfont However the panda uses a different font and the bamboo sticks suddenly look a lot less tasty, they look just too thin: \badbamboo{0.8pt}{\fontcharht\font`I}\,.}

The panda package provides estimates for the stroke width of fonts and how black the fonts look on the page.
Creators of symbols etc.\ can use these estimates to adjust their drawings to better blend in with the surrounding text.

{\fontfamily{ugq}\selectfont Yummy bamboo sticks: \busybamboo\,\busybamboo\,\busybamboo}

The results won't be a perfect match for all fonts and strokes can also look differently compared to the surrounding text depending on the PDF viewer and zoom level, but the results should be good enough to ensure that the symbols don't stand out immediately.

Note that this package was developed and tested with texts in Latin scripts -- results might be less satisfactory if used with other scripts.

\blurb

\section{Clever Panda}

The Clever Panda approach is inspired by a brilliant answer by @Skillmon\footnote{see \url{https://topanswers.xyz/tex?q=8139\#a7747}}, which uses the difference between the height of a full stop and 1\,pt to estimate a proxy for the stroke width of the current font.

The Clever Panda uses a simpler approach of directly employing a fraction of the full stop height and combines this with a manually curated list of exceptions.

Compared to the Busy Panda approach, described in the next section, this approach should be faster and isn't limited to what fonts are in a look-up table.

\subsection{Usage (simple case)}

After loading the package, the command \saminline|\CleverPanda{}| will expand to a length, which can be used as stroke width for drawing symbols etc.
\begin{tcolorbox}[title={Basic usage}]
\begin{samcode}
\documentclass{article}
\usepackage{CleverPanda}
\begin{document}
  cmr: \rule{ \CleverPanda{} }{8pt}

  \fontfamily{ugq}\selectfont
  ugq: \rule{ \CleverPanda{} }{8pt}
\end{document}
\end{samcode}
\tcblower
\raggedright
cmr: \rule{\CleverPanda{}}{8pt}

\fontfamily{ugq}\selectfont
ugq: \rule{\CleverPanda{}}{8pt}
\end{tcolorbox}

There are two options for customisation: the \saminline|scale=...| package option sets a default scaling value.
For example, \saminline|scale=10| means that \saminline|\CleverPanda{}| returns a length ten times as long as it normally would.
The scaling factor can also be set locally in the mandatory argument, e.g.\ \saminline|\CleverPanda{42}|.
\begin{tcolorbox}[title={Options}]
\begin{samcode}
\documentclass{article}
\usepackage[scale=10]{CleverPanda}
\begin{document}
  cmr: \rule{ \CleverPanda{} }{8pt}

  cmr: \rule{ \CleverPanda{42} }{8pt}
\end{document}
\end{samcode}
\tcblower
\raggedright
cmr: \rule{\CleverPanda{10}}{8pt}

cmr: \rule{\CleverPanda{42}}{8pt}
\end{tcolorbox}

\subsection{Special Case (\TikZ)}

The \saminline|\CleverPanda{}| command works well, as long as the height of the full stop is available when the macro is used.
Unfortunately, that's not always the case.

One notable problem case are \TikZ\ options, e.g.\ while setting line widths.
As a workaround, one can use the command \saminline|\CleverPandaSet| before the \TikZ\ picture and then \saminline|\CleverPandaGet{}| in the \TikZ\ options to access the stroke width:
\begin{tcolorbox}[title={\TikZ}]
\begin{samcode}
\documentclass{article}
\usepackage{CleverPanda}
\usepackage{tikz}
\newlength{\IHeight}

\newcommand{\tikzbamboo}{%
  \setlength{\IHeight}{\fontcharht\font`I}%
  \CleverPandaSet
  \begin{tikzpicture}[baseline]
    \draw[line width=\CleverPandaGet{}]
         (0,0) -- (0,\IHeight);
  \end{tikzpicture}%
}

\begin{document}
  cmr:  \tikzbamboo

  \fontfamily{ugq}\selectfont
  ugq: \tikzbamboo
\end{document}
\end{samcode}
\tcblower
\raggedright
cmr:  \tikzbamboo

\fontfamily{ugq}\selectfont
ugq: \tikzbamboo
\end{tcolorbox}

\clearpage
\section{Busy Panda}

The approach of the Busy Panda is different.
To get an estimate of how black text in a particular font looks like, the Busy Panda uses a sample PDF with:
\begin{center}
\ttfamily Lorem ipsum dolor sit amet, consectetur adip
\end{center}
After converting the PDF into a pixel graphic, the average blackness of the pixels can be calculated.
After repeating this for different fonts (including their bold versions), the Busy Panda now contains a look-up table with the average blackness for about 8800 fonts, normalised to the average blackness of Computer Modern Roman.
Tests with different sample texts and image resolutions indicate that the error of this method is about 10\%.
While not perfect, this is usually accurate enough to make symbols blend in with the surrounding text.

Compared to the Clever Panda approach from the previous section, this approach gives more accurate results, in particular for very heavy fonts, and is more versatile to use.

\subsection{Usage}

The basic usage is simple: after loading the package, the command \saminline|\BusyPanda{}| provides a normalised estimate of the blackness of the current font:
\begin{tcolorbox}[title={Basic usage}]
\begin{samcode}
\documentclass{article}
\usepackage{BusyPanda}
\begin{document}
  cmr: \BusyPanda{}

  \fontfamily{ugq}\selectfont
  ugq: \BusyPanda{}
\end{document}
\end{samcode}
\tcblower
\raggedright
cmr: \BusyPanda{}

\fontfamily{ugq}\selectfont
ugq: \BusyPanda{}
\end{tcolorbox}

There are two options for customisation: the \saminline|scale=...| package option sets a default scaling value.
For example, \saminline|scale=10| means that \saminline|\BusyPanda{}| no longer returns 1 for Computer Modern Roman, but 10.
The scaling factor can also be set locally in the mandatory argument, e.g.\ \saminline|\BusyPanda{42}|.
\begin{tcolorbox}[title={Options}]
\begin{samcode}
\documentclass{article}
\usepackage[scale=10]{BusyPanda}
\begin{document}
  cmr: \BusyPanda{}

  cmr: \BusyPanda{42}
\end{document}
\end{samcode}
\tcblower
\raggedright
cmr: \BusyPanda{10}

cmr: \BusyPanda{42}
\end{tcolorbox}

\subsection{Creating symbols}

There is still a hungry panda sitting next to you, so let's create some extra yummy bamboo sticks:
\begin{tcolorbox}[title={Feeding the panda}]
\begin{samcode}
\documentclass{article}
\usepackage{BusyPanda}
\newlength{\IHeight}

\newcommand{\bamboo}{%
  \setlength{\IHeight}{\fontcharht\font`I}%
  \rule{%
    \BusyPanda{0.125}\IHeight
  }{%
    \IHeight
  }%
}

\begin{document}
Normal bamboo: \bamboo

\fontfamily{ugq}\selectfont
Extra yummy bamboo: \bamboo
\end{document}
\end{samcode}
\tcblower
\raggedright
Normal bamboo: \busybamboo

\fontfamily{ugq}\selectfont
Extra yummy bamboo: \busybamboo
\end{tcolorbox}

In the example above, \saminline|\BusyPanda{}| is used in combination with the height of the letter I.
This ensures that the stroke width will scale with the size of the text.

\subsection{Unsupported fonts}

Given the seemingly infinite number of fonts out there, this package will never be able to support them all -- but more can be added with your help!

If the font you are using isn't yet supported, then please get the test document from:

\url{https://github.com/samcarter/panda/blob/main/BusyPanda_unsupportedFonts.tex}

You only need to change the font name in the second line and compile with either \saminline|pdflatex| or \saminline|lualatex|.
Then open an issue at \url{https://github.com/samcarter/panda/issues} and upload the compiled PDF there.

How to know if your font is unsupported? \saminline|\BusyPanda{}| will return the same value as for Computer Modern Roman (1) and, if compiled with \LuaLaTeX\, a warning can be found in the .log file. Furthermore, the package option \saminline|unknown error| can be used to enable an error for all engines.

\clearpage
\section{Examples}

\ExplSyntaxOn

\clist_const:Nn \c_fonts_clist {
%<*fontlist>
AccanthisADFStdNoThree-LF, Alegreya-OsF, Alegreya-TLF, AnonymousPro, antp, antt, anttc, anttl, anttlc, artemisia, augie, auncl, bch, bodoni, Cabin-TLF, Caladea-TLF, cantarell-OsF, ccr, Cinzel-LF, CinzelDecorative-LF, ClearSans-TLF, clm, clmd, clmqs, clms, clmt, clmv, cmbr, cmdh, cmin, cmr, cmss, cmtt, cmvtt, comfortaa, Crlt-TLF, cyklop, DejaVuSans-TLF, DejaVuSansMono-TLF, DejaVuSerif-TLF, droidsans, droidsansmono, droidserif, EBGaramond-OsF, erewhon-OsF, fav, fbb-LF, FiraSans-TLF, frc, fve, fvm, fvs, GilliusADF-LF, GilliusADFNoTwo-LF, Heuristica-TLF, iwona, iwonac, iwonal, iwonalc, jkp, jkp, jkpl, jkpss, jkpss, jkptt, jkpx, kurier, kurierc, kurierl, kurierlc, lato-OsF, Lbstr-LF, LibreBskvl-LF, LibreCsln-TLF, LinuxBiolinumT-OsF, LinuxLibertineDisplayT-OsF, LinuxLibertineMonoT-TLF, LinuxLibertineT-OsF, lmr, lmss, lmtt, lmvtt, Merriwthr-OsF, MerriwthrSans-TLF, MintSpirit-LF, nanumgt, nanummj, neohellenic, opensans-TLF, Ovrlck-LF, Ovrlck-LF, pag, pbk, pcr, phv, PlyfrDisplay-OsF, pnc, ppl, ptm, PTMono-TLF, PTSans-TLF, PTSansCaption-TLF, PTSansNarrow-TLF, PTSerif-TLF, PTSerifCaption-TLF, put, pzc, qag, qbk, qcr, qcs, qhv, qhvc, qpl, qtm, Quattro-LF, QuattroSans-LF, Roboto-TLF, RobotoSlab-TLF, SourceCodePro-TLF, SourceSansPro-TLF, sqrc, stix, ua1, uaq, udidot, ugm, ugq, ul9, ulg, UniversalisADFStd-LF, ybd, ybv, yes, yfrak, yly, yrd, yv1, yv1d, yv2, yv3, yvo, yvod, yvt, yvtd, zgmx, zi4%
%</fontlist>
}

% from https://tex.stackexchange.com/a/728458/36296
\tl_new:N \l_panda_tblr_spec_tl
\cs_new:Npn \__panda_tblr_gen:n #1 { \clist_item:Nn \c_fonts_clist { #1 }  &  & \\}
\tl_set:Ne \l_panda_tblr_spec_tl { \int_step_function:nN {143} \__panda_tblr_gen:n }
\SetTblrOuter[longtblr]{expand=\l_panda_tblr_spec_tl}
\begin{longtblr}[theme=naked]{
  colspec={@{}Xll@{}},
  hline{1,2,Z}={0.7pt},
  row{2-Z}={font=\fontfamily{\clist_item:Nn \c_fonts_clist {\therownum-1}}\selectfont},
  cell{2-Z}{2}={cmd={Lorem~\cleverbamboo\space Lipsum}},
  cell{2-Z}{3}={cmd={Lorem~\busybamboo\space Lipsum}},
  rowhead = 1
}
  Font~Family & Clever~Panda & Busy~Panda\\
  \l_panda_tblr_spec_tl
\end{longtblr}

\ExplSyntaxOff

\end{document}
