From dee5a0565221f167a090266d7eaad8830a9ed972 Mon Sep 17 00:00:00 2001 From: Hakan Seven Date: Fri, 26 Jun 2026 00:27:40 +0300 Subject: [PATCH] fix(text): address PR #180 review MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Follow-up cleanups on the merged TTF-fill work: - Drop the unconditional resolve_text_style log line — it fired for every text style and spammed ~7k stderr lines opening a single real drawing. - Drop the per-glyph tessellation-error eprintln; a malformed font would otherwise log once per character. The glyph still falls back silently. - Tessellate glyph fills with the nonzero winding rule (lyon defaults to even-odd, which mis-fills TrueType outlines whose contours overlap). - Make the sysfont prefix fallback deterministic (sorted) and require ≥3 chars so a 1–2 letter request can't grab an arbitrary family. - Remove the unrelated windows-gnu --exclude-all-symbols linker flag that slipped into .cargo/config.toml. Co-Authored-By: Claude Opus 4.8 --- .cargo/config.toml | 3 --- src/entities/text_support.rs | 6 +----- src/scene/text/sysfont.rs | 22 ++++++++++++++++------ src/scene/text/ttf_glyph.rs | 26 +++++++++++++++++--------- 4 files changed, 34 insertions(+), 23 deletions(-) diff --git a/.cargo/config.toml b/.cargo/config.toml index e137468b3..7ec6f88e6 100644 --- a/.cargo/config.toml +++ b/.cargo/config.toml @@ -1,6 +1,3 @@ -[target.x86_64-pc-windows-gnu] -rustflags = ["-C", "link-arg=-Wl,--exclude-all-symbols"] - # getrandom 0.3 (pulled by ahash via acadrust) needs this cfg to select its # browser backend on wasm32-unknown-unknown, alongside its `wasm_js` feature # (enabled in Cargo.toml for the wasm target). Without it the web build fails diff --git a/src/entities/text_support.rs b/src/entities/text_support.rs index 4f9715481..f579ffa07 100644 --- a/src/entities/text_support.rs +++ b/src/entities/text_support.rs @@ -51,11 +51,7 @@ pub fn resolve_text_style(style_name: &str, document: &CadDocument) -> ResolvedT } ResolvedTextStyle { - font_name: { - eprintln!("[resolve_text_style] style={:?} font_file={:?} true_type_font={:?} → font_name={:?}", - style.map(|s| &s.name), style.map(|s| &s.font_file), style.map(|s| &s.true_type_font), &font_name); - font_name - }, + font_name, width_factor: style.map(|s| s.width_factor as f32).unwrap_or(1.0), oblique_angle: style.map(|s| s.oblique_angle as f32).unwrap_or(0.0), is_backward: style.map(|s| s.is_backward()).unwrap_or(false), diff --git a/src/scene/text/sysfont.rs b/src/scene/text/sysfont.rs index 8cbf08dfc..b326752d7 100644 --- a/src/scene/text/sysfont.rs +++ b/src/scene/text/sysfont.rs @@ -74,12 +74,22 @@ pub fn canonical_family_name(family: &str) -> Option { } } - // 4. Try matching prefix/subset case-insensitively - if let Some(matched) = fonts().families.iter().find(|&f| { - let f_low = f.to_lowercase(); - f_low.starts_with(&family_lower) || family_lower.starts_with(&f_low) - }) { - return Some(matched.clone()); + // 4. Try matching prefix/subset case-insensitively. Require at least 3 + // chars so a 1–2 letter request can't grab an arbitrary family by the + // first iteration order. Iterating sorted keeps the pick deterministic. + if family_lower.len() >= 3 { + let mut candidates: Vec<&String> = fonts() + .families + .iter() + .filter(|&f| { + let f_low = f.to_lowercase(); + f_low.starts_with(&family_lower) || family_lower.starts_with(&f_low) + }) + .collect(); + candidates.sort(); + if let Some(matched) = candidates.first() { + return Some((*matched).clone()); + } } None diff --git a/src/scene/text/ttf_glyph.rs b/src/scene/text/ttf_glyph.rs index 48b5dc174..c773f2b6c 100644 --- a/src/scene/text/ttf_glyph.rs +++ b/src/scene/text/ttf_glyph.rs @@ -23,7 +23,9 @@ use std::sync::{Arc, Mutex, OnceLock}; use lyon_tessellation::math::point; use lyon_tessellation::path::Path; -use lyon_tessellation::{FillTessellator, FillOptions, BuffersBuilder, VertexBuffers, FillVertex}; +use lyon_tessellation::{ + BuffersBuilder, FillOptions, FillRule, FillTessellator, FillVertex, VertexBuffers, +}; /// Bézier flattening step counts. Outlines are small on screen most of the /// time; these are a fixed budget that keeps curves smooth without exploding @@ -206,14 +208,20 @@ fn triangulate_contours(contours: &[Vec<[f32; 2]>]) -> Vec<[f32; 2]> { let mut geometry: VertexBuffers<[f32; 2], u32> = VertexBuffers::new(); let mut tessellator = FillTessellator::new(); - if let Err(e) = tessellator.tessellate_path( - &path, - &FillOptions::default(), - &mut BuffersBuilder::new(&mut geometry, |vertex: FillVertex| { - vertex.position().to_array() - }), - ) { - eprintln!("[ttf_glyph] Tessellation error: {:?}", e); + // TrueType outlines use the nonzero winding rule; lyon's default even-odd + // rule mis-fills glyphs whose contours overlap. On a tessellation failure + // the glyph falls back to an empty fill silently — per-glyph logging would + // otherwise spam stderr once per character for a malformed font. + if tessellator + .tessellate_path( + &path, + &FillOptions::default().with_fill_rule(FillRule::NonZero), + &mut BuffersBuilder::new(&mut geometry, |vertex: FillVertex| { + vertex.position().to_array() + }), + ) + .is_err() + { return Vec::new(); }