Skip to content

Hyperrick/typst-framefit

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

4 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Framefit

Fit text into fixed Typst frames by adjusting the text size.

Framefit is useful when the frame size is fixed but the text is variable: labels, badges, cards, flyers, product sheets, certificates, data-driven templates, or any layout where user-provided copy must stay inside a known box.

The package measures the rendered text and chooses a font-size percentage that fits the available width and height. It can grow short text, shrink long text, cap the allowed growth, or keep text within a maximum number of lines.

Preview

Finite maximum:

Framefit example with finite max percentage

No configured maximum:

Framefit example with max none

Shrink to minimum:

Framefit example shrinking text to the minimum size

Maximum lines:

Framefit example limiting fitted text to three lines

Font and line spacing sensitivity:

Framefit example with different fonts and paragraph leading

Features

  • Fit text to a fixed width and height.
  • Grow short text up to the largest fitting size.
  • Shrink overflowing text down to a configured minimum.
  • Use max: none or max: -1 for no configured upper limit.
  • Use a finite max, for example max: 140%, to cap growth.
  • Use max-lines, for example max-lines: 3, to keep text within a measured line count.
  • Use only-if-overflow: true to keep normal text unchanged unless it would overflow.

Installation

After publishing to Typst Universe:

#import "@preview/framefit:0.1.0": framefit, fit-copy

For local development from this repository:

#import "lib.typ": framefit, fit-copy

Quick Start

Create a fitted frame directly:

#import "@preview/framefit:0.1.0": framefit

#framefit(
  width: 70mm,
  height: 24mm,
  min: 70%,
  max: none,
  inset: 6pt,
  stroke: 0.5pt,
)[
  This text grows or shrinks until it fits the frame.
]

Use the lower-level helper inside an existing frame:

#import "@preview/framefit:0.1.0": fit-copy

#block(width: 70mm, height: 24mm, stroke: 0.5pt, inset: 6pt)[
  #fit-copy(min: 70%)[
    This text uses the surrounding block as the frame.
  ]
]

Common Recipes

Grow Until The Frame Is Full

max: none is the default. Framefit grows the text until it first overflows, then backs off to the largest fitting size.

#framefit(width: 60mm, height: 18mm, min: 70%, max: none)[
  Short headline
]

max: -1 is accepted as an alias for max: none.

Cap Growth

Use a percentage max when text should grow, but not beyond a design limit.

#framefit(width: 60mm, height: 18mm, min: 70%, max: 180%)[
  Short headline
]

Shrink Only If Text Overflows

Use only-if-overflow: true to keep text at 100% when it already fits.

#framefit(
  width: 70mm,
  height: 24mm,
  min: 70%,
  max: 130%,
  only-if-overflow: true,
)[
  Text stays at normal size unless it would overflow.
]

Limit The Number Of Lines

Use max-lines when the text should stay within a fixed number of measured lines as well as the physical frame.

#framefit(
  width: 70mm,
  height: 30mm,
  min: 60%,
  max: none,
  max-lines: 3,
)[
  This text is fitted while staying within three measured lines.
]

Line spacing is configured with Typst's normal paragraph setting:

#set par(leading: 4pt)

#framefit(width: 70mm, height: 30mm, max-lines: 3)[
  This text is fitted using the active paragraph leading.
]

API

framefit

#framefit(
  width: auto,
  height: auto,
  min: 70%,
  max: none,
  max-lines: none,
  steps: 24,
  inset: 0pt,
  stroke: none,
  fill: none,
  radius: 0pt,
  only-if-overflow: false,
  body,
)

Creates a block frame and fits body inside it.

Key arguments:

  • width, height: frame dimensions passed to block.
  • min: smallest allowed text size as a percentage of the current text size.
  • max: largest allowed text size as a percentage, or none / -1 for no configured maximum.
  • max-lines: maximum measured line count, or none.
  • steps: binary-search iterations. The default is usually enough.
  • inset, stroke, fill, radius: forwarded to the created block.
  • only-if-overflow: if true, text that already fits stays at 100%.

fit-copy

#fit-copy(
  min: 70%,
  max: none,
  max-lines: none,
  steps: 24,
  only-if-overflow: false,
  body,
)

Fits body to the size of the surrounding layout container. Use this when you already have a custom block or another layout container and only need the text-fitting behavior.

How It Works

Framefit uses Typst's layout measurement:

  1. Measure the content at a candidate font-size percentage.
  2. Check whether the measured width and height fit the available frame.
  3. If max-lines is set, measure an equivalent line-count sample in the current text style and compare heights.
  4. Use binary search to choose the largest fitting percentage.

Conceptually, max-lines is the maximum allowed text height for the requested line count. Framefit lets Typst calculate that height instead of multiplying manually, because the real line box depends on font metrics, text size, top/bottom edges, and par(leading:).

Examples

The examples/ folder contains rendered documents for the main cases:

Each example includes a visible "Settings used" block before each result.

Overflow Behavior

If text still does not fit at min, compilation fails with a clear error:

framefit: content does not fit at the minimum size.
Make the frame larger, reduce the content, or lower `min`.

This is intentional. It prevents silent clipping or hidden layout failures.

Limitations

  • Designed for paged output: PDF, PNG, and SVG.
  • The fitting calculation uses Typst layout measurement, so unusual content may need manual checking.
  • max-lines is intended for ordinary text. It is less exact for content that changes style inside the body, includes non-text elements, uses manual line breaks, or relies on hyphenation.
  • The MVP focuses on text content. Other content can work, but is not the primary target.

Development

Compile all examples:

for file in examples/*.typ; do
  [ "$file" = "examples/_helpers.typ" ] && continue
  docker run --rm -v "$PWD":/work -w /work ghcr.io/typst/typst:latest \
    compile --root /work "$file" "${file%.typ}.pdf"
done

Run the compile checks:

for file in tests/*.typ; do
  [ "$file" = "tests/impossible.typ" ] && continue
  docker run --rm -v "$PWD":/work -w /work ghcr.io/typst/typst:latest \
    compile --root /work "$file" "${file%.typ}.svg"
done

tests/impossible.typ is expected to fail because it verifies the minimum-size overflow error.

About

typst package for fitting text into frames

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors

Languages