# Sprint Backlog
**Sprint:** 9 **Dates:** 2026-10-27 - 2026-10-31
**Sprint Goal:** Give static and dynamic text elements a real, independently adjustable width and height, so that content too long to fit wraps onto additional lines and is clipped (not silently overrun) once it exceeds the box — using Debenu's native wrap/box-drawing primitives for the real PDF and GDI+'s native rectangle-wrap for a close design-time approximation on the canvas and preview panel, directly answering the human product owner's "make the boxes longer" request.
## Committed Items
| Story | Size | Status | Tasks |
|---|---|---|---|
| Add an adjustable width and height with text wrapping to static and dynamic text elements | 13 points | Done | - [x] Add `Width`/`Height` properties to `TextElementLayout` (Desktop.Core) and `TemplateElement` (CLI), both optional/nullable so a template saved before this story (no attributes) keeps today's unbounded behavior exactly
- [x] Persist the new `width`/`height` attributes on `` in `TemplateLayoutXmlSerializer` and `TemplateXmlParser`, following the same "optional, default preserves old behavior" convention as `angle`/`collapsible`; round-trip tests on both sides
- [x] Empirically confirm Debenu's `DPLDrawWrappedText`/`DPLDrawTextBox` semantics against the real vendor DLL (alignment flag meaning, whether `DrawTextBox` clips to height on its own or only wraps to width) before committing to which entry point `DebenuPdfRenderer.AddPage` calls when a draw's width/height is set
- [x] Add optional `Width`/`Height` to `TextDraw` and branch `DebenuPdfRenderer.AddPage`: no width/height set -> today's plain `DrawText` call, unchanged; width/height set -> the confirmed wrap/box entry point (`DrawTextBox`/`DrawRotatedTextBox`, whose own return value directly reports lines drawn vs. `GetWrappedTextLineCount`'s total, so clipping needs no separate detection call)
- [x] Update `RenderEngine.BuildDraws` to pass the resolved element's `Width`/`Height` through to `TextDraw`, reusing the existing shared `TextResolver` for the text itself (no new resolution path)
- [x] Change `TemplateCanvasControl.DrawElement`/`MeasureElement` and `TemplatePreviewBuilder`'s standalone-element path to use GDI+'s rectangle-bounded `Graphics.DrawString`/`MeasureString` overloads when Width/Height is set, documented explicitly as a design-time approximation (not guaranteed pixel-for-point parity with the Debenu-rendered PDF, mirroring `MeasureElement`'s existing language)
- [x] Add a draggable width/height resize handle (and/or properties-panel numeric fields, mirroring the Address Control's X/Y/Width pattern) to `CanvasElementEditor`/`TemplateCanvasControl` — implemented as the *existing* font-size resize handle becoming context-sensitive (box mode vs. font-scale mode, checked once at drag-start via `HasBox`) rather than a second, visually-competing handle, a simpler resolution to the story's own open design question than either originally-considered option
- [x] Unit tests: model/persistence round-trip (CLI and desktop), `DebenuPdfRenderer` real-DLL wrap/clip regression tests (including one licensed, pixel-verified clipping test), canvas rectangle-measurement tests
- [x] Live actual built-form smoke: a real CLI render of a template with a narrow, short static and a narrow, short dynamic element against the real sample CSV, confirming actual multi-line wrapped output and clipped overflow in the produced PDF; a reflection-driven harness against the real built `TemplateDesignerForm` (real content TextBox, real Box width/height NumericUpDown fields, real mouse-driven resize-handle drag), with a real canvas screenshot |
## Daily Scrum Log
| Day | Date | Completed | Planned | Blocked/At risk |
|---|---|---|---|---|
| 1 | 2026-10-27 | **Story complete** ("Add an adjustable width and height with text wrapping to static and dynamic text elements", 13 points), all 6 ACs met, bringing Sprint 9 to 13/13 points Done. Added nullable `Width`/`Height` to `TextElementLayout` (Desktop.Core) and `TemplateElement` (CLI), persisted as optional `width`/`height` attributes on `` (both sides independently implemented, matching this project's established convention). Real vendor-DLL investigation (a standalone probe against the actual Debenu DLL, licensed via the project's own `key.txt`) found and confirmed native `DrawWrappedText`/`DrawTextBox`/`DrawRotatedTextBox`/`GetWrappedTextHeight`/`GetWrappedTextLineCount` entry points — real screenshots proved `DrawWrappedText` wraps a long line onto 6 measured lines, a 20pt-tall `DrawTextBox` clips to only the first 2 (returning `2`, not an error), and `DrawRotatedTextBox` composes rotation and wrapping correctly in one native call (a de-risking find for the dependent "Support rotated wrapped text..." story, flagged for re-sizing at the next refinement rather than assumed away). `DebenuPdfRenderer.AddPage` now branches to these native calls when a draw carries a box, with zero changes to the pre-existing unrotated/rotated `DrawText`/`DrawRotatedText` paths. Canvas (`TemplateCanvasControl`) and preview panel (`TemplatePreviewBuilder`/`TemplatePreviewControl`) both use GDI+'s native rectangle-bounded `DrawString` for a documented design-time approximation, since the desktop process has no shared code path with the CLI's Debenu engine (confirmed by inspection). The resize-handle coexistence question was resolved more simply than either option the sizing note offered: the existing font-size handle became context-sensitive (`CanvasElementEditor.BeginResizeDrag`/`ResizeDragTo` branch on `HasBox`, captured once at drag-start), so there is only ever one handle per element, never two. Properties panel gained "Box width"/"Box height" fields (0 = no box). Tests: 23 new (7 CLI, 16 desktop, including a licensed real-DLL test proving clipping via actual rasterized pixel rows). Full suite 448/448 -> 476/476. Live verification: real CLI render (real license key, real 392-record sample CSV) of a template with a 150x80pt static box and a 180x30pt dynamic box bound to `Full Name`; page 1, rasterized via Debenu's own `RenderPageToFile` and visually inspected, shows the static text genuinely wrapped across 4 lines and "WILLIAM EDWARD ZIMMERMAN JR" genuinely clipped to "WILLIAM EDWARD" by the 30pt height — real, observed, end-to-end proof (Designer XML -> CLI -> Debenu -> PDF), not merely unit-level. Also live-verified through a reflection-driven built-form smoke against the real `TemplateDesignerForm`: real content typed and committed via the actual `_contentInput`/`CommitContentFromPanel` path, 140x90pt set through the real `_elementWidthInput`/`_elementHeightInput` panel fields, then the real resize handle dragged through the actual `OnMouseDown`/`OnMouseMove`/`OnMouseUp` pipeline — Width/Height grew to ~198x133.5pt while `FontSize` stayed exactly 12, confirming box mode (not font-scale mode) was genuinely active; a real canvas screenshot from this run shows the wrapped text and both handles correctly positioned at the resized box. Process note for the retrospective: the project's own `key.txt` (repo root, one level above `code/`) was located during this story after several earlier sprints' reviews disclosed "no license key available" — those searches only checked under `code/`, short of where the resolver's own upward walk (and the real key) both reach; several earlier evidence-depth caveats were avoidable, not genuine environment constraints. | Sprint 9 complete — move to product-owner Sprint Review. | None. |
## Notes
- **Capacity signal:** completed totals across Sprints 1-8 are **20, 19, 18, 18, 15, 18, 18, 13** — eight data points. The 18-20 range from six of those sprints is this team's proven, repeatable velocity; both 15 (Sprint 5) and 13 (Sprint 8) are documented, deliberate under-commits around one large/risky or fresh story, not capacity misses.
- **Deliberate under-commit, reasoned explicitly, not defaulted:** two larger combinations were considered and rejected at planning. Pulling this story's own dependent follow-up ("Support rotated wrapped text and a live wrap/clip indicator," 8 pts) in the same sprint would total 21 points, a new team-wide high, for a first-of-its-kind capability with two real, still-unconfirmed vendor-API unknowns — and unlike prior "pull the whole dependent pair" precedents (each ≤13-18 points), this story is independently valuable and shippable without its follow-up, so there is no "no independent value alone" pressure to pull both at once. Pulling epic 6's multi-select/align pair (10 pts) alongside this story (23 points total) was rejected for stacking two large, genuinely novel, first-of-its-kind initiatives (a new multi-item selection model, and a new element-geometry/wrap-render model) in one sprint — exactly the risk profile this team has consistently avoided, with no external pressure cited to justify reaching for an unproven new high instead. Full reasoning: `backlog/backlog.md`'s Sprint 9 planning outcome note.
- **No smaller item found to round out capacity**, the same conclusion Sprint 8 planning reached: epic 6's multi-select cannot be cleanly pulled alone without its align/distribute pair per this team's established practice for that specific pair, and "Complete the first text-only operator workflow" (epic 1, 5 pts) remains flagged as a stale label needing re-verification before ever being pulled blind.
- **Real technical risk to watch during execution, flagged at sizing (not yet resolved):** the desktop app (GDI+-based canvas/preview) and the CLI (Debenu-based render) are two separate processes with no shared wrap/line-breaking code path — canvas/preview wrap is an accepted design-time approximation, not guaranteed to match the final PDF's exact wrap points. This is a documented, accepted scope boundary (mirroring `MeasureElement`'s existing "design-time visual approximation" language), not a defect to fix in this sprint.
- **Sequencing recommendation beyond this sprint:** "Support rotated wrapped text and a live wrap/clip indicator for text elements" (8 pts, epic 4) is the natural next pull once this story lands, then "Add an adjustable width and height with text wrapping to Address Control lines" (8 pts, epic 4). Epic 6's multi-select/align pair (10 pts) and undo/redo (13 pts) remain queued and unchanged, available whenever a future planning session chooses them over continuing the wrap/clip chain.
- Impediments: template asset path strategy (absolute vs. relative) and UNC timeout/retry behavior remain open in `logs/impediment_log.md`. Neither blocks this sprint's committed item.
- Carried over from previous sprint: none (Sprint 8 completed both committed stories, 13/13 points).
## Execution Order
Single committed story this sprint; no dependency ordering needed.
## Daily Scrum Log
| Day | Date | Completed | Planned | Blocked/At risk |
|---|---|---|---|---|