Skip to content

Use system fonts as catalog on Desktop App#4035

Open
Macman1234 wants to merge 5 commits intoGraphiteEditor:masterfrom
Macman1234:desktop-system-fonts
Open

Use system fonts as catalog on Desktop App#4035
Macman1234 wants to merge 5 commits intoGraphiteEditor:masterfrom
Macman1234:desktop-system-fonts

Conversation

@Macman1234
Copy link
Copy Markdown

@Macman1234 Macman1234 commented Apr 22, 2026

This PR adds functionality to optionally use system fonts instead of remote fonts on the desktop app. A user can switch between remote fonts and system fonts using a checkbox in the experimental section of Preferences.

Copy link
Copy Markdown
Contributor

@gemini-code-assist gemini-code-assist Bot left a comment

Choose a reason for hiding this comment

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

Code Review

This pull request adds system font loading for desktop environments using the parley crate. The implementation eagerly loads all system font data and sends it through the message system, which presents significant performance and memory efficiency concerns. Feedback suggests transitioning to lazy loading, avoiding expensive collection clones, and replacing unwrap() calls with safer error handling to prevent application panics. Additionally, the logging level for font discovery should be downgraded from error to debug.

Comment on lines +465 to +471
let mut font_data_vec = Vec::new();
font_data_vec.extend(font.load(None).unwrap().data());
responses.add(PortfolioMessage::FontLoaded {
font_family: name.to_owned(),
font_style: style.to_named_style(),
data: font_data_vec,
});
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.

critical

Loading the full data for every system font and sending it through the message system is extremely inefficient. This will cause significant performance degradation and high memory consumption on startup, especially for users with many installed fonts. Font data should be loaded lazily only when a specific font is required for rendering, rather than populating the entire cache upfront.

// simultaneously?? call font loaded a bunch of times to shove data into font data cache
let mut system_font_family_names = Vec::new();
system_font_family_names.extend(system_font_collection.family_names());
log::error!("fonts are: {:?}", system_font_family_names);
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.

medium

Using log::error for informational logging of font names is inappropriate. This should be changed to log::debug or log::trace to avoid cluttering the error logs.

Suggested change
log::error!("fonts are: {:?}", system_font_family_names);
log::debug!("fonts are: {:?}", system_font_family_names);

log::error!("fonts are: {:?}", system_font_family_names);
let mut families_for_catalog = Vec::new();
for name in system_font_family_names {
let family = system_font_collection_again.family_by_name(name).unwrap();
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.

medium

Avoid using unwrap() on family_by_name. If a font family name returned by the collection is unexpectedly missing, this will cause the application to panic. It is safer to handle this case gracefully.

Suggested change
let family = system_font_collection_again.family_by_name(name).unwrap();
let Some(family) = system_font_collection_again.family_by_name(name) else { continue };

url: "".to_owned(),
};
let mut font_data_vec = Vec::new();
font_data_vec.extend(font.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.

medium

font.load(None) can fail if a font file is corrupt or inaccessible. Using unwrap() here makes the application fragile. Use a guard or if let to skip fonts that cannot be loaded.

								let Some(font_data) = font.load(None) else { continue };
								font_data_vec.extend(font_data.data());

if Editor::environment().is_desktop() {
let system_font_context = FontContext::new(); // create system font context
let mut system_font_collection = system_font_context.collection;
let mut system_font_collection_again = system_font_collection.clone(); // because parley was not made correctly
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.

medium

Cloning the entire font collection is an expensive operation. If parley requires ownership or a mutable reference that prevents using the original system_font_collection, consider if there is a way to restructure the access to avoid this clone.

@Macman1234 Macman1234 marked this pull request as ready for review April 23, 2026 23:20
Copy link
Copy Markdown
Contributor

@cubic-dev-ai cubic-dev-ai Bot left a comment

Choose a reason for hiding this comment

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

2 issues found across 8 files

Confidence score: 3/5

  • There is a concrete user-impacting risk in editor/src/messages/portfolio/portfolio_message_handler.rs: unchecked unwrap() calls during system font loading can panic if a system font is corrupted or unreadable.
  • Font catalog generation in editor/src/messages/portfolio/portfolio_message_handler.rs runs synchronously in the portfolio message handler, which can block the dispatcher/event loop and lead to UI stalls on systems with large font sets.
  • Given the medium-to-high severities (6–7/10) and high confidence, this looks mergeable with caution but carries real regression risk unless these paths are hardened.
  • Pay close attention to editor/src/messages/portfolio/portfolio_message_handler.rs - panic handling in font loading and synchronous catalog generation may impact stability and responsiveness.
Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="editor/src/messages/portfolio/portfolio_message_handler.rs">

<violation number="1" location="editor/src/messages/portfolio/portfolio_message_handler.rs:375">
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.</violation>

<violation number="2" location="editor/src/messages/portfolio/portfolio_message_handler.rs:424">
P1: Unchecked `unwrap()` calls in system font loading can panic on corrupted or unreadable system fonts.</violation>
</file>

Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.

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>

}
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>

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant