Skip to content

Commit

Permalink
Merge pull request #5 from 448-engineering/static_assets
Browse files Browse the repository at this point in the history
Font now handled at compile time using macros and linked statically
  • Loading branch information
448-OG committed Nov 11, 2023
2 parents a3e65e2 + 6168230 commit 9523a7b
Show file tree
Hide file tree
Showing 12 changed files with 77 additions and 180 deletions.
5 changes: 2 additions & 3 deletions Lib/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
[package]
name = "puppeteer"
authors = ["448 Engineering Developers <superuser@448.africa>"]
version = "2.2.0"
version = "2.2.1"
edition = "2021"
description = "A Minimal Dependency Easy to Use GUI Builder in Rust using Async Channels"
categories = ["graphics", "gui"]
Expand All @@ -20,7 +20,7 @@ base64ct = { version = "1.6.0", features = ["std"] }
blake3 = "1.5.0"
bytes = { version = "1.5.0", default-features = false }
camino = "1.1.6"
file-format = { version = "0.21.0", features = [
file-format = { version = "0.22.0", features = [
"reader",
"reader-asf",
"reader-cfb",
Expand All @@ -33,7 +33,6 @@ file-format = { version = "0.21.0", features = [
"reader-xml",
"reader-zip",
] }
futures-lite = "2.0.0"
once_cell = "1.18.0"
smol = "1.3.0"
thiserror = "1.0.50"
Expand Down
Binary file removed Lib/examples/assets/fonts/rockville_solid.woff2
Binary file not shown.
Binary file added Lib/examples/assets/fonts/warteg.woff2
Binary file not shown.
22 changes: 11 additions & 11 deletions Lib/examples/hello-puppeteer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@ use puppeteer::{
async_trait::{self},
smol::lock::Mutex,
tracing::{self, Level},
ActiveAppEnv, ContextMenu, ModifyView, Puppeteer, PuppeteerApp, Shell, DEFAULT_WINDOW_ACTIONS,
DEFAULT_WINDOW_ACTIONS_SCRIPT, DEFAULT_WINDOW_ACTIONS_STYLE,
ActiveAppEnv, ContextMenu, ModifyView, Puppeteer, PuppeteerApp, Shell, StaticAsset,
DEFAULT_WINDOW_ACTIONS, DEFAULT_WINDOW_ACTIONS_SCRIPT, DEFAULT_WINDOW_ACTIONS_STYLE,
};
use std::{borrow::Cow, collections::HashMap};
use tracing_subscriber::FmtSubscriber;
Expand All @@ -25,14 +25,14 @@ fn main() {

tracing::subscriber::set_global_default(subscriber).expect("setting default subscriber failed");

const FONTS: [StaticAsset; 2] = puppeteer::load_assets!(
("centauri", "assets/fonts/centauri.woff2"),
("warteg", "assets/fonts/warteg.woff2"),
);

smol::block_on(async {
PuppeteerApp::<AppTest>::init("Puppeteer Test App")
.with_fonts_dir(concat!(
env!("CARGO_MANIFEST_DIR"),
"/examples/assets/fonts"
))
.await
.unwrap()
.with_fonts(&FONTS)
.start()
.await
.unwrap()
Expand Down Expand Up @@ -111,7 +111,7 @@ function email_ops() {

#[async_trait::async_trait]
impl Puppeteer for AppTest {
async fn shell() -> Shell {
fn shell() -> Shell {
let context_menu_script = ContextMenu::new()
.add_id("context-menu-identifier")
.build_script();
Expand Down Expand Up @@ -152,8 +152,8 @@ impl Puppeteer for AppTest {
<div id="logo-icon" class="drag-region frow row-start p-5 col-xs-1-4"> { PUPPETEER_ICON }</div>
<div class="drag-region frow row-end col-xs-3-4"> { DEFAULT_WINDOW_ACTIONS }</div>
</div>
<div class="frow"><h1 style="font-family: 'rockville_solid','sans-serif'">"HELLO from PUPPETEER"</h1></div>
<div class="frow"><h3 style="font-family: 'centauri','sans-serif'">"Nice Font :)"</h3></div>
<div class="frow"><h1 style="font-family: 'warteg','sans-serif'">"HELLO from PUPPETEER"</h1></div>
<div class="frow"><h3 style="font-family: 'centauri' ,'sans-serif'">"Nice Font :)"</h3></div>
<div class="frow direction-column row-center">
<input class="frow col-md-1-2 mt-40" type="email" id="user_email" name="name" required placeholder="Enter Your Email Address" onkeydown="email_ops()"/>

Expand Down
22 changes: 7 additions & 15 deletions Lib/examples/load_resource.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use file_format::FileFormat;
use puppeteer::{path_from_manifest, AssetFileLoader, AssetProperties, StaticAssetProperties};
use puppeteer::{AssetFileLoader, AssetProperties, StaticAssetProperties};

fn main() {
smol::block_on(async {
Expand Down Expand Up @@ -32,18 +32,15 @@ fn main() {
assert_eq!("frow.min", frowcss.name());
assert_eq!("text/plain", frowcss.format().media_type());

let _path = path_from_manifest!("examples/assets", "frow.min.css");

let counter = puppeteer::items_counter!("foos", "two");
assert_eq!(counter, 2usize);
let counter = puppeteer::items_counter!(
("frow.min", "assets/frow.min.css"),
("centauri", "assets/fonts/centauri.woff2"),
("rockville_solid", "assets/fonts/rockville_solid.woff2"),
);
assert_eq!(counter, 3usize);
let counter = puppeteer::items_counter!(1, 2, 3);
assert_eq!(counter, 3usize);

assert_eq!(
"foo/bar/baz/foo.txt",
&puppeteer::concat_paths!("foo", "bar", "baz", "foo.txt")
);

let assets = puppeteer::load_assets!(
("frow.min", "assets/frow.min.css"),
("centauri", "assets/fonts/centauri.woff2"),
Expand All @@ -61,10 +58,5 @@ fn main() {
"53a3c3ce4bdb8062c464f624a72a8c7589cc04c612ffbfbf3b07e36e45249104",
blake3::hash(assets[2].bytes).to_hex().as_str()
);

assert_eq!(
format!("{}/foo/bar/baz/foo.txt", env!("CARGO_MANIFEST_DIR")).as_str(),
&puppeteer::manifest_paths!("foo", "bar", "baz", "foo.txt")
);
})
}
20 changes: 12 additions & 8 deletions Lib/src/app.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
use crate::{
AppEnvironment, Logging, ModifyView, PuppeteerError, PuppeteerResult, StaticCowStr, UiPaint,
AppEnvironment, Logging, ModifyView, PuppeteerError, PuppeteerResult, StaticAsset, UiPaint,
WindowResize,
};
use std::{marker::PhantomData, path::Path};
use std::marker::PhantomData;
use tracing::Level;
use wry::{
application::{
Expand Down Expand Up @@ -35,7 +35,7 @@ pub struct ActiveAppEnv {
/// This is mostly useful for desktops where there could be multiple monitors connected
pub available_monitors: Vec<MonitorHandle>,
/// List all the fonts that were loaded by the app
pub fonts: Vec<StaticCowStr>,
pub fonts: &'static [StaticAsset],
}

/// This struct us used to build your app
Expand Down Expand Up @@ -68,17 +68,21 @@ where
primary_monitor: Option::default(),
current_monitor: Option::default(),
available_monitors: Vec::default(),
fonts: Vec::default(),
fonts: &[StaticAsset {
name: "",
bytes: &[0u8],
}],
},
phantom: PhantomData,
}
}

/// Load fonts directory
pub async fn with_fonts_dir(mut self, path: impl AsRef<Path>) -> PuppeteerResult<Self> {
T::shell().await.load_fonts_dir(path, &mut self.env).await?;
pub fn with_fonts(mut self, fonts: &'static [StaticAsset]) -> Self {
self.env.fonts = fonts;
T::shell().add_fonts(&self.env);

Ok(self)
self
}

/// Start the event loop.
Expand Down Expand Up @@ -237,7 +241,7 @@ where

let devtools_enabled = cfg!(debug_assertions);

let shell = smol::block_on(async { T::shell().await });
let shell = T::shell();

let webview = WebViewBuilder::new(window)?
.with_html(shell.to_html())?
Expand Down
24 changes: 22 additions & 2 deletions Lib/src/assets.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,14 +14,23 @@ pub const BUFFER_CAPACITY: usize = 1024 * 64; //16KiB
pub const DEFAULT_RESOURCE_SIZE: usize = 1024 * 1024; //1MiB

/// An asset that has a static lifetime
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
pub struct StaticAsset {
/// The name of the asset
pub name: &'static str,
/// The bytes contained in the asset
pub bytes: &'static [u8],
}

impl core::fmt::Debug for StaticAsset {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("StaticAsset")
.field("name", &self.name)
.field("bytes", &self.hash())
.finish()
}
}

impl StaticAssetProperties for StaticAsset {
fn name(&self) -> &'static str {
self.name
Expand All @@ -44,13 +53,14 @@ impl StaticAssetProperties for StaticAsset {
+ Cow::Owned(Base64::encode_string(self.bytes))
}

/// The blake3 Hash of the bytes contained in [Self] field `bytes`
fn hash(&self) -> blake3::Hash {
blake3::hash(self.bytes)
}
}

/// An asset to be used in the app
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
#[derive(Clone, PartialEq, Eq, PartialOrd, Ord)]
pub struct AssetFile<'p> {
/// The name of the asset
pub name: CowStr<'p>,
Expand Down Expand Up @@ -80,11 +90,21 @@ impl<'p> AssetProperties for AssetFile<'p> {
+ Cow::Owned(Base64::encode_string(&self.bytes))
}

/// The blake3 Hash of the bytes contained in [Self] field `bytes`
fn hash(&self) -> blake3::Hash {
blake3::hash(&self.bytes)
}
}

impl<'p> core::fmt::Debug for AssetFile<'p> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("AssetFile")
.field("name", &self.name)
.field("bytes", &self.hash())
.finish()
}
}

/// Default resource file size is 1MiB
#[derive(Debug)]
pub struct AssetFileLoader<'p> {
Expand Down
4 changes: 2 additions & 2 deletions Lib/src/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -55,8 +55,8 @@ pub enum PuppeteerError {
)]
FontsDirPermissionDenied,
/// For webview only WOFF2 font format is supported.
#[error("The font path detected is not a valid `WOFF2` format for the web.")]
InvalidFontExpectedWoff2,
#[error("The font detected ({0:?}) is not a valid `WOFF2` format for the web.")]
InvalidFontExpectedWoff2(String),
/// Tried to get a file name without the extension part using `Path::file_stem()` but the file stem does not exist
#[error("Tried to get a file name without the extension part using `Path::file_stem()` but the file stem does not exist")]
InvalidFileStemName,
Expand Down
1 change: 0 additions & 1 deletion Lib/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,6 @@ pub use assets::*;
//
pub use arrayvec;
pub use async_trait;
pub use futures_lite;
pub use smol;
pub use thiserror;
pub use tracing;
Expand Down
91 changes: 16 additions & 75 deletions Lib/src/shell.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,6 @@
use crate::{
ActiveAppEnv, AssetFileLoader, AssetProperties, PuppeteerError, PuppeteerResult, StaticCowStr,
StaticStr, UiPaint,
};
use crate::{ActiveAppEnv, StaticAssetProperties, StaticCowStr, StaticStr, UiPaint};
use file_format::FileFormat;
use futures_lite::{AsyncReadExt, StreamExt};
use smol::fs::read_dir;
use std::{borrow::Cow, io::ErrorKind, path::Path};
use std::borrow::Cow;
use wry::application::window::Theme as WryTheme;

/// The HTML element where all the app body will be injected
Expand Down Expand Up @@ -71,32 +66,6 @@ impl Shell {
self
}

/// Add all the styles in a certain directory `path` with the file names provided.
/// NOTE: CSS style sheets are cascading so the order in which the file names for
/// the styles are added is important.
pub async fn add_styles_dir(
mut self,
path: &str,
files: impl AsRef<[&str]>,
) -> PuppeteerResult<Self> {
let paths = files
.as_ref()
.iter()
.map(|file| path.to_owned() + file + ".css")
.collect::<Vec<String>>();

while let Some(file) = futures_lite::stream::iter(&paths).next().await {
let mut file = smol::fs::File::open(file).await?;
let mut contents = String::new();

file.read_to_string(&mut contents).await?;

self.styles.push(contents.into());
}

Ok(self)
}

/// Add the scripts in the `<body></body>` field
pub fn add_script(mut self, script: StaticCowStr) -> Self {
self.scripts.push(script);
Expand All @@ -111,13 +80,6 @@ impl Shell {
self
}

/// Add font to the shell. The font is required to be Base64
pub fn add_fonts(mut self, font_bytes: &'static str) -> Self {
self.fonts.push(Cow::Borrowed(font_bytes));

self
}

/// Get the head_links
pub fn head_links(&self) -> &[StaticCowStr] {
self.head_links.as_slice()
Expand All @@ -133,50 +95,29 @@ impl Shell {
self.scripts.as_slice()
}

/// Load fonts in a particular directory
pub async fn load_fonts_dir(
mut self,
path_to_fonts: impl AsRef<Path>,
app_env: &mut ActiveAppEnv,
) -> PuppeteerResult<Self> {
let mut entries = match read_dir(path_to_fonts).await {
Ok(dir) => dir,
Err(error) => {
if error.kind() == ErrorKind::NotFound {
return Err(PuppeteerError::FontsDirNotFound);
} else if error.kind() == ErrorKind::PermissionDenied {
return Err(PuppeteerError::FontsDirPermissionDenied);
} else {
return Err(error.into());
}
}
};

while let Some(entry) = entries.try_next().await? {
let path = entry.path().to_path_buf().to_string_lossy().to_string();

let resource = AssetFileLoader::new(&path).load().await?;

let file_format_detected = resource.format();
/// Add user specified fonts
pub fn add_fonts(mut self, app_env: &ActiveAppEnv) -> Self {
app_env.fonts.iter().for_each(|font| {
let file_format_detected = font.format();

if file_format_detected != FileFormat::WebOpenFontFormat2 {
return Err(PuppeteerError::InvalidFontExpectedWoff2);
panic!(
"Invalid font type `{:?}` for file `{}`",
font.format(),
font.name
);
}

tracing::trace!("LOADED FONT: {:?}", &resource.name());
app_env
.fonts
.push(StaticCowStr::Owned(resource.name().to_string()));
tracing::info!("LOADED FONT: {:?}", &font.name());

let font = resource.base64();
let injector = Cow::Borrowed("var dataUri = \"")
+ font
+ font.base64()
+ "\";"
+ Cow::Borrowed(
r#"
var fontFace = new FontFace(""#,
)
+ resource.name()
+ font.name()
+ Cow::Borrowed(
r#"", `url(${dataUri})`, {
style: "normal",
Expand All @@ -187,9 +128,9 @@ impl Shell {
"#,
); //FIXME Add styles for fonts here*/
self.fonts.push(Cow::Owned(injector.to_string()));
}
});

Ok(self)
self
}
}

Expand Down

0 comments on commit 9523a7b

Please sign in to comment.