fix(text): address PR #180 review

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 <noreply@anthropic.com>
This commit is contained in:
Hakan Seven 2026-06-26 00:27:40 +03:00
commit dee5a05652
4 changed files with 34 additions and 23 deletions

View file

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

View file

@ -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),

View file

@ -74,12 +74,22 @@ pub fn canonical_family_name(family: &str) -> Option<String> {
}
}
// 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 12 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

View file

@ -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();
}