Meander:0.2.2 -- Wrapping text around images

Meander allows you to wrap content around images. It supports images of arbitrary shape, and is therefore currently the best Typst alternative to both LaTeX’s parshape and wrapfig.

It’s as easy as:

#let my-img-1 = box(width: 7cm, height: 7cm, fill: orange)
#let my-img-2 = box(width: 5cm, height: 3cm, fill: blue)
#let my-img-3 = box(width: 8cm, height: 4cm, fill: green)
#let my-img-4 = box(width: 5cm, height: 5cm, fill: red)
#let my-img-5 = box(width: 4cm, height: 3cm, fill: yellow)

#import "@preview/meander:0.2.2"

#meander.reflow({
  import meander: *

  // Place as many obstacles as you want.
  placed(top + left, my-img-1)
  placed(top + right, my-img-2)
  placed(horizon + right, my-img-3)
  placed(bottom + left, my-img-4)
  placed(bottom + left, dx: 32%, my-img-5)

  // The container wraps around all.
  container()

  // The content is automatically threaded through
  // the segmented container.
  content[
    #set par(justify: true)
    #lorem(430)
  ]
})

As of version 0.2.2 released today, here are some examples of outputs that Meander allows you to achieve relatively easily:


(Requested in: [1], [2]; realized in examples/5181-a)


(Requested in: [1], [2]; realized in examples/5181-b)


(Inspired by: [3]; realized in examples/area-of-a-circle)


(Requested in: [2]; realized in examples/talmudifier)


(Requested in: [1]; realized in examples/cow)

You can find more details in the documentation, and you’re welcome to open a new issue if your use-case is not supported or not convenient.

16 Likes

I will say that I assumed (from the first straightforward example) it will auto-wrap the circle, but apparently you need to add this lengthy boundary:

    boundary: contour.grid(div: 40,
      (x, y) => calc.pow(2 * x - 1, 2) + calc.pow(2 * y - 1, 2) <= 1,
    ),

Plus probably contour.margin(2pt). At least maybe make a shortcut function for circles/ellipses? This will be a huge UX improvement.

I also tried adding text after the reflow. It gets put in the same place where reflow placed text. Considering that too much text in the reflow gives a warning, how am I supposed to write multipage documents? After reading a bit of docs, you either add manual containers, and keep track of how many you need, which is not convenient at all, or you can use placement: box on the reflow. Still, if one-page text spills on the next, you have to add a container call to remove the warning.

It’s definitely much more involved that I thought, I also thought that I maybe can have a global show rule that auto-magically finds all the placed stuff and wraps content, but I guess it will be too hard to handle.

It’s nice that the docs is actually insanely big. There is like 19 pages + 24 pages of API reference. I think it should cover the questions one can have, but as always the problem is that no one usually RTFM, let alone a big one. But maybe a search feature can help, if you know what to search for.

There is a mention and reused stuff from wrap-it in docs/source, but I expected it to be mentioned here as well. And after reading its docs, I guess it can’t handle non-rectangle objects? I thought it could, so the claim of being the best immediately caused skepticism. Well, it probably is the best right now, judging from the limited knowledge.

I think it gives the same feeling of approachability (hard to approach) as Touying, but it’s still probably much easier to get used to/set up than that Touying, they are very different after all.

P.S. Started as a rant, but that’s because of deceiving first impression and I didn’t RTFM.

P.S. Started as a rant, but that’s because of deceiving first impression and I didn’t RTFM.

That’s fine, I appreciate the feedback anyway :)

I assumed (from the first straightforward example) it will auto-wrap the circle

That’s… basically impossible. measure outputs a rectangle, so rectangular obstacles are all meander can handle. The innovation compared to wrap-it is that since meander can support arbitrarily many obstacles, we can approximate a non-rectangular obstacle with many rectangles, and that’s what all the contour functions are helpers for. There are definitely plans to make additional utils for contour, I just haven’t come around to it yet.

I also tried adding text after the reflow. It gets put in the same place where reflow placed text.

There’s a few solutions here.
If the reflow takes the whole page and you want the overflow on the next page without having to add containers, add overflow: pagebreak:

#import "@preview/meander:0.2.2"

#meander.reflow(
  overflow: pagebreak, {
  import meander: *
  container(width: 50% - 3mm, margin: 6mm, style: (text-fill: red))
  container(style: (text-fill: red))
  content[#lorem(1000)]
})

Output:


(the overflow is in black)

If the reflow is only part of the page, add placement: box and overflow: text.

#import "@preview/meander:0.2.2"

#lorem(200)
#meander.reflow(
  placement: box,
  overflow: text, {
  import meander: *
  container(width: 50% - 3mm, margin: 6mm, style: (text-fill: red), height: 10cm)
  container(style: (text-fill: red), height: 10cm)
  content[#lorem(400)]
})

Output:

There is a mention and reused stuff from wrap-it in docs/source

I wouldn’t say any code is reused from wrap-it. What actually happened is that I looked at wrap-it’s implementation to understand the internal representation of content, which prior to this I had no idea how it actually worked.

as always the problem is that no one usually RTFM

You’re right… I’ll work on a cheat sheet.

1 Like

Huh? But you use placed to wrap circle, can’t you just check the content and handle circle separately? You can check its properties and auto-apply contour. Well, I don’t know how useful it is to place random shapes in your document, but if it’s for booklets and stuff where you can potentially see shapes in text, it would be cool.

Right, I think it was an “inspiration”.

I mean, don’t you already have it in the manual? I saw some sections that are pretty short and about a specific use case, I think. Well, I guess you can add a FAQ or something, with a common problem and short solution, not sure. The readme is already not short, maybe some things can be added there.

can’t you just check the content and handle circle separately?

It would be pretty silly to handle

placed(...)[#circle(...)]

but not

placed(...)[
  #circle(...)
]

or

placed(...)[#scale(150%)[#circle(...)]]

or

placed(...)[#rotate(90deg)[#circle(...)]]

I’d have to reimplement the entire Typst compiler to know the shape of any object that gets placed. In short I have no plans to implement automatic detection of the boundaries of an object, but I can absolutely improve support for making those boundaries easier to define manually or allow interfacing with other tools that would compute those boundaries.

3 Likes