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}