diff --git a/CHANGELOG.md b/CHANGELOG.md index 243a4918b..3c024c985 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,13 @@ follow semantic versioning; release dates are ISO 8601. ### Public API +- **A drawing over text in a row nested through a one-layer stack takes its room in DOCX.** + A row cannot sit in a row cell, so templates wrap one in a layer stack of one layer; the + row is written in the flow, and a drawing in it is drawn where the page puts it. Its room + was kept only outside every overlay, so `IndigoProposal`'s meta and feature tiles, a disc + or a rounded tile over each label, set every label 24pt to 28pt higher than the page. A + drawing with a block after it in its section now takes its room there; one alone in its + section is left to its row. `IndigoProposal`'s lines more than 2pt off fall from 16 to none in both editors. - **A table or row the layout moves to a new page keeps its top edge there in DOCX.** The page starts a block a page break moves down at its own top edge, and leaves the gap before it at the foot of the page above where it fits there. Word has no space above a table, so the edge was diff --git a/docs/recipes/docx-export.md b/docs/recipes/docx-export.md index ce81ad3e3..79fefcc3b 100644 --- a/docs/recipes/docx-export.md +++ b/docs/recipes/docx-export.md @@ -765,7 +765,10 @@ shape still takes its room: its placed height and margins are owed as space above what follows, and so are those of a layer stack or shape container holding only drawing, counted once however deeply it is nested. A cell holding nothing else keeps that space on a -paragraph a tenth of a point tall. +paragraph a tenth of a point tall. In a row nested through a layer stack of one +layer — the way a template puts a row in a row cell — a drawing with a block after +it in its section takes its room too; one alone in its section, an icon beside a +heading, is left to the row it sits in, which is held as tall as the page makes it. A barcode in the body is not skipped: it exports as a picture of the symbol at its size, the same matrix the PDF draws, so it scans, with its data as the diff --git a/qa/src/test/resources/docx-fidelity/libreoffice-linux-lines.tsv b/qa/src/test/resources/docx-fidelity/libreoffice-linux-lines.tsv index ecd00458d..df2e7a762 100644 --- a/qa/src/test/resources/docx-fidelity/libreoffice-linux-lines.tsv +++ b/qa/src/test/resources/docx-fidelity/libreoffice-linux-lines.tsv @@ -3828,57 +3828,57 @@ proposal-indigo 0:76f9df77e8af 0.18 partnership proposal-indigo 0:f33b9e206de3 0.81 empoweringbrightfutureltd.withmo proposal-indigo 0:16a14b235896 0.81 infrastructure,globalcapabilitie proposal-indigo 0:2fdd6f092df5 0.80 experienceforyourcustomers. -proposal-indigo 0:0e87632cd46b -24.60 date -proposal-indigo 0:dc9696f61d36 -24.61 27may2026 -proposal-indigo 0:621f581e97b4 -24.60 proposalid -proposal-indigo 0:2b2adf7998f8 -24.61 kes-2026-0527-01 -proposal-indigo 0:4bf91849cfb4 -24.60 validuntil -proposal-indigo 0:93059e9a1c07 -24.61 26june2026 -proposal-indigo 0:8d6867ab3a4c -24.60 preparedby -proposal-indigo 0:50910841c2da -24.61 kestrelbusinessteam -proposal-indigo 0:69cca54632df -0.77 aboutkestrelbusiness -proposal-indigo 0:81a3de6caa05 -0.89 kestrelbusinessprovidesmodernfin -proposal-indigo 0:a999713d90ce -0.89 forward-thinkingcompanies.frommu -proposal-indigo 0:8001c2743965 -27.90 global -proposal-indigo 0:f469802a3144 -27.88 scale -proposal-indigo 0:774cdf08f6a8 -27.90 modern -proposal-indigo 0:9fec08e4574b -27.88 infrastructure -proposal-indigo 0:5924acfba1a5 -27.90 secure& -proposal-indigo 0:b67c263c0379 -27.88 compliant -proposal-indigo 0:88fa0d759f84 -27.90 developer -proposal-indigo 0:962cba9fd476 -27.88 firstapis -proposal-indigo 0:4f30d3328d6f -0.69 projectoverview -proposal-indigo 0:a73369734180 -0.79 thisproposaloutlinesatailoredfin -proposal-indigo 0:19a7c085cb24 -0.78 supportbrightfutureltd.inenablin -proposal-indigo 0:ff5888ea18e0 -0.78 financialservicesforyourbusiness -proposal-indigo 0:2855076f724f -0.67 multi-currencybusinessaccounts -proposal-indigo 0:4e36c64ccf3a -0.66 hold,send,receivein30+currencies -proposal-indigo 0:17f85ac3692b -0.58 corporatecards&expenses -proposal-indigo 0:82c5a2c4443e -0.57 issuephysical&virtualcardswithsm -proposal-indigo 0:e05293fab5bc -0.48 payments&collections -proposal-indigo 0:1443f4651241 -0.47 local&globalpayments,directdebit -proposal-indigo 0:32df55902e8b -0.39 embeddedfinanceapis -proposal-indigo 0:8ac47b282ff6 -0.38 buildfinancialproductsdirectlyin -proposal-indigo 0:58520edff52a -0.30 compliance&risk -proposal-indigo 0:05aecb87ba6a -0.28 kyc,aml,fraudmonitoringandregula -proposal-indigo 0:ec5294e7a376 -0.69 investmentsummary -proposal-indigo 0:af60f4182ca5 -0.69 implementation&onboarding -proposal-indigo 0:572a8dc5a455 -0.69 £2,500 -proposal-indigo 0:760775d91a04 -0.58 platformaccess&apis -proposal-indigo 0:6ca9376c51ca -0.58 £3,000 -proposal-indigo 0:9a38b75a07fb -0.47 transactionfees(est.) -proposal-indigo 0:3b325109deb0 -0.47 variable -proposal-indigo 0:bfece4405b19 -0.36 support&accountmanagement -proposal-indigo 0:7f0f70e643bc -0.36 included -proposal-indigo 0:e0bd5388bdc9 -0.24 estimatedfirstyearinvestment -proposal-indigo 0:51db6c622be8 -0.21 £5,500 -proposal-indigo 0:ab5aa97074c4 -0.26 notes -proposal-indigo 0:b9e9c18c4d13 -0.27 •pricingexcludesapplicabletaxes. -proposal-indigo 0:e4239ffa2bb6 -0.26 •transactionfeesarebasedonusagea -proposal-indigo 0:82eb848e77b3 -0.26 •thisproposalisvalidfor30daysfro -proposal-indigo 0:732bcbb66cec -0.27 kestrelpaymentsltd -proposal-indigo 0:3898e7b5f79b -0.24 7harbourcircus,canarywharf,londo -proposal-indigo 0:29208032c874 -0.23 kestrel.example/business|busines +proposal-indigo 0:0e87632cd46b 0.25 date +proposal-indigo 0:dc9696f61d36 0.24 27may2026 +proposal-indigo 0:621f581e97b4 0.25 proposalid +proposal-indigo 0:2b2adf7998f8 0.24 kes-2026-0527-01 +proposal-indigo 0:4bf91849cfb4 0.25 validuntil +proposal-indigo 0:93059e9a1c07 0.24 26june2026 +proposal-indigo 0:8d6867ab3a4c 0.25 preparedby +proposal-indigo 0:50910841c2da 0.24 kestrelbusinessteam +proposal-indigo 0:69cca54632df 0.28 aboutkestrelbusiness +proposal-indigo 0:81a3de6caa05 0.16 kestrelbusinessprovidesmodernfin +proposal-indigo 0:a999713d90ce 0.16 forward-thinkingcompanies.frommu +proposal-indigo 0:8001c2743965 0.25 global +proposal-indigo 0:f469802a3144 0.27 scale +proposal-indigo 0:774cdf08f6a8 0.25 modern +proposal-indigo 0:9fec08e4574b 0.27 infrastructure +proposal-indigo 0:5924acfba1a5 0.25 secure& +proposal-indigo 0:b67c263c0379 0.27 compliant +proposal-indigo 0:88fa0d759f84 0.25 developer +proposal-indigo 0:962cba9fd476 0.27 firstapis +proposal-indigo 0:4f30d3328d6f 0.36 projectoverview +proposal-indigo 0:a73369734180 0.26 thisproposaloutlinesatailoredfin +proposal-indigo 0:19a7c085cb24 0.27 supportbrightfutureltd.inenablin +proposal-indigo 0:ff5888ea18e0 0.27 financialservicesforyourbusiness +proposal-indigo 0:2855076f724f 0.38 multi-currencybusinessaccounts +proposal-indigo 0:4e36c64ccf3a 0.39 hold,send,receivein30+currencies +proposal-indigo 0:17f85ac3692b 0.47 corporatecards&expenses +proposal-indigo 0:82c5a2c4443e 0.48 issuephysical&virtualcardswithsm +proposal-indigo 0:e05293fab5bc 0.57 payments&collections +proposal-indigo 0:1443f4651241 0.58 local&globalpayments,directdebit +proposal-indigo 0:32df55902e8b 0.66 embeddedfinanceapis +proposal-indigo 0:8ac47b282ff6 0.67 buildfinancialproductsdirectlyin +proposal-indigo 0:58520edff52a 0.75 compliance&risk +proposal-indigo 0:05aecb87ba6a 0.77 kyc,aml,fraudmonitoringandregula +proposal-indigo 0:ec5294e7a376 0.36 investmentsummary +proposal-indigo 0:af60f4182ca5 0.36 implementation&onboarding +proposal-indigo 0:572a8dc5a455 0.36 £2,500 +proposal-indigo 0:760775d91a04 0.47 platformaccess&apis +proposal-indigo 0:6ca9376c51ca 0.47 £3,000 +proposal-indigo 0:9a38b75a07fb 0.58 transactionfees(est.) +proposal-indigo 0:3b325109deb0 0.58 variable +proposal-indigo 0:bfece4405b19 0.69 support&accountmanagement +proposal-indigo 0:7f0f70e643bc 0.69 included +proposal-indigo 0:e0bd5388bdc9 0.81 estimatedfirstyearinvestment +proposal-indigo 0:51db6c622be8 0.84 £5,500 +proposal-indigo 0:ab5aa97074c4 0.79 notes +proposal-indigo 0:b9e9c18c4d13 0.78 •pricingexcludesapplicabletaxes. +proposal-indigo 0:e4239ffa2bb6 0.79 •transactionfeesarebasedonusagea +proposal-indigo 0:82eb848e77b3 0.79 •thisproposalisvalidfor30daysfro +proposal-indigo 0:732bcbb66cec 0.78 kestrelpaymentsltd +proposal-indigo 0:3898e7b5f79b 0.81 7harbourcircus,canarywharf,londo +proposal-indigo 0:29208032c874 0.82 kestrel.example/business|busines proposal-modern 0:ecd1378bc9dc 0.00 proposal proposal-modern 0:f592a683c61d 0.27 documentplatformconsolidation proposal-modern 0:bbe07492ee1f 0.32 proposalgc-p-2026-014prepared02a diff --git a/qa/src/test/resources/docx-fidelity/libreoffice-linux.tsv b/qa/src/test/resources/docx-fidelity/libreoffice-linux.tsv index 0ff38aac0..8f6760d65 100644 --- a/qa/src/test/resources/docx-fidelity/libreoffice-linux.tsv +++ b/qa/src/test/resources/docx-fidelity/libreoffice-linux.tsv @@ -57,7 +57,7 @@ letter-panel 1 1 13 13 0.15 0.21 0 letter-sidebar_portrait 1 1 12 12 0.43 0.51 0 letter-timeline_minimal 1 1 15 15 1.32 1.52 0 proposal-editorial 2 2 132 129 44.55 45.38 72 -proposal-indigo 1 1 72 70 0.57 27.88 16 +proposal-indigo 1 1 72 70 0.27 0.79 0 proposal-modern 1 1 44 44 0.50 0.75 0 proposal-northline 2 2 132 132 0.27 0.57 0 receipt-modern 1 1 57 57 0.26 0.29 0 diff --git a/qa/src/test/resources/docx-fidelity/libreoffice-windows-lines.tsv b/qa/src/test/resources/docx-fidelity/libreoffice-windows-lines.tsv index b92a1a2f0..9f3484505 100644 --- a/qa/src/test/resources/docx-fidelity/libreoffice-windows-lines.tsv +++ b/qa/src/test/resources/docx-fidelity/libreoffice-windows-lines.tsv @@ -3729,57 +3729,57 @@ proposal-indigo 0:76f9df77e8af 0.18 partnership proposal-indigo 0:f33b9e206de3 1.06 empoweringbrightfutureltd.withmo proposal-indigo 0:16a14b235896 1.06 infrastructure,globalcapabilitie proposal-indigo 0:2fdd6f092df5 1.05 experienceforyourcustomers. -proposal-indigo 0:0e87632cd46b -24.60 date -proposal-indigo 0:dc9696f61d36 -24.61 27may2026 -proposal-indigo 0:621f581e97b4 -24.60 proposalid -proposal-indigo 0:2b2adf7998f8 -24.61 kes-2026-0527-01 -proposal-indigo 0:4bf91849cfb4 -24.60 validuntil -proposal-indigo 0:93059e9a1c07 -24.61 26june2026 -proposal-indigo 0:8d6867ab3a4c -24.60 preparedby -proposal-indigo 0:50910841c2da -24.61 kestrelbusinessteam -proposal-indigo 0:69cca54632df -0.77 aboutkestrelbusiness -proposal-indigo 0:81a3de6caa05 -0.79 kestrelbusinessprovidesmodernfin -proposal-indigo 0:a999713d90ce -0.79 forward-thinkingcompanies.frommu -proposal-indigo 0:8001c2743965 -27.90 global -proposal-indigo 0:f469802a3144 -27.88 scale -proposal-indigo 0:774cdf08f6a8 -27.90 modern -proposal-indigo 0:9fec08e4574b -27.88 infrastructure -proposal-indigo 0:5924acfba1a5 -27.90 secure& -proposal-indigo 0:b67c263c0379 -27.88 compliant -proposal-indigo 0:88fa0d759f84 -27.90 developer -proposal-indigo 0:962cba9fd476 -27.88 firstapis -proposal-indigo 0:4f30d3328d6f -0.69 projectoverview -proposal-indigo 0:a73369734180 -0.69 thisproposaloutlinesatailoredfin -proposal-indigo 0:19a7c085cb24 -0.68 supportbrightfutureltd.inenablin -proposal-indigo 0:ff5888ea18e0 -0.68 financialservicesforyourbusiness -proposal-indigo 0:2855076f724f -0.67 multi-currencybusinessaccounts -proposal-indigo 0:4e36c64ccf3a -0.66 hold,send,receivein30+currencies -proposal-indigo 0:17f85ac3692b -0.58 corporatecards&expenses -proposal-indigo 0:82c5a2c4443e -0.57 issuephysical&virtualcardswithsm -proposal-indigo 0:e05293fab5bc -0.48 payments&collections -proposal-indigo 0:1443f4651241 -0.47 local&globalpayments,directdebit -proposal-indigo 0:32df55902e8b -0.39 embeddedfinanceapis -proposal-indigo 0:8ac47b282ff6 -0.38 buildfinancialproductsdirectlyin -proposal-indigo 0:58520edff52a -0.30 compliance&risk -proposal-indigo 0:05aecb87ba6a -0.28 kyc,aml,fraudmonitoringandregula -proposal-indigo 0:ec5294e7a376 -0.69 investmentsummary -proposal-indigo 0:af60f4182ca5 -0.69 implementation&onboarding -proposal-indigo 0:572a8dc5a455 -0.69 £2,500 -proposal-indigo 0:760775d91a04 -0.58 platformaccess&apis -proposal-indigo 0:6ca9376c51ca -0.58 £3,000 -proposal-indigo 0:9a38b75a07fb -0.47 transactionfees(est.) -proposal-indigo 0:3b325109deb0 -0.47 variable -proposal-indigo 0:bfece4405b19 -0.36 support&accountmanagement -proposal-indigo 0:7f0f70e643bc -0.36 included -proposal-indigo 0:e0bd5388bdc9 -0.24 estimatedfirstyearinvestment -proposal-indigo 0:51db6c622be8 -0.21 £5,500 -proposal-indigo 0:ab5aa97074c4 -0.26 notes -proposal-indigo 0:b9e9c18c4d13 -0.27 •pricingexcludesapplicabletaxes. -proposal-indigo 0:e4239ffa2bb6 -0.26 •transactionfeesarebasedonusagea -proposal-indigo 0:82eb848e77b3 -0.26 •thisproposalisvalidfor30daysfro -proposal-indigo 0:732bcbb66cec -0.27 kestrelpaymentsltd -proposal-indigo 0:3898e7b5f79b -0.24 7harbourcircus,canarywharf,londo -proposal-indigo 0:29208032c874 -0.23 kestrel.example/business|busines +proposal-indigo 0:0e87632cd46b 0.25 date +proposal-indigo 0:dc9696f61d36 0.24 27may2026 +proposal-indigo 0:621f581e97b4 0.25 proposalid +proposal-indigo 0:2b2adf7998f8 0.24 kes-2026-0527-01 +proposal-indigo 0:4bf91849cfb4 0.25 validuntil +proposal-indigo 0:93059e9a1c07 0.24 26june2026 +proposal-indigo 0:8d6867ab3a4c 0.25 preparedby +proposal-indigo 0:50910841c2da 0.24 kestrelbusinessteam +proposal-indigo 0:69cca54632df 0.28 aboutkestrelbusiness +proposal-indigo 0:81a3de6caa05 0.26 kestrelbusinessprovidesmodernfin +proposal-indigo 0:a999713d90ce 0.26 forward-thinkingcompanies.frommu +proposal-indigo 0:8001c2743965 0.25 global +proposal-indigo 0:f469802a3144 0.27 scale +proposal-indigo 0:774cdf08f6a8 0.25 modern +proposal-indigo 0:9fec08e4574b 0.27 infrastructure +proposal-indigo 0:5924acfba1a5 0.25 secure& +proposal-indigo 0:b67c263c0379 0.27 compliant +proposal-indigo 0:88fa0d759f84 0.25 developer +proposal-indigo 0:962cba9fd476 0.27 firstapis +proposal-indigo 0:4f30d3328d6f 0.36 projectoverview +proposal-indigo 0:a73369734180 0.36 thisproposaloutlinesatailoredfin +proposal-indigo 0:19a7c085cb24 0.37 supportbrightfutureltd.inenablin +proposal-indigo 0:ff5888ea18e0 0.37 financialservicesforyourbusiness +proposal-indigo 0:2855076f724f 0.38 multi-currencybusinessaccounts +proposal-indigo 0:4e36c64ccf3a 0.39 hold,send,receivein30+currencies +proposal-indigo 0:17f85ac3692b 0.47 corporatecards&expenses +proposal-indigo 0:82c5a2c4443e 0.48 issuephysical&virtualcardswithsm +proposal-indigo 0:e05293fab5bc 0.57 payments&collections +proposal-indigo 0:1443f4651241 0.58 local&globalpayments,directdebit +proposal-indigo 0:32df55902e8b 0.66 embeddedfinanceapis +proposal-indigo 0:8ac47b282ff6 0.67 buildfinancialproductsdirectlyin +proposal-indigo 0:58520edff52a 0.75 compliance&risk +proposal-indigo 0:05aecb87ba6a 0.77 kyc,aml,fraudmonitoringandregula +proposal-indigo 0:ec5294e7a376 0.36 investmentsummary +proposal-indigo 0:af60f4182ca5 0.36 implementation&onboarding +proposal-indigo 0:572a8dc5a455 0.36 £2,500 +proposal-indigo 0:760775d91a04 0.47 platformaccess&apis +proposal-indigo 0:6ca9376c51ca 0.47 £3,000 +proposal-indigo 0:9a38b75a07fb 0.58 transactionfees(est.) +proposal-indigo 0:3b325109deb0 0.58 variable +proposal-indigo 0:bfece4405b19 0.69 support&accountmanagement +proposal-indigo 0:7f0f70e643bc 0.69 included +proposal-indigo 0:e0bd5388bdc9 0.81 estimatedfirstyearinvestment +proposal-indigo 0:51db6c622be8 0.84 £5,500 +proposal-indigo 0:ab5aa97074c4 0.79 notes +proposal-indigo 0:b9e9c18c4d13 0.78 •pricingexcludesapplicabletaxes. +proposal-indigo 0:e4239ffa2bb6 0.79 •transactionfeesarebasedonusagea +proposal-indigo 0:82eb848e77b3 0.79 •thisproposalisvalidfor30daysfro +proposal-indigo 0:732bcbb66cec 0.78 kestrelpaymentsltd +proposal-indigo 0:3898e7b5f79b 0.81 7harbourcircus,canarywharf,londo +proposal-indigo 0:29208032c874 0.82 kestrel.example/business|busines proposal-modern 0:ecd1378bc9dc 0.00 proposal proposal-modern 0:f592a683c61d 0.27 documentplatformconsolidation proposal-modern 0:bbe07492ee1f 0.32 proposalgc-p-2026-014prepared02a diff --git a/qa/src/test/resources/docx-fidelity/libreoffice-windows.tsv b/qa/src/test/resources/docx-fidelity/libreoffice-windows.tsv index c88046b0e..b1cdf790e 100644 --- a/qa/src/test/resources/docx-fidelity/libreoffice-windows.tsv +++ b/qa/src/test/resources/docx-fidelity/libreoffice-windows.tsv @@ -57,7 +57,7 @@ letter-panel 1 1 13 13 0.15 0.21 0 letter-sidebar_portrait 1 1 12 12 0.43 0.51 0 letter-timeline_minimal 1 1 15 15 1.32 1.52 0 proposal-editorial 2 2 132 129 43.44 45.33 72 -proposal-indigo 1 1 72 70 0.57 27.88 16 +proposal-indigo 1 1 72 70 0.36 0.79 0 proposal-modern 1 1 44 44 0.41 0.75 0 proposal-northline 2 2 132 132 0.42 1.32 0 receipt-modern 1 1 57 57 0.26 0.29 0 diff --git a/qa/src/test/resources/docx-fidelity/word-windows-lines.tsv b/qa/src/test/resources/docx-fidelity/word-windows-lines.tsv index ca1e36a8a..29fa5f5b0 100644 --- a/qa/src/test/resources/docx-fidelity/word-windows-lines.tsv +++ b/qa/src/test/resources/docx-fidelity/word-windows-lines.tsv @@ -3828,57 +3828,57 @@ proposal-indigo 0:76f9df77e8af 0.19 partnership proposal-indigo 0:f33b9e206de3 0.45 empoweringbrightfutureltd.withmo proposal-indigo 0:16a14b235896 0.49 infrastructure,globalcapabilitie proposal-indigo 0:2fdd6f092df5 0.49 experienceforyourcustomers. -proposal-indigo 0:0e87632cd46b -24.44 date -proposal-indigo 0:dc9696f61d36 -24.52 27may2026 -proposal-indigo 0:621f581e97b4 -24.44 proposalid -proposal-indigo 0:2b2adf7998f8 -24.52 kes-2026-0527-01 -proposal-indigo 0:4bf91849cfb4 -24.44 validuntil -proposal-indigo 0:93059e9a1c07 -24.52 26june2026 -proposal-indigo 0:8d6867ab3a4c -24.44 preparedby -proposal-indigo 0:50910841c2da -24.52 kestrelbusinessteam -proposal-indigo 0:69cca54632df -0.71 aboutkestrelbusiness -proposal-indigo 0:81a3de6caa05 -0.95 kestrelbusinessprovidesmodernfin -proposal-indigo 0:a999713d90ce -0.97 forward-thinkingcompanies.frommu -proposal-indigo 0:8001c2743965 -27.75 global -proposal-indigo 0:f469802a3144 -27.80 scale -proposal-indigo 0:774cdf08f6a8 -27.75 modern -proposal-indigo 0:9fec08e4574b -27.80 infrastructure -proposal-indigo 0:5924acfba1a5 -27.75 secure& -proposal-indigo 0:b67c263c0379 -27.80 compliant -proposal-indigo 0:88fa0d759f84 -27.75 developer -proposal-indigo 0:962cba9fd476 -27.80 firstapis -proposal-indigo 0:4f30d3328d6f -0.49 projectoverview -proposal-indigo 0:a73369734180 -0.87 thisproposaloutlinesatailoredfin -proposal-indigo 0:19a7c085cb24 -0.88 supportbrightfutureltd.inenablin -proposal-indigo 0:ff5888ea18e0 -0.87 financialservicesforyourbusiness -proposal-indigo 0:2855076f724f -0.63 multi-currencybusinessaccounts -proposal-indigo 0:4e36c64ccf3a -0.55 hold,send,receivein30+currencies -proposal-indigo 0:17f85ac3692b -0.51 corporatecards&expenses -proposal-indigo 0:82c5a2c4443e -0.43 issuephysical&virtualcardswithsm -proposal-indigo 0:e05293fab5bc -0.39 payments&collections -proposal-indigo 0:1443f4651241 -0.30 local&globalpayments,directdebit -proposal-indigo 0:32df55902e8b -0.26 embeddedfinanceapis -proposal-indigo 0:8ac47b282ff6 -0.18 buildfinancialproductsdirectlyin -proposal-indigo 0:58520edff52a -0.26 compliance&risk -proposal-indigo 0:05aecb87ba6a -0.18 kyc,aml,fraudmonitoringandregula -proposal-indigo 0:ec5294e7a376 -0.61 investmentsummary -proposal-indigo 0:af60f4182ca5 -0.51 implementation&onboarding -proposal-indigo 0:572a8dc5a455 -0.51 £2,500 -proposal-indigo 0:760775d91a04 -0.43 platformaccess&apis -proposal-indigo 0:6ca9376c51ca -0.43 £3,000 -proposal-indigo 0:9a38b75a07fb -0.27 transactionfees(est.) -proposal-indigo 0:3b325109deb0 -0.27 variable -proposal-indigo 0:bfece4405b19 -0.23 support&accountmanagement -proposal-indigo 0:7f0f70e643bc -0.23 included -proposal-indigo 0:e0bd5388bdc9 -0.14 estimatedfirstyearinvestment -proposal-indigo 0:51db6c622be8 -0.04 £5,500 -proposal-indigo 0:ab5aa97074c4 -0.10 notes -proposal-indigo 0:b9e9c18c4d13 -0.11 •pricingexcludesapplicabletaxes. -proposal-indigo 0:e4239ffa2bb6 -0.12 •transactionfeesarebasedonusagea -proposal-indigo 0:82eb848e77b3 -0.13 •thisproposalisvalidfor30daysfro -proposal-indigo 0:732bcbb66cec -0.13 kestrelpaymentsltd -proposal-indigo 0:3898e7b5f79b -0.18 7harbourcircus,canarywharf,londo -proposal-indigo 0:29208032c874 -0.19 kestrel.example/business|busines +proposal-indigo 0:0e87632cd46b 0.40 date +proposal-indigo 0:dc9696f61d36 0.32 27may2026 +proposal-indigo 0:621f581e97b4 0.40 proposalid +proposal-indigo 0:2b2adf7998f8 0.32 kes-2026-0527-01 +proposal-indigo 0:4bf91849cfb4 0.40 validuntil +proposal-indigo 0:93059e9a1c07 0.32 26june2026 +proposal-indigo 0:8d6867ab3a4c 0.40 preparedby +proposal-indigo 0:50910841c2da 0.32 kestrelbusinessteam +proposal-indigo 0:69cca54632df 0.37 aboutkestrelbusiness +proposal-indigo 0:81a3de6caa05 0.01 kestrelbusinessprovidesmodernfin +proposal-indigo 0:a999713d90ce 0.11 forward-thinkingcompanies.frommu +proposal-indigo 0:8001c2743965 0.33 global +proposal-indigo 0:f469802a3144 0.40 scale +proposal-indigo 0:774cdf08f6a8 0.33 modern +proposal-indigo 0:9fec08e4574b 0.40 infrastructure +proposal-indigo 0:5924acfba1a5 0.33 secure& +proposal-indigo 0:b67c263c0379 0.40 compliant +proposal-indigo 0:88fa0d759f84 0.33 developer +proposal-indigo 0:962cba9fd476 0.40 firstapis +proposal-indigo 0:4f30d3328d6f 0.47 projectoverview +proposal-indigo 0:a73369734180 0.09 thisproposaloutlinesatailoredfin +proposal-indigo 0:19a7c085cb24 0.20 supportbrightfutureltd.inenablin +proposal-indigo 0:ff5888ea18e0 0.21 financialservicesforyourbusiness +proposal-indigo 0:2855076f724f 0.45 multi-currencybusinessaccounts +proposal-indigo 0:4e36c64ccf3a 0.53 hold,send,receivein30+currencies +proposal-indigo 0:17f85ac3692b 0.57 corporatecards&expenses +proposal-indigo 0:82c5a2c4443e 0.65 issuephysical&virtualcardswithsm +proposal-indigo 0:e05293fab5bc 0.69 payments&collections +proposal-indigo 0:1443f4651241 0.78 local&globalpayments,directdebit +proposal-indigo 0:32df55902e8b 0.70 embeddedfinanceapis +proposal-indigo 0:8ac47b282ff6 0.78 buildfinancialproductsdirectlyin +proposal-indigo 0:58520edff52a 0.82 compliance&risk +proposal-indigo 0:05aecb87ba6a 0.90 kyc,aml,fraudmonitoringandregula +proposal-indigo 0:ec5294e7a376 0.47 investmentsummary +proposal-indigo 0:af60f4182ca5 0.45 implementation&onboarding +proposal-indigo 0:572a8dc5a455 0.45 £2,500 +proposal-indigo 0:760775d91a04 0.65 platformaccess&apis +proposal-indigo 0:6ca9376c51ca 0.65 £3,000 +proposal-indigo 0:9a38b75a07fb 0.81 transactionfees(est.) +proposal-indigo 0:3b325109deb0 0.81 variable +proposal-indigo 0:bfece4405b19 0.85 support&accountmanagement +proposal-indigo 0:7f0f70e643bc 0.85 included +proposal-indigo 0:e0bd5388bdc9 0.94 estimatedfirstyearinvestment +proposal-indigo 0:51db6c622be8 0.92 £5,500 +proposal-indigo 0:ab5aa97074c4 0.98 notes +proposal-indigo 0:b9e9c18c4d13 0.97 •pricingexcludesapplicabletaxes. +proposal-indigo 0:e4239ffa2bb6 0.96 •transactionfeesarebasedonusagea +proposal-indigo 0:82eb848e77b3 0.95 •thisproposalisvalidfor30daysfro +proposal-indigo 0:732bcbb66cec 0.95 kestrelpaymentsltd +proposal-indigo 0:3898e7b5f79b 0.90 7harbourcircus,canarywharf,londo +proposal-indigo 0:29208032c874 0.89 kestrel.example/business|busines proposal-modern 0:ecd1378bc9dc 0.18 proposal proposal-modern 0:f592a683c61d 0.31 documentplatformconsolidation proposal-modern 0:bbe07492ee1f 0.28 proposalgc-p-2026-014prepared02a diff --git a/qa/src/test/resources/docx-fidelity/word-windows.tsv b/qa/src/test/resources/docx-fidelity/word-windows.tsv index f96f6ed0f..5e64d10a3 100644 --- a/qa/src/test/resources/docx-fidelity/word-windows.tsv +++ b/qa/src/test/resources/docx-fidelity/word-windows.tsv @@ -57,7 +57,7 @@ letter-panel 1 1 13 13 0.17 0.25 0 letter-sidebar_portrait 1 1 12 12 0.41 0.52 0 letter-timeline_minimal 1 1 15 15 1.30 1.63 0 proposal-editorial 2 2 132 130 0.50 1.81 11 -proposal-indigo 1 1 72 70 0.43 27.75 16 +proposal-indigo 1 1 72 70 0.40 0.90 0 proposal-modern 1 1 44 44 0.35 0.73 0 proposal-northline 2 2 132 132 0.43 1.48 0 receipt-modern 1 1 57 57 0.33 0.44 0 diff --git a/render-docx/src/main/java/com/demcha/compose/document/backend/semantic/docx/DocxLayoutMetrics.java b/render-docx/src/main/java/com/demcha/compose/document/backend/semantic/docx/DocxLayoutMetrics.java index e8bfb974a..ecc2713a7 100644 --- a/render-docx/src/main/java/com/demcha/compose/document/backend/semantic/docx/DocxLayoutMetrics.java +++ b/render-docx/src/main/java/com/demcha/compose/document/backend/semantic/docx/DocxLayoutMetrics.java @@ -1090,6 +1090,32 @@ List passFragments() { parent.placementX() + parent.placementWidth() - parent.padding().right()}; } + /** + * Whether the layout placed something after a node in the same parent — a block under it + * in a section. + * + * @param node a placed node + * @return true when a later sibling was placed + */ + boolean followedInItsParent(DocumentNode node) { + PlacedNode box = placedFor(node); + if (box == null || box.parentPath() == null) { + return false; + } + if (lastChildIndex == null) { + lastChildIndex = new HashMap<>(); + for (PlacedNode other : placed.values()) { + if (other.parentPath() != null) { + lastChildIndex.merge(other.parentPath(), other.childIndex(), Math::max); + } + } + } + return lastChildIndex.getOrDefault(box.parentPath(), -1) > box.childIndex(); + } + + /** The highest child index the layout placed under each parent path, built when first asked. */ + private Map lastChildIndex; + /** The placed node for a semantic node, or null when this index knows neither. */ private PlacedNode placedFor(DocumentNode node) { String path = paths.get(node); diff --git a/render-docx/src/main/java/com/demcha/compose/document/backend/semantic/docx/DocxSemanticBackend.java b/render-docx/src/main/java/com/demcha/compose/document/backend/semantic/docx/DocxSemanticBackend.java index b7a46aa69..718d055b9 100644 --- a/render-docx/src/main/java/com/demcha/compose/document/backend/semantic/docx/DocxSemanticBackend.java +++ b/render-docx/src/main/java/com/demcha/compose/document/backend/semantic/docx/DocxSemanticBackend.java @@ -2132,10 +2132,16 @@ private void writeNodeContentOf(XWPFDocument document, DocumentNode node) throws return; } } - // Only in the flow: inside an overlay, the overlay's own place already holds it. - if (overlayDepth == 0 - && (node instanceof com.demcha.compose.document.node.LayerStackNode || node instanceof ShapeContainerNode) - && onlyDrawn(node) && holdTheSpaceOf(node)) { + // Only in the flow: inside an overlay, the overlay's own place already holds it. Inside + // layer stacks of one layer only it is in the flow — a template wraps a row in one to + // nest it — and a drawing with a sibling after it takes its room there as anywhere else: + // IndigoProposal's meta tiles set a disc over each label, and without the disc's room + // every label stood 24pt high. The last in its parent, an icon in a section of its own + // beside a heading, is held by its row (holdRowAtLeast). + if ((node instanceof com.demcha.compose.document.node.LayerStackNode || node instanceof ShapeContainerNode) + && onlyDrawn(node) + && (overlayDepth == 0 || overlayDepth == oneLayerDepth && layout.followedInItsParent(node)) + && holdTheSpaceOf(node)) { // Its drawing is drawn and takes no room: the space held above is its room. drawOutlineOf(node); ShapeContainerNode outerClip = clipContainer; diff --git a/render-docx/src/test/java/com/demcha/compose/document/backend/semantic/docx/DocxNestedRowDrawingTest.java b/render-docx/src/test/java/com/demcha/compose/document/backend/semantic/docx/DocxNestedRowDrawingTest.java new file mode 100644 index 000000000..50a20b265 --- /dev/null +++ b/render-docx/src/test/java/com/demcha/compose/document/backend/semantic/docx/DocxNestedRowDrawingTest.java @@ -0,0 +1,108 @@ +package com.demcha.compose.document.backend.semantic.docx; + +import com.demcha.compose.document.dsl.LayerStackBuilder; +import com.demcha.compose.document.dsl.PageFlowBuilder; +import com.demcha.compose.document.dsl.RowBuilder; +import com.demcha.compose.document.dsl.SectionBuilder; +import com.demcha.compose.document.dsl.ShapeContainerBuilder; +import com.demcha.compose.document.node.DocumentNode; +import com.demcha.compose.document.node.LayerAlign; +import com.demcha.compose.document.style.DocumentColor; +import com.demcha.compose.document.style.DocumentInsets; +import org.apache.poi.xwpf.usermodel.XWPFDocument; +import org.apache.poi.xwpf.usermodel.XWPFParagraph; +import org.apache.poi.xwpf.usermodel.XWPFTableCell; +import org.junit.jupiter.api.Test; +import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTSpacing; + +import java.util.function.Consumer; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * A drawing in a row nested through a layer stack of one layer takes its room in the flow. + * + *

