Embedding terminals in LaTeX

3 min

Recently I’ve written about embedding terminals on the web. This is the equivalent post for embedding colorful terminal output in LaTeX.

And we’re going to achieve this by exporting our terminal as a PDF!

PDFs

“Why a PDF in the first place?”, you might ask.

Well… the answer is that LaTeX has no way of parsing the ANSI codes that tell your Terminal what colors to use1. This means that LaTeX cannot render the terminal itself2. So usually when embedding terminals we just use screenshots, but these have multiple drawbacks compared to PDFs:

  • They are more annoying to capture if your terminal doesn’t allow exporting them.
  • They are limited in resolution
  • The text is not selectable, since it’s not really text.
  • They are usually larger than PDFs

Essentially PDFs are in every way better than screenshots and seem to be the best solution that I could find to embed terminals in LaTeX.

In practice a document defined like this:

\documentclass{scrartcl}
\usepackage{minted}
\usepackage{graphicx}
\begin{document}
What follows is a beautiful terminal embed:
\begin{listing}[!ht]
\centering
\includegraphics[width=1\linewidth]{neofetch.pdf}
\caption{The neofetch command, ran in a terminal}
\label{lst:neofetch}
\end{listing}
\end{document}

Will render this document:

Accurate colors and selectable text! Now the only thing we have to do is reduce the friction, meaning the PDF exporting workflow, so much that it is easier than taking a screenshot.

PDF-Kitten

In this part I would originally talk about the shell-script I’ve written which takes HTML produced by Konsole and converts it into a nice embeddable PDF.

But while tinkering with Kitty (an extensible terminal emulator) for the web terminals post, I’ve learned about its capabilities and thought I can do one better. So I created a kitten (an extension for kitty) that allows you to export a clean PDF from your terminal output.

Here is what it does:

  1. It copies your terminal selection including the ANSI codes.
  2. It then opens that in the editor of your choice so you can make edits.
    This is useful for things like redacting information.
  3. It converts the edited output into a nicely cropped PDF.
  4. It finally opens that PDF with your default application.

I named this little extension “PDF-Kitten”. Pretty creative, I know 🐱.

What this looks like

I made a screen recording that shows what this work-flow looks like for me in practice.

In this case my configured editor is micro, and my PDF viewer is Okular. You can change this to whatever you want though.

How this works

TIP

If you don’t care about the how, check out the Git repository to try it for yourself.

Kitty kittens are really just Python scripts. When I press the key F11, which I mapped to PDF-Kitten a Python script is called.

  1. It uses the Kitty API to get your current selection including the ANSI codes.
  2. It cleans the OSCs that gave me grief in the web terminal post.
  3. It uses the Kitty API to open the output in the configured editor.
  4. When the editor closes it converts the output to HTML using ansi2html.
  5. It fetches Kitty’s currently configured colors and font and applies them to the generated HTML.
  6. It uses weasyprint to produce a PDF from the HTML.
  7. It crops that PDF to the height of the output using pdfCropMargins.
    The width is fixed using full-width embedding in LaTeX produces consistent results.
  8. Finally, it uses the Kitty API to open the cropped PDF.

It’s honestly really impressive how extensible Kitty is. It even provides us with a lot of helper APIs to interact with the OS.

And also as someone who kind of disliked Python for its nightmarish package management3 and its indentation model I found a new appreciation for it. There really is a Python library for everything.

Installation

If you want to now try PDF-Kitten you can check out its Git repository on GitHub. The installation and usage is explained in the README.

Footnotes

  1. My post on web-terminals goes into a lot more detail about ANSI codes and how they work.

  2. LuaLaTeX technically can, but that way you would be restricted to use that compiler and that compiler only.

  3. These days this problem is largely solved; All hail uv!