Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
Binary file modified crates/story-web/fonts/IBMPlexSans-Regular.ttf
Binary file not shown.
Binary file modified crates/story-web/fonts/Inter-Regular.ttf
Binary file not shown.
Binary file modified crates/story-web/fonts/JetBrainsMono-Regular.ttf
Binary file not shown.
Binary file modified crates/story-web/fonts/NotoSansSC-Regular-subset.ttf
Binary file not shown.
14 changes: 12 additions & 2 deletions crates/story-web/scripts/subset-fonts.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,14 @@
"""

from pathlib import Path
import re
import subprocess
import tempfile


ROOT = Path(__file__).resolve().parents[3]
FONTS = ROOT / "crates/story-web/fonts"
INCLUDE_STR = re.compile(r'include_str!\(\s*"([^"]+)"\s*\)')


def project_text() -> str:
Expand All @@ -27,7 +29,12 @@ def project_text() -> str:
text = ""
for root in roots:
for path in sorted(root.rglob("*.rs")):
text += path.read_text(encoding="utf-8")
source = path.read_text(encoding="utf-8")
text += source
# Files pulled in with `include_str!` are rendered too, such as the
# README the welcome story shows with its box-drawing crate tree.
for included in INCLUDE_STR.findall(source):
text += (path.parent / included).read_text(encoding="utf-8")
# UI punctuation plus a compact set used by examples and empty/error states.
# Emoji are not subset: the web platform draws them through the browser.
return "".join(sorted(set(text + "–—…‘’“”•→←↑↓✓✕⚠★☆❤")))
Expand All @@ -36,8 +43,11 @@ def project_text() -> str:
def subset(source: Path, output: Path, text_file: Path) -> None:
subprocess.run(
[
"uvx", "--from", "fonttools[woff]", "pyftsubset", str(source),
"uvx", "--from", "fonttools[woff]==4.66.0", "pyftsubset", str(source),
f"--text-file={text_file}", f"--output-file={output}",
# Always write uncompressed OpenType, even from the WOFF2 Inter
# source: newer FontTools otherwise keeps the input flavor.
"--flavor=none",
"--layout-features=*", "--glyph-names", "--symbol-cmap",
"--legacy-cmap", "--notdef-glyph", "--notdef-outline",
"--recommended-glyphs", "--name-IDs=*", "--name-legacy",
Expand Down
2 changes: 1 addition & 1 deletion website/docs/fonts.md
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,7 @@ italic text; a single regular file is not a promise of all styles. Bundling
also makes a desktop app independent of whether that family is installed on
the user's machine. Keep the font's redistribution license with the app.

The [GPUI Kit web gallery](https://github.com/longbridge/gpui-kit/blob/main/crates/story-web/src/lib.rs) bundles `Inter Variable`, `JetBrains Mono`, a subset of `Noto Sans SC`, and `IBM Plex Sans` this way. Rust's [`include_bytes!`](https://doc.rust-lang.org/std/macro.include_bytes.html) puts those font bytes into the WebAssembly download. The gallery's CJK subset is about 25 KB, compared with about 1.2 MB for its source font: subset known interface copy to limit initial payload, then plan separately for arbitrary text entered by users.
The [GPUI Kit web gallery](https://github.com/longbridge/gpui-kit/blob/main/crates/story-web/src/lib.rs) bundles `Inter Variable`, `JetBrains Mono`, a subset of `Noto Sans SC`, and `IBM Plex Sans` this way. Rust's [`include_bytes!`](https://doc.rust-lang.org/std/macro.include_bytes.html) puts those font bytes into the WebAssembly download. The gallery's CJK subset is about 42 KB, compared with about 1.2 MB for its source font: subset known interface copy to limit initial payload, then plan separately for arbitrary text entered by users.

### Try a bundled font in `hello_world`

Expand Down
2 changes: 1 addition & 1 deletion website/docs/webassembly.md
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ Use this sequence when moving a desktop app to the browser: retain its GPUI view

The GPUI Web platform starts with **no installed font database**. In the gallery, `include_bytes!` embeds four subset TTF files in the WASM module: Inter for UI text, JetBrains Mono for code, Noto Sans SC for the Chinese characters used by the stories, and IBM Plex Sans for GPUI's `.SystemUIFont` alias. The last family must be loaded before the first window, because even initial text measurement can use the default window style and fail when the family is absent. After loading them with `cx.text_system().add_fonts(...)`, the gallery applies its theme and forces its UI and mono family back to bundled fonts; a selected theme can otherwise name a desktop-only family. See the [font setup and theme code](https://github.com/longbridge/gpui-kit/blob/main/crates/story-web/src/lib.rs) and [Fonts](./fonts.md).

The [subset script](https://github.com/longbridge/gpui-kit/blob/main/crates/story-web/scripts/subset-fonts.py) scans the story sources and retains their characters. In this checkout, the Noto Sans SC subset is **25,484 bytes** versus **1,213,236 bytes** for its checked-in source TTF. Those are font file sizes, not the change in compressed WASM transfer size. New text entered by a visitor is not guaranteed to be in that subset.
The [subset script](https://github.com/longbridge/gpui-kit/blob/main/crates/story-web/scripts/subset-fonts.py) scans the story sources, plus the files they pull in with `include_str!` such as the README, and retains their characters. In this checkout, the Noto Sans SC subset is **42,092 bytes** versus **1,213,236 bytes** for its checked-in source TTF. Those are font file sizes, not the change in compressed WASM transfer size. New text entered by a visitor is not guaranteed to be in that subset.

For eligible missing emoji and horizontal Han, kana and modern Hangul graphemes, `CanvasFontFallback::EmojiAndCjk` can let the browser measure and draw from its local fonts. GPUI's loaded fonts remain preferred. This fallback works on individual graphemes, so browser font coverage, spacing and typography can differ; it is not a full CJK font replacement. The default policy covers emoji only, while `Disabled` uses loaded fonts alone. The policy is chosen when constructing `WebPlatform` and cannot be changed later.

Expand Down
2 changes: 1 addition & 1 deletion website/zh-CN/docs/fonts.md
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,7 @@ Theme::update(cx, |theme| theme.font_family = "MyFont".into());

这里要写字体文件**内部**的 family 名称,它可能与文件名不同。若要稳定显示常规、粗体和斜体,应注册所需的各个字体文件;单个常规字体文件不保证所有样式。打包字体也能让桌面应用不依赖用户是否已安装该字体族。分发时要保留相应的字体许可。

[GPUI Kit Web 画廊](https://github.com/longbridge/gpui-kit/blob/main/crates/story-web/src/lib.rs)就这样打包 `Inter Variable`、`JetBrains Mono`、`Noto Sans SC` 子集和 `IBM Plex Sans`。Rust 的 [`include_bytes!`](https://doc.rust-lang.org/std/macro.include_bytes.html) 会把这些字体字节放入 WebAssembly 下载包。画廊所用的 CJK 子集约 25 KB,源字体约 1.2 MB:已知界面文案可以制作子集来控制初始体积,但用户任意输入的文字需要另行安排字体来源。
[GPUI Kit Web 画廊](https://github.com/longbridge/gpui-kit/blob/main/crates/story-web/src/lib.rs)就这样打包 `Inter Variable`、`JetBrains Mono`、`Noto Sans SC` 子集和 `IBM Plex Sans`。Rust 的 [`include_bytes!`](https://doc.rust-lang.org/std/macro.include_bytes.html) 会把这些字体字节放入 WebAssembly 下载包。画廊所用的 CJK 子集约 42 KB,源字体约 1.2 MB:已知界面文案可以制作子集来控制初始体积,但用户任意输入的文字需要另行安排字体来源。

### 跟做:在 `hello_world` 中注册打包字体

Expand Down
2 changes: 1 addition & 1 deletion website/zh-CN/docs/webassembly.md
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ make dev

GPUI Web 平台启动时的字体数据库是**空的**。画廊使用 `include_bytes!` 将四个 TTF 子集嵌入 WASM:Inter 用于界面文字,JetBrains Mono 用于代码,Noto Sans SC 包含故事中用到的汉字,IBM Plex Sans 对应 GPUI 的 `.SystemUIFont` 别名。最后这个字体必须先于首个窗口加载:初始文本测量也可能沿用窗口默认样式,如果对应字体不存在,文本系统就会报错。画廊先调用 `cx.text_system().add_fonts(...)`,再应用主题,并把界面字体与等宽字体强制设回已打包的字体;选中的主题可能指定仅桌面端存在的字体。详见[字体初始化与主题代码](https://github.com/longbridge/gpui-kit/blob/main/crates/story-web/src/lib.rs)和[字体](./fonts.md)。

[子集脚本](https://github.com/longbridge/gpui-kit/blob/main/crates/story-web/scripts/subset-fonts.py)扫描故事源码,只保留其中出现的字符。当前检出中,Noto Sans SC 子集为 **25,484 字节**,仓库内对应的原始 TTF 为 **1,213,236 字节**。这是字体文件大小,不能当作压缩后 WASM 传输量的差值。访客输入的新文字不一定包含在该子集中。
[子集脚本](https://github.com/longbridge/gpui-kit/blob/main/crates/story-web/scripts/subset-fonts.py)扫描故事源码,以及这些源码通过 `include_str!` 引入的文件(例如 README),只保留其中出现的字符。当前检出中,Noto Sans SC 子集为 **42,092 字节**,仓库内对应的原始 TTF 为 **1,213,236 字节**。这是字体文件大小,不能当作压缩后 WASM 传输量的差值。访客输入的新文字不一定包含在该子集中。

对于符合条件、打包字体缺少的 emoji,以及横排汉字、假名和现代谚文字素,`CanvasFontFallback::EmojiAndCjk` 可让浏览器用本机字体测量并绘制。GPUI 已加载的字体仍优先。回退按单个字素工作,因此字形覆盖、字距和排版特性取决于浏览器,不能代替完整的中日韩字体。默认策略仅覆盖 emoji,`Disabled` 则只使用已加载字体。策略在构造 `WebPlatform` 时确定,之后不能更改。

Expand Down
Loading