Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions editor/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ vello = { workspace = true }
base64 = { workspace = true }
spin = { workspace = true }
image = { workspace = true }
parley = { workspace = true }

# Optional local dependencies
wgpu-executor = { workspace = true, optional = true }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -300,7 +300,32 @@ impl PreferencesDialogMessageHandler {
.widget_instance(),
];

rows.extend_from_slice(&[header, node_graph_wires_label, graph_wire_style, brush_tool]);
let fonts_checkbox_id = CheckboxId::new();
let system_fonts_description = "
Switch the font catalog to system fonts. On Desktop, this queries the system fonts from the backend. In future versions, this will query system fonts in the browser.\n\
\n\
This experimental functionality is slated for replacement in future versions of Graphite that will have more detailed font management.\n\
\n\
*Default: Off.*
"
.trim();
let system_fonts = vec![
Separator::new(SeparatorStyle::Unrelated).widget_instance(),
Separator::new(SeparatorStyle::Unrelated).widget_instance(),
CheckboxInput::new(preferences.system_fonts)
.tooltip_label("System Fonts")
.tooltip_description(system_fonts_description)
.on_update(|checkbox_input: &CheckboxInput| PreferencesMessage::SystemFonts { enabled: checkbox_input.checked }.into())
.for_label(fonts_checkbox_id)
.widget_instance(),
TextLabel::new("System Fonts")
.tooltip_label("System Fonts")
.tooltip_description(system_fonts_description)
.for_checkbox(fonts_checkbox_id)
.widget_instance(),
];

rows.extend_from_slice(&[header, node_graph_wires_label, graph_wire_style, brush_tool, system_fonts]);
}

// =============
Expand Down
1 change: 1 addition & 0 deletions editor/src/messages/portfolio/portfolio_message.rs
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ pub enum PortfolioMessage {
},
DestroyAllDocuments,
EditorPreferences,
LoadFontCatalog,
FontCatalogLoaded {
catalog: FontCatalog,
},
Expand Down
59 changes: 55 additions & 4 deletions editor/src/messages/portfolio/portfolio_message_handler.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use super::document::utility_types::document_metadata::LayerNodeIdentifier;
use super::document::utility_types::network_interface;
use super::persistent_state::{PersistentStateMessage, PersistentStateMessageContext, PersistentStateMessageHandler};
use super::utility_types::{CachedData, PanelLayoutSubdivision, PanelType, WorkspacePanelLayout};
use super::utility_types::{CachedData, FontCatalog, FontCatalogFamily, FontCatalogStyle, PanelLayoutSubdivision, PanelType, WorkspacePanelLayout};
use crate::application::{Editor, generate_uuid};
use crate::consts::{DEFAULT_DOCUMENT_NAME, DEFAULT_STROKE_WIDTH, FILE_EXTENSION};
use crate::messages::animation::TimingInformation;
Expand Down Expand Up @@ -34,6 +34,8 @@ use graphene_std::subpath::BezierHandles;
use graphene_std::text::Font;
use graphene_std::vector::misc::HandleId;
use graphene_std::vector::{PointId, SegmentId, Vector, VectorModificationType};
use parley::FontStyle;
use parley::fontique::{Collection, CollectionOptions};
use std::path::PathBuf;
use std::vec;

