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.
| 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 <text> 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 |
| 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 <text> (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. |
backlog/backlog.md's Sprint 9 planning outcome note.MeasureElement's existing “design-time visual approximation” language), not a defect to fix in this sprint.logs/impediment_log.md. Neither blocks this sprint's committed item.Single committed story this sprint; no dependency ordering needed.
| Day | Date | Completed | Planned | Blocked/At risk |
|---|
Powered by TurnKey Linux.