Skip to content

Commit a25d405

Browse files
committed
content: Add baseline anchors
1 parent 4b6f933 commit a25d405

File tree

2 files changed

+77
-30
lines changed

2 files changed

+77
-30
lines changed

src/draw/shapes.typ

Lines changed: 66 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -736,6 +736,12 @@
736736
///
737737
/// ## Anchors
738738
/// Supports border anchors.
739+
/// - **mid**: Content center, from baseline to top bounds
740+
/// - **mid-east**: Content center extended to the east
741+
/// - **mid-west**: Content center extended to the west
742+
/// - **base**: Horizontally centered baseline of the content
743+
/// - **base-east**: Baseline height extended to the east
744+
/// - **base-west**: Baseline height extended to the west
739745
#let content(
740746
..args-style,
741747
angle: 0deg,
@@ -788,47 +794,82 @@
788794
// Typst's `rotate` function is clockwise relative to x-axis, which is backwards from us
789795
angle = angle * -1
790796

797+
// Height from the baseline to content-north
798+
let (_, baseline-height) = util.measure(ctx, text(top-edge: "bounds", bottom-edge: "baseline", body))
799+
800+
// Size of the bounding box
791801
let (width, height, ..) = if auto-size {
792-
util.measure(ctx, body)
802+
util.measure(ctx, text(top-edge: "bounds", bottom-edge: "bounds", body))
793803
} else {
794804
vector.sub(b, a)
795805
}
796806

797-
width = (calc.abs(width)
798-
+ padding.at("left", default: 0)
799-
+ padding.at("right", default: 0))
800-
height = (calc.abs(height)
801-
+ padding.at("top", default: 0)
802-
+ padding.at("bottom", default: 0))
807+
let bounds-width = calc.abs(width)
808+
let bounds-height = calc.abs(height)
809+
baseline-height = bounds-height - baseline-height
810+
811+
width = bounds-width + padding.left + padding.right
812+
height = bounds-height + padding.top + padding.bottom
803813

804814
let anchors = {
805-
let w = width/2
806-
let h = height/2
807-
let center = if auto-size {
815+
let w = width / 2
816+
let h = height / 2
817+
let bh = (baseline-height - padding.top - padding.bottom) / 2
818+
819+
let bounds-center = if auto-size {
808820
a
809821
} else {
810822
vector.lerp(a, b, .5)
811823
}
812824

813825
// Only the center anchor gets transformed. All other anchors
814826
// must be calculated relative to the transformed center!
815-
center = matrix.mul4x4-vec3(ctx.transform,
816-
vector.as-vec(center, init: (0,0,0)))
827+
bounds-center = matrix.mul4x4-vec3(ctx.transform,
828+
vector.as-vec(bounds-center, init: (0,0,0)))
829+
830+
let east-dir = vector.rotate-z((1, 0, 0), angle)
831+
let north-dir = vector.rotate-z((-1, 0, 0), angle + 90deg)
832+
let east-scaled = vector.scale(east-dir, +w)
833+
let west-scaled = vector.scale(east-dir, -w)
834+
let north-scaled = vector.scale(north-dir, +h)
835+
let south-scaled = vector.scale(north-dir, -h)
836+
837+
let north = vector.add(bounds-center, north-scaled)
838+
let south = vector.add(bounds-center, south-scaled)
839+
let east = vector.add(bounds-center, east-scaled)
840+
let west = vector.add(bounds-center, west-scaled)
841+
let north-east = vector.add(bounds-center, vector.add(north-scaled, east-scaled))
842+
let north-west = vector.sub(bounds-center, vector.add(south-scaled, east-scaled))
843+
let south-east = vector.add(bounds-center, vector.add(south-scaled, east-scaled))
844+
let south-west = vector.sub(bounds-center, vector.add(north-scaled, east-scaled))
845+
846+
let base = vector.add(south,
847+
vector.scale(north-dir, padding.bottom + baseline-height))
848+
let mid = vector.lerp(
849+
vector.sub(north, vector.scale(north-dir, padding.top)),
850+
base,
851+
0.5)
852+
let base-east = vector.add(base, east-scaled)
853+
let base-west = vector.add(base, west-scaled)
854+
let mid-east = vector.add(mid, east-scaled)
855+
let mid-west = vector.add(mid, west-scaled)
817856

818-
let north = (calc.sin(angle)*h, -calc.cos(angle)*h,0)
819-
let east = (calc.cos(-angle)*w, -calc.sin(-angle)*w,0)
820-
let south = vector.scale(north, -1)
821-
let west = vector.scale(east, -1)
822857
(
823-
center: center,
824-
north: vector.add(center, north),
825-
north-east: vector.add(center, vector.add(north, east)),
826-
east: vector.add(center, east),
827-
south-east: vector.add(center, vector.add(south, east)),
828-
south: vector.add(center, south),
829-
south-west: vector.add(center, vector.add(south, west)),
830-
west: vector.add(center, west),
831-
north-west: vector.add(center, vector.add(north, west)),
858+
center: bounds-center,
859+
mid: mid,
860+
mid-east: mid-east,
861+
mid-west: mid-west,
862+
base: base,
863+
base-east: base-east,
864+
base-west: base-west,
865+
north: north,
866+
north-east: north-east,
867+
north-west: north-west,
868+
south: south,
869+
south-east: south-east,
870+
south-west: south-west,
871+
east: east,
872+
west: west,
832873
)
833874
}
834875

@@ -858,9 +899,6 @@
858899
(anchors.north-west, anchors.north-east,
859900
anchors.south-west, anchors.south-east)))
860901

861-
let corners = (anchors.north-east, anchors.north-west,
862-
anchors.south-west, anchors.south-east)
863-
864902
let drawables = ()
865903
if style.frame != none {
866904
drawables.push(border)

tests/content/anchor/test.typ

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,17 @@
33
#import "/tests/helper.typ": *
44
#import draw: content, rotate, scale, translate
55

6-
#for a in ("center", "north", "south", "east", "west", "north-east", "north-west", "south-east", "south-west") {
6+
#test-case({
7+
import draw: *
8+
content((0, 0), text(size: 40pt)[Yogurt], padding: (rest: 1, top: 2), frame: "rect", name: "content")
9+
line("content.base-west", "content.base-east", stroke: green)
10+
for-each-anchor("content", name => {
11+
content((), text(size: 6pt)[#name], frame: "rect",
12+
fill: white, stroke: none)
13+
})
14+
})
15+
16+
#for a in ("center", "north", "south", "east", "west", "north-east", "north-west", "south-east", "south-west", "mid", "base") {
717
test-case({
818
cross((0,0))
919
content((0,0), [#a], anchor: a)
@@ -28,5 +38,4 @@
2838
cross((0,0))
2939
content((0,0), [#a (scale)], anchor: a)
3040
})
31-
[\ ]
3241
}

0 commit comments

Comments
 (0)