A row cannot sit in a row cell, so templates wrap one in a layer stack of one layer to + * nest it; nothing is laid over anything there, and the row is written in the flow. A disc + * drawn over a label in one of its cells is drawn where the page puts it, and its room was not + * kept: {@code IndigoProposal}'s meta tiles and feature tiles set each label a disc's or a + * tile's height, 24pt to 28pt, higher than the page.

+ * + * @author Artem Demchyshyn + */ +class DocxNestedRowDrawingTest { + + private static final double DISC = 24; + private static final double LABEL_GAP = 6; + + @Test + void aDiscOverALabelHoldsItsRoomAboveTheLabel() throws Exception { + try (XWPFDocument document = DocxExports.withLayout(400, 600, 20, nested(row -> row.weights(1, 1) + .addSection("Tile", tile -> tile.spacing(0) + .add(disc()) + .addParagraph(p -> p.text("DATE").margin(DocumentInsets.top(LABEL_GAP)))) + .addSection("Other", tile -> tile.spacing(0).addParagraph("27 May 2026"))))) { + XWPFParagraph label = paragraphHolding(document, "DATE"); + + assertThat(before(label)).as("the disc and the label's own gap above it") + .isEqualTo(Math.round((DISC + LABEL_GAP) * 20)); + } + } + + @Test + void anIconAloneInItsCellLeavesItToTheRow() throws Exception { + // PaymentsInvoice's card head: the disc alone in a section of its own, beside the heading. + // Its row is held as tall as the page makes it; space held in its cell as well would + // make the row taller. + try (XWPFDocument document = DocxExports.withLayout(400, 600, 20, nested(row -> row.weights(1, 3) + .addSection("Icon", icon -> icon.spacing(0).add(disc())) + .addParagraph("Bank details")))) { + XWPFTableCell icon = paragraphHolding(document, "Bank details").getBody() instanceof XWPFTableCell cell + ? cell.getTableRow().getCell(0) + : null; + + assertThat(icon).isNotNull(); + for (XWPFParagraph paragraph : icon.getParagraphs()) { + assertThat(before(paragraph)).as("no disc's room in its own cell").isLessThan(Math.round(DISC * 20)); + } + } + } + + private static DocumentNode disc() { + return new ShapeContainerBuilder().name("Disc").circle(DISC) + .fillColor(DocumentColor.rgb(230, 228, 250)) + .center(new com.demcha.compose.document.dsl.ShapeBuilder().name("Mark").size(10, 10) + .fillColor(DocumentColor.rgb(61, 37, 173)).build()) + .build(); + } + + /** A row wrapped in a layer stack of one layer, the way a template nests one. */ + private static Consumer nested(Consumer row) { + SectionBuilder holder = new SectionBuilder().name("Holder"); + holder.addRow("Tiles", row); + DocumentNode node = holder.build(); + return page -> page + .addParagraph("Above") + .add(new LayerStackBuilder().name("TilesLayer").layer(node, LayerAlign.TOP_LEFT, 0).build()); + } + + private static XWPFParagraph paragraphHolding(XWPFDocument document, String text) { + for (var table : document.getTables()) { + for (var row : table.getRows()) { + for (var cell : row.getTableCells()) { + for (XWPFParagraph paragraph : cell.getParagraphs()) { + if (paragraph.getText().equals(text)) { + return paragraph; + } + } + } + } + } + throw new AssertionError("no paragraph holding " + text); + } + + private static long before(XWPFParagraph paragraph) { + CTSpacing spacing = paragraph.getCTP().getPPr() == null ? null : paragraph.getCTP().getPPr().getSpacing(); + return spacing == null || !spacing.isSetBefore() ? 0 : DocxTwips.of(spacing.getBefore()); + } +}