Add support for CSS hyphens and hyphenate-character#66
Open
maitredede wants to merge 5 commits into
Open
Conversation
Implement the manual side of CSS `hyphens` (plutoprint#28): draw a trailing hyphen glyph when a line breaks right after a soft hyphen (U+00AD), and suppress soft-hyphen break opportunities for `hyphens: none` while keeping every other break the line iterator reports. `LineBreakIterator`'s break queries take an optional `skipSoftHyphen` flag so `none` can drop only the hyphenation opportunities. `LineBreaker` reads the inherited `hyphens` value, and `breakText` shapes a hyphen glyph (U+2010) for the broken run, accounting for its advance in the line fit. `TextLineBox` carries the hyphen shape and paints it at the visual line-end (after the text in LTR, before it in RTL). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Parse `hyphenate-character: auto | <string>`, inherit it, and expose it as `BoxStyle::hyphenString()` which resolves `auto` to U+2010. The line breaker now shapes the trailing hyphen from this value instead of a hard-coded hyphen, so authors can pick the glyph shown at a hyphenation break. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Wire `hyphens: auto` to a dictionary-based hyphenator built on libhyphen, which is an optional dependency: when it is missing, `auto` transparently degrades to `manual`. `Hyphenator` loads a Hunspell/libhyphen dictionary per locale (cached, thread-local), resolving it from the directories in `PLUTOBOOK_HYPHEN_PATH` and then `/usr/share/hyphen`, and maps the UTF-8 break positions it returns back to the UTF-16 offsets the line breaker uses. In `breakText`, the overflowing word is hyphenated at the last dictionary break that fits, either to avoid overflow or to fill the line better than the word boundary, and the existing trailing-hyphen rendering covers the glyph. The build probes `<hyphen.h>` and `libhyphen` directly since the library ships no pkg-config file, guarded by the `hyphen` feature option. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Add a build-time pipeline that embeds selected libhyphen dictionaries into the library, consulted by `Hyphenator` when no system dictionary is found. `tools/embed_dicts.py` turns each dictionary chosen by the new `hyphen-embed-dicts` option (en-US only by default) into a byte array, and the embedded copy is loaded in memory via `fmemopen` + `hnj_hyphen_load_file`. Lookup falls back from a full locale to any embedded dictionary for the same language, so `lang="en"` resolves to the bundled en-US patterns. The en-US patterns (dicts/hyph_en_US.dic, tri-licensed GPL-2+/LGPL-2.1+/ MPL-1.1+) are bundled under the MPL and credited in CREDITS. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Add `hyphen` to the optional dependencies (and `libhyphen-dev` to the Ubuntu/Debian command), and a "Hyphenation Dictionaries" section covering the runtime dictionary search order (PLUTOBOOK_HYPHEN_PATH, /usr/share/hyphen, embedded), installing system language packs, and embedding dictionaries at build time via the `hyphen-embed-dicts` option. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
sammycage
marked this pull request as ready for review
July 18, 2026 10:52
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Implements CSS
hyphens(closes #28; addresses the hyphenation part of #16),including the
hyphenate-characterproperty and automatic, dictionary-basedhyphenation via libhyphen as an optional dependency.
What's included
hyphens: none | manual— soft hyphens (U+00AD) are honored as breakopportunities and now render a trailing hyphen glyph at the break;
nonesuppresses hyphenation opportunities while keeping every other line break.
hyphenate-character—auto | <string>, inherited; resolvesautotoU+2010 and is used as the glyph drawn at a hyphenation break.
hyphens: auto— dictionary hyphenation through libhyphen, wired into theline breaker so the overflowing word is broken at the last dictionary point
that fits (to avoid overflow or to fill the line better than the word break).
PLUTOBOOK_HYPHEN_PATH→/usr/share/hyphen→dictionaries embedded at build time. en-US is embedded by default; more can be
selected with the
hyphen-embed-dictsbuild option.Dependency
libhyphenis optional (hyphenfeature, auto-detected). When it isabsent,
hyphens: autotransparently degrades tomanual, andnone/manualwork with no extra dependency. Docs updated in the Installation Guide.
Verification
Rendered end-to-end (bitmap output) against a real libhyphen install:
manualbreaksmulti­plication→multi-/plication;nonekeeps it whole.hyphenate-character: '='→multi=/plication.auto, en-US via the embedded dictionary →extraor-/dinary respons-/bility hy-/phen-/ation.auto, de via a system dictionary →Silbentren-/nung,Fußgängerü-/bergänge(validates the UTF-16↔UTF-8 offset mapping across multi-byte characters).Not included (follow-ups)
autohyphenation incomputeIntrinsicWidths(min-content); today ahyphenatable word still contributes its full width to intrinsic sizing. This is
a sizing fidelity gap, not a rendering bug.
but not exhaustively tested.
Opening as a draft for early feedback on the approach.