# How ImageKit Manages Embedded Fonts and Assets in Rust

> Learn how ImageKit in Rust embeds fonts and assets using rust-embed for self-contained text watermarking. Discover runtime asset retrieval with Asset::get.

- Repository: [hzbd/imagekit](https://github.com/hzbd/imagekit)
- Tags: internals
- Published: 2026-03-03

---

**ImageKit bundles font files directly into the binary at compile time using the `rust-embed` crate, then retrieves them at runtime via the `Asset::get` method to enable self-contained text watermarking without external file dependencies.**

ImageKit is a Rust image processing library that requires reliable access to font assets for rendering watermarks. Rather than depending on system-installed fonts or fragile file paths, it manages embedded assets by compiling them into the binary itself. This approach ensures the library works identically across all deployment environments.

## Embedding Font Assets at Compile Time

ImageKit uses the **`rust-embed`** crate (version 8.0, as specified in [[`Cargo.toml`](https://github.com/hzbd/imagekit/blob/main/Cargo.toml)](https://github.com/hzbd/imagekit/blob/master/Cargo.toml#L12)) to embed files from the repository's `assets/` directory. The embedding logic is defined in [[`src/assets.rs`](https://github.com/hzbd/imagekit/blob/main/src/assets.rs)](https://github.com/hzbd/imagekit/blob/master/src/assets.rs), where the `Asset` struct derives `RustEmbed`:

```rust
#[derive(RustEmbed)]
#[folder = "assets/"]
pub struct Asset;

```

The `#[folder = "assets/"]` attribute instructs the crate to scan the specified directory during compilation and include every file—such as `Roboto-Regular.ttf`, `SourceHanSansSC-Regular.otf`, and `NotoSansThai-Regular.ttf`—as static bytes inside the final binary.

## Loading Embedded Fonts at Runtime

At library initialization, ImageKit loads the embedded font bytes from the binary using the `Asset::get` method. In [[`src/lib.rs`](https://github.com/hzbd/imagekit/blob/main/src/lib.rs)](https://github.com/hzbd/imagekit/blob/master/src/lib.rs#L24‑L40), the code retrieves each font file by name and converts the bytes into `Font` objects using the `rusttype` crate:

```rust
use assets::Asset;
use rusttype::Font;

// Primary Latin font
let primary_font_data = Asset::get("Roboto-Regular.ttf")
    .context("Could not find font 'Roboto-Regular.ttf'")?;
let primary_font = Font::try_from_vec(primary_font_data.data.into_owned())
    .context("Error constructing primary font")?;

// CJK fallback
let cjk_font_data = Asset::get("SourceHanSansSC-Regular.otf")
    .context("Could not find CJK font")?;
let cjk_font = Font::try_from_vec(cjk_font_data.data.into_owned())
    .context("Error constructing CJK font")?;

// Thai fallback
let thai_font_data = Asset::get("NotoSansThai-Regular.ttf")
    .context("Could not find Thai font")?;
let thai_font = Font::try_from_vec(thai_font_data.data.into_owned())
    .context("Error constructing Thai font")?;

```

The `Asset::get` function returns an `EmbeddedFile` struct containing the file contents as `Cow<'static, [u8]>`. The `.data.into_owned()` call converts this into an owned `Vec<u8>`, which `Font::try_from_vec` requires to construct valid font objects.

## Font Fallback Strategy for International Text

Once loaded, the fonts are stored in an **`Arc<Vec<Font<'static>>>`** to allow thread-safe sharing across the application. When the `processor::add_watermark` function renders text, it iterates through this font vector to locate the appropriate glyph for each character. If the primary font (Roboto) lacks a glyph—common with Chinese, Japanese, Korean (CJK), or Thai characters—the renderer automatically falls back to `SourceHanSansSC-Regular.otf` or `NotoSansThai-Regular.ttf`. This ensures watermarks render correctly across multiple languages without requiring users to install system fonts.

## Testing with Embedded Assets

The library's integration tests verify that embedded assets are accessible and functional without external file I/O. In [[`tests/integration_test.rs`](https://github.com/hzbd/imagekit/blob/main/tests/integration_test.rs)](https://github.com/hzbd/imagekit/blob/master/tests/integration_test.rs#L20‑L28), tests load the same CJK font using the identical `Asset::get` pattern:

```rust
let fallback_font_data = Asset::get("SourceHanSansSC-Regular.otf")
    .context("Test setup failed: Could not find 'SourceHanSansSC-Regular.otf'")?;
let fallback_font = Font::try_from_vec(fallback_font_data.data.into_owned())
    .context("Test setup failed: Could not parse fallback font")?;

```

This demonstrates that the `rust-embed` mechanism works consistently in both production code and test environments, eliminating the need for mock file systems or test-specific asset paths.

## Summary

- **Compile-time embedding**: The `Asset` struct in [`src/assets.rs`](https://github.com/hzbd/imagekit/blob/main/src/assets.rs) uses `#[derive(RustEmbed)]` and `#[folder = "assets/"]` to bundle files into the binary.
- **Runtime access**: Call `Asset::get("filename.ttf")` to retrieve embedded bytes as an `EmbeddedFile` object.
- **Font construction**: Convert embedded data to `Font` objects using `Font::try_from_vec(data.into_owned())`.
- **Multi-language support**: Three fonts are embedded—Roboto-Regular, SourceHanSansSC-Regular, and NotoSansThai-Regular—with automatic glyph fallback.
- **Thread-safe storage**: Fonts are stored in `Arc<Vec<Font<'static>>>` for safe sharing across processing threads.
- **Test compatibility**: Integration tests use the same `Asset::get` API to verify rendering without external dependencies.

## Frequently Asked Questions

### What crate does ImageKit use to embed font files?

ImageKit uses **`rust-embed`** version 8.0, declared in [`Cargo.toml`](https://github.com/hzbd/imagekit/blob/main/Cargo.toml). This crate generates the `Asset` struct at compile time, embedding all files from the `assets/` directory directly into the binary's read-only data section.

### How do you access an embedded font at runtime in ImageKit?

You access embedded fonts by calling **`Asset::get("filename.ttf")`**, which returns an `Option<EmbeddedFile>`. The `EmbeddedFile.data` field contains the file bytes as `Cow<'static, [u8]>`, which you can convert to an owned vector and pass to `Font::try_from_vec()` to create a usable font object.

### Does ImageKit support fallback fonts for different languages?

Yes. ImageKit loads three fonts at startup: **Roboto-Regular.ttf** for Latin text, **SourceHanSansSC-Regular.otf** for CJK characters, and **NotoSansThai-Regular.ttf** for Thai. During watermark rendering, the system iterates through these fonts in order to find a matching glyph, automatically falling back when the primary font lacks the required character.

### Can embedded assets be used in ImageKit's test suite?

Yes. The integration tests in [`tests/integration_test.rs`](https://github.com/hzbd/imagekit/blob/main/tests/integration_test.rs) import the same `Asset` struct and use `Asset::get()` to load fonts. This allows tests to verify CJK rendering and other text features without relying on external file paths or environment-specific font installations.