forked from 1b-agent/opencadstudio-upstream
fix(text): no stroke-font tracking after big-font and fallback glyphs (#1470)
Every glyph advanced by the face's letter spacing, including CJK glyphs a style's big font or the TrueType fallback supplies. Their advances already carry the gap (big font) or side bearings (TrueType), so with an LFF primary such as iso each ideograph took 1.33 heights instead of one. Face::spacing_after(ch) gives the tracking only to glyphs the stroke font draws itself; every layout path uses it. The 1.8-height widths in the report were the TEXT bounds estimate counting UTF-8 bytes; opencadcodec 1109d52 counts characters. Pins opencadcodec 1109d52, opencadkernel 8a246b0, opencadgraph 3c4af1c. Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
1 parent
fdc01e13c8
commit
89512c2f20
9 files changed
+41
-20
No files matched your search
Generated
+4
-4
@@ -4201,7 +4201,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "opencadcodec"
|
||||
version = "0.5.5"
|
||||
source = "git+https://github.com/HakanSeven12/opencadcodec.git?rev=7c20aa7#7c20aa7fad405845c5bd5cdcc2a232cdc2a4687d"
|
||||
source = "git+https://github.com/HakanSeven12/opencadcodec.git?rev=1109d52#1109d52f65ae8f00d75f43445908bff48807c0c5"
|
||||
dependencies = [
|
||||
"ahash 0.8.12",
|
||||
"anyhow",
|
||||
@@ -4224,7 +4224,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "opencadgraph"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/HakanSeven12/opencadgraph.git?rev=1543a5a#1543a5aeb0b871a2f23a3b55117c7f5af1e1d229"
|
||||
source = "git+https://github.com/HakanSeven12/opencadgraph.git?rev=3c4af1c#3c4af1c3bdebc968630ad3c28046173bed0aa200"
|
||||
dependencies = [
|
||||
"opencadkernel",
|
||||
"serde_json",
|
||||
@@ -4233,7 +4233,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "opencadkernel"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/HakanSeven12/opencadkernel.git?rev=d71a828#d71a8284f0388025e07f219c7b8f6bb86e7188ec"
|
||||
source = "git+https://github.com/HakanSeven12/opencadkernel.git?rev=8a246b0#8a246b0026fb9c626a39a6ba8ccf681f1bf0461e"
|
||||
dependencies = [
|
||||
"cavalier_contours",
|
||||
"opencadcodec",
|
||||
@@ -4244,7 +4244,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "opencadkernel-constraints"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/HakanSeven12/opencadkernel.git?rev=d71a828#d71a8284f0388025e07f219c7b8f6bb86e7188ec"
|
||||
source = "git+https://github.com/HakanSeven12/opencadkernel.git?rev=8a246b0#8a246b0026fb9c626a39a6ba8ccf681f1bf0461e"
|
||||
dependencies = [
|
||||
"nalgebra",
|
||||
]
|
||||
|
||||
+4
-4
@@ -33,10 +33,10 @@ rfd = "0.17"
|
||||
clap = { version = "4", features = ["derive", "string"] }
|
||||
env_logger = "0.11"
|
||||
log = "0.4"
|
||||
codec = { package = "opencadcodec", git = "https://github.com/HakanSeven12/opencadcodec.git", rev = "7c20aa7", features = ["serde"] }
|
||||
kernel = { package = "opencadkernel", git = "https://github.com/HakanSeven12/opencadkernel.git", rev = "d71a828", features = ["acis", "offset"] }
|
||||
kernel-constraints = { package = "opencadkernel-constraints", git = "https://github.com/HakanSeven12/opencadkernel.git", rev = "d71a828" }
|
||||
graph = { package = "opencadgraph", git = "https://github.com/HakanSeven12/opencadgraph.git", rev = "1543a5a" }
|
||||
codec = { package = "opencadcodec", git = "https://github.com/HakanSeven12/opencadcodec.git", rev = "1109d52", features = ["serde"] }
|
||||
kernel = { package = "opencadkernel", git = "https://github.com/HakanSeven12/opencadkernel.git", rev = "8a246b0", features = ["acis", "offset"] }
|
||||
kernel-constraints = { package = "opencadkernel-constraints", git = "https://github.com/HakanSeven12/opencadkernel.git", rev = "8a246b0" }
|
||||
graph = { package = "opencadgraph", git = "https://github.com/HakanSeven12/opencadgraph.git", rev = "3c4af1c" }
|
||||
dwg-thumbnailer = { path = "crates/dwg-thumbnailer" }
|
||||
flate2 = "1"
|
||||
image = { version = "0.25", default-features = false, features = ["png", "jpeg", "bmp", "tiff"] }
|
||||
|
||||
@@ -15,7 +15,7 @@ serde_json = { version = "1", optional = true }
|
||||
# Pulled in only by the `host` feature, which adds the `opencadcodec`-typed
|
||||
# `HostApi` runtime surface. The default crate stays dependency-free so engine
|
||||
# crates and external tooling can depend on the manifest/ribbon contract cheaply.
|
||||
codec = { package = "opencadcodec", git = "https://github.com/HakanSeven12/opencadcodec.git", rev = "7c20aa7", optional = true, features = ["serde"] }
|
||||
codec = { package = "opencadcodec", git = "https://github.com/HakanSeven12/opencadcodec.git", rev = "1109d52", optional = true, features = ["serde"] }
|
||||
|
||||
# Runtime IPC and serialization (host feature only).
|
||||
interprocess = { version = "2", optional = true }
|
||||
@@ -39,7 +39,7 @@ serde_json = "1"
|
||||
serde = { version = "1", features = ["derive"] }
|
||||
cargo-lock = "11"
|
||||
# opencadcodec is scanned at build time to generate the embedded type registry.
|
||||
codec = { package = "opencadcodec", git = "https://github.com/HakanSeven12/opencadcodec.git", rev = "7c20aa7", features = ["serde"] }
|
||||
codec = { package = "opencadcodec", git = "https://github.com/HakanSeven12/opencadcodec.git", rev = "1109d52", features = ["serde"] }
|
||||
|
||||
[dev-dependencies]
|
||||
serde_json = "1"
|
||||
@@ -8,7 +8,7 @@ publish = false
|
||||
crate-type = ["cdylib"]
|
||||
|
||||
[dependencies]
|
||||
codec = { package = "opencadcodec", git = "https://github.com/HakanSeven12/opencadcodec.git", rev = "7c20aa7", features = ["serde"] }
|
||||
codec = { package = "opencadcodec", git = "https://github.com/HakanSeven12/opencadcodec.git", rev = "1109d52", features = ["serde"] }
|
||||
bincode = "1.3"
|
||||
serde = { version = "1", features = ["derive"] }
|
||||
console_error_panic_hook = "0.1"
|
||||
|
||||
Generated
+1
-1
@@ -1265,7 +1265,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "opencadcodec"
|
||||
version = "0.5.5"
|
||||
source = "git+https://github.com/HakanSeven12/opencadcodec.git?rev=7c20aa7#7c20aa7fad405845c5bd5cdcc2a232cdc2a4687d"
|
||||
source = "git+https://github.com/HakanSeven12/opencadcodec.git?rev=1109d52#1109d52f65ae8f00d75f43445908bff48807c0c5"
|
||||
dependencies = [
|
||||
"ahash 0.8.12",
|
||||
"anyhow",
|
||||
|
||||
@@ -214,7 +214,7 @@ pub fn text_local_bounds(
|
||||
max_y = max_y.max(sy);
|
||||
}
|
||||
}
|
||||
cursor_x += glyph.advance + face.letter_spacing();
|
||||
cursor_x += glyph.advance + face.spacing_after(ch);
|
||||
}
|
||||
None => {
|
||||
cursor_x += 6.0 + face.letter_spacing();
|
||||
@@ -964,7 +964,7 @@ pub fn measure_word(
|
||||
let mut w = 0.0_f32;
|
||||
for ch in text.chars() {
|
||||
w += match face.glyph(ch) {
|
||||
Some(g) => (g.advance + face.letter_spacing() * state.tracking) * scale,
|
||||
Some(g) => (g.advance + face.spacing_after(ch) * state.tracking) * scale,
|
||||
None => (6.0 + face.letter_spacing() * state.tracking) * scale,
|
||||
};
|
||||
}
|
||||
@@ -2315,7 +2315,7 @@ pub fn layout_mtext(opts: &MTextRenderOpts) -> MTextLayout {
|
||||
for (ci, ch) in text.chars().enumerate() {
|
||||
let adv = match face.glyph(ch) {
|
||||
Some(g) => {
|
||||
(g.advance + face.letter_spacing() * tracking) * scale
|
||||
(g.advance + face.spacing_after(ch) * tracking) * scale
|
||||
}
|
||||
None => (6.0 + face.letter_spacing() * tracking) * scale,
|
||||
};
|
||||
|
||||
@@ -158,6 +158,25 @@ impl Face {
|
||||
}
|
||||
}
|
||||
|
||||
/// [`letter_spacing`](Self::letter_spacing) after the glyph drawn for
|
||||
/// `ch`: a stroke font's tracking belongs to its own glyphs only. A
|
||||
/// big-font glyph's advance already carries its gap and a TrueType
|
||||
/// stand-in's its side bearings, so tracking them too spread CJK text
|
||||
/// by a third of the height per character (#1470).
|
||||
pub fn spacing_after(&self, ch: char) -> f32 {
|
||||
match self {
|
||||
Face::Lff(f) if f.glyph(ch).is_none() => 0.0,
|
||||
Face::WithBig { primary, big } => {
|
||||
if ttf_glyph::is_full_width(ch) && shx::bigfont_glyph(big, ch).is_some() {
|
||||
0.0
|
||||
} else {
|
||||
primary.spacing_after(ch)
|
||||
}
|
||||
}
|
||||
_ => self.letter_spacing(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Width of a space (9-unit).
|
||||
pub fn word_spacing(&self) -> f32 {
|
||||
match self {
|
||||
|
||||
@@ -143,12 +143,14 @@ pub fn layout_glyph_quads(
|
||||
uv_min: e.uv_min,
|
||||
uv_max: e.uv_max,
|
||||
});
|
||||
cursor_x += e.advance + face.letter_spacing() * tracking;
|
||||
cursor_x += e.advance + face.spacing_after(ch) * tracking;
|
||||
}
|
||||
None => {
|
||||
// No ink (whitespace glyph) or atlas full: advance only.
|
||||
let adv = face.glyph(ch).map(|g| g.advance).unwrap_or(6.0);
|
||||
cursor_x += adv + face.letter_spacing() * tracking;
|
||||
cursor_x += match face.glyph(ch) {
|
||||
Some(g) => g.advance + face.spacing_after(ch) * tracking,
|
||||
None => 6.0 + face.letter_spacing() * tracking,
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -441,7 +441,7 @@ pub fn tessellate_text_run(
|
||||
Some(glyph) => {
|
||||
emit_glyph(out, &glyph.strokes, *cursor_x);
|
||||
emit_fill(fill_tris, &glyph.fill_tris, *cursor_x);
|
||||
*cursor_x += glyph.advance + face.letter_spacing() * tracking;
|
||||
*cursor_x += glyph.advance + face.spacing_after(ch) * tracking;
|
||||
}
|
||||
None => {
|
||||
warn_missing_glyph(font_name, ch);
|
||||
@@ -470,7 +470,7 @@ pub fn tessellate_text_run(
|
||||
Some(glyph) => {
|
||||
emit_glyph(&mut out, &glyph.strokes, cursor_x);
|
||||
emit_fill(&mut fill_tris, &glyph.fill_tris, cursor_x);
|
||||
cursor_x += glyph.advance + face.letter_spacing() * tracking;
|
||||
cursor_x += glyph.advance + face.spacing_after(*c) * tracking;
|
||||
}
|
||||
None => {
|
||||
warn_missing_glyph(font_name, *c);
|
||||
|
||||
Reference in new issue
Block a user