Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
d5fc1c9
add bidi plan
elcritch Jun 17, 2026
c26ed0e
add glyph id text layout data
elcritch Jun 17, 2026
39024c6
prepare for harfbuzz
elcritch Jun 17, 2026
1ba60f8
wiring in glyphid
elcritch Jun 18, 2026
a174237
finish font shaping backend
elcritch Jun 18, 2026
03e141c
add text shaping demo
elcritch Jun 18, 2026
3ddff7a
wiring in glyphid
elcritch Jun 18, 2026
0bde02c
wiring in glyphid
elcritch Jun 18, 2026
d16787b
fix genny version
elcritch Jun 18, 2026
4428bb0
update ci
elcritch Jun 18, 2026
3f100d6
update genny to latest actual genny commit
elcritch Jun 18, 2026
cdf3aa5
fixes for bindings
elcritch Jun 18, 2026
e96403f
add font fallback and shaping controls
elcritch Jun 18, 2026
d1f333f
cleanup
elcritch Jun 18, 2026
d6e8672
cleanup
elcritch Jun 18, 2026
96df14c
cleanup
elcritch Jun 18, 2026
523dede
install harfbuzz headers in ci
elcritch Jun 18, 2026
69e5026
make ci text deps noninteractive
elcritch Jun 18, 2026
0df40de
add classical chinese
elcritch Jun 18, 2026
8a4498d
add ligature examples
elcritch Jun 18, 2026
95009cb
add coding ligatures demo
elcritch Jun 18, 2026
39592ba
add ligature examples
elcritch Jun 18, 2026
23a02d9
add Devanagari detection
elcritch Jun 18, 2026
baa9da3
updates
elcritch Jun 18, 2026
92d6ab7
updates
elcritch Jun 18, 2026
d3e818d
updates
elcritch Jun 18, 2026
80ad4c5
refine text source selection helpers
elcritch Jun 18, 2026
90d5f5d
updates
elcritch Jun 18, 2026
67f4d62
fix shaped text selection carets
elcritch Jun 19, 2026
307f356
add harfbuzz docs to readme
elcritch Jun 19, 2026
5b01a13
bump version
elcritch Jun 19, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 14 additions & 3 deletions .github/workflows/build-full.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ jobs:
strategy:
matrix:
nimversion:
- '2.2.8'
- '2.2.10'
os:
- ubuntu-24.04
- macos-14
Expand All @@ -26,9 +26,10 @@ jobs:

- name: Setup software OpenGL/Vulkan (Mesa) + Weston (Wayland)
if: runner.os == 'Linux'
timeout-minutes: 15
run: |
sudo apt-get update
sudo apt-get install -y --no-install-recommends \
sudo DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends \
weston \
xwayland \
wayland-protocols \
Expand All @@ -49,6 +50,16 @@ jobs:
libegl-mesa0 \
libgles2

- name: Install Linux text shaping dependencies
if: runner.os == 'Linux'
timeout-minutes: 5
run: |
sudo DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends \
pkg-config \
libharfbuzz-dev \
libfribidi-dev
pkg-config --cflags --libs harfbuzz

- name: Vulkan diagnostics
if: runner.os == 'Linux'
run: |
Expand Down Expand Up @@ -91,7 +102,7 @@ jobs:
key: ${{ runner.os }}-${{ hashFiles('figdraw.nimble') }}

- name: Install Deps
run: atlas install -t:8 --features:windy,sdl2,siwin,sharedlib
run: atlas install -t:8 --features:windy,sdl2,siwin,sharedlib,harfbuzz

