forked from 1b-agent/opencadstudio-upstream
feat(fonts): system TrueType support with shaping and fallback
Add a TrueType/OpenType render path alongside the LFF stroke engine so users can pick fonts installed on their machine for text and MText. - Discover system families via fontdb (sysfont) and list them in the text-style picker. - Extract glyph outlines with ttf-parser, flattened into wire strokes normalized to the same 9-unit cap-height space as LFF glyphs. - Shape runs with cosmic-text (ligatures, Arabic joining, kerning, bidi) plus automatic font fallback: a glyph missing from the chosen family is taken from another installed font. - Unify both sources behind a single Face type; a shared run tokenizer drives the stroke (per-glyph) and shaped (TTF) renderers identically. - Stroke fonts now fall back to a system TTF (cosmic-text picks it) for characters no LFF font covers, replacing the unicode/iso3098 chain. Also trim the embedded LFF set from 47 to 26, keeping the AutoCAD-SHX equivalents and dropping LibreCAD-only fonts now covered by TrueType. LFF builtins keep priority over same-named system fonts so DXF text renders identically across machines. Co-Authored-By: Claude Opus 4.8 <[email protected]>
This commit is contained in:
1 parent
eff6c7c730
commit
cd048165a9
31 files changed
+1037
-763578
No files matched your search
+51
-2
@@ -71,7 +71,7 @@ const LIST: Color = Color {
|
||||
|
||||
const BUILTIN_FONTS: &[&str] = &[
|
||||
"Standard", "ISO", "Simplex", "RomanS", "RomanD", "RomanC", "RomanT", "ItalicC", "ItalicT",
|
||||
"ScriptS", "ScriptC", "GothGBT", "GothGRT", "GothITT", "Cursive", "GreekC", "Symbol", "ISO",
|
||||
"ScriptS", "ScriptC", "GothGBT", "GothGRT", "GothITT", "GreekC", "Symbol",
|
||||
"ISO3098", "Unicode",
|
||||
];
|
||||
|
||||
@@ -255,7 +255,56 @@ pub fn view_window<'a>(v: TextStyleView<'a>) -> Element<'a, Message> {
|
||||
left: 8.0,
|
||||
});
|
||||
|
||||
let editor = row![font_panel, vsep(), props_panel].height(Fill);
|
||||
// ── System TrueType fonts (read from the user's machine via fontdb) ──
|
||||
let ttf_items: Vec<Element<'_, Message>> = crate::scene::sysfont::families()
|
||||
.iter()
|
||||
.map(|fam| {
|
||||
let is_sel = ttf_buf.eq_ignore_ascii_case(fam);
|
||||
button(text(fam).size(10))
|
||||
.on_press(Message::TextStyleEdit {
|
||||
field: "ttf",
|
||||
value: fam.clone(),
|
||||
})
|
||||
.style(list_item(is_sel))
|
||||
.padding([3, 8])
|
||||
.width(Fill)
|
||||
.into()
|
||||
})
|
||||
.collect();
|
||||
|
||||
let ttf_panel = container(
|
||||
column![
|
||||
text("TrueType (system)").size(10).color(DIM),
|
||||
container(scrollable(column(ttf_items).spacing(1)).height(Fill))
|
||||
.style(|_: &Theme| container::Style {
|
||||
background: Some(Background::Color(LIST)),
|
||||
border: Border {
|
||||
color: BORDER,
|
||||
width: 1.0,
|
||||
radius: 3.0.into()
|
||||
},
|
||||
..Default::default()
|
||||
})
|
||||
.width(Fill)
|
||||
.height(Fill)
|
||||
.padding(2),
|
||||
text_input("TrueType font…", ttf_buf)
|
||||
.on_input(|v| Message::TextStyleEdit {
|
||||
field: "ttf",
|
||||
value: v
|
||||
})
|
||||
.style(field_style)
|
||||
.size(11)
|
||||
.width(Fill),
|
||||
]
|
||||
.spacing(6)
|
||||
.height(Fill),
|
||||
)
|
||||
.width(190)
|
||||
.height(Fill)
|
||||
.padding([12, 8]);
|
||||
|
||||
let editor = row![font_panel, vsep(), ttf_panel, vsep(), props_panel].height(Fill);
|
||||
|
||||
crate::ui::style_manager::view(crate::ui::style_manager::Scaffold {
|
||||
kind: StyleKind::Text,
|
||||
|
||||
Reference in new issue
Block a user