DEV Community

Vehbi Sinan Tunalioglu
Vehbi Sinan Tunalioglu

Posted on • Originally published at thenegation.com

More Haskell Diagrams: Wrapping Text

Working with text, especially wrapping it, can be tricky when generating images with Haskell's diagrams library. In this blog post, we will write a literate Haskell program to generate an image with text that fits in a box and wraps if we want so.

Problem

Since I am getting more invested in diagrams, I want to be equipped with additional superpowers to deal with text.

Working with text in diagrams is not as straightforward as I thought. Indeed, the primary function to work with text is text and its documentation says:

Note that it takes up no space, as text size information is not available.

Bummer! This means we cannot calculate the size of the text to wrap it in a limited area.

Solution

It seems that there is a nice library to work with text: SVGFonts. It advertised itself as "Native font support for the diagrams framework". Also do not let the name fool you:

Note that this package can be used with any diagrams backend, not just the SVG backend. The SVG-font format is easy to parse and was therefore chosen for a font library completely written in Haskell.

Program

We will use the 4 Haskell packages for this Literate Haskell program: diagrams, diagrams-cairo, SVGFonts and markdown-unlit.

Let's import our modules of interest:

import Diagrams.Backend.Cairo
import Diagrams.Prelude
import qualified Graphics.SVGFonts as F
import qualified Graphics.SVGFonts.Wrap as F.Wrap
import Data.Maybe (fromMaybe)
import System.Environment (getArgs)
Enter fullscreen mode Exit fullscreen mode

We will render a few diagrams with our program. Let's implement our entry point what will render these diagrams:

main :: IO ()
main = do
  dir <- head <$> getArgs
  render dir "diagram1.png" diagram1
  render dir "diagram2.png" diagram2
  render dir "diagram3.png" diagram3
  render dir "diagram4.png" diagram4
  where
    render dpath fname = renderCairo (dpath <> "/" <> fname) (mkSizeSpec2D (Just 800) Nothing)
Enter fullscreen mode Exit fullscreen mode

This is how we will run our blog post:

runhaskell \
  -pgmLmarkdown-unlit \
  content/posts/2024-08-13_haskell-diagrams-text.lhs \
  static/assets/media/posts/haskell-diagrams-text
Enter fullscreen mode Exit fullscreen mode

We need an inspiring text to work with:

nice :: String
nice = "Any application that can be written in JavaScript, will eventually be written in JavaScript."
Enter fullscreen mode Exit fullscreen mode

OK, let's put it out there, on a rectable of size 16x9:

diagram1 :: Diagram B
diagram1 =
  (text nice) `atop` (rect 16 9 # bg white)
Enter fullscreen mode Exit fullscreen mode

Let's fit it in a box of size 16x9:

diagram2 :: Diagram B
diagram2 =
  (text nice # fontSizeL 0.25) `atop` (rect 16 9 # bg white)
Enter fullscreen mode Exit fullscreen mode

We can not afford playing with the font size everytime. How about giving a shot to the fit_width function from SVGFonts?

diagram3 :: Diagram B
diagram3 =
  (nice # F.svgText def # F.fit_width 16 # F.set_envelope # fc black # lw none # center) `atop` (rect 16 9 # bg white)
Enter fullscreen mode Exit fullscreen mode

Yes, there is a lot more ceremony. But knowing the canvas' width is enough for us.

Ok, let's wrap it. This is going to be tricky, if not too difficult:

diagram4 :: Diagram B
diagram4 =
  bgFrame 0 white $ vsep 0.2 (fmap putTxt (fromMaybe ["what happened?"] mLines))
  where
    mLines = F.Wrap.wrapText def 1 [(F.Wrap.splitAtSpaces, (14 :: Double, 16))] nice
    putTxt = lw none . fc black . F.set_envelope . F.svgText def
Enter fullscreen mode Exit fullscreen mode

Wrap-Up

SVGFonts is quite interesting. For my purpose here, i.e. wrapping text, the magic function is wrapText. It takes text options, height, split strategies and a text. I am having difficulties in understanding the split strategies. I will need to dig deeper. But, if the split boundaries are wide enough, it will wrap the text. Since I am not re-implementing LaTeX, but generating completely unnecessary images, I am confident that I can work with this SVGFonts library.

Image of Datadog

The Future of AI, LLMs, and Observability on Google Cloud

Datadog sat down with Google’s Director of AI to discuss the current and future states of AI, ML, and LLMs on Google Cloud. Discover 7 key insights for technical leaders, covering everything from upskilling teams to observability best practices

Learn More

Top comments (0)

A Workflow Copilot. Tailored to You.

Pieces.app image

Our desktop app, with its intelligent copilot, streamlines coding by generating snippets, extracting code from screenshots, and accelerating problem-solving.

Read the docs

👋 Kindness is contagious

Please leave a ❤️ or a friendly comment on this post if you found it helpful!

Okay