mirror of
https://github.com/storytold/pdfcraft.git
synced 2026-11-02 22:41:47 -06:00
fix(forms-ui): measure auto-size text at no more than the screen-font bound
An auto-size single-line field's size follows its height, up to about 12,500 pt for a 14,400 pt field. The editor measured the line's width by laying it out at that size, before `field_screen_font` clamps to `MAX_SCREEN_FONT`, and egui rasterizes every glyph it lays out: one such measurement took 1.2 s in a debug build, on every frame. The width is now measured at no more than `MAX_SCREEN_FONT` and scaled, which is exact up to the bound and linear above it. `tools::forms_ui::tests::a_line_is_never_laid_out_above_the_screen_font_bound` fails with the bound removed (laid out at 12,522 pt). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01SqmHYx8FiWM5PcsoiErgp9
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
cef9908343
commit
3c6612a3f9
@@ -528,7 +528,9 @@ pub fn field_text_size(painter: &egui::Painter, f: &FormField, w: &FormWidget, t
|
||||
return pdfcraft_engine::field_comb_auto_text_size(height);
|
||||
}
|
||||
let font = |size: f64| egui::FontId::proportional(size as f32);
|
||||
let line_width = |size: f64| f64::from(painter.layout_no_wrap(text.to_string(), font(size), Color32::BLACK).size().x);
|
||||
let line_width = |size: f64| {
|
||||
bounded_line_width(size, |at| painter.layout_no_wrap(text.to_string(), egui::FontId::proportional(at), Color32::BLACK).size().x)
|
||||
};
|
||||
// Wrapped as the editor and the page preview wrap it.
|
||||
let wrap = width.max(1.0) as f32;
|
||||
let wrapped_height = |size: f64| f64::from(painter.layout(text.to_string(), font(size), Color32::BLACK, wrap).size().y);
|
||||
@@ -537,6 +539,14 @@ pub fn field_text_size(painter: &egui::Painter, f: &FormField, w: &FormWidget, t
|
||||
size as f32
|
||||
}
|
||||
|
||||
/// The width of a line at `size` points from `measure`, the width at a given size, called at no
|
||||
/// more than [`MAX_SCREEN_FONT`] and scaled: egui rasterizes every glyph it lays out, and a tall
|
||||
/// field's automatic size can be thousands of points.
|
||||
fn bounded_line_width(size: f64, measure: impl Fn(f32) -> f32) -> f64 {
|
||||
let at = size.min(f64::from(MAX_SCREEN_FONT));
|
||||
if at > 0.0 { f64::from(measure(at as f32)) * size / at } else { f64::from(measure(at as f32)) }
|
||||
}
|
||||
|
||||
/// The largest font, in screen points, the on-page field editor draws with: a bound that keeps
|
||||
/// one rasterized glyph (font size times pixels per point) well inside egui's font atlas.
|
||||
pub const MAX_SCREEN_FONT: f32 = 256.0;
|
||||
@@ -960,3 +970,22 @@ pub fn field_screen_rect(view: &DocView, info: &DocInfo, f: &FormField, widget:
|
||||
let xf = view.page_xform(page)?;
|
||||
Some(widget_rect(&xf, info, page, w.rect))
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::{MAX_SCREEN_FONT, bounded_line_width};
|
||||
use std::cell::Cell;
|
||||
|
||||
#[test]
|
||||
fn a_line_is_never_laid_out_above_the_screen_font_bound() {
|
||||
let largest = Cell::new(0.0f32);
|
||||
let measure = |at: f32| {
|
||||
largest.set(largest.get().max(at));
|
||||
at * 3.0
|
||||
};
|
||||
assert_eq!(bounded_line_width(12_522.0, measure), 12_522.0 * 3.0, "the width scales");
|
||||
assert_eq!(largest.get(), MAX_SCREEN_FONT, "laid out at the bound");
|
||||
assert_eq!(bounded_line_width(10.0, measure), 30.0, "below it, at the size itself");
|
||||
assert_eq!(largest.get(), MAX_SCREEN_FONT);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user