Universe

meander provides a core function reflow to segment pages and wrap content around images.

See the documentation.

Quick start

The function meander.reflow splits the input sequence into

  • obstacles: all objects created via the function placed (which is like place),
  • containers: produced by container, optionally specifying an alignment and dimensions,
  • flowing content: produced by content,
  • optionally pagebreaks so that the layout can cover multiple consecutive pages.
#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.4"

#meander.reflow({
  import meander: *

  // 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()
  content[
    #set par(justify: true)
    #lorem(430)
  ]
})

a page where text flows between 5 rectangular obstacles


Use multiple containers to produce layouts in columns.

#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)

#import "@preview/meander:0.2.4"

#meander.reflow({
  import meander: *

  placed(bottom + right, my-img-1)
  placed(center + horizon, my-img-2)
  placed(top + right, my-img-3)

  // With two containers we can
  // emulate two columns.

  // The first container takes 60%
  // of the page width.
  container(width: 60%, margin: 5mm)
  // The second container automatically
  // fills the remaining space.
  container()

  content[#lorem(470)]
})

a two-column page with 3 obstacles


If your Meander environment shares page(s) with other content, use the option placement: box. The overflow parameter determines what happens to text that doesn’t fit inside the provided containers.

You can see this in effect in the example below:

  • the text in green is before the Meander environment
  • the text in blue is the Meander environment itself
  • the text in black is the overflow handled by Meander
  • the text in red is after the meander environment
#import "@preview/meander:0.2.4"
#set par(justify: true)

#text(fill: green)[
  #lorem(200)
]

#meander.reflow(
  placement: box,
  overflow: text,
{
  import meander: *
  container(
    width: 48%,
    height: 50%,
    style: (text-fill: blue),
  )
  container(
    width: 48%,
    height: 50%,
    align: right,
    style: (text-fill: blue),
  )

  content[#lorem(700)]
})

#text(fill: red)[
  #lorem(200)
]

Content that overflows the environment (page 1/2) Content that overflows the environment (page 2/2)


Meander allows precise control over the boundaries of obstacles, to draw complex paragraph shapes.

#import "@preview/meander:0.2.4"

#meander.reflow({
  import meander: *

  placed(
    center + horizon,
    boundary:
      // Override the default margin
      contour.margin(1cm) +
      // Then redraw the shape as a grid
      contour.grid(
        // 25 vertical and horizontal subdivisions.
        // Just pick a number that looks good.
        // A good rule of thumb is to start with obstacles
        // about as high as one line of text.
        div: 25,
        // Equation for a circle of center (0.5, 0.5) and radius 0.5
        (x, y) => calc.pow(2 * x - 1, 2) + calc.pow(2 * y - 1, 2) <= 1
      ),
    // Underlying object
    circle(radius: 3cm, fill: yellow),
  )

  container(width: 48%)
  container(align: right, width: 48%)
  content[
    #set par(justify: true)
    #lorem(570)
  ]
})

text with a circular cutout


For a more in-depth introduction, including

  • debug mode,
  • alternative recontouring techniques,
  • styling options,
  • multi-page handling and page overflow options,
  • tips to get better segmentation,

please consult the documentation.