From cc6773c95f1f23800c82329ec404f68868c8b22b Mon Sep 17 00:00:00 2001 From: MarekSpodBiedry <73122968+MarekSpodBiedry@users.noreply.github.com> Date: Thu, 22 Oct 2026 21:02:59 +0200 Subject: [PATCH] Use evaluated field values in the placed text so the PDF text layer shows the slide number (#1153) --- crates/render/src/placed.rs | 21 ++++++++++++++++++--- crates/render/src/tests.rs | 12 ++++++++++++ 2 files changed, 30 insertions(+), 3 deletions(-) diff --git a/crates/render/src/placed.rs b/crates/render/src/placed.rs index 0164f3f07..f83af46d8 100644 --- a/crates/render/src/placed.rs +++ b/crates/render/src/placed.rs @@ -2,9 +2,9 @@ //! that put real text over a raster (PDF text layer, links) and for text extraction. use deckcraft_model::resolve::{self, Ctx}; -use deckcraft_model::text::{Hyperlink, TextBody}; +use deckcraft_model::text::{Hyperlink, RunKind, TextBody}; use deckcraft_model::{Presentation, Shape, ShapeId, ShapeKind}; -use deckcraft_text::{Opts, TextLayout}; +use deckcraft_text::{Fields, Opts, TextLayout}; use kurbo::{Affine, Rect}; use crate::{SlideFields, shape_geometry, table}; @@ -162,7 +162,22 @@ fn place_text(out: &mut Placed, rctx: &Ctx, fields: &SlideFields, owner: ShapeId let r = Rect::new(x0, line.top, x1, line.bottom); out.links.push(PlacedLink { rect: m.transform_rect_bbox(r), link, shape: owner, text: true }); } - out.texts.push(PlacedText { layout, transform: m, body: body.clone() }); + // Field runs carry their saved text (`‹#›`); the layout drew their values, so the body that + // goes with it (character offsets, text layer) must hold the same values. + let mut body = body.clone(); + for para in &mut body.paragraphs { + let src = para.clone(); + for run in &mut para.runs { + if let RunKind::Field { field } = &run.kind { + let lang = resolve::run(rctx, s, &src, &run.props).lang; + if let Some(v) = fields.field_in(field, lang.as_deref()) { + run.text = v; + run.kind = RunKind::Text; + } + } + } + } + out.texts.push(PlacedText { layout, transform: m, body }); } /// The hyperlink of the model run containing character `at` of paragraph `para`. diff --git a/crates/render/src/tests.rs b/crates/render/src/tests.rs index 4ee1c889d..9987bd7c9 100644 --- a/crates/render/src/tests.rs +++ b/crates/render/src/tests.rs @@ -2595,3 +2595,15 @@ fn empty_bullet_is_gray_only_while_editing_and_never_in_exports() { assert!(changed.iter().any(|(px, _)| px[0] == 179 && px[1] == 179 && px[2] == 179)); assert!(changed.iter().all(|(px, _)| px[0] >= 179 && px[0] == px[1] && px[1] == px[2])); } + +#[test] +fn placed_text_holds_the_field_value_not_the_saved_placeholder() { + let mut p = deck_with(vec![field_shape(Some("slidenum"), "<#>")]); + let second = p.slides[0].clone(); + p.slides.push(second); + let placed = place_slide(&p, 1); + assert_eq!(placed.texts.len(), 1); + assert_eq!(placed.texts[0].body.text(), "2"); + // The layout was drawn from the same value, so the offsets agree with the body. + assert_eq!(placed.texts[0].layout.runs.iter().map(|r| r.chars.1).max(), Some(1)); +}