- name: Build Bindings
run: |
Expand Down
89 changes: 88 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@ Features:
- Rects & shadows default to SDF (signed-distance-field) primitives for crisp, dynamic, and low memory UI primitives
- Lightweight, multiplatform, and high performance by design! (few or no allocations for each frame)
- Thread-safe renderer pipeline. (render tree construction and preparation can be done off the main thread)
- Modern and fast text rendering and layout using [Pixie](https://github.com/treeform/pixie/) with thread-safe Text API.
- Fast pure Nimtext rendering and layout using [Pixie](https://github.com/treeform/pixie/) by default
- Optional Harfbuzzy support for shaping and rendering more complex scripts, font fallback, ligatures, and variable fonts!
- Image rendering using a texture atlas.
- Supports layering and multiple "roots" per layer - great for menus, overlays, etc.
- SDF/MSDF (Multi-SDF) based glyph rendering.
Expand Down Expand Up @@ -284,6 +285,92 @@ proc makeGradientDemo(w, h: float32, uiFont: FigFont): Renders =
))
```

## Font Shaping with Harfbuzzy

FigDraw uses Pixie text layout by default. For complex scripts, glyph ligatures,
font fallback, OpenType features, and variable-font axes, compile with the
optional Harfbuzzy backend.

Install the optional Harfbuzzy dependency when trying the repo:

```sh
atlas install --feature:windy --feature:harfbuzz
```

Add the `harfbuzz` feature when using FigDraw from another project:

```nim
requires "https://github.com/elcritch/figdraw[windy,harfbuzz]"
```

Note: Windy is the default example. Harfbuzz support works with Siwin or other WMs.

Then compile with the Harfbuzzy text backend:

```sh
nim r -d:figdrawTextBackend=harfbuzzy examples/windy_text_shaping_demo.nim
```

For an example or app that should always use Harfbuzzy, put the backend switch
in a sibling `.nims` file:

```nim
# examples/windy_text_shaping_demo.nims
switch("define", "figdrawTextBackend=harfbuzzy")
```

The public text API stays FigDraw-owned: use `typeset` as usual, but pass
shaping controls through `FigFont`.

```nim
import figdraw/commons
import figdraw/common/fonttypes
import chroma

let
ui = loadTypeface("data/Ubuntu.ttf")
arabic = loadTypeface("examples/fonts/NotoNaskhArabic-wght.ttf")
hebrew = loadTypeface("examples/fonts/NotoSansHebrew-wdth-wght.ttf")

bodyFont = FigFont(
typefaceId: ui,
size: 22.0'f32,
fallbackTypefaceIds: @[arabic, hebrew],
features: @[fontFeature("kern"), fontFeature("liga")],
variations: @[fontVariation("wght", 560.0'f32)],
)

let layout = typeset(
rect(0, 0, 520, 120),
[span(bodyFont, rgba(30, 34, 40, 255), "Hello שלום السلام")],
minContent = false,
wrap = true,
)
```

Useful backend modes:

- `-d:figdrawTextBackend=pixie`: default Pixie layout and raster path.
- `-d:figdrawTextBackend=harfbuzzy`: Harfbuzzy shaping with glyph-id
rasterization.
- `-d:figdrawTextBackend=hybrid`: Harfbuzzy layout converted through the Pixie
compatibility raster path, useful for diagnostics.

When shaping is enabled, one visual glyph is not necessarily one source rune.
Use the source-aware helpers for selection, hit testing, and carets:

- `selectionRectsFor(sourceRange)`: merged visual selection bands for source
rune ranges.
- `glyphSelectionRectsFor(sourceRange)`: raw per-glyph rectangles for
diagnostics.
- `sourceRuneRangeAt(point)`: source range under a local text-layout point.
- `caretPositionsFor(sourceRune)`: visual caret rectangles for a source
insertion index, including split positions at bidi boundaries.

See [docs/font_shaping.md](docs/font_shaping.md) for the data model details and
[examples/windy_text_shaping_demo.nim](examples/windy_text_shaping_demo.nim) for
a complete Arabic, Hebrew, Devanagari, fallback, and ligature demo.

## Layers and ZLevel

`Renders` is an ordered table of `ZLevel -> RenderList`. Lower zlevels are drawn first, so higher
Expand Down
2 changes: 2 additions & 0 deletions config.nims
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,8 @@ task test, "run unit test":
for file in listFiles("examples"):
if file.startsWith("examples/windy_") and file.endsWith(".nim"):
nimExec("c", file)
elif file.startsWith("examples/siwin_") and file.endsWith(".nim"):
nimExec("c", file)
elif file.startsWith("examples/sdl2_") and file.endsWith(".nim"):
if enableSdl2:
nimExec("c", file, "-d:figdraw.metal=off -d:figdraw.vulkan=off")
Expand Down
Loading
Loading