Compare commits

..
Author SHA1 Message Date
Brandon ThomasandClaude Opus 5.5 a5466741c4 Text: superscript and subscript advance at their drawn size
Superscript and subscript glyphs were drawn at two thirds of the run's size but
advanced by the full size, so the letters spread apart ("s u p e r") and the
line ran wider than in PowerPoint. The cluster advance now uses the same scale
as the glyphs.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-12 13:57:30 -04:00
Brandon ThomasandClaude Opus 5.5 688c7ef592 Release 0.6.1: rebuild with the fixed shared release workflow
The previous release build failed in craft-common-actions, not in this
repo: riscv64 packaging got --formats deb,rpm,tar (fixed in
craft-common-actions#3), Linux jobs lacked the ALSA headers
(craft-common-actions#5), and Windows signing needed the AZURE_* secrets
listed in the caller.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 11:11:13 -04:00
Brandon ThomasandClaude Opus 5.5 a4540c1f0e Release workflow: pass the Azure signing secrets so Windows builds are signed (#181)
Since the release workflow moved to storytold/craft-common-actions, it has
passed only the Apple and WINDOWS_CERTIFICATE secrets. The signing job reads
values from the release environment, but only for names the caller lists, so
the AZURE_* names that Trusted Signing uses came through empty and Windows
builds would ship unsigned. Same fix as storytold/craft-launcher#174.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 11:08:13 -04:00
Brandon ThomasandClaude Opus 5.5 0ec0cc5e81 Release prep: 0.6.0
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 05:44:22 -04:00
4 changed files with 76 additions and 32 deletions
Generated
+27 -27
View File
@@ -877,7 +877,7 @@ checksum = "0c87e182de0887fd5361989c677c4e8f5000cd9491d6d563161a8f3a5519fc7f"
[[package]]
name = "deckcraft"
version = "0.5.0"
version = "0.6.1"
dependencies = [
"arboard",
"cpal",
@@ -896,7 +896,7 @@ dependencies = [
[[package]]
name = "deckcraft-anim"
version = "0.5.0"
version = "0.6.1"
dependencies = [
"deckcraft-geom",
"deckcraft-model",
@@ -905,7 +905,7 @@ dependencies = [
[[package]]
name = "deckcraft-av1"
version = "0.5.0"
version = "0.6.1"
dependencies = [
"deckcraft-bitstream",
"rayon",
@@ -914,14 +914,14 @@ dependencies = [
[[package]]
name = "deckcraft-bitstream"
version = "0.5.0"
version = "0.6.1"
dependencies = [
"proptest",
]
[[package]]
name = "deckcraft-cli"
version = "0.5.0"
version = "0.6.1"
dependencies = [
"deckcraft-engine",
"deckcraft-mcp",
@@ -930,14 +930,14 @@ dependencies = [
[[package]]
name = "deckcraft-color"
version = "0.5.0"
version = "0.6.1"
dependencies = [
"serde",
]
[[package]]
name = "deckcraft-engine"
version = "0.5.0"
version = "0.6.1"
dependencies = [
"deckcraft-color",
"deckcraft-fonts",
@@ -959,7 +959,7 @@ dependencies = [
[[package]]
name = "deckcraft-fonts"
version = "0.5.0"
version = "0.6.1"
dependencies = [
"epaint_default_fonts",
"harfrust 0.13.3",
@@ -971,7 +971,7 @@ dependencies = [
[[package]]
name = "deckcraft-format"
version = "0.5.0"
version = "0.6.1"
dependencies = [
"deckcraft-model",
"serde",
@@ -982,7 +982,7 @@ dependencies = [
[[package]]
name = "deckcraft-geom"
version = "0.5.0"
version = "0.6.1"
dependencies = [
"kurbo",
"serde",
@@ -990,7 +990,7 @@ dependencies = [
[[package]]
name = "deckcraft-h264"
version = "0.5.0"
version = "0.6.1"
dependencies = [
"deckcraft-bitstream",
"rayon",
@@ -999,7 +999,7 @@ dependencies = [
[[package]]
name = "deckcraft-hevc"
version = "0.5.0"
version = "0.6.1"
dependencies = [
"deckcraft-bitstream",
"rayon",
@@ -1008,7 +1008,7 @@ dependencies = [
[[package]]
name = "deckcraft-isobmff"
version = "0.5.0"
version = "0.6.1"
dependencies = [
"deckcraft-bitstream",
"thiserror 2.0.21",
@@ -1016,11 +1016,11 @@ dependencies = [
[[package]]
name = "deckcraft-matroska"
version = "0.5.0"
version = "0.6.1"
[[package]]
name = "deckcraft-mcp"
version = "0.5.0"
version = "0.6.1"
dependencies = [
"deckcraft-engine",
"deckcraft-render",
@@ -1030,7 +1030,7 @@ dependencies = [
[[package]]
name = "deckcraft-media"
version = "0.5.0"
version = "0.6.1"
dependencies = [
"deckcraft-av1",
"deckcraft-h264",
@@ -1048,7 +1048,7 @@ dependencies = [
[[package]]
name = "deckcraft-model"
version = "0.5.0"
version = "0.6.1"
dependencies = [
"deckcraft-color",
"deckcraft-geom",
@@ -1059,15 +1059,15 @@ dependencies = [
[[package]]
name = "deckcraft-ogg"
version = "0.5.0"
version = "0.6.1"
[[package]]
name = "deckcraft-opus"
version = "0.5.0"
version = "0.6.1"
[[package]]
name = "deckcraft-pdf"
version = "0.5.0"
version = "0.6.1"
dependencies = [
"deckcraft-fonts",
"deckcraft-model",
@@ -1082,7 +1082,7 @@ dependencies = [
[[package]]
name = "deckcraft-pptx"
version = "0.5.0"
version = "0.6.1"
dependencies = [
"deckcraft-color",
"deckcraft-engine",
@@ -1098,7 +1098,7 @@ dependencies = [
[[package]]
name = "deckcraft-render"
version = "0.5.0"
version = "0.6.1"
dependencies = [
"deckcraft-color",
"deckcraft-fonts",
@@ -1114,7 +1114,7 @@ dependencies = [
[[package]]
name = "deckcraft-text"
version = "0.5.0"
version = "0.6.1"
dependencies = [
"chrono",
"deckcraft-color",
@@ -1128,7 +1128,7 @@ dependencies = [
[[package]]
name = "deckcraft-ui-egui"
version = "0.5.0"
version = "0.6.1"
dependencies = [
"deckcraft-anim",
"deckcraft-color",
@@ -1150,7 +1150,7 @@ dependencies = [
[[package]]
name = "deckcraft-vp9"
version = "0.5.0"
version = "0.6.1"
dependencies = [
"deckcraft-bitstream",
"rayon",
@@ -1159,7 +1159,7 @@ dependencies = [
[[package]]
name = "deckcraft-web"
version = "0.5.0"
version = "0.6.1"
dependencies = [
"deckcraft-engine",
"deckcraft-ui-egui",
@@ -5655,7 +5655,7 @@ checksum = "9440ea3e5aeabb0ac63af70daf835274065238cdd0cec83418f417eae38bacee"
[[package]]
name = "xtask"
version = "0.5.0"
version = "0.6.1"
dependencies = [
"serde_json",
]
+1 -1
View File
@@ -4,7 +4,7 @@ members = ["crates/*", "apps/*", "xtask"]
default-members = ["crates/*", "apps/deckcraft", "apps/deckcraft-cli"]
[workspace.package]
version = "0.5.0"
version = "0.6.1"
edition = "2024"
license = "MIT OR Apache-2.0"
rust-version = "1.90"
+10 -4
View File
@@ -566,6 +566,9 @@ fn shape_cells(ctx: &Ctx, text: &str, char_style: &[usize], styles: &[Style], fa
cells
}
/// Size of superscript and subscript text relative to the run's size.
const SUB_SUPER: f64 = 0.66;
/// Shape cells `range` (one item) and hang each cluster's glyphs on its first cell, sharing the
/// cluster's advance among its graphemes (by the ligature's GDEF carets when it has them).
#[allow(clippy::too_many_arguments)]
@@ -593,7 +596,11 @@ fn shape_item(
let st = styles.get(cell.style);
let size = st.map(|s| s.size).unwrap_or(18.0);
let small = st.is_some_and(|s| s.caps == Caps::Small) && cell.ch.is_lowercase();
(if small { size * 0.8 } else { size }) / upem
let size = if small { size * 0.8 } else { size };
// Superscript and subscript glyphs are drawn at two thirds of the size, so they advance by
// two thirds too.
let size = if st.is_some_and(|s| s.baseline != 0.0) { size * SUB_SUPER } else { size };
size / upem
};
// Cluster heads in this item, ascending; each cluster spans to the next head.
let mut heads: Vec<usize> = glyphs.iter().filter_map(|g| char_at_byte.get(&g.cluster).copied()).filter(|h| range.contains(h)).collect();
@@ -1155,7 +1162,7 @@ fn layout_scaled(ctx: &Ctx, shape: &Shape, body: &TextBody, bp: &BodyProps, opts
let Some(st) = p.styles.get(cell.style) else { continue };
let Some(&(cx, cx1)) = line.edges.get(k) else { continue };
let base_y = line.baseline - st.baseline * st.size;
let size = if st.baseline != 0.0 { st.size * 0.66 } else { st.size };
let size = if st.baseline != 0.0 { st.size * SUB_SUPER } else { st.size };
let size = if st.caps == Caps::Small && cell.ch.is_lowercase() { size * 0.8 } else { size };
let key = (cell.style, cell.face);
if key != cur_key {
@@ -1204,9 +1211,8 @@ fn layout_scaled(ctx: &Ctx, shape: &Shape, body: &TextBody, bp: &BodyProps, opts
}
}
}
let k66 = if st.baseline != 0.0 { 0.66 } else { 1.0 };
for (gid, gx, gy) in &cell.glyphs {
r.glyphs.push((*gid, cl + kash + gx * k66, base_y + gy));
r.glyphs.push((*gid, cl + kash + gx, base_y + gy));
r.cells.push((ci, cell.ch));
}
}
+38
View File
@@ -395,3 +395,41 @@ fn hostile_bidi_input_never_panics() {
}
}
}
/// Width of `word` laid out in a run with `baseline` (0 = normal, 0.3 = superscript …).
fn run_width(word: &str, baseline: Option<f64>) -> f64 {
let mut run = Run::new(word);
run.props.size = Some(24.0);
run.props.baseline = baseline;
let (p, sh) = bidi_setup(vec![run], 2000.0, false, Align::Left);
let l = lay(&p, &sh, 2000.0, 300.0);
let line = &l.lines[0];
line.caret_x.last().unwrap() - line.caret_x.first().unwrap()
}
#[test]
fn superscript_and_subscript_advance_at_their_drawn_size() {
// The glyphs are drawn at two thirds of the size; advancing by the full size spread the
// letters apart ("s u p e r").
let normal = run_width("superscript", None);
for b in [Some(0.3), Some(-0.25)] {
let w = run_width("superscript", b);
assert!((w / normal - SUB_SUPER).abs() < 0.02, "{b:?}: {w} vs {normal}");
}
}
#[test]
fn superscript_glyphs_sit_inside_their_cells() {
let mut run = Run::new("super");
run.props.size = Some(24.0);
run.props.baseline = Some(0.3);
let (p, sh) = bidi_setup(vec![run], 2000.0, false, Align::Left);
let l = lay(&p, &sh, 2000.0, 300.0);
let line = &l.lines[0];
let r = l.runs.iter().find(|r| !r.cells.is_empty()).unwrap();
assert!((r.size - 24.0 * SUB_SUPER).abs() < 1e-6);
for (g, (ci, _)) in r.glyphs.iter().zip(&r.cells) {
let (x0, x1) = line.edges[*ci - line.start];
assert!(g.1 >= x0 - 0.01 && g.1 < x1, "glyph at {} outside its cell {x0}..{x1}", g.1);
}
}