Skip to content

render: text in scaled groups is placed where it is drawn - #1938

Open
scks1 wants to merge 1 commit into
storytold:mainfrom
scks1:fix/placed-group-text
Open

scks1 wants to merge 1 commit into
storytold:mainfrom
scks1:fix/placed-group-text

Conversation

@scks1

@scks1 scks1 commented Nov 7, 2026 •

Copy link
Copy Markdown
Contributor

Follows #340; split out of #1853, where a review asked for this as its own change.

#340 made the renderer set the text of a shape inside a group whose child coordinates scale it at the shape's size on the slide, so font sizes and insets stay in points (Renderer::text_space). place() in crates/render/src/placed.rs still set that text in the group's child units and then scaled it with the group, so placed text broke its lines differently from the drawn text and lay elsewhere. In the test below, a 24 pt line in a group whose child space is 1/1600 of its size was placed as 20 glyphs where the same line on its own is placed as 24. Placed text is what the PDF export lays over each slide as its text layer, and the boxes of text hyperlinks come from it (the PDF's link areas and the clickable areas of linked text in the slide show), so for text in such groups they missed the drawn words.

This PR moves text_space's rule into placed.rs as one function that Renderer::text_space and place() both call. Renderer::text_space keeps its signature. SmartArt groups, whose child space equals their frame, are unaffected, and so is table cell text: tables draw their text under the group's scale, and place() already placed it that way.

Test (crates/render/src/tests_placed_group.rs): text_in_a_scaled_group_is_placed_as_drawn sets one 24 pt line in groups whose child space is 1, 1/1600, 1/4 and 4 times the group's size and checks each glyph's id, position on the slide and drawn size against the same line on its own. ROADMAP.md has a status line, a progress-log row and a revision-history row.

Validation and scope

At ba63277 (this branch on daeedd5), cargo xtask ci passed all 8 steps (fmt, pdf-deps, clippy, test, doctest, assets, layers, wasm), with 2,433 tests passed and 6 existing skipped. On daeedd5 with only the new test added, the test failed at the 1/1600 group, which placed 20 glyphs where the line on its own places 24.

#1853 merges this branch (its commit a56ac87), so it carries this change until this PR merges and then drops it from its diff. Checked on 2026-11-07 against main 0627fb2 and the 400 open PRs: this branch merges into main cleanly. Merged, it adds textual conflicts with #1029, #1891, #1905 and #1906, all in ROADMAP.md. Thirty-four older PRs already conflict with main in files this branch changes (in crates/render/src/placed.rs: #304, #855, #895, #1138, #1197 and #1470), and merging it adds no conflicted file to any of them. #259 and #819 share no history with main and were not checked. #1629 adds a call to Renderer::text_space, which keeps its signature; #1095 changes text editing coordinates inside transformed groups in the engine and touches none of these files. Whichever lands second rebases.

Summary by CodeRabbit

  • Bug Fixes
    • Text and hyperlink boxes now align more closely with their drawn text when exported from groups that scale their contents. Groups whose child coordinate space matches their frame retain their existing behavior.
    • Text placement remains consistent across supported group scaling levels, including very large and very small scales.

The renderer sets the text of a shape inside a group whose child
coordinates scale it at the shape's size on the slide, so font sizes and
insets stay in points (Renderer::text_space). place() set the same text in
the group's child units and then scaled it with the group, so placed text
broke its lines differently and lay elsewhere than the drawn text. The PDF
text layer and the text hyperlink areas read placed text.

text_space's rule moves into placed.rs as one function that the renderer
and place() both call. Table cell text is unchanged: tables draw their
text under the group's scale, and place() already did the same.

Test: text_in_a_scaled_group_is_placed_as_drawn puts one line in groups
whose child space is 1, 1600, 4 and 1/4 times smaller than the group and
checks each glyph's id, position on the slide and drawn size against the
same line on its own.

ROADMAP.md gets a status line, a progress-log row and a revision-history
row.
@coderabbitai

coderabbitai Bot commented Nov 7, 2026 •

Copy link
Copy Markdown
Contributor

Explain Change →

No actionable comments were generated in the recent review.

Walkthrough

Text placement now adjusts layout dimensions and transforms for scaled groups. Regression tests compare grouped and ungrouped glyph output at several scales. The roadmap records the change and notes the stated unchanged SmartArt case and parity scores.

Changes: Scaled-group text export

