Merge PR #592 from NikitkaRa: Notes Page view draws master text wrapped (stacked on #583)

This commit is contained in:
Brandon Thomas
2026-10-14 13:30:54 -04:00
4 changed files with 66 additions and 8 deletions
+48
View File
@@ -260,6 +260,54 @@ pub fn master_text_style(pres: &Presentation, m: &deckcraft_model::Master, sh: &
TextStyle { family, size: r.size.unwrap_or(12.0).clamp(1.0, 400.0), bold: r.bold == Some(true), italic: r.italic == Some(true), color }
}
/// One laid-out piece of master text on a page of `paper` points: a run's text on one line, its
/// baseline start and its size, colour, weight and slant.
#[derive(Clone, Debug, PartialEq)]
pub struct TextPiece {
pub at: (f64, f64),
pub text: String,
pub size: f64,
pub color: deckcraft_model::Rgba,
pub bold: bool,
pub italic: bool,
}
/// The header, date, footer and page number of `m` for page `n`, laid out by the text engine
/// (wrapped, mixed formatting) and scaled to `paper`, for drawing without glyph access (the
/// Notes Page view).
pub fn master_text_pieces(pres: &Presentation, m: &deckcraft_model::Master, paper: (f64, f64), n: usize) -> Vec<TextPiece> {
let (kx, ky) = (paper.0 / pres.notes_size.width.max(1.0), paper.1 / pres.notes_size.height.max(1.0));
let mut out = vec![];
for pt in deckcraft_render::place_master_texts(pres, m, n) {
for run in &pt.layout.runs {
let Some(g) = run.glyphs.first() else { continue };
let text: String = pt
.body
.paragraphs
.get(run.para)
.map(|p| p.runs.iter().map(|r| r.text.as_str()).collect::<String>())
.unwrap_or_default()
.chars()
.skip(run.chars.0)
.take(run.chars.1.saturating_sub(run.chars.0))
.collect();
if text.trim().is_empty() {
continue;
}
let p = pt.transform * kurbo::Point::new(g.1, g.2);
out.push(TextPiece {
at: (p.x * kx, p.y * ky),
text,
size: run.size * ky,
color: run.color,
bold: run.fake_bold || run.face.weight >= 600.0,
italic: run.fake_italic || run.face.italic,
});
}
}
out
}
/// The header, date, footer and page-number text a master puts on page `n`, with where it goes,
/// whether it's right-aligned and its style. Placeholders with no text are left out; the page
/// number is always `n`.
+8
View File
@@ -209,6 +209,14 @@ fn master_text_wraps_and_keeps_mixed_formatting() {
assert!(h.runs.iter().any(|r| r.color == Rgba::rgb(200, 0, 0)));
let num: String = placed[1].body.text();
assert_eq!(num, "4");
// The same layout as pieces for the Notes Page view: on more than one line, scaled to paper.
let pieces = master_text_pieces(&p, &m, (306.0, 396.0), 4);
let mut ys: Vec<i64> = pieces.iter().map(|x| x.at.1.round() as i64).collect();
ys.dedup();
assert!(ys.len() >= 3, "two header lines and the number: {pieces:?}");
assert!(pieces.iter().any(|x| x.bold && (x.size - 9.0).abs() < 0.5), "18 pt bold at half size");
assert!(pieces.iter().any(|x| x.color == Rgba::rgb(200, 0, 0) && !x.bold));
assert!(pieces.iter().any(|x| x.text == "4"));
p.notes_master = Some(Arc::new(m));
assert!(export(&p, &PdfOptions { layout: PageLayout::Notes, ..Default::default() }).unwrap().starts_with(b"%PDF"));
}
+6 -5
View File
@@ -378,11 +378,12 @@ pub fn notes_page(app: &mut SlideApp, ui: &mut Ui) {
let srect = to_screen((slide_box.x0, slide_box.y0, slide_box.x1, slide_box.y1));
let sw = srect.width();
if let Some(m) = doc.notes_master.as_deref() {
for (r, text, right, st) in deckcraft_engine::pdf::master_texts(&doc, m, paper, idx + 1) {
let r = to_screen((r.x0, r.y0, r.x1, r.y1));
let (at, align) = if right { (r.right_center(), egui::Align2::RIGHT_CENTER) } else { (r.left_center(), egui::Align2::LEFT_CENTER) };
let c = Color32::from_rgba_unmultiplied(st.color.r, st.color.g, st.color.b, st.color.a);
ui.painter().text(at, align, text.lines().next().unwrap_or(""), theme::font((st.size as f32 * k).max(6.0)), c);
// Laid out by the text engine as on the PDF page: wrapped, each run in its size and colour.
for piece in deckcraft_engine::pdf::master_text_pieces(&doc, m, paper, idx + 1) {
let size = (piece.size as f32 * k).max(4.0);
let at = page.min + vec2(piece.at.0 as f32 * k, piece.at.1 as f32 * k - size * 0.8);
let c = Color32::from_rgba_unmultiplied(piece.color.r, piece.color.g, piece.color.b, piece.color.a);
ui.painter().text(at, egui::Align2::LEFT_TOP, &piece.text, theme::font(size), c);
}
}
let ppp = ui.ctx().pixels_per_point();
+4 -3
View File
@@ -250,9 +250,9 @@ Detail in [animation-parity.md](animation-parity.md). 10–16 h; the pane is a *
PDF) place the slide and notes where the notes master does and print its header, date, footer
and page number in their own fonts, sizes and colours; PDF handouts print the handout master's.
Notes Master view previews the slide in its slide-image placeholder. PDF pages lay master text
out with the text engine (wrapping, mixed formatting). Left: the on-screen Notes Page view still
draws it as one line per placeholder.
- **Estimate:** 0.5 h.
out with the text engine (wrapping, mixed formatting), and the Notes Page view draws the same
layout. Left: the view draws it in the interface font (sizes, colours and weights kept).
- **Estimate:** done for beta.
### Edit points, text warp, 3-D
- **Evidence:** checklist rows "Edit points" (P1 missing), "Text warp transforms" (partial: all 40
@@ -387,6 +387,7 @@ Detail in [localization-parity.md](localization-parity.md).
| 2026-10-14 | minor | Size and Position dialog landed (position, size, rotation, scale, lock aspect, one undo). The dialog gap remains Font, Bullets and Numbering, Find/Replace, Replace Fonts, Action Settings, Print, Insert Object and the rest of the ~60 |
| 2026-10-14 | minor | File › New has six original starter decks; a large template gallery is still open |
| 2026-10-13 | minor | Artistic effects: 7 of 22 (blur, pencil grayscale, line drawing, film grain, cutout, glow edges, photocopy) applied, removed and round-tripped (`a14:imgLayer`) |
| 2026-10-14 | trivial | Notes Page view draws master text wrapped, as the PDF does |
| 2026-10-14 | trivial | PDF master header/footer text wraps and keeps mixed formatting |
| 2026-10-14 | trivial | Notes Master slide preview; master header/footer text keeps its formatting |
| 2026-10-14 | minor | Notes pages and PDF handouts follow the notes and handout masters |