Expand Down Expand Up @@ -368,6 +370,33 @@ impl MessageHandler<PortfolioMessage, PortfolioMessageContext<'_>> for Portfolio
responses.add(MenuBarMessage::SendLayout);
responses.add(PersistentStateMessage::WriteState);
}
PortfolioMessage::LoadFontCatalog => {
if Editor::environment().is_desktop() && preferences.system_fonts {
self.cached_data.system_font_collection.0 = Collection::new(CollectionOptions { shared: false, system_fonts: true });
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: System font catalog generation is performed synchronously in the portfolio message handler, which can block the dispatcher/event loop and cause UI stalls on machines with large font libraries.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At editor/src/messages/portfolio/portfolio_message_handler.rs, line 375:

<comment>System font catalog generation is performed synchronously in the portfolio message handler, which can block the dispatcher/event loop and cause UI stalls on machines with large font libraries.</comment>

<file context>
@@ -368,6 +370,33 @@ impl MessageHandler<PortfolioMessage, PortfolioMessageContext<'_>> for Portfolio
 			}
+			PortfolioMessage::LoadFontCatalog => {
+				if Editor::environment().is_desktop() && preferences.system_fonts {
+					self.cached_data.system_font_collection.0 = Collection::new(CollectionOptions { shared: false, system_fonts: true });
+					// shove font metadata into cached data catalog
+					let system_font_family_names: Vec<String> = self.cached_data.system_font_collection.0.family_names().map(|n| n.to_owned()).collect();
</file context>

// shove font metadata into cached data catalog
let system_font_family_names: Vec<String> = self.cached_data.system_font_collection.0.family_names().map(|n| n.to_owned()).collect();
let mut families_for_catalog = Vec::new();
for name in system_font_family_names {
let family = self.cached_data.system_font_collection.0.family_by_name(&name).unwrap();
let mut styles = Vec::new();
for font in family.fonts() {
let style = FontCatalogStyle {
weight: (font.weight().value()) as u32,
italic: font.style() == FontStyle::Italic,
url: "_SYSTEM".to_owned(),
};
styles.push(style);
}
families_for_catalog.push(FontCatalogFamily { name: name.to_owned(), styles });
}
responses.add(PortfolioMessage::FontCatalogLoaded {
catalog: FontCatalog(families_for_catalog),
});
} else {
// if not desktop or not system fonts, call to frontend as usual
responses.add_front(FrontendMessage::TriggerFontCatalogLoad);
}
}
PortfolioMessage::FontCatalogLoaded { catalog } => {
self.cached_data.font_catalog = catalog;

Expand All @@ -384,7 +413,28 @@ impl MessageHandler<PortfolioMessage, PortfolioMessageContext<'_>> for Portfolio
let font = Font::new(font.font_family, style.to_named_style());

if !self.cached_data.font_cache.loaded_font(&font) {
responses.add(FrontendMessage::TriggerFontDataLoad { font, url: style.url });
if style.url == "_SYSTEM" {
let system_font_family_names: Vec<String> = self.cached_data.system_font_collection.0.family_names().map(|n| n.to_owned()).collect();
for name in system_font_family_names {
if name == font.font_family {
let family = self.cached_data.system_font_collection.0.family_by_name(&name).unwrap();
for system_font_style in family.fonts() {
if (system_font_style.weight().value() as u32 == style.weight) && ((system_font_style.style() == FontStyle::Italic) == style.italic) {
let mut font_data_vec = Vec::new();
font_data_vec.extend(system_font_style.load(None).unwrap().data());
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1: Unchecked unwrap() calls in system font loading can panic on corrupted or unreadable system fonts.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At editor/src/messages/portfolio/portfolio_message_handler.rs, line 424:

<comment>Unchecked `unwrap()` calls in system font loading can panic on corrupted or unreadable system fonts.</comment>

<file context>
@@ -384,7 +413,28 @@ impl MessageHandler<PortfolioMessage, PortfolioMessageContext<'_>> for Portfolio
+									for system_font_style in family.fonts() {
+										if (system_font_style.weight().value() as u32 == style.weight) && ((system_font_style.style() == FontStyle::Italic) == style.italic) {
+											let mut font_data_vec = Vec::new();
+											font_data_vec.extend(system_font_style.load(None).unwrap().data());
+											responses.add(PortfolioMessage::FontLoaded {
+												font_family: name.clone(),
</file context>

responses.add(PortfolioMessage::FontLoaded {
font_family: name.clone(),
font_style: style.to_named_style(),
data: font_data_vec,
});
return;
}
}
}
}
} else {
responses.add(FrontendMessage::TriggerFontDataLoad { font, url: style.url });
}
}
}
}
Expand Down Expand Up @@ -439,9 +489,10 @@ impl MessageHandler<PortfolioMessage, PortfolioMessageContext<'_>> for Portfolio
PortfolioMessage::EditorPreferences => self.executor.update_editor_preferences(preferences.editor_preferences()),
PortfolioMessage::LoadDocumentResources { document_id } => {
let catalog = &self.cached_data.font_catalog;

// add handling for if font catalog preference has changed or maybe if user
// has requested font catalog refresh?
if catalog.0.is_empty() {
responses.add_front(FrontendMessage::TriggerFontCatalogLoad);
responses.add(PortfolioMessage::LoadFontCatalog);
return;
}

Expand Down
12 changes: 12 additions & 0 deletions editor/src/messages/portfolio/utility_types.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,23 @@
use graphene_std::Color;
use graphene_std::raster::Image;
use graphene_std::text::{Font, FontCache};
use parley::fontique;
use std::fmt;

#[derive(Debug, Default)]
pub struct CachedData {
pub font_cache: FontCache,
pub font_catalog: FontCatalog,
pub system_font_collection: FontCollection,
}

#[derive(Default)]
pub struct FontCollection(pub fontique::Collection);

impl fmt::Debug for FontCollection {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "Font Collection")
}
}

// TODO: Should this be a BTreeMap instead?
Expand Down
3 changes: 3 additions & 0 deletions editor/src/messages/preferences/preferences_message.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,9 @@ pub enum PreferencesMessage {
BrushTool {
enabled: bool,
},
SystemFonts {
enabled: bool,
},
ModifyLayout {
zoom_with_scroll: bool,
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ pub struct PreferencesMessageHandler {
pub selection_mode: SelectionMode,
pub zoom_with_scroll: bool,
pub brush_tool: bool,
pub system_fonts: bool,
pub graph_wire_style: GraphWireStyle,
pub viewport_zoom_wheel_rate: f64,
pub ui_scale: f64,
Expand Down Expand Up @@ -61,6 +62,7 @@ impl Default for PreferencesMessageHandler {
selection_mode: SelectionMode::Touched,
zoom_with_scroll: matches!(MappingVariant::default(), MappingVariant::ZoomWithScroll),
brush_tool: false,
system_fonts: false,
graph_wire_style: GraphWireStyle::default(),
viewport_zoom_wheel_rate: VIEWPORT_ZOOM_WHEEL_RATE,
ui_scale: UI_SCALE_DEFAULT,
Expand Down Expand Up @@ -103,6 +105,12 @@ impl MessageHandler<PreferencesMessage, PreferencesMessageContext<'_>> for Prefe

responses.add(ToolMessage::RefreshToolShelf);
}
// Per-preference messages
PreferencesMessage::SystemFonts { enabled } => {
self.system_fonts = enabled;

responses.add(PortfolioMessage::LoadFontCatalog);
}
PreferencesMessage::ModifyLayout { zoom_with_scroll } => {
self.zoom_with_scroll = zoom_with_scroll;

Expand Down