Layer / File(s) Summary
Text-space calculation and placement
crates/render/src/placed.rs, crates/render/src/renderer.rs, crates/render/src/lib.rs, crates/render/src/tests_placed_group.rs, ROADMAP.md
place uses text_space to calculate text dimensions and transforms. The renderer delegates to the helper. Tests compare grouped and ungrouped glyph output at scales 1, 1600, 4, and 0.25. The roadmap records the change and states that text and hyperlink boxes align with drawn text in scaled groups.

Estimated code review effort: 2 (Simple) | ~10 minutes

Change: Bug fix · Severity of issue fixed: Low

Suggested reviewers: dylanmtaylor

Merge Risk: Minimal · up to ba632

No concrete merge-blocking issue is identified; the change is ready for normal checks.

Pre-merge checks: Passed 5

Check name Status Explanation
Description Check Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check Passed The title clearly describes the fix for text placement in scaled groups.
Docstring Coverage Passed Docstring coverage is 83.33% which is sufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff with cyclomatic complexity of at least 2. Documentati…
Linked Issues check Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check Passed Check skipped because no linked issues were found for this pull request.
Full details: Docstring Coverage

Explanation

Docstring coverage is 83.33% which is sufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff with cyclomatic complexity of at least 2. Documentation-only edits and deletions within surviving functions are included; unchanged diff context and fully deleted functions are excluded. Analyzed 6 functions across 4 files. (1 skipped: 1 unsupported.)

Finishing Touches

Generate unit tests (beta)

  • Create a new PR
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

Share

Recent review info

Run configuration

Commits

Reviewing files that changed from the base of the PR and between 0627fb2 and ba63277.

Files selected for processing: 5

  • ROADMAP.md
  • crates/render/src/lib.rs
  • crates/render/src/placed.rs
  • crates/render/src/renderer.rs
  • crates/render/src/tests_placed_group.rs

Included review availability: This review used your included allowance. Your plan provides up to 8 included reviews per hour; 6 remain after this review.

@aiongg aiongg added the bugfix Fixes something that is broken: wrong result, crash, data loss label Nov 7, 2026

@aiongg aiongg left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

AI-generated review (Opus). Not a maintainer decision.

No blocking problems found.

The PR shares the group text-space rule between drawing and placement, so placed text (PDF text layer, text link areas) in a group whose child coordinates scale it is laid out at the shape's size in points, as drawn.

  • crates/render/src/placed.rs:156 passes m as both the relative and the drawing transform. That is right because placement already works in slide points, which matches Renderer::text_space (crates/render/src/renderer.rs:369-371) with the slide view taken out.
  • Table cell text (crates/render/src/placed.rs:149) stays under the group's scale, as the table drawing code does (crates/render/src/renderer.rs:497).
  • Link boxes (crates/render/src/placed.rs:191) and the PDF text layer (crates/pdf/src/lib.rs:608) both go through the returned transform, so both now lie on the drawn words.
  • text_outline (crates/render/src/placed.rs:236) still lays out text at the shape's own size without this rule; it could use text_space in a follow-up if Merge Shapes on scaled group children matters.

Claims checked:

  • verified: place() now sets text in scaled groups at the shape's size on the slide, as the renderer draws it, by calling one shared text_space function (crates/render/src/placed.rs:156-158 calls text_space(m, m, depth, x.w, x.h) and builds the text rect from the returned size; crates/render/src/renderer.rs:36…
  • verified: Renderer::text_space keeps its signature and behaviour (crates/render/src/renderer.rs:369 keeps fn text_space(&self, m, depth, w, h); the body moved unchanged to crates/render/src/placed.rs:201-213)
  • verified: PDF text layer and text hyperlink boxes (PDF links, slide show link areas) now lie on the drawn text (crates/render/src/placed.rs:167 and :191 build link rects from the text-space transform; crates/pdf/src/lib.rs:608 places glyphs with view * pt.transform; crates/ui-egui/src/show/media.rs:190 uses…
  • verified: Table cell text and SmartArt groups whose child space equals their frame are unaffected (crates/render/src/placed.rs:149 still passes m for table cells, matching crates/render/src/renderer.rs:497 (table::draw under m); text_space returns (w, h, m) when both scales are 1 (crates/render/src/placed.rs…

Reviewed at ba63277.

Recommendation: merge, after a maintainer's decision.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bugfix Fixes something that is broken: wrong result, crash, data loss

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants