From e5c6a645de6b1cffaf36c9f13ea83a782aad606a Mon Sep 17 00:00:00 2001 From: Firestar99 Date: Mon, 30 Jun 2025 18:50:57 +0200 Subject: [PATCH] WIP Extract `gtext` --- Cargo.lock | 17 ++++++++++ Cargo.toml | 3 ++ node-graph/gapplication-io/src/lib.rs | 34 +++---------------- node-graph/gcore/src/lib.rs | 1 - node-graph/gstd/Cargo.toml | 1 + node-graph/gstd/src/lib.rs | 2 +- node-graph/gtext/Cargo.toml | 28 +++++++++++++++ .../src/text => gtext/src}/font_cache.rs | 22 +++++++++--- .../{gcore/src/text.rs => gtext/src/lib.rs} | 2 ++ .../src/text.rs => gtext/src/text_node.rs} | 5 ++- .../{gcore/src/text => gtext/src}/to_path.rs | 2 +- 11 files changed, 77 insertions(+), 40 deletions(-) create mode 100644 node-graph/gtext/Cargo.toml rename node-graph/{gcore/src/text => gtext/src}/font_cache.rs (80%) rename node-graph/{gcore/src/text.rs => gtext/src/lib.rs} (66%) rename node-graph/{gstd/src/text.rs => gtext/src/text_node.rs} (83%) rename node-graph/{gcore/src/text => gtext/src}/to_path.rs (99%) diff --git a/Cargo.lock b/Cargo.lock index ed8ade561..5e9b6039e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2313,6 +2313,7 @@ dependencies = [ "graphene-path-bool", "graphene-raster-nodes", "graphene-svg-renderer", + "graphene-text", "image", "log", "ndarray", @@ -2344,6 +2345,22 @@ dependencies = [ "vello", ] +[[package]] +name = "graphene-text" +version = "0.1.0" +dependencies = [ + "bezier-rs", + "dyn-any", + "glam", + "graphene-application-io", + "graphene-core", + "kurbo", + "node-macro", + "rustybuzz 0.20.1", + "serde", + "specta", +] + [[package]] name = "graphite-desktop" version = "0.1.0" diff --git a/Cargo.toml b/Cargo.toml index 0fc9eef1d..9caf77d1e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -15,6 +15,7 @@ members = [ "node-graph/graphene-cli", "node-graph/graster-nodes", "node-graph/gsvg-renderer", + "node-graph/gtext", "node-graph/interpreted-executor", "node-graph/node-macro", "node-graph/preprocessor", @@ -37,6 +38,7 @@ default-members = [ "node-graph/graphene-cli", "node-graph/graster-nodes", "node-graph/gsvg-renderer", + "node-graph/gtext", "node-graph/interpreted-executor", "node-graph/node-macro", ] @@ -59,6 +61,7 @@ graph-craft = { path = "node-graph/graph-craft" } graphene-raster-nodes = { path = "node-graph/graster-nodes" } graphene-std = { path = "node-graph/gstd" } graphene-svg-renderer = { path = "node-graph/gsvg-renderer" } +graphene-text = { path = "node-graph/gtext" } interpreted-executor = { path = "node-graph/interpreted-executor" } node-macro = { path = "node-graph/node-macro" } wgpu-executor = { path = "node-graph/wgpu-executor" } diff --git a/node-graph/gapplication-io/src/lib.rs b/node-graph/gapplication-io/src/lib.rs index d0fd84c6d..8406bb16a 100644 --- a/node-graph/gapplication-io/src/lib.rs +++ b/node-graph/gapplication-io/src/lib.rs @@ -1,14 +1,14 @@ use dyn_any::{DynAny, StaticType, StaticTypeSized}; use glam::{DAffine2, UVec2}; -use graphene_core::text::FontCache; use graphene_core::transform::Footprint; use graphene_core::vector::style::ViewMode; +use std::any::Any; use std::fmt::Debug; use std::future::Future; use std::hash::{Hash, Hasher}; use std::pin::Pin; use std::ptr::addr_of; -use std::sync::Arc; +use std::sync::{Arc, Mutex}; use std::time::Duration; #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize)] @@ -254,7 +254,7 @@ impl GetEditorPreferences for DummyPreferences { pub struct EditorApi { /// Font data (for rendering text) made available to the graph through the [`WasmEditorApi`]. - pub font_cache: FontCache, + pub font_cache: Mutex>>, /// Gives access to APIs like a rendering surface (native window handle or HTML5 canvas) and WGPU (which becomes WebGPU on web). pub application_io: Option>, pub node_graph_message_sender: Box, @@ -262,12 +262,10 @@ pub struct EditorApi { pub editor_preferences: Box, } -impl Eq for EditorApi {} - impl Default for EditorApi { fn default() -> Self { Self { - font_cache: FontCache::default(), + font_cache: Mutex::new(None), application_io: None, node_graph_message_sender: Box::new(Logger), editor_preferences: Box::new(DummyPreferences), @@ -275,30 +273,6 @@ impl Default for EditorApi { } } -impl Hash for EditorApi { - fn hash(&self, state: &mut H) { - self.font_cache.hash(state); - self.application_io.as_ref().map_or(0, |io| io.as_ref() as *const _ as usize).hash(state); - (self.node_graph_message_sender.as_ref() as *const dyn NodeGraphUpdateSender).hash(state); - (self.editor_preferences.as_ref() as *const dyn GetEditorPreferences).hash(state); - } -} - -impl PartialEq for EditorApi { - fn eq(&self, other: &Self) -> bool { - self.font_cache == other.font_cache - && self.application_io.as_ref().map_or(0, |io| addr_of!(io) as usize) == other.application_io.as_ref().map_or(0, |io| addr_of!(io) as usize) - && std::ptr::eq(self.node_graph_message_sender.as_ref() as *const _, other.node_graph_message_sender.as_ref() as *const _) - && std::ptr::eq(self.editor_preferences.as_ref() as *const _, other.editor_preferences.as_ref() as *const _) - } -} - -impl Debug for EditorApi { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - f.debug_struct("EditorApi").field("font_cache", &self.font_cache).finish() - } -} - unsafe impl StaticType for EditorApi { type Static = EditorApi; } diff --git a/node-graph/gcore/src/lib.rs b/node-graph/gcore/src/lib.rs index 78d659bf4..ebac17112 100644 --- a/node-graph/gcore/src/lib.rs +++ b/node-graph/gcore/src/lib.rs @@ -20,7 +20,6 @@ pub mod raster; pub mod raster_types; pub mod registry; pub mod structural; -pub mod text; pub mod transform; pub mod uuid; pub mod value; diff --git a/node-graph/gstd/Cargo.toml b/node-graph/gstd/Cargo.toml index 844c9c0d3..15eb2b9b9 100644 --- a/node-graph/gstd/Cargo.toml +++ b/node-graph/gstd/Cargo.toml @@ -35,6 +35,7 @@ graphene-application-io = { workspace = true } graphene-element-nodes = { workspace = true } graphene-raster-nodes = { workspace = true } graphene-brush = { workspace = true } +graphene-text = { workspace = true } # Workspace dependencies fastnoise-lite = { workspace = true } diff --git a/node-graph/gstd/src/lib.rs b/node-graph/gstd/src/lib.rs index 68e08d296..952a585a5 100644 --- a/node-graph/gstd/src/lib.rs +++ b/node-graph/gstd/src/lib.rs @@ -1,6 +1,5 @@ pub mod any; pub mod http; -pub mod text; #[cfg(feature = "wasm")] pub mod wasm_application_io; @@ -13,6 +12,7 @@ pub use graphene_element_nodes::animation; pub use graphene_math_nodes as math_nodes; pub use graphene_path_bool as path_bool; pub use graphene_raster_nodes as raster_nodes; +pub use graphene_text as text; /// stop gap solutions until all paths have been replaced with their absolute ones pub mod renderer { diff --git a/node-graph/gtext/Cargo.toml b/node-graph/gtext/Cargo.toml new file mode 100644 index 000000000..8ada5788c --- /dev/null +++ b/node-graph/gtext/Cargo.toml @@ -0,0 +1,28 @@ +[package] +name = "graphene-text" +version = "0.1.0" +edition = "2024" +description = "graphene text nodes" +authors = ["Graphite Authors "] +license = "MIT OR Apache-2.0" + +[features] +default = ["serde"] +serde = ["dep:serde"] + +[dependencies] +# Local dependencies +dyn-any = { workspace = true } +bezier-rs = { workspace = true } +graphene-core = { workspace = true } +graphene-application-io = { workspace = true } +node-macro = { workspace = true } + +# Workspace dependencies +kurbo = { workspace = true } +glam = { workspace = true } +specta = { workspace = true } +rustybuzz = { workspace = true } + +# Optional workspace dependencies +serde = { workspace = true, optional = true, features = ["derive"] } diff --git a/node-graph/gcore/src/text/font_cache.rs b/node-graph/gtext/src/font_cache.rs similarity index 80% rename from node-graph/gcore/src/text/font_cache.rs rename to node-graph/gtext/src/font_cache.rs index a872166fb..08bcdfa78 100644 --- a/node-graph/gcore/src/text/font_cache.rs +++ b/node-graph/gtext/src/font_cache.rs @@ -1,4 +1,6 @@ use dyn_any::DynAny; +use graphene_application_io::EditorApi; +use graphene_core::consts::{DEFAULT_FONT_FAMILY, DEFAULT_FONT_STYLE}; use std::collections::HashMap; /// A font type (storing font family and font style and an optional preview URL) @@ -16,7 +18,7 @@ impl Font { } impl Default for Font { fn default() -> Self { - Self::new(crate::consts::DEFAULT_FONT_FAMILY.into(), crate::consts::DEFAULT_FONT_STYLE.into()) + Self::new(DEFAULT_FONT_FAMILY.into(), DEFAULT_FONT_STYLE.into()) } } /// A cache of all loaded font data and preview urls along with the default font (send from `init_app` in `editor_api.rs`) @@ -33,9 +35,7 @@ impl FontCache { if self.font_file_data.contains_key(font) { Some(font) } else { - self.font_file_data - .keys() - .find(|font| font.font_family == crate::consts::DEFAULT_FONT_FAMILY && font.font_style == crate::consts::DEFAULT_FONT_STYLE) + self.font_file_data.keys().find(|font| font.font_family == DEFAULT_FONT_FAMILY && font.font_style == DEFAULT_FONT_STYLE) } } @@ -78,3 +78,17 @@ fn migrate_font_style<'de, D: serde::Deserializer<'de>>(deserializer: D) -> Resu use serde::Deserialize; String::deserialize(deserializer).map(|name| if name == "Normal (400)" { "Regular (400)".to_string() } else { name }) } + +pub trait EditorApiFontCacheExt { + fn get_font_cache(&self) -> &FontCache; +} + +impl EditorApiFontCacheExt for EditorApi { + fn get_font_cache(&self) -> &FontCache { + let mut guard = self.font_cache.lock().unwrap(); + if *guard == None { + *guard = Some(Box::new(FontCache::default())); + } + *guard.as_ref().unwrap().downcast_ref().unwrap(); + } +} diff --git a/node-graph/gcore/src/text.rs b/node-graph/gtext/src/lib.rs similarity index 66% rename from node-graph/gcore/src/text.rs rename to node-graph/gtext/src/lib.rs index 485cb2a88..1bc5fb924 100644 --- a/node-graph/gcore/src/text.rs +++ b/node-graph/gtext/src/lib.rs @@ -1,5 +1,7 @@ mod font_cache; +mod text_node; mod to_path; pub use font_cache::*; +pub use text_node::*; pub use to_path::*; diff --git a/node-graph/gstd/src/text.rs b/node-graph/gtext/src/text_node.rs similarity index 83% rename from node-graph/gstd/src/text.rs rename to node-graph/gtext/src/text_node.rs index 0f69ae1bf..74d7bdbc4 100644 --- a/node-graph/gstd/src/text.rs +++ b/node-graph/gtext/src/text_node.rs @@ -1,7 +1,6 @@ -use crate::vector::{VectorData, VectorDataTable}; -use graph_craft::wasm_application_io::WasmEditorApi; +use crate::{Font, TypesettingConfig, load_face, to_path}; use graphene_core::Ctx; -pub use graphene_core::text::*; +use graphene_core::vector::{VectorData, VectorDataTable}; #[node_macro::node(category(""))] fn text<'i: 'n>( diff --git a/node-graph/gcore/src/text/to_path.rs b/node-graph/gtext/src/to_path.rs similarity index 99% rename from node-graph/gcore/src/text/to_path.rs rename to node-graph/gtext/src/to_path.rs index 9988fcae3..c55be8fdd 100644 --- a/node-graph/gcore/src/text/to_path.rs +++ b/node-graph/gtext/src/to_path.rs @@ -1,6 +1,6 @@ -use crate::vector::PointId; use bezier_rs::{ManipulatorGroup, Subpath}; use glam::DVec2; +use graphene_core::vector::PointId; use rustybuzz::ttf_parser::{GlyphId, OutlineBuilder}; use rustybuzz::{GlyphBuffer, UnicodeBuffer};