How can I indent a paragraph so that it begins exactly at the middle of the page width?

I’m trying to define a function to inset a paragraph by exactly half a page (ignoring the margin), i.e. the left margin of the paragraph should be at (page.width - page.margin.left - page.margin.right)/2, but this function doesn’t seem to be straightforward to define.

I tried

#let right(body) = block(inset:(left:50%), body)

but the behavior is very weird and it doesn’t do what I intended

#set par(justify: true)
Left.

#let right(body) = block(inset:(left:50%), stroke:1pt+black, body)

#right[
  Right.
]

#let right(body) = block(inset:(left:60%), stroke:1pt+black, body)

#right[
  Right.
]

#let right(body) = block(inset:(left:70%), stroke:1pt+black, body)

#right[
  Right.
]

#let right(body) = block(inset:(left:80%), stroke:1pt+black, body)

#right[
  Right.
]

#let right(body) = block(inset:(left:90%), stroke:1pt+black, body)

#right[
  Right.
]

#let right(body) = block(inset:(left:100%), stroke:1pt+black, body)

#right[
  Right.
]

#let right(body) = block(inset:(left:50%), stroke:1pt+black, body)

#right[
  Right. #lorem(50)
]

#let right(body) = block(inset:(left:60%), stroke:1pt+black, body)

#right[
  Right. #lorem(20)
]

#let right(body) = block(inset:(left:70%), stroke:1pt+black, body)

#right[
  Right. #lorem(20)
]

#let right(body) = block(inset:(left:80%), stroke:1pt+black, body)

#right[
  Right. #lorem(20)
]

#let right(body) = block(inset:(left:90%), stroke:1pt+black, body)

#right[
  Right. #lorem(20)
]

#let right(body) = block(inset:(left:100%), stroke:1pt+black, body)

#right[
  Right. #lorem(20)
]

I want to set the inset amount to (page.width - page.margin.left - page.margin.right)/2 but page.margin is unreliable because it can be auto instead of a value, and I’m not familiar with the context syntax. I’m kind of stuck not knowing how to proceed.

Any help would be appreciated.

Would this solve your needs?

#set par(justify: true)
Left.

#let right(body) = block(
  width: 100%, // 👈 Let the block take all available space
  inset: (left: 50%), // 👈 Use the left half of the block as inset
  stroke: 0.5pt,
  body,
)

#right[Right.]
#right(lorem(100))

Ratio-insets are relative to the block’s size, not the page’s size. This result in the seemingly weird behaviour.

Coincidentally, I’ve just submitted a pull request that improves the docs on it.

3 Likes

Hi kst,

If it should be half the page width, shouldn’t it be (page.width / 2 - page.margin.left)?

This might be helpful(?):

#set page(
  paper: "a5",
  margin: (left: 20pt, right: 15pt),
)

#let calc-pos(margin, shape) = if margin == auto {
  (page.width / 2 - (2.5 / 21 * calc.min(..shape)))
} else {
  (page.width / 2 - margin.left)
}

#context {
  place(
    dx: calc-pos(page.margin, (page.width, page.height)),
    block(stroke: 1pt, inset: 10pt, width: 50%)[Hello World #lorem(30)]
  )
}

Depending on the use case, maybe columns [1] or a grid could be useful as well:

#grid(
  columns: (1fr, 1fr),
  [], // empty left column
  lorem(20)
)

You could possibly also use par’s first-line-indent and hanging-indent but they don’t accept relative lengths, so you would need to get access to the page width. Something like this might work (but the other approaches seem more reliable):

#layout(size => [
  #let indent = size.width / 2
  #set par(
    first-line-indent: (all: true, amount: indent),
    hanging-indent: indent,
    justify: true,
  )

  #lorem(20)
])

  1. Can be used within a page as well, does not have to be used for the complete page. ↩︎