grida icon indicating copy to clipboard operation
grida copied to clipboard

skia-safe 0.91.0

Open softmarshmallow opened this issue 1 week ago • 2 comments

  • [x] update path building practices
  • [ ] update font managing practices (needs review)
  • [ ] introduce tests including actual rendering pipeline, automated, for testing wasm bins

Investigate: texts wont render properly only on wasm target (https://github.com/rust-skia/rust-skia/issues/1234)

need comprehensive render tests over text rendering and path building as 91+ introduces major changes.

Links:

  • https://github.com/rust-skia/rust-skia/compare/0.89.0...0.91.0
  • https://github.com/rust-skia/rust-skia/compare/0.90.0...0.91.0

Internal notes (AI researched, may contain false):

Font Manager Changes in skia-safe 0.89.0 → 0.91.0

Background: Upgrading from skia-safe 0.89.0 to 0.91.0 pulls in Skia’s milestone 143, which significantly changed font handling. In particular, Skia removed the use of a built-in “default” font manager (especially in builds with no system fonts, like WASM). As a result, the default font/typeface is now essentially empty, meaning if you don’t explicitly provide a font, Skia will find nothing and render the “.notdef” missing-glyph box . This is why after the upgrade your text shows no characters – the font manager isn’t automatically picking up any fonts.

New FontMgr::custom_empty API: The Rust bindings responded to this change in PR #1224, which introduced a new FontMgr::custom_empty() function . This function creates a font manager with no built-in fonts. The presence of this API (and the lack of a detailed changelog) hints that the default FontMgr::new() no longer returns any usable fonts in certain contexts. In a WASM build (where system fonts are disabled), FontMgr::new() will effectively be empty – hence the need for a “custom empty” font manager that you populate manually. The 0.91.0 release notes explicitly mention this addition, aligning with Skia’s removal of default fonts .

Origin of the Change: Upstream Skia decided to remove the global default SkFontMgr to avoid conflicts and give developers control over font loading (see Skia milestone 143). In practice, this means Skia no longer provides any fallback fonts out-of-the-box on platforms like WASM. The Rust-skia maintainers noted that “there were some changes with the default font/typeface (empty since 0.70.0)”, advising developers to be explicit about which fonts to use . Your upgrade to 0.91.0 hit this exact issue: the default font manager became empty, so without changes your custom fonts weren’t being found or used – resulting in only “notdef” glyphs being drawn.

Related Issues: This change has been observed in the community. For example, an issue report showed that trying to load fonts via FontMgr on WASM failed silently. The user found that FontMgr::new().new_from_data(...) always returned an error (no font) on WASM, whereas using Typeface::from_data(...) directly did work . In other words, creating a typeface from data was fine, but using the font manager’s method was not – because the font manager was essentially empty in that environment. This aligns with what you’re seeing after the upgrade.

How to Migrate (Avoiding “notdef” Glyphs)

To fix font rendering after 0.91.0, explicitly supply and use your custom fonts – you can no longer rely on any default font fallback. Here are steps you can take:
• Use a Typeface Provider: If you’re using Skia’s text layout (e.g. skia_safe::textlayout module), create a TypefaceFontProvider and register your font data with it (via register_typeface). Then use a FontCollection to set this provider as the default. Make sure to call font_collection.set_default_font_manager(FontMgr::custom_empty(), some_provider) or similar, so that the text shaper knows about your fonts and doesn’t try to use system fonts. This ensures your text runs through the fonts you provided and nothing else.
• Directly set Typefaces: If you are drawing text via SkPaint/Canvas::drawTextBlob (without the higher-level paragraph API), load your custom font with Typeface::from_data (or FontMgr::new_from_data if available) and then set that Typeface on your Font or Paint. For example, in skia-safe you might do let tf = Typeface::from_data(font_data, None).unwrap() and then paint.set_typeface(tf). This way you bypass the default font manager entirely and use your font directly. The Rust-skia maintainer specifically suggests using TextStyle::set_typeface() to avoid issues with empty defaults .
• Avoid relying on FontMgr::new(): In WASM builds, FontMgr::new() (which creates a system font manager) is effectively a no-op now – it returns an object with no fonts. Instead, use FontMgr::custom_empty()  or FontMgr::empty() and then populate it with fonts if needed. The new custom_empty was added precisely to let you create a font manager that you control. For instance, you could call let fm = FontMgr::custom_empty(); and then use Skia’s C++ API (via SkTypeface or font provider) to add fonts to that manager.

By updating your code to explicitly load and set your custom fonts, you should regain proper text rendering. In summary, the upgrade to 0.91.0 broke font rendering because Skia-safe stopped providing any default fonts – the change originates from Skia’s decision to remove the default font manager, captured in Rust-skia’s 0.91.0 release (see PR #1224) . After migrating to the new approach (using custom_empty font manager or setting typefaces directly), you’ll avoid the “notdef” issue and get your custom fonts rendering again.

References:
• Rust-skia 0.91.0 release notes – added FontMgr::custom_empty and updated to Skia m143 .
• Maintainer’s Q&A on font default being empty (no default fonts since 0.70) .
• Issue example (WASM): using FontMgr::new().new_from_data yielded no font; direct Typeface::from_data was required .

softmarshmallow avatar Dec 09 '25 06:12 softmarshmallow

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Review Updated (UTC)
backgrounds Ready Ready Preview, Comment Dec 14, 2025 9:14am
blog Ready Ready Preview, Comment Dec 14, 2025 9:14am
docs Ready Ready Preview, Comment Dec 14, 2025 9:14am
grida Error Error Dec 14, 2025 9:14am
viewer Ready Ready Preview, Comment Dec 14, 2025 9:14am
2 Skipped Deployments
Project Deployment Review Updated (UTC)
code Ignored Ignored Dec 14, 2025 9:14am
legacy Ignored Ignored Dec 14, 2025 9:14am

vercel[bot] avatar Dec 09 '25 06:12 vercel[bot]

Walkthrough

Upgrades skia-safe to 0.91.0 and consistently migrates path construction from mutable Path APIs to skia_safe::PathBuilder, replacing in-place transforms with functional make_transform and adding a new paragraph_to_vector_network helper.

Changes

Cohort / File(s) Summary
Dependency Manifests
crates/grida-canvas/Cargo.toml, crates/grida-dev/Cargo.toml, crates/grida-canvas-wasm/package.json
Bump skia-safe to 0.91.0 (features updated in grida-dev); package version bump for wasm crate.
Examples
crates/grida-canvas/examples/*
crates/grida-canvas/examples/golden_boolop.rs, .../golden_path_discrete.rs, .../golden_sk_mask.rs, .../golden_sk_paints.rs, .../golden_sk_paragraph_path_vector.rs, .../golden_sk_svg_filters/main.rs, .../golden_sk_text_backdrop_blur_path.rs
Replace manual Path construction with Path::rect/Path::circle and PathBuilder usage; remove mutable Path mutations.
Devtools & Overlays
crates/grida-canvas/src/devtools/*
.../hit_overlay.rs, .../ruler_overlay.rs, .../stroke_overlay.rs, .../text_overlay.rs
Swap path.transform(...) for path = path.make_transform(...); where building paths, use PathBuilder and detach().
Painter & Hit-testing
crates/grida-canvas/src/painter/*, crates/grida-canvas/src/hittest/hit_tester.rs
.../geometry.rs, .../layer.rs, .../painter.rs, .../text_stroke.rs, .../hit_tester.rs
Migrate glyph/mask/path composition to PathBuilder; apply make_transform instead of in-place transforms; adjust per-glyph offset handling and detach builders.
Shapes
crates/grida-canvas/src/shape/*
.../ellipse.rs, .../ellipse_ring.rs, .../ellipse_ring_sector.rs, .../ellipse_sector.rs, .../polygon.rs, .../rect.rs, .../rrect.rs, .../srrect_orthogonal.rs, .../stroke_rect.rs, .../stroke_varwidth.rs
Replace Path mutations with PathBuilder across shape builders; use direct constructors (Path::oval, Path::rect, Path::polygon) where applicable; update helper signature for catmull segments to accept &mut PathBuilder.
Core Path Utilities & Text
crates/grida-canvas/src/*
.../sk_tiny/mod.rs, .../svg/from_usvg_tree.rs, .../text/mod.rs, .../vectornetwork/vn.rs, .../window/application.rs
Use PathBuilder for segment assembly; change mutable path parameters to immutable where appropriate; add pub fn paragraph_to_vector_network(paragraph: &mut Paragraph) -> VectorNetwork.
Tests
crates/grida-canvas/tests/*
.../dashed_stroke.rs, .../open_path_stroke_align.rs, .../stroke_dash_width.rs
Update test path setup to build paths with PathBuilder and detach() instead of mutating Path.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

  • Wide but homogeneous refactor: many files (~40+) changed with the same patterns (PathBuilder adoption, make_transform).
  • Review focus:
    • Confirm PathBuilder usage and detach() semantics match prior mutable-path behavior.
    • Verify make_transform yields equivalent transformed geometry in hit-testing, overlays, and painter flows.
    • Inspect stroke_varwidth::add_catmull_segments signature change and all updated call sites.
    • Check new public helper paragraph_to_vector_network for correctness and potential API stability.

Possibly related PRs

  • gridaco/grida#402 — Overlaps in shape module changes and Skia path handling patterns.
  • gridaco/grida#395 — Related widespread migration from mutable Path APIs to PathBuilder/make_transform.
  • gridaco/grida#427 — Adds/overlaps paragraph_to_vector_network and text/path construction changes.

Suggested labels

canvas, wasm32, migration

Poem

🐰
I hop and stitch each curve anew,
Builders lay the pathways true.
From mutable mud to tidy art,
Skia’s new steps — a gentle start.
— your refactoring rabbit 🎨✨

Pre-merge checks and finishing touches

✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title "skia-safe 0.91.0" directly and clearly identifies the primary change: upgrading the skia-safe dependency to version 0.91.0. The changeset extensively reflects this upgrade across multiple files, path building refactoring patterns, and dependency updates in Cargo.toml files.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
✨ Finishing touches
🧪 Generate unit tests (beta)
  • [ ] Create PR with unit tests
  • [ ] Post copyable unit tests in a comment
  • [ ] Commit unit tests in branch skia-91

📜 Recent review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 4c9c6aee60e4ef41d52079cb98eafe1e6df1b1f1 and b88154955f18427fc4fb9f05e575e19f2d65134a.

⛔ Files ignored due to path filters (1)
  • crates/grida-canvas-wasm/lib/bin/grida_canvas_wasm.wasm is excluded by !**/*.wasm
📒 Files selected for processing (2)
  • crates/grida-canvas-wasm/package.json (1 hunks)
  • crates/grida-dev/Cargo.toml (1 hunks)
✅ Files skipped from review due to trivial changes (1)
  • crates/grida-canvas-wasm/package.json
🧰 Additional context used
🧠 Learnings (16)
📓 Common learnings
Learnt from: CR
Repo: gridaco/grida PR: 0
File: crates/grida-canvas/AGENTS.md:0-0
Timestamp: 2025-12-01T00:22:28.164Z
Learning: Applies to crates/grida-canvas/**/*.rs : Use `skia-safe` crate for all painting and rendering operations
Learnt from: CR
Repo: gridaco/grida PR: 0
File: crates/grida-canvas-fonts/AGENTS.md:0-0
Timestamp: 2025-12-01T00:22:06.800Z
Learning: Applies to crates/grida-canvas-fonts/**/*.rs : Run all tests with: `cargo test`
Learnt from: CR
Repo: gridaco/grida PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-01T00:21:48.564Z
Learning: Use Skia as the graphics backend for 2D graphics bound with skia-safe
Learnt from: CR
Repo: gridaco/grida PR: 0
File: editor/app/(tools)/tools/halftone/AGENTS.md:0-0
Timestamp: 2025-12-01T00:22:56.899Z
Learning: Applies to editor/app/(tools)/tools/halftone/app/(tools)/tools/halftone/_page.tsx : Use Canvas 2D API with path commands for rendering geometric shapes (circle, square, triangle, etc.)
📚 Learning: 2025-12-01T00:22:28.164Z
Learnt from: CR
Repo: gridaco/grida PR: 0
File: crates/grida-canvas/AGENTS.md:0-0
Timestamp: 2025-12-01T00:22:28.164Z
Learning: Applies to crates/grida-canvas/**/*.rs : Use `skia-safe` crate for all painting and rendering operations

Applied to files:

  • crates/grida-dev/Cargo.toml
📚 Learning: 2025-12-01T00:22:06.800Z
Learnt from: CR
Repo: gridaco/grida PR: 0
File: crates/grida-canvas-fonts/AGENTS.md:0-0
Timestamp: 2025-12-01T00:22:06.800Z
Learning: Applies to crates/grida-canvas-fonts/**/Cargo.toml : Use `ttf-parser = "0.25"` as the required dependency for font parsing backend

Applied to files:

  • crates/grida-dev/Cargo.toml
📚 Learning: 2025-12-01T00:22:36.510Z
Learnt from: CR
Repo: gridaco/grida PR: 0
File: crates/grida-dev/AGENTS.md:0-0
Timestamp: 2025-12-01T00:22:36.510Z
Learning: Applies to crates/grida-dev/grida-dev/Cargo.toml : The `grida-dev` crate is marked `publish = false` and is intended solely for local development workflows and devtools

Applied to files:

  • crates/grida-dev/Cargo.toml
📚 Learning: 2025-12-01T00:22:19.083Z
Learnt from: CR
Repo: gridaco/grida PR: 0
File: crates/grida-canvas-wasm/AGENTS.md:0-0
Timestamp: 2025-12-01T00:22:19.083Z
Learning: Applies to crates/grida-canvas-wasm/**/main.rs : Update `grida-canvas-wasm.d.ts` TypeScript definitions file when new APIs are introduced via `main.rs`

Applied to files:

  • crates/grida-dev/Cargo.toml
📚 Learning: 2025-12-01T00:22:06.800Z
Learnt from: CR
Repo: gridaco/grida PR: 0
File: crates/grida-canvas-fonts/AGENTS.md:0-0
Timestamp: 2025-12-01T00:22:06.800Z
Learning: Applies to crates/grida-canvas-fonts/**/Cargo.toml : Include dev-dependency `serde_json = "1.0"` for JSON testing

Applied to files:

  • crates/grida-dev/Cargo.toml
📚 Learning: 2025-12-01T00:22:28.164Z
Learnt from: CR
Repo: gridaco/grida PR: 0
File: crates/grida-canvas/AGENTS.md:0-0
Timestamp: 2025-12-01T00:22:28.164Z
Learning: Applies to crates/grida-canvas/**/*.rs : Use `math2` crate for all geometry and common math operations

Applied to files:

  • crates/grida-dev/Cargo.toml
📚 Learning: 2025-12-01T00:22:06.800Z
Learnt from: CR
Repo: gridaco/grida PR: 0
File: crates/grida-canvas-fonts/AGENTS.md:0-0
Timestamp: 2025-12-01T00:22:06.800Z
Learning: Applies to crates/grida-canvas-fonts/**/*.rs : Run all tests with: `cargo test`

Applied to files:

  • crates/grida-dev/Cargo.toml
📚 Learning: 2025-12-01T00:22:06.800Z
Learnt from: CR
Repo: gridaco/grida PR: 0
File: crates/grida-canvas-fonts/AGENTS.md:0-0
Timestamp: 2025-12-01T00:22:06.800Z
Learning: Applies to crates/grida-canvas-fonts/**/Cargo.toml : Include optional `serde = "1.0"` with `derive` feature for JSON serialization support

Applied to files:

  • crates/grida-dev/Cargo.toml
📚 Learning: 2025-12-01T00:22:19.083Z
Learnt from: CR
Repo: gridaco/grida PR: 0
File: crates/grida-canvas-wasm/AGENTS.md:0-0
Timestamp: 2025-12-01T00:22:19.083Z
Learning: Applies to crates/grida-canvas-wasm/**/+(grida-canvas-wasm.js|grida-canvas-wasm.wasm) : Include WASM artifacts (`grida-canvas-wasm.js` and `grida-canvas-wasm.wasm`) in git for faster CI builds

Applied to files:

  • crates/grida-dev/Cargo.toml
📚 Learning: 2025-12-01T00:22:19.083Z
Learnt from: CR
Repo: gridaco/grida PR: 0
File: crates/grida-canvas-wasm/AGENTS.md:0-0
Timestamp: 2025-12-01T00:22:19.083Z
Learning: Applies to crates/grida-canvas-wasm/**/Cargo.toml : Use the target `wasm32-unknown-emscripten` when building Rust code for WebAssembly compilation

Applied to files:

  • crates/grida-dev/Cargo.toml
📚 Learning: 2025-12-01T00:22:06.800Z
Learnt from: CR
Repo: gridaco/grida PR: 0
File: crates/grida-canvas-fonts/AGENTS.md:0-0
Timestamp: 2025-12-01T00:22:06.800Z
Learning: Applies to crates/grida-canvas-fonts/**/serde_test.rs : Run tests with serde feature using: `cargo test --features serde`

Applied to files:

  • crates/grida-dev/Cargo.toml
📚 Learning: 2025-12-01T00:22:19.083Z
Learnt from: CR
Repo: gridaco/grida PR: 0
File: crates/grida-canvas-wasm/AGENTS.md:0-0
Timestamp: 2025-12-01T00:22:19.083Z
Learning: Applies to crates/grida-canvas-wasm/**/Cargo.toml : Set environment variables `CC=emcc`, `CXX=em++`, and `AR=emar` when building Rust code for WebAssembly targets

Applied to files:

  • crates/grida-dev/Cargo.toml
📚 Learning: 2025-12-01T00:22:36.510Z
Learnt from: CR
Repo: gridaco/grida PR: 0
File: crates/grida-dev/AGENTS.md:0-0
Timestamp: 2025-12-01T00:22:36.510Z
Learning: The `cg` crate must remain platform-agnostic; all winit/glutin integration code must live in the `grida-dev` crate

Applied to files:

  • crates/grida-dev/Cargo.toml
📚 Learning: 2025-12-01T00:22:28.164Z
Learnt from: CR
Repo: gridaco/grida PR: 0
File: crates/grida-canvas/AGENTS.md:0-0
Timestamp: 2025-12-01T00:22:28.164Z
Learning: Applies to crates/grida-canvas/**/*.rs : All internal structs (NodeRecs, SceneGraph, caches) must use `NodeId` (u64) for high-performance counter-based IDs in the rendering engine

Applied to files:

  • crates/grida-dev/Cargo.toml
📚 Learning: 2025-12-01T00:21:48.564Z
Learnt from: CR
Repo: gridaco/grida PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-01T00:21:48.564Z
Learning: Applies to crates/**/*.rs : Use Rust 2024 edition for WASM builds and graphics core

Applied to files:

  • crates/grida-dev/Cargo.toml
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: cargo test
  • GitHub Check: test
🔇 Additional comments (1)
crates/grida-dev/Cargo.toml (1)

22-22: Feature alignment is consistent with grida-canvas.

The expanded feature set ["gpu", "gl", "textlayout", "pdf", "svg", "webp"] matches grida-canvas exactly, and the version bump to 0.91.0 aligns with the broader workspace update.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

coderabbitai[bot] avatar Dec 09 '25 06:12 coderabbitai[bot]

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard. To continue using code reviews, you can upgrade your account or add credits to your account and enable them for code reviews in your settings.