
Word-wrapping and indenting utilities for nicely formatting text, with Unicode-aware wrapping, optional optimal-fit (look-ahead) algorithm, and optional hyphenation via language patterns.
This is a Kotlin Multiplatform line-by-line transliteration port of mgeisler/textwrap.
Original Project: This port is based on mgeisler/textwrap. All design credit and project intent belong to the upstream authors; this repository is a faithful port to Kotlin Multiplatform with no behavioural changes intended.
This is an in-progress port. The goal is feature parity with the upstream Rust crate while providing a native Kotlin Multiplatform API. Every Kotlin file carries a // port-lint: source <path> header naming its upstream Rust counterpart so the AST-distance tool can track provenance.
The text below is reproduced and lightly edited from
https://github.com/mgeisler/textwrap. It is the upstream project's own description and remains under the upstream authors' authorship; links have been rewritten to absolute upstream URLs so they continue to resolve from this repository.
Textwrap is a library for wrapping and indenting text. It is most often used by command-line programs to format dynamic output nicely so it looks good in a terminal. You can also use Textwrap to wrap text set in a proportional font—such as text used to generate PDF files, or drawn on a HTML5 canvas using WebAssembly.
To use the textwrap crate, add this to your Cargo.toml file:
[dependencies]
textwrap = "0.16"By default, this enables word wrapping with support for Unicode strings. Extra features can be enabled with Cargo features—and the Unicode support can be disabled if needed. This allows you slim down the library and so you will only pay for the features you actually use.
Please see the Cargo Features in the crate documentation for a full list of the available features as well as their impact on the size of your binary.
Word wrapping is easy using the wrap and fill functions:
#[cfg(feature = "smawk")] {
let text = "textwrap: an efficient and powerful library for wrapping text.";
assert_eq!(
textwrap::wrap(text, 28),
vec![
"textwrap: an efficient",
"and powerful library for",
"wrapping text.",
]
);
}Sharp-eyed readers will notice that the first line is 22 columns wide. So why is the word “and” put in the second line when there is space for it in the first line?
The explanation is that textwrap does not just wrap text one line at a time.
Instead, it uses an optimal-fit algorithm which looks ahead and chooses line
breaks which minimize the gaps left at ends of lines. This is controlled with
the smawk Cargo feature, which is why the example is wrapped in the
cfg-block.
Without look-ahead, the first line would be longer and the text would look like this:
#[cfg(not(feature = "smawk"))] {
let text = "textwrap: an efficient and powerful library for wrapping text.";
assert_eq!(
textwrap::wrap(text, 28),
vec![
"textwrap: an efficient and",
"powerful library for",
"wrapping text.",
]
);
}The second line is now shorter and the text is more ragged. The kind of wrapping
can be configured via Options::wrap_algorithm.
If you enable the hyphenation Cargo feature, you get support for automatic
hyphenation for about 70 languages via high-quality TeX hyphenation
patterns.
Your program must load the hyphenation pattern and configure
Options::word_splitter to use it:
#[cfg(feature = "hyphenation")] {
use hyphenation::{Language, Load, Standard};
use textwrap::{fill, Options, WordSplitter};
let dictionary = Standard::from_embedded(Language::EnglishUS).unwrap();
let options = textwrap::Options::new(28).word_splitter(WordSplitter::Hyphenation(dictionary));
let text = "textwrap: an efficient and powerful library for wrapping text.";
assert_eq!(
textwrap::wrap(text, &options),
vec![
"textwrap: an efficient and",
"powerful library for wrap-",
"ping text."
]
);
}The US-English hyphenation patterns are embedded when you enable the
hyphenation feature. They are licensed under a
permissive license and take up about 88 KB in your binary. If
you need hyphenation for other languages, you need to download a
precompiled .bincode file and load it yourself. Please see the
hyphenation documentation for details.
If your strings are known at compile time, please take a look at the procedural
macros from the textwrap-macros crate.
The library comes with a collection of small example programs that shows various features.
If you want to see Textwrap in action right away, then take a look at
examples/wasm/, which shows how to wrap sans-serif, serif, and monospace
text. It uses WebAssembly and is automatically deployed to
https://mgeisler.github.io/textwrap/.
For the command-line examples, you’re invited to clone the repository and try
them out for yourself! Of special note is examples/interactive.rs. This is a
demo program which demonstrates most of the available features: you can enter
text and adjust the width at which it is wrapped interactively. You can also
adjust the Options used to see the effect of different WordSplitters and
wrap algorithms.
Run the demo with
$ cargo run --example interactiveThe demo needs a Linux terminal to function.
Please see the CHANGELOG file for details on the changes made in each release.
Textwrap can be distributed according to the MIT license. Contributions will be accepted under the same license.
dependencies {
implementation("io.github.kotlinmania:textwrap-kotlin:0.1.0")
}./gradlew build
./gradlew testSee AGENTS.md and CLAUDE.md for translator discipline, port-lint header convention, and Rust → Kotlin idiom mapping.
This Kotlin port is distributed under the same MIT license as the upstream mgeisler/textwrap. See LICENSE (and any sibling LICENSE-* / NOTICE files mirrored from upstream) for the full text.
Original work copyrighted by the textwrap authors.
Kotlin port: Copyright (c) 2026 Sydney Renee and The Solace Project.
Thanks to the mgeisler/textwrap maintainers and contributors for the original Rust implementation. This port reproduces their work in Kotlin Multiplatform; bug reports about upstream design or behavior should go to the upstream repository.
This is a Kotlin Multiplatform line-by-line transliteration port of mgeisler/textwrap.
Original Project: This port is based on mgeisler/textwrap. All design credit and project intent belong to the upstream authors; this repository is a faithful port to Kotlin Multiplatform with no behavioural changes intended.
This is an in-progress port. The goal is feature parity with the upstream Rust crate while providing a native Kotlin Multiplatform API. Every Kotlin file carries a // port-lint: source <path> header naming its upstream Rust counterpart so the AST-distance tool can track provenance.
The text below is reproduced and lightly edited from
https://github.com/mgeisler/textwrap. It is the upstream project's own description and remains under the upstream authors' authorship; links have been rewritten to absolute upstream URLs so they continue to resolve from this repository.
Textwrap is a library for wrapping and indenting text. It is most often used by command-line programs to format dynamic output nicely so it looks good in a terminal. You can also use Textwrap to wrap text set in a proportional font—such as text used to generate PDF files, or drawn on a HTML5 canvas using WebAssembly.
To use the textwrap crate, add this to your Cargo.toml file:
[dependencies]
textwrap = "0.16"By default, this enables word wrapping with support for Unicode strings. Extra features can be enabled with Cargo features—and the Unicode support can be disabled if needed. This allows you slim down the library and so you will only pay for the features you actually use.
Please see the Cargo Features in the crate documentation for a full list of the available features as well as their impact on the size of your binary.
Word wrapping is easy using the wrap and fill functions:
#[cfg(feature = "smawk")] {
let text = "textwrap: an efficient and powerful library for wrapping text.";
assert_eq!(
textwrap::wrap(text, 28),
vec![
"textwrap: an efficient",
"and powerful library for",
"wrapping text.",
]
);
}Sharp-eyed readers will notice that the first line is 22 columns wide. So why is the word “and” put in the second line when there is space for it in the first line?
The explanation is that textwrap does not just wrap text one line at a time.
Instead, it uses an optimal-fit algorithm which looks ahead and chooses line
breaks which minimize the gaps left at ends of lines. This is controlled with
the smawk Cargo feature, which is why the example is wrapped in the
cfg-block.
Without look-ahead, the first line would be longer and the text would look like this:
#[cfg(not(feature = "smawk"))] {
let text = "textwrap: an efficient and powerful library for wrapping text.";
assert_eq!(
textwrap::wrap(text, 28),
vec![
"textwrap: an efficient and",
"powerful library for",
"wrapping text.",
]
);
}The second line is now shorter and the text is more ragged. The kind of wrapping
can be configured via Options::wrap_algorithm.
If you enable the hyphenation Cargo feature, you get support for automatic
hyphenation for about 70 languages via high-quality TeX hyphenation
patterns.
Your program must load the hyphenation pattern and configure
Options::word_splitter to use it:
#[cfg(feature = "hyphenation")] {
use hyphenation::{Language, Load, Standard};
use textwrap::{fill, Options, WordSplitter};
let dictionary = Standard::from_embedded(Language::EnglishUS).unwrap();
let options = textwrap::Options::new(28).word_splitter(WordSplitter::Hyphenation(dictionary));
let text = "textwrap: an efficient and powerful library for wrapping text.";
assert_eq!(
textwrap::wrap(text, &options),
vec![
"textwrap: an efficient and",
"powerful library for wrap-",
"ping text."
]
);
}The US-English hyphenation patterns are embedded when you enable the
hyphenation feature. They are licensed under a
permissive license and take up about 88 KB in your binary. If
you need hyphenation for other languages, you need to download a
precompiled .bincode file and load it yourself. Please see the
hyphenation documentation for details.
If your strings are known at compile time, please take a look at the procedural
macros from the textwrap-macros crate.
The library comes with a collection of small example programs that shows various features.
If you want to see Textwrap in action right away, then take a look at
examples/wasm/, which shows how to wrap sans-serif, serif, and monospace
text. It uses WebAssembly and is automatically deployed to
https://mgeisler.github.io/textwrap/.
For the command-line examples, you’re invited to clone the repository and try
them out for yourself! Of special note is examples/interactive.rs. This is a
demo program which demonstrates most of the available features: you can enter
text and adjust the width at which it is wrapped interactively. You can also
adjust the Options used to see the effect of different WordSplitters and
wrap algorithms.
Run the demo with
$ cargo run --example interactiveThe demo needs a Linux terminal to function.
Please see the CHANGELOG file for details on the changes made in each release.
Textwrap can be distributed according to the MIT license. Contributions will be accepted under the same license.
dependencies {
implementation("io.github.kotlinmania:textwrap-kotlin:0.1.0")
}./gradlew build
./gradlew testSee AGENTS.md and CLAUDE.md for translator discipline, port-lint header convention, and Rust → Kotlin idiom mapping.
This Kotlin port is distributed under the same MIT license as the upstream mgeisler/textwrap. See LICENSE (and any sibling LICENSE-* / NOTICE files mirrored from upstream) for the full text.
Original work copyrighted by the textwrap authors.
Kotlin port: Copyright (c) 2026 Sydney Renee and The Solace Project.
Thanks to the mgeisler/textwrap maintainers and contributors for the original Rust implementation. This port reproduces their work in Kotlin Multiplatform; bug reports about upstream design or behavior should go to the upstream repository.