diff --git a/frontend/src/app/render_wasm/api.cljs b/frontend/src/app/render_wasm/api.cljs index 01c14a0ec..f36e7ada4 100644 --- a/frontend/src/app/render_wasm/api.cljs +++ b/frontend/src/app/render_wasm/api.cljs @@ -134,6 +134,32 @@ (aget buffer 3)))) shape-ids)) +(defn- get-string-length [string] (+ (count string) 1)) + +;; IMPORTANT: It should be noted that only TTF fonts can be stored. +(defn- store-font + [family-name font-array-buffer] + (let [family-name-size (get-string-length family-name) + font-array-buffer-size (.-byteLength font-array-buffer) + size (+ font-array-buffer-size family-name-size) + ptr (h/call internal-module "_alloc_bytes" size) + family-name-ptr (+ ptr font-array-buffer-size) + heap (gobj/get ^js internal-module "HEAPU8") + mem (js/Uint8Array. (.-buffer heap) ptr size)] + (.set mem (js/Uint8Array. font-array-buffer)) + (h/call internal-module "stringToUTF8" family-name family-name-ptr family-name-size) + (h/call internal-module "_store_font" family-name-size font-array-buffer-size))) + +;; This doesn't work +#_(store-font-url "roboto-thin-italic" "https://fonts.gstatic.com/s/roboto/v32/KFOiCnqEu92Fr1Mu51QrEzAdLw.woff2") +;; This does +#_(store-font-url "sourcesanspro-regular" "http://localhost:3449/fonts/sourcesanspro-regular.ttf") +(defn- store-font-url + [family-name font-url] + (-> (p/then (js/fetch font-url) + (fn [response] (.arrayBuffer response))) + (p/then (fn [array-buffer] (store-font family-name array-buffer))))) + (defn- store-image [id] (let [buffer (uuid/get-u32 id) diff --git a/render-wasm/src/main.rs b/render-wasm/src/main.rs index 20dd67cfd..1942d542d 100644 --- a/render-wasm/src/main.rs +++ b/render-wasm/src/main.rs @@ -286,6 +286,27 @@ pub extern "C" fn add_shape_fill_stops(ptr: *mut shapes::RawStopData, n_stops: u } } +#[no_mangle] +pub extern "C" fn store_font(family_name_size: u32, font_size: u32) { + let state = unsafe { STATE.as_mut() }.expect("Got an invalid state pointer"); + unsafe { + let font_bytes = + Vec::::from_raw_parts(mem::buffer_ptr(), font_size as usize, font_size as usize); + let family_name = String::from_raw_parts( + mem::buffer_ptr().add(font_size as usize), + family_name_size as usize, + family_name_size as usize, + ); + match state.render_state().add_font(family_name, &font_bytes) { + Err(msg) => { + eprintln!("{}", msg); + } + _ => {} + } + mem::free_bytes(); + } +} + #[no_mangle] pub extern "C" fn store_image(a: u32, b: u32, c: u32, d: u32, size: u32) { let state = unsafe { STATE.as_mut() }.expect("Got an invalid state pointer"); diff --git a/render-wasm/src/render.rs b/render-wasm/src/render.rs index d242d9abb..76f388df1 100644 --- a/render-wasm/src/render.rs +++ b/render-wasm/src/render.rs @@ -24,6 +24,7 @@ pub trait Renderable { surface: &mut skia::Surface, images: &ImageStore, scale: f32, + font_provider: &skia::textlayout::TypefaceFontProvider, ) -> Result<(), String>; fn blend_mode(&self) -> BlendMode; fn opacity(&self) -> f32; @@ -55,6 +56,7 @@ pub(crate) struct RenderState { pub final_surface: skia::Surface, pub drawing_surface: skia::Surface, pub debug_surface: skia::Surface, + pub font_provider: skia::textlayout::TypefaceFontProvider, pub cached_surface_image: Option, options: RenderOptions, pub viewbox: Viewbox, @@ -74,12 +76,19 @@ impl RenderState { .new_surface_with_dimensions((width, height)) .unwrap(); + let mut font_provider = skia::textlayout::TypefaceFontProvider::new(); + let default_font = skia::FontMgr::default() + .new_from_data(include_bytes!("fonts/RobotoMono-Regular.ttf"), None) + .expect("Failed to load font"); + font_provider.register_typeface(default_font, "robotomono-regular"); + RenderState { gpu_state, final_surface, drawing_surface, debug_surface, cached_surface_image: None, + font_provider, options: RenderOptions::default(), viewbox: Viewbox::new(width as f32, height as f32), images: ImageStore::new(), @@ -87,6 +96,15 @@ impl RenderState { } } + pub fn add_font(&mut self, family_name: String, font_data: &[u8]) -> Result<(), String> { + let typeface = skia::FontMgr::default() + .new_from_data(font_data, None) + .expect("Failed to add font"); + self.font_provider + .register_typeface(typeface, family_name.as_ref()); + Ok(()) + } + pub fn add_image(&mut self, id: Uuid, image_data: &[u8]) -> Result<(), String> { self.images.add(id, image_data) } @@ -164,7 +182,7 @@ impl RenderState { pub fn render_single_element(&mut self, element: &impl Renderable) { let scale = self.viewbox.zoom * self.options.dpr(); element - .render(&mut self.drawing_surface, &self.images, scale) + .render(&mut self.drawing_surface, &self.images, scale, &self.font_provider) .unwrap(); self.drawing_surface.draw( diff --git a/render-wasm/src/shapes/renderable.rs b/render-wasm/src/shapes/renderable.rs index b3e161022..8df509aec 100644 --- a/render-wasm/src/shapes/renderable.rs +++ b/render-wasm/src/shapes/renderable.rs @@ -11,6 +11,7 @@ impl Renderable for Shape { surface: &mut skia_safe::Surface, images: &ImageStore, scale: f32, + font_provider: &skia::textlayout::TypefaceFontProvider, ) -> Result<(), String> { let transform = self.transform.to_skia_matrix();