Commit Graph

875 Commits

Author SHA1 Message Date
Mitchell Hashimoto
2f1a30ddb0 font: add Windows font discovery backend (#12386)
Adds a FreeType-based `Discover` implementation for Windows. It walks
the system font directory (`%SYSTEMROOT%\Fonts`) and the per-user
directory (`%LOCALAPPDATA%\Microsoft\Windows\Fonts`), matches
descriptors by FreeType `family_name` (falling back to the SFNT name
table), and, when a codepoint is in the descriptor, filters on CMap
coverage.

Wired up as a new `.freetype_windows` backend which `Backend.default()`
now returns on Windows. Existing freetype-only paths are untouched and
no other platform is affected; cross-platform switches were extended to
handle the new enum value the same way they handle `.freetype`.

With this in place, the standard code paths (`+list-fonts`,
`SharedGridSet` font-family lookup, `CodepointResolver` fallback) work
on Windows without any `os.tag == .windows` branches in the caller.

Verified by the `build-libghostty-windows-gnu` CI job. No runtime binary
ships yet on Windows (no apprt), but this is a drop-in for the discovery
API that the Win32 apprt (and the revisited `+list-fonts` PR #12384)
will use. Once this lands, #12384 can be closed and `+list-fonts` will
work on Windows through the ordinary discovery code path, which
addresses the review feedback that `+list-fonts` should only show fonts
the internal discovery can find.

---

AI usage disclosure: developed with Claude Code (Claude Opus 4.7).
Claude drafted the implementation based on my design direction -- I
picked the "add a Discover backend" shape over the ad-hoc approach in
the earlier `+list-fonts` PR. I reviewed each diff and validated it with
a Windows GNU-ABI smoke build before pushing.

Part of the Win32 apprt upstreaming series (see discussion #2563 /
mattn/ghostty#1).
2026-04-23 10:45:50 -07:00
Qwerasd
464c50457b font/opentype: accept header-only simple glyf entry 2026-04-23 12:41:14 -04:00
Yasuhiro Matsumoto
0343a4d98f address review: update DeferredFace test discover callsites
Two more holdouts in DeferredFace.zig test helpers calling
Fontconfig.init / CoreText.init with no args; Nix test CI surfaced
them for the fontconfig path.

Co-authored-by: Claude <noreply@anthropic.com>
2026-04-24 01:40:01 +09:00
Yasuhiro Matsumoto
fe725b5da1 address review: update shaper test discover callsites
CI on Windows (MSVC) surfaced three remaining callers of the old
zero-arg `Discover.init()` in shaper test helpers that the earlier
commit missed. Pass `lib` to match the new signature.

Co-authored-by: Claude <noreply@anthropic.com>
2026-04-24 01:27:58 +09:00
Yasuhiro Matsumoto
5aef2541b0 address review: Discover.init takes a Library across all backends
Per review feedback, drop the `if (Discover == Windows)` comptime
branches in SharedGridSet and list_fonts by making every backend's
`init` take a Library and ignore it when unused. Call sites just do
`Discover.init(self.font_lib)` now.

Also adds a discovery test for the Windows backend that looks up
Arial and checks the returned face has the 'A' codepoint.

Co-authored-by: Claude <noreply@anthropic.com>
2026-04-23 23:06:21 +09:00
Yasuhiro Matsumoto
fe2a909782 font/discovery: use %SYSTEMROOT%\Fonts instead of a hardcoded path
Resolve the system font directory from SYSTEMROOT rather than assuming
it lives on C:. If SYSTEMROOT is somehow unset we skip the system
directory instead of falling back to a literal drive letter.

Co-authored-by: Claude <noreply@anthropic.com>
2026-04-23 14:32:49 +09:00
Yasuhiro Matsumoto
61fce4d0a4 font: add Windows font discovery backend
Adds a FreeType-based Discover implementation for Windows that walks
the system (C:\Windows\Fonts) and per-user
(%LOCALAPPDATA%\Microsoft\Windows\Fonts) font directories, matching
descriptors via family_name / SFNT name table and optionally codepoint
presence.

Wired up as a new .freetype_windows backend which Backend.default() now
returns on Windows. Existing freetype-only paths are untouched.

With this in place, standard code paths -- +list-fonts, SharedGridSet
font-family lookup, CodepointResolver fallback -- work on Windows
without any os.tag == .windows branches in the caller.

Co-authored-by: Claude <noreply@anthropic.com>
2026-04-23 14:32:47 +09:00
Qwerasd
50869952af font/opentype: use packed struct for glyf point flags
Also fixes a logic bug where we weren't counting the length of x
coordinates and y coordinates correctly when we had repeated flags.
2026-04-22 13:40:24 -04:00
Qwerasd
d778be20dd font/opentype: add glyf table entry validation
We want to have this for the glyph protocol so that we can validate
passed glyf data in libghostty without having to link freetype or
anything like that.
2026-04-21 19:27:54 -04:00
Alessandro De Blasis
650b9d470a font: handle CRLF line endings in octants.txt parsing
Trim trailing \r when splitting octants.txt by \n at comptime. On
Windows, git may convert LF to CRLF on checkout, leaving \r at the
end of each line. Without trimming, the parser tries to use \r as
a struct field name in @field(), causing a compile error.

Follows the same pattern used in x11_color.zig for rgb.txt parsing.
2026-03-27 06:11:20 -07:00
Alessandro De Blasis
ce99300513 build: fix freetype C enum signedness for MSVC
MSVC translates C enums as signed int, while GCC/Clang uses unsigned
int. The freetype Zig bindings hardcode c_uint for enum backing types,
causing type mismatches when compiling with MSVC target.

Fix by adding @intCast at call sites where enum values are passed to
C functions, and @bitCast for the glyph format tag extraction where
bit-shift operations require unsigned integers.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-24 17:12:35 +01:00
Jonathan Marler
542d6aa14d windows: avoid fontconfig and ensure build compiles 2026-03-20 10:27:40 -06:00
Kat
6fabf775bb Lots of duplicate word typos + typo. 2026-03-16 09:19:09 +11:00
Mitchell Hashimoto
2044e5030f terminal: make stream processing infallible
The terminal.Stream next/nextSlice functions can now no longer fail.
All prior failure modes were fully isolated in the handler `vt`
callbacks. As such, vt callbacks are now required to not return an error
and handle their own errors somehow.

Allowing streams to be fallible before was an incorrect design. It
caused problematic scenarios like in `nextSlice` early terminating
processing due to handler errors. This should not be possible.

There is no safe way to bubble up vt errors through the stream because
if nextSlice is called and multiple errors are returned, we can't
coalesce them. We could modify that to return a partial result but its
just more work for stream that is unnecessary. The handler can do all of
this.

This work was discovered due to cleanups to prepare for more C APIs.
Less errors make C APIs easier to implement! And, it helps clean up our
Zig, too.
2026-03-13 13:56:14 -07:00
Jeffrey C. Ollie
c1313294cd add comments about why tests are disabled 2026-03-10 13:29:50 -05:00
Jeffrey C. Ollie
96f9772cd8 tests: disable tests that fail if you have locally installed fonts
If you have "Noto Sans Tai Tham" and/or "Noto Sans Javanese" installed
locally on Linux, three tests fail. This PR disables those tests until a
more permanent solution can be found.
2026-03-09 19:59:21 -05:00
Jacob Sandlund
77957aa319 Fix Bengali test due to wider grapheme 2026-01-28 09:49:07 -05:00
Jacob Sandlund
44aa761733 skip testShaperWithDiscoveredFont if discovery is not available 2026-01-26 11:00:15 -05:00
Jacob Sandlund
57f3973040 fix Tai Tham test for FreeType 2026-01-26 10:46:40 -05:00
Jacob Sandlund
c2601dc7ec don't double deinit 2026-01-26 10:32:58 -05:00
Jacob Sandlund
17af9c13e2 Bengali text fix (likely grapheme break changes) 2026-01-26 10:22:17 -05:00
Jacob Sandlund
4f7fcd5956 Skip tests if font family doesn't match 2026-01-26 10:14:03 -05:00
Jacob Sandlund
3b75d25e6c fix typo 2026-01-23 08:48:16 -05:00
Jacob Sandlund
2c32e167fe Merge remote-tracking branch 'upstream/main' into harfbuzz-positions 2026-01-23 08:47:55 -05:00
Mitchell Hashimoto
01f1611c9f tripwire: change backing store from ArrayHashMap to EnumMap
This eliminates all allocation from Tripwire.
2026-01-21 15:30:49 -08:00
Mitchell Hashimoto
3fdff49a82 font: fix memory leak in SharedGrid.init on late failure
Add errdefer cleanup for codepoints and glyphs hash maps in init().
Previously, if ensureTotalCapacity or reloadMetrics() failed after
allocating these maps, they would leak.

Add tripwire test to verify all failure points in init().
2026-01-21 12:37:21 -08:00
Mitchell Hashimoto
b606b71cda font: fix missing errdefer rollback in SharedGrid.renderGlyph
Add errdefer to remove cache entry after getOrPut if subsequent
operations fail (getPresentation, atlas.grow, renderGlyph). Without
this, failed renders would leave uninitialized/garbage entries in
the glyph cache, potentially causing crashes or incorrect rendering.

Add tripwire test to verify the rollback behavior.
2026-01-21 12:23:28 -08:00
Mitchell Hashimoto
a83bd6a111 font: add tripwire tests to Atlas 2026-01-21 11:34:59 -08:00
Mitchell Hashimoto
112363c4e1 font: Collection.getEntry explicit error set 2026-01-20 11:57:22 -08:00
Mitchell Hashimoto
e875b453b7 font/shaper: hook functions can't fail 2026-01-20 11:51:01 -08:00
Mitchell Hashimoto
49b2b8d644 unicode: switch to uucode grapheme break to (mostly) match unicode spec (#9680)
This PR builds on https://github.com/ghostty-org/ghostty/pull/9678 ~so
the diff from there is included here (it's not possible to stack PRs
unless it's a PR against my own fork)--review that one first!~

This PR updates the `graphemeBreak` calculation to use `uucode`'s
`computeGraphemeBreakNoControl`, which has [tests in
uucode](215ff09730/src/x/grapheme.zig (L753))
that confirm it passes the `GraphemeBreakTest.txt` (minus some
exceptions).

Note that the `grapheme_break` (and `grapheme_break_no_control`)
property in `uucode` incorporates `emoji_modifier` and
`emoji_modifier_base`, diverging from UAX #29 but matching UTS #51. See
[this comment in
uucode](215ff09730/src/grapheme.zig (L420-L434))
for details.

The `grapheme_break_no_control` property and
`computeGraphemeBreakNoControl` both assume `control`, `cr`, and `lf`
have been filtered out, matching the current grapheme break logic in
Ghostty.

This PR keeps the `Precompute.data` logic mostly equivalent, since the
`uucode` `precomputedGraphemeBreak` lacks benchmarks in the `uucode`
repository (it was benchmarked in [the original PR adding `uucode` to
Ghostty](https://github.com/ghostty-org/ghostty/pull/8757)). Note
however, that due to `grapheme_break` being one bit larger than
`grapheme_boundary_class` and the new `BreakState` also being one bit
larger, the state jumps up by a factor of 8 (u10 -> u13), to 8KB.

## Benchmarks

~I benchmarked the old `main` version versus this PR for
`+grapheme-break` and surprisingly this PR is 2% faster (?). Looking at
the assembly though, I'm thinking something else might be causing that.
Once I get to the bottom of that I'll remove the below TODO and include
the benchmark results here.~

When seeing the speedup with `data.txt` and maybe a tiny speedup on
English wiki, I was surprised given the 1KB -> 8KB tables. Here's what
AI said when I asked it to inspect the assembly:
https://ampcode.com/threads/T-979b1743-19e7-47c9-8074-9778b4b2a61e, and
here's what it said when I asked it to predict the faster version:
https://ampcode.com/threads/T-3291dcd3-7a21-4d24-a192-7b3f6e18cd31

It looks like two loads got reordered and that put the load that
depended on stage1 -> stage2 -> stage3 second, "hiding memory latency".
So that makes the new one faster when looking up the `grapheme_break`
property. These gains go away with the Japanese and Arabic benchmarks,
which spend more time processing utf8, and may even have more grapheme
clusters too.

### with data.txt (200 MB ghostty-gen random utf8)

<img width="1822" height="464" alt="CleanShot 2025-11-26 at 08 42 03@2x"
src="https://github.com/user-attachments/assets/56d4ee98-21db-4eab-93ab-a0463a653883"
/>

### with English wiki dump

<img width="2012" height="506" alt="CleanShot 2025-11-26 at 08 43 15@2x"
src="https://github.com/user-attachments/assets/230fbfb7-272d-4a2a-93e7-7268962a9814"
/>

### with Japanese wiki dump

<img width="2008" height="518" alt="CleanShot 2025-11-26 at 08 43 49@2x"
src="https://github.com/user-attachments/assets/edb408c8-a604-4a8f-bd5b-80f19e3d65ee"
/>

### with Arabic wiki dump

<img width="2010" height="512" alt="CleanShot 2025-11-26 at 08 44 25@2x"
src="https://github.com/user-attachments/assets/81a29ac8-0586-4e82-8276-d7fa90c31c90"
/>


TODO:

* [x] Take a closer look at the assembly and understand why this PR (8
KB vs 1 KB table) is faster on my machine.
* [x] _(**edit**: checking this off because it seems unnecessary)_ If
this turns out to actually be unacceptably slower, one possibility is to
switch to `uucode`'s `precomputedGraphemeBreak` which uses a 1445 byte
table since it uses a dense table (indexed using multiplication instead
of bitCast, though, which did show up in the initial benchmarks from
https://github.com/ghostty-org/ghostty/pull/8757 a small amount.)

AI was used in some of the uucode changes in
https://github.com/ghostty-org/ghostty/pull/9678 (Amp--primarily for
tests), but everything was carefully vetted and much of it done by hand.
This PR was made without AI with the exception of consulting AI about
whether the "Prepend + ASCII" scenario is common (hopefully it's right
about that being uncommon).
2026-01-20 09:44:15 -08:00
Jacob Sandlund
c587d7a3a0 fix Bengali, Tai Tham letters, and Devanagari tests 2026-01-16 10:29:49 -05:00
Jacob Sandlund
80bf50be1d set cluster level to match CoreText logic 2026-01-16 09:39:47 -05:00
Jacob Sandlund
e48cf5a6e3 Merge remote-tracking branch 'upstream/main' into harfbuzz-positions 2026-01-16 09:05:04 -05:00
Jacob Sandlund
e7e83d6314 Update harfbuzz logic, debugging, and tests to match CoreText changes 2026-01-15 09:40:02 -05:00
Jacob Sandlund
3aba038d38 Merge remote-tracking branch 'upstream/main' into harfbuzz-positions 2026-01-14 08:56:56 -05:00
Jacob Sandlund
55583d9f27 fix Devanagari test 2026-01-13 10:36:20 -05:00
Jacob Sandlund
2a0a575065 comment fixup 2026-01-12 10:22:37 -05:00
Jacob Sandlund
9c6f40680c Merge remote-tracking branch 'upstream/main' into ligature-detect 2026-01-12 09:57:05 -05:00
Jacob Sandlund
f37b0c56ec Keep track of run's max cluster seen. 2026-01-12 09:56:31 -05:00
Jacob Sandlund
8ebb8470b7 Fix unsigned subtraction from zero 2026-01-07 10:46:01 -05:00
Jacob Sandlund
13e125a057 Add a big comment for the heuristic to detect ligatures. 2026-01-07 10:13:33 -05:00
Jacob Sandlund
48dd6314dc Use Python syntax for easier debugging 2026-01-07 09:57:16 -05:00
Britt Binler
896615c004 font: add bitmap font tests for BDF, PCF, and OTB formats
Add test coverage for bitmap font rendering using the Spleen 8x16 font
in three formats: BDF (Bitmap Distribution Format), PCF (Portable
Compiled Format), and OTB (OpenType Bitmap). Tests validate glyph
rendering against expected pixel patterns.

Addresses #8524.
2026-01-06 09:14:33 -08:00
Jacob Sandlund
f258265ef0 font/shaper: keep codepoints in same cell if detecting ligature 2026-01-06 10:30:13 -05:00
Jacob Sandlund
15899b70fb simplify run_offset_x comment 2026-01-05 10:35:22 -05:00
Jacob Sandlund
7aec7effea Add test for Tai Tham letter position.y difference 2026-01-05 10:12:05 -05:00
Jacob Sandlund
41f63384f5 Turn off debugging 2026-01-05 09:59:37 -05:00
Jacob Sandlund
d38558aee1 Show current cp with ▸ in list of cps 2026-01-05 09:34:17 -05:00
Jacob Sandlund
1d4a5d91e0 More debugging for position.y differences 2026-01-04 21:24:45 -05:00