Skip to content

Commit aed9b73

Browse files
authored
meander:0.3.1 (#3713)
1 parent 143ada0 commit aed9b73

File tree

24 files changed

+2733
-0
lines changed

24 files changed

+2733
-0
lines changed
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
MIT License
2+
3+
Copyright (c) 2025 Neven Villani
4+
5+
Permission is hereby granted, free of charge, to any person obtaining a copy
6+
of this software and associated documentation files (the "Software"), to deal
7+
in the Software without restriction, including without limitation the rights
8+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
copies of the Software, and to permit persons to whom the Software is
10+
furnished to do so, subject to the following conditions:
11+
12+
The above copyright notice and this permission notice shall be included in all
13+
copies or substantial portions of the Software.
14+
15+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21+
SOFTWARE.
Lines changed: 193 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,193 @@
1+
# Meander
2+
3+
`meander` provides a core function `reflow` to segment pages and wrap content around images.
4+
5+
<!-- @scrybe(not version; panic Please specify a version number) -->
6+
<!-- @scrybe(if publish; grep https; grep {{version}}) -->
7+
See the [documentation](https://github.com/Vanille-N/meander.typ/releases/download/v0.3.1/docs.pdf).
8+
9+
## Quick start
10+
11+
The function `meander.reflow` takes a sequence of
12+
- obstacles: use `placed` to put content at specific positions of the page,
13+
- containers: use `container` to specify where text can be laid out,
14+
- flowing content: provide text with `content`.
15+
- optionally `pagebreak`, `colbreak`, `colfill` can be used to produce multi-page
16+
layouts and fine-tune which text goes into which container.
17+
18+
<!-- @scrybe(not publish; jump import; grep local; grep {{version}}) -->
19+
<!-- @scrybe(if publish; jump import; grep preview; grep {{version}}) -->
20+
<!-- @scrybe(jump let; until ```; diff tests/gallery/multi-obstacles/test.typ) -->
21+
```typ
22+
#let my-img-1 = box(width: 7cm, height: 7cm, fill: orange)
23+
#let my-img-2 = box(width: 5cm, height: 3cm, fill: blue)
24+
#let my-img-3 = box(width: 8cm, height: 4cm, fill: green)
25+
#let my-img-4 = box(width: 5cm, height: 5cm, fill: red)
26+
#let my-img-5 = box(width: 4cm, height: 3cm, fill: yellow)
27+
28+
#import "@preview/meander:0.3.1"
29+
30+
#meander.reflow({
31+
import meander: *
32+
33+
// As many obstacles as you want
34+
placed(top + left, my-img-1)
35+
placed(top + right, my-img-2)
36+
placed(horizon + right, my-img-3)
37+
placed(bottom + left, my-img-4)
38+
placed(bottom + left, dx: 32%, my-img-5)
39+
40+
// The container wraps around all
41+
container()
42+
content[
43+
#set par(justify: true)
44+
#lorem(430)
45+
]
46+
})
47+
```
48+
![a page where text flows between 5 rectangular obstacles](tests/gallery/multi-obstacles/ref/1.png)
49+
50+
-----
51+
52+
Use multiple `container`s to produce layouts in columns.
53+
54+
<!-- @scrybe(not publish; jump import; grep local; grep {{version}}) -->
55+
<!-- @scrybe(if publish; jump import; grep preview; grep {{version}}) -->
56+
<!-- @scrybe(jump let; until ```; diff tests/gallery/two-columns/test.typ) -->
57+
```typ
58+
#let my-img-1 = box(width: 7cm, height: 7cm, fill: orange)
59+
#let my-img-2 = box(width: 5cm, height: 3cm, fill: blue)
60+
#let my-img-3 = box(width: 8cm, height: 4cm, fill: green)
61+
62+
#import "@preview/meander:0.3.1"
63+
64+
#meander.reflow({
65+
import meander: *
66+
67+
placed(bottom + right, my-img-1)
68+
placed(center + horizon, my-img-2)
69+
placed(top + right, my-img-3)
70+
71+
// With two containers we can
72+
// emulate two columns.
73+
74+
// The first container takes 60%
75+
// of the page width.
76+
container(width: 60%, margin: 5mm)
77+
// The second container automatically
78+
// fills the remaining space.
79+
container()
80+
81+
content[#lorem(470)]
82+
})
83+
```
84+
![a two-column page with 3 obstacles](tests/gallery/two-columns/ref/1.png)
85+
86+
------
87+
88+
If your Meander environment shares page(s) with other content,
89+
use the option `placement: box`.
90+
The `overflow` parameter determines what happens to text that doesn't
91+
fit inside the provided containers.
92+
93+
You can see this in effect in the example below:
94+
- the text in red is outside of the Meander environment
95+
- the text in blue is the Meander environment itself
96+
- the text in black is the overflow handled by Meander
97+
<!-- @scrybe(not publish; jump import; grep local; grep {{version}}) -->
98+
<!-- @scrybe(not publish; jump import; grep local; grep {{version}}) -->
99+
<!-- @scrybe(jump import; until ```; diff tests/gallery/placement/test.typ) -->
100+
```typ
101+
#import "@preview/meander:0.3.1"
102+
#set par(justify: true)
103+
104+
#text(fill: red)[#lorem(200)]
105+
106+
#meander.reflow({
107+
import meander: *
108+
// Gets rid of the paragraph break between
109+
// the columns and the overflow.
110+
opt.placement.spacing(below: 0.65em)
111+
112+
// This turns on some debugging information,
113+
// specifically showing the boundaries
114+
// of the boxes in green.
115+
opt.debug.post-thread()
116+
117+
container(
118+
width: 48%,height: 50%,
119+
style: (text-fill: blue),
120+
)
121+
container(
122+
width: 48%, height: 50%, align: right,
123+
style: (text-fill: blue),
124+
)
125+
126+
content[#lorem(700)]
127+
128+
// This applies a style to the text
129+
// that overflows the layout.
130+
opt.overflow.custom(data => {
131+
set text(fill: orange)
132+
data.styled
133+
})
134+
})
135+
136+
#text(fill: red)[#lorem(200)]
137+
```
138+
![Content that overflows the environment (page 1/2)](tests/gallery/placement/ref/1.png)
139+
![Content that overflows the environment (page 2/2)](tests/gallery/placement/ref/2.png)
140+
141+
------
142+
143+
Meander allows precise control over the boundaries of obstacles, to draw complex paragraph shapes.
144+
145+
<!-- @scrybe(not publish; jump import; grep local; grep {{version}}) -->
146+
<!-- @scrybe(if publish; jump import; grep preview; grep {{version}}) -->
147+
<!-- @scrybe(jump import; until ```; diff tests/gallery/circle-hole/test.typ) -->
148+
```typ
149+
#import "@preview/meander:0.3.1"
150+
151+
#meander.reflow({
152+
import meander: *
153+
154+
placed(
155+
center + horizon,
156+
boundary:
157+
// Override the default margin
158+
contour.margin(1cm) +
159+
// Then redraw the shape as a grid
160+
contour.grid(
161+
// 25 vertical and horizontal subdivisions.
162+
// Just pick a number that looks good.
163+
// A good rule of thumb is to start with obstacles
164+
// about as high as one line of text.
165+
div: 25,
166+
// Equation for a circle of center (0.5, 0.5) and radius 0.5
167+
(x, y) => calc.pow(2 * x - 1, 2) + calc.pow(2 * y - 1, 2) <= 1
168+
),
169+
// Underlying object
170+
circle(radius: 3cm, fill: yellow),
171+
)
172+
173+
container(width: 48%)
174+
container(align: right, width: 48%)
175+
content[
176+
#set par(justify: true)
177+
#lorem(570)
178+
]
179+
})
180+
```
181+
![text with a circular cutout](tests/gallery/circle-hole/ref/1.png)
182+
183+
------
184+
185+
For a more in-depth introduction, including
186+
- alternative recontouring techniques,
187+
- styling options,
188+
- advanced multi-page layouts,
189+
- control over content that overflows,
190+
- tips to get better segmentation,
191+
<!-- @scrybe(if publish; grep https; grep {{version}}) -->
192+
please consult the [documentation](https://github.com/Vanille-N/meander.typ/releases/download/v0.3.1/docs.pdf).
193+

0 commit comments

Comments
 (0)