Skip to main content

bevy_woff/
lib.rs

1use bevy_app::{App, Plugin};
2#[cfg(any(feature = "woff1", feature = "woff2"))]
3use bevy_asset::{AssetApp, AssetLoader, LoadContext, io::Reader};
4#[cfg(any(feature = "woff1", feature = "woff2"))]
5use bevy_reflect::TypePath;
6#[cfg(any(feature = "woff1", feature = "woff2"))]
7use bevy_text::Font;
8
9pub struct WoffPlugin;
10
11impl Plugin for WoffPlugin {
12    fn build(&self, #[allow(unused_variables)] app: &mut App) {
13        #[cfg(feature = "woff1")]
14        app.register_asset_loader(Woff1AssetLoader);
15        #[cfg(feature = "woff2")]
16        app.register_asset_loader(Woff2AssetLoader);
17    }
18}
19
20#[cfg(any(feature = "woff1", feature = "woff2"))]
21#[derive(Debug, thiserror::Error)]
22enum WoffLoadError {
23    #[error(transparent)]
24    Io(#[from] std::io::Error),
25    #[error("woff decompression failed: {0}")]
26    Decompress(wuff::WuffErr),
27    #[error("invalid font data: {0}")]
28    Font(Box<dyn std::error::Error + Send + Sync>),
29}
30
31#[cfg(any(feature = "woff1", feature = "woff2"))]
32fn load_font(ttf_bytes: Vec<u8>) -> Result<Font, WoffLoadError> {
33    Font::try_from_bytes(ttf_bytes).map_err(|e| WoffLoadError::Font(Box::new(e)))
34}
35
36#[cfg(feature = "woff1")]
37#[derive(Default, TypePath)]
38struct Woff1AssetLoader;
39
40#[cfg(feature = "woff1")]
41impl AssetLoader for Woff1AssetLoader {
42    type Asset = Font;
43    type Settings = ();
44    type Error = WoffLoadError;
45
46    async fn load(
47        &self,
48        reader: &mut dyn Reader,
49        _settings: &Self::Settings,
50        _load_context: &mut LoadContext<'_>,
51    ) -> Result<Self::Asset, Self::Error> {
52        let mut woff_bytes = Vec::new();
53        reader.read_to_end(&mut woff_bytes).await?;
54        let ttf_bytes = wuff::decompress_woff1(&woff_bytes).map_err(WoffLoadError::Decompress)?;
55        load_font(ttf_bytes)
56    }
57
58    fn extensions(&self) -> &[&str] {
59        &["woff"]
60    }
61}
62
63#[cfg(feature = "woff2")]
64#[derive(Default, TypePath)]
65struct Woff2AssetLoader;
66
67#[cfg(feature = "woff2")]
68impl AssetLoader for Woff2AssetLoader {
69    type Asset = Font;
70    type Settings = ();
71    type Error = WoffLoadError;
72
73    async fn load(
74        &self,
75        reader: &mut dyn Reader,
76        _settings: &Self::Settings,
77        _load_context: &mut LoadContext<'_>,
78    ) -> Result<Self::Asset, Self::Error> {
79        let mut woff2_bytes = Vec::new();
80        reader.read_to_end(&mut woff2_bytes).await?;
81        let ttf_bytes = wuff::decompress_woff2(&woff2_bytes).map_err(WoffLoadError::Decompress)?;
82        load_font(ttf_bytes)
83    }
84
85    fn extensions(&self) -> &[&str] {
86        &["woff2"]
87    }
88}