Skip to content

Strange additional padding at the bottom of a multi-line content with auto linebreaks #992

@YDX-2147483647

Description

@YDX-2147483647

When cetz v0.4.2 calculates border anchors for cetz.draw.content, it changes text.bottom-edge and increases the effective line spacing, resulting in an additional bottom padding if the content is multi-line.

cetz/src/draw/shapes.typ

Lines 1143 to 1155 in 0459295

// Compute the baseline offset
let (_, line-baseline-height) = util.measure(ctx, text(top-edge: "cap-height", bottom-edge: "baseline",
[ #show linebreak: [ ]; #body]))
let (_, line-bounds-height) = util.measure(ctx, text(top-edge: "cap-height", bottom-edge: "bounds",
[ #show linebreak: [ ]; #body]))
let baseline-offset = line-bounds-height - line-baseline-height
// Size of the bounding box
let (content-width, content-height, ..) = if auto-size {
util.measure(ctx, text(top-edge: "cap-height", bottom-edge: "baseline", body))
} else {
vector.sub(b, a)
}

Minimal example

In the following example, the height of the green border line is expected to equal to the height of the aqua block, but it isn't.

#import "@preview/cetz:0.4.2"

#cetz.canvas({
  import cetz.draw: *

  content((0, 0), name: "a", block(
    width: 6em,
    fill: aqua.lighten(50%),
    lorem(10),
  ))
  line(
    "a.north-west",
    "a.north-east",
    "a.south-east",
    "a.south-west",
    close: true,
    stroke: green,
  )
})
Image

Links and comparisons

#894 is a similar issue. #956 (first appeared in v0.4.2) has fixed that issue, but not this one.
The difference is that linebreaks in #894 are inserted manually, but here they're inserted automatically (by putting contents in a fixed-width block).
Therefore, the #show linebreak: [ ] hack used by #956 is not helpful.

In the minimal example above, changing a.south-{west,east} to a.base-{west,east} would draw the correct border line.
However, there exist certain cases that base-{west,east} are not applicable.
For instance, when drawing a chat bubble, you may want the lower-left corner of the whole group, including a triangle and a cetz.draw.content. This point locates at (x: group.triangle.west.x, y: group.content.base-west.y). If cetz had measured correctly, then this can be simplified as group.south-west.

Image

Workaround

Set text.{top,bottom}-edge explicitly and control spacing on your own.

#import "@preview/cetz:0.4.2"

#cetz.canvas({
  import cetz.draw: *

  content((0, 0), name: "a", block(
    width: 6em,
    fill: aqua.lighten(50%),
    {
      // 👇 Edit here
      set text(top-edge: "cap-height", bottom-edge: "baseline")
      v(0.5em) // or use `block.inset`
      lorem(10)
      v(0.5em)
    },
  ))
  line(
    "a.north-west",
    "a.north-east",
    "a.base-east",
    "a.base-west",
    close: true,
    stroke: green,
  )
})
Image

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions