Skip to content

Commit

Permalink
fix: behaviors of fit-to-height and fit-to-width (touying-typ#131)
Browse files Browse the repository at this point in the history
* refactor: use substitute hacks with std functions in `fit-to-height`, `fit-to-width`

* docs: update examples to use `%` instead of `fr`
  • Loading branch information
enklht authored and enkaito committed Dec 6, 2024
1 parent 8bdd5ed commit 3e01ae8
Showing 1 changed file with 14 additions and 53 deletions.
67 changes: 14 additions & 53 deletions src/utils.typ
Original file line number Diff line number Diff line change
Expand Up @@ -577,7 +577,7 @@

/// Fit content to specified height.
///
/// Example: `#utils.fit-to-height(1fr)[BIG]`
/// Example: `#utils.fit-to-height(100%)[BIG]`
///
/// - width (length, fraction, relative): Will determine the width of the content after scaling. So, if you want the scaled content to fill half of the slide width, you can use `width: 50%`.
///
Expand All @@ -600,42 +600,9 @@
height,
body,
) = {
// Place two labels with the requested vertical separation to be able to
// measure their vertical distance in pt.
// Using this approach instead of using `measure` allows us to accept fractions
// like `1fr` as well.
// The label must be attached to content, so we use a show rule that doesn't
// display anything as the anchor.
let before-label = label("touying-fit-height-before")
let after-label = label("touying-fit-height-after")
[
#show before-label: none
#show after-label: none
#v(1em)
hidden#before-label
#v(height)
hidden#after-label
]

context {
let before = query(selector(before-label).before(here()))
let before-pos = before.last().location().position()
let after = query(selector(after-label).before(here()))
let after-pos = after.last().location().position()

let available-height = after-pos.y - before-pos.y

layout(container-size => {
// Helper function to more easily grab absolute units
let get-pts(body, w-or-h) = {
let dim = if w-or-h == "w" {
container-size.width
} else {
container-size.height
}
_size-to-pt(body, dim)
}

let available-height = _size-to-pt(height, container-size.height)
// Provide a sensible initial width, which will define initial scale parameters.
// Note this is different from the post-scale width, which is a limiting factor
// on the allowable scaling ratio
Expand All @@ -650,7 +617,7 @@
if width == none {
mutable-width = container-size.width
}
mutable-width = get-pts(mutable-width, "w")
mutable-width = _size-to-pt(mutable-width, container-size.width)

let size = measure(boxed-content)
if size.height == 0pt or size.width == 0pt {
Expand All @@ -662,15 +629,12 @@

if ((shrink and (ratio < 100%)) or (grow and (ratio > 100%))) {
let new-width = size.width * ratio
v(-available-height)
// If not boxed, the content can overflow to the next page even though it will
// fit. This is because scale doesn't update the layout information.
// Boxing in a container without clipping will inform typst that content
// will indeed fit in the remaining space
box(
width: new-width,
height: available-height,
scale(x: ratio, y: ratio, origin: top + left, boxed-content),
scale(
x: ratio,
y: ratio,
origin: top + left,
boxed-content,
reflow: true,
)
} else {
body
Expand All @@ -682,7 +646,7 @@

/// Fit content to specified width.
///
/// Example: `#utils.fit-to-width(1fr)[BIG]`
/// Example: `#utils.fit-to-width(100%)[BIG]`
///
/// - grow (boolean): Indicates whether the content should be scaled up if it is smaller than the available width. Default is `true`.
///
Expand All @@ -695,21 +659,18 @@
/// -> content
#let fit-to-width(grow: true, shrink: true, width, content) = {
layout(layout-size => {
let content-size = measure(content)
let content-width = content-size.width
let content-width = measure(content).width
let width = _size-to-pt(width, layout-size.width)
if (content-width != 0pt and ((shrink and (width < content-width)) or (grow and (width > content-width)))) {
let ratio = width / content-width * 100%
// The first box keeps content from prematurely wrapping
let scaled = scale(
scale(
// The box keeps content from prematurely wrapping
box(content, width: content-width),
origin: top + left,
x: ratio,
y: ratio,
reflow: true,
)
// The second box lets typst know the post-scaled dimensions, since `scale`
// doesn't update layout information
box(scaled, width: width, height: content-size.height * ratio)
} else {
content
}
Expand Down

0 comments on commit 3e01ae8

Please sign in to comment.