From 92b623057edf21c0090e01e56e72df22837a811e Mon Sep 17 00:00:00 2001
From: DemchaAV
Date: Sun, 4 Oct 2026 14:13:18 +0100
Subject: [PATCH 1/2] fix(docx): space a centred or right-aligned line of its
own to the page's width
Word sets a size the page gives to the tenth at the half point, and a
centred line moves its first letter by half the width that makes, a
right-aligned one by all of it: CenteredHeadline's 8.3pt contact line
stood 4pt left of the page's. Where the difference is a point or more,
the line's letters are now spaced by it (w:spacing), the size left as
Word sets it, and the line is given room for the page's width; a tracked
line and a difference under a point are left alone.
Lines more than 2pt off across fall from 23 to 12 in Word and from 29 to
18 in LibreOffice; nothing moves down.
---
CHANGELOG.md | 12 ++
.../architecture/backend-capability-matrix.md | 2 +-
docs/recipes/docx-export.md | 10 +-
.../libreoffice-windows-lines.tsv | 150 +++++++++---------
.../docx-fidelity/word-windows-lines.tsv | 150 +++++++++---------
.../semantic/docx/DocxSemanticBackend.java | 80 +++++++++-
.../docx/DocxWordSizeMeasureTest.java | 58 +++++++
7 files changed, 308 insertions(+), 154 deletions(-)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index da9e9ca52..23321aeb6 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -8,6 +8,18 @@ follow semantic versioning; release dates are ISO 8601.
### Public API
+- **A centred or right-aligned DOCX line of its own stands as wide as the page sets it.**
+ Word states a type size in half points, and a line set at a size the page gives to the tenth
+ is that much wider or narrower; a centred line moves its first letter by half the difference
+ and a right-aligned one by all of it: `CenteredHeadline`'s 8.3pt contact line, set at 8.5,
+ stood 10pt wider in Word, its first letter 4pt left of the page's, and `ClassicSerif`'s
+ 8.7pt one 4pt right. Where the difference is a point or more, its letters are now spaced by
+ it (`w:spacing`), the size left as Word sets it, so the line's ends stand within a fraction
+ of a point of the page's, and the line is given room for the page's width as one Word sets
+ wider is; a line already tracked, and a difference under a point, are left as they are. Lines
+ more than 2pt off across fall from 23 to 12 in Word (74 lines nearer the page, one 0.02pt
+ further) and from 29 to 18 in LibreOffice on Windows (70 nearer, five at most 0.11pt
+ further); nothing moves down.
- **A nested list item with no marker stands where the page sets its text in DOCX.** In a
list of paragraphs — rich items — a nested item was written two spaces a level in, with no
indent: the project descriptions `Panel`, `Executive`, `ModernProfessional`,
diff --git a/docs/architecture/backend-capability-matrix.md b/docs/architecture/backend-capability-matrix.md
index c7111669a..edaa8bf4a 100644
--- a/docs/architecture/backend-capability-matrix.md
+++ b/docs/architecture/backend-capability-matrix.md
@@ -62,7 +62,7 @@ Payload records live in `core` under
| Capability (payload) | PDF (fixed) | PPTX (fixed) | DOCX (semantic) |
|---|---|---|---|
-| Paragraph — pre-wrapped lines, runs, alignment (`ParagraphFragmentPayload`) | ✅ `PdfParagraphFragmentRenderHandler` | ✅ `PptxParagraphFragmentRenderHandler` (one absolute, wrap-disabled frame per measured line) | ⚠️ semantic paragraphs (`DocxSemanticBackend`) — each run keeps its own style, falling back to the paragraph's when it has none; a `linkTarget` becomes a `w:hyperlink`, with a relationship for an address or `w:anchor` for one of the document's own anchors, and a run's own link wins over the paragraph's; a paragraph seated off its baseline (`TextVerticalAlign`) has its runs raised or lowered in the line (`w:position`) by the PDF backend's own correction (`ParagraphSeating`), one shift for the paragraph where the page seats each line by its own; Word and LibreOffice stand an exact line's baseline four fifths of the way down it whatever the face, where the page sets it the face's ascent down, so a paragraph whose face puts the two half a point or more apart — Spectral's, not Lato's — has its text moved to the page's baseline in the same position, matched at its middle line (not yet a list item's or a table text cell's; a picture among it moves with it in Word and stays on its own baseline in LibreOffice); lines a container stacks over one another tighter than their face each end halfway between their letters and the next line's (Word draws an exact line's text on screen only inside the line; its PDF export does not cut it), and the last layer of a shape container on one page, where its line runs past the foot, ends at the foot or below its letters; letters two lines share are split halfway so the page does not move, and a stack that holds a picture keeps its lines' own heights; a `bulletOffset` of spaces becomes the paragraph's indent (`w:ind` left, hanging or first line, by `indentStrategy`) in the flow and in cells, not yet over the flow, in an overlay's left-and-right pair or in a header or footer; one with letters in it is not written, its wrapped lines still set after the spaces that cover it |
+| Paragraph — pre-wrapped lines, runs, alignment (`ParagraphFragmentPayload`) | ✅ `PdfParagraphFragmentRenderHandler` | ✅ `PptxParagraphFragmentRenderHandler` (one absolute, wrap-disabled frame per measured line) | ⚠️ semantic paragraphs (`DocxSemanticBackend`) — each run keeps its own style, falling back to the paragraph's when it has none; a centred or right-aligned line of its own that Word sets a point or more wider or narrower at its half-point size has its letters spaced by the difference (`w:spacing`), unless it is tracked already; a `linkTarget` becomes a `w:hyperlink`, with a relationship for an address or `w:anchor` for one of the document's own anchors, and a run's own link wins over the paragraph's; a paragraph seated off its baseline (`TextVerticalAlign`) has its runs raised or lowered in the line (`w:position`) by the PDF backend's own correction (`ParagraphSeating`), one shift for the paragraph where the page seats each line by its own; Word and LibreOffice stand an exact line's baseline four fifths of the way down it whatever the face, where the page sets it the face's ascent down, so a paragraph whose face puts the two half a point or more apart — Spectral's, not Lato's — has its text moved to the page's baseline in the same position, matched at its middle line (not yet a list item's or a table text cell's; a picture among it moves with it in Word and stays on its own baseline in LibreOffice); lines a container stacks over one another tighter than their face each end halfway between their letters and the next line's (Word draws an exact line's text on screen only inside the line; its PDF export does not cut it), and the last layer of a shape container on one page, where its line runs past the foot, ends at the foot or below its letters; letters two lines share are split halfway so the page does not move, and a stack that holds a picture keeps its lines' own heights; a `bulletOffset` of spaces becomes the paragraph's indent (`w:ind` left, hanging or first line, by `indentStrategy`) in the flow and in cells, not yet over the flow, in an overlay's left-and-right pair or in a header or footer; one with letters in it is not written, its wrapped lines still set after the spaces that cover it |
| List hanging indent — a marker column and a content column (`ListBuilder.hangingIndent(true)`, `markerGap(...)`) | ✅ marker and content emitted as separate `ParagraphFragmentPayload` fragments at the resolved `markerX` / `contentX` | ✅ the same fragments — the fixed-layout pipeline resolves the geometry before either backend sees it | ⚠️ the top level only. `DocxSemanticBackend` exports a list as a real Word list — `numbering.xml`, `w:numPr` per item, the level carrying the marker — or, with rich items or a drawn marker, as paragraphs; content and nesting are unaffected. With the flag, the top level's marker column is the layout's — the marker's width and `markerGap`, the text and its wrapped lines where the page sets them — where the gap covers what Word may set the marker wider: a picture at its written size, its edges included, or text in the page's face (embedded, or a standard one Word sets in the same widths) grown to its half-point size, half a point clear. A Word list's level then indents and hangs by that column; a list of paragraphs writes the marker, a tab to a stop there, and hangs the item there. Word places content at absolute indents and has no relative-advance primitive, so without the layout's measure the gap could not be honoured; a Word list without the flag that the layout placed and that does not nest takes the page's column too, the spaces the page sets its wrapped lines after, its marker followed by a space (`w:suff`) and an item that wraps measured at Word's half-point size; a list that nests items, a list built as a tree of items (laid out flattened), and a marker the gap does not clear keep the stated column (180 twips, plus 120 per nesting level) — except, in a list of paragraphs, a nested rich item with no marker, which stands where the layout set its text, its measure weighed at Word's half-point sizes, where the layout's items are matched to the list's; a list that nests only such items sets its top level at the page's column too |
| Inline code/badge chips (`InlineBackground` on text spans) | ✅ `PdfParagraphFragmentRenderHandler` | ✅ `PptxParagraphFragmentRenderHandler` | ⚠️ `DocxSemanticBackend` — the fill becomes the run's own `w:shd`, in a paragraph and in a list item alike, so a badge still reads as a badge. What Word has no way to say is the shape: shading covers the glyph box, so the corner radius and the padding that widens the run on the page are not in the file, and the export records both. A `w:shd` fill is opaque, so a translucent chip is flattened first against what this export wrote underneath it — the paragraph's shading, the cell's, or the page — so the chip agrees with the file it is in, which on a white page is the colour the PDF shows. It stops being translucent, and that is recorded with the rest |
| Inline images (`ParagraphImageSpan`) | ✅ `PdfParagraphFragmentRenderHandler` | ✅ `PptxParagraphFragmentRenderHandler` | ✅ `DocxSemanticBackend.writeInlinePicture` (a picture in its own run where it sits among the words, at its size, inside the run's or the paragraph's link; raised or lowered by `w:position` to where the page's alignment and `baselineOffset` put it, from the layout's measure of the paragraph's first line — in a list, the list's text on a line as tall as the item's own tallest picture; LibreOffice ignores `w:position` on a picture and stands it on the baseline, so a picture the export draws itself (icon, emoji, shape) that the page raises carries the rise as transparent rows and needs no `w:position`, while one the page lowers stands in LibreOffice higher than on the page by as much as the page lowers it — up to the text's descent for a centred icon as tall as its line; the editor clips a picture to an exact line height, so a paragraph holding a picture that leaves its text — past the ascent or the descent, in Word's placement or on the baseline — has its lines written at least the height the picture reaches, grown by the editor rather than clipped, every line of the paragraph since Word has one line height for it, and each as tall as the editor's font makes it — for 14pt text about 2.5pt taller than the page's in LibreOffice; a picture inside its text in both editors keeps the exact height; a paragraph of one line of text in a Word paragraph of its own, with room above for its pictures' reach, keeps an exact line at the page's height of it, the pictures set in it where the page puts them in Word and what their ink reaches past it taken from the gaps around it, and in LibreOffice a lowered picture there stands higher and loses what passes the line's top; its description is the text it stands for or empty) |
diff --git a/docs/recipes/docx-export.md b/docs/recipes/docx-export.md
index 104cde11d..2e099f393 100644
--- a/docs/recipes/docx-export.md
+++ b/docs/recipes/docx-export.md
@@ -327,7 +327,15 @@ it. In a table cell, where Word draws no text past the cell's left edge, the lef
gives no more than it has, and the right edge gives the rest, the line moving by that much.
Any other centred or right-aligned paragraph keeps the page's measure: in one of several
lines a wider measure takes another word onto a line, and a short line's box already has
-room for it. A list's items, a line pair's line, text over the flow and a header's or
+room for it. A centred or right-aligned paragraph of one line moves its first letter by the
+line's difference — by half of it, or all of it — so where Word sets it a point or more
+wider or narrower than the page, its letters are spaced by the difference (`w:spacing`, in
+twentieths of a point, the size left as Word sets it) and its ends stand within a fraction of
+a point of the page's; such a line is given room for the page's width and a few hundredths
+more, as one Word sets wider is. A line that already carries tracking is left as it is: Word
+rounds tracking its own way and spaces the last letter too, which the size does not tell. So is a difference under a point — measured, `ObsidianInvoice`'s 9.34pt
+amounts, half a point wider by the size alone, came out as wide as the page's in Word. Text
+typed into a spaced line takes the same spacing. A list's items, a line pair's line, text over the flow and a header's or
footer's line keep the page's measure too.
## Named styles, so the document can be restyled
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 5bfa1bc50..2f1271e80 100644
--- a/qa/src/test/resources/docx-fidelity/libreoffice-windows-lines.tsv
+++ b/qa/src/test/resources/docx-fidelity/libreoffice-windows-lines.tsv
@@ -6,18 +6,18 @@ cv-blue_banner 0:8d6758a29c08 -0.51 0.10 platformengineerwith10+yearsbuil
cv-blue_banner 0:f0d993eb5f9d -0.50 0.10 high-throughputpdfrendering,sema
cv-blue_banner 0:c2f296ef30a3 -0.19 -0.46 professionalexperience
cv-blue_banner 0:a76f47480818 -0.25 0.10 seniorplatformengineer
-cv-blue_banner 0:060300b18051 -0.20 1.42 2024-present
+cv-blue_banner 0:060300b18051 -0.20 0.32 2024-present
cv-blue_banner 0:f890faef907f -0.20 0.10 northwindsystems
cv-blue_banner 0:e9ca73f80176 -0.49 0.10 ledthereusabledocument-generatio
cv-blue_banner 0:4d5fc9766174 -0.48 0.10 70%byretiringper-teampdfscriptsi
cv-blue_banner 0:daf65306b3b6 -0.22 0.10 softwareengineer
-cv-blue_banner 0:e7cc1822c6dc -0.17 1.22 2021-2024
+cv-blue_banner 0:e7cc1822c6dc -0.17 0.42 2021-2024
cv-blue_banner 0:998c1a985022 -0.17 0.10 brightleaflabs
cv-blue_banner 0:57305305a2eb -0.46 0.10 builtbackendservicesandproductio
cv-blue_banner 0:7b7239897a2f -0.45 0.10 layoutengine,eliminatinglicensin
cv-blue_banner 0:41bb54fbae8d -0.19 0.10 backendengineer
-cv-blue_banner 0:3fa772117260 -0.14 1.22 2019-2021
-cv-blue_banner 0:3fa772117260#1 -0.13 1.22 2019-2021
+cv-blue_banner 0:3fa772117260 -0.14 0.42 2019-2021
+cv-blue_banner 0:3fa772117260#1 -0.13 0.42 2019-2021
cv-blue_banner 0:ef148f221e31 -0.14 0.10 helixprintco
cv-blue_banner 0:d7119ba1b2b4 -0.13 0.10 maintainedahigh-volumeinvoice-pr
cv-blue_banner 0:68bf0109c6f8 -0.12 0.24 education
@@ -25,11 +25,11 @@ cv-blue_banner 0:0ad1957d6bab -0.18 0.10 msccomputerscience
cv-blue_banner 0:47ce0218bbea -0.13 0.10 universityofmanchester
cv-blue_banner 0:2ffe115b06e5 -0.12 0.10 distinction.thesis:composablelay
cv-blue_banner 0:b6749d0b616b -0.16 0.10 bscsoftwareengineering
-cv-blue_banner 0:30f9262640a0 -0.11 1.22 2015-2019
+cv-blue_banner 0:30f9262640a0 -0.11 0.42 2015-2019
cv-blue_banner 0:89ad9656f393 -0.11 0.10 imperialcollegelondon
cv-blue_banner 0:baa173b4a82e -0.10 0.10 first-classhonours.specialisatio
cv-blue_banner 0:6d3d5673c39f -0.14 0.10 oraclejavacertification
-cv-blue_banner 0:d40b22f00acb -0.09 1.22 2023-2024
+cv-blue_banner 0:d40b22f00acb -0.09 0.42 2023-2024
cv-blue_banner 0:2669e1f6d55c -0.09 0.10 professionaltrack
cv-blue_banner 0:3857373829c1 -0.08 0.10 java17platformdeep-dive:records,
cv-blue_banner 0:5f94c50dc5bf -0.07 -0.01 technicalskills
@@ -90,7 +90,7 @@ cv-boxed_sections 0:fb1927f88597 -0.32 0.10 •chromeforge(java,graphcompose,p
cv-boxed_sections 0:e7e62230eb1f -0.32 0.11 editorial-magazinedocumenttoolki
cv-boxed_sections 0:c2f296ef30a3 -0.30 1.51 professionalexperience
cv-boxed_sections 0:a76f47480818 -0.31 0.10 seniorplatformengineer
-cv-boxed_sections 0:060300b18051 -0.34 -0.99 2024-present
+cv-boxed_sections 0:060300b18051 -0.34 0.01 2024-present
cv-boxed_sections 0:f890faef907f -0.32 0.10 northwindsystems
cv-boxed_sections 1:49faf14f1f56 -0.13 0.10 ledthereusabledocument-generatio
cv-boxed_sections 1:6bd3f168e89e -0.13 0.10 maintenancetimeby70%byretiringpe
@@ -111,7 +111,7 @@ cv-boxed_sections 1:dd20ae01c3cf 0.25 0.10 opensource:maintainerofgraphcomp
cv-boxed_sections 1:33c35e9d236d 0.25 0.10 speaking:jvmsummit2024,devoxxuk2
cv-centered_headline 0:8f1010120ed6 -0.50 2.44 jordanrivera
cv-centered_headline 0:87ca614d0eab -0.18 1.41 professionaltitle
-cv-centered_headline 0:8d7a2cca3060 -0.23 -3.67 +442055551000|jordan.rivera@exam
+cv-centered_headline 0:8d7a2cca3060 -0.23 0.48 +442055551000|jordan.rivera@exam
cv-centered_headline 0:b944906ed84c -0.28 0.10 professionalsummary
cv-centered_headline 0:f43d42692f27 0.06 0.10 platformengineerwith10+yearsbuil
cv-centered_headline 0:5f94c50dc5bf -0.25 0.10 technicalskills
@@ -247,9 +247,9 @@ cv-charcoal_gold 0:b1ab1e892617 0.36 0.00 2022
cv-charcoal_gold 0:c2f5106fbd5a 0.33 0.00 topperformer
cv-charcoal_gold 0:058521fbf0d1 0.37 0.00 innovatechsystems
cv-charcoal_gold 0:3def82282260 0.30 -0.71 technicaltools
-cv-charcoal_gold 0:eb04bd42c7fb 0.36 -3.19 microsoft365|jira|confluence|asa
+cv-charcoal_gold 0:eb04bd42c7fb 0.36 -0.94 microsoft365|jira|confluence|asa
cv-classic_serif 0:8f1010120ed6 -0.15 2.65 jordanrivera
-cv-classic_serif 0:8d7a2cca3060 0.19 4.18 +442055551000|jordan.rivera@exam
+cv-classic_serif 0:8d7a2cca3060 0.19 -0.42 +442055551000|jordan.rivera@exam
cv-classic_serif 0:3c69d6bb4892 0.24 2.27 professionalprofile
cv-classic_serif 0:32c1cf49a2fe 0.14 0.10 skills
cv-classic_serif 0:873a24d153c8 0.11 0.10 languages:java21,kotlin,groovy,p
@@ -260,7 +260,7 @@ cv-classic_serif 0:181ddda7f959 0.01 0.10 testing:junit5,assertj,pdfbox-ba
cv-classic_serif 0:c40c656d9558 -0.01 0.10 distribution:mavencentral,sonaty
cv-classic_serif 0:53e5e7c5a884 0.00 0.10 experience
cv-classic_serif 0:a76f47480818 0.01 0.10 seniorplatformengineer
-cv-classic_serif 0:060300b18051 -0.02 1.41 2024-present
+cv-classic_serif 0:060300b18051 -0.02 0.31 2024-present
cv-classic_serif 0:f890faef907f -0.01 0.10 northwindsystems
cv-classic_serif 0:49faf14f1f56 -0.24 0.10 ledthereusabledocument-generatio
cv-classic_serif 0:6bd3f168e89e -0.25 0.10 maintenancetimeby70%byretiringpe
@@ -441,12 +441,12 @@ cv-editorial_blue 1:f20997652453 0.19 0.10 languages:english(fluent),german
cv-editorial_blue 1:ec9d3622395e 0.20 0.10 workeligibility:eligibletoworkin
cv-editorial_blue 1:dd20ae01c3cf 0.20 0.10 opensource:maintainerofgraphcomp
cv-editorial_blue 1:33c35e9d236d 0.21 0.10 speaking:jvmsummit2024,devoxxuk2
-cv-editorial_blue 1:0b516aa076e0 0.18 -0.67 referencesavailableuponrequest.
+cv-editorial_blue 1:0b516aa076e0 0.18 0.13 referencesavailableuponrequest.
cv-engineering_resume 0:8f1010120ed6 0.15 0.10 jordanrivera
cv-engineering_resume 0:2d58f0cadc2d -0.25 0.10 platformengineer
-cv-engineering_resume 0:fbeea547d99e -0.21 1.26 london,uk
-cv-engineering_resume 0:89674e6f130e -0.20 1.84 +442055551000
-cv-engineering_resume 0:e0f850305ded -0.19 2.65 jordan.rivera@example.com
+cv-engineering_resume 0:fbeea547d99e -0.21 0.36 london,uk
+cv-engineering_resume 0:89674e6f130e -0.20 0.34 +442055551000
+cv-engineering_resume 0:e0f850305ded -0.19 0.25 jordan.rivera@example.com
cv-engineering_resume 0:84308bea4540 -0.18 0.95 linkedin
cv-engineering_resume 0:c0b0109d9439 -0.17 0.85 github
cv-engineering_resume 0:32c1cf49a2fe -0.50 0.10 skills
@@ -683,7 +683,7 @@ cv-minimal_underlined 0:fb1927f88597 -1.10 0.10 •chromeforge(java,graphcompose
cv-minimal_underlined 0:e7e62230eb1f -1.09 0.11 editorial-magazinedocumenttoolki
cv-minimal_underlined 0:c2f296ef30a3 -1.06 0.10 professionalexperience
cv-minimal_underlined 0:a76f47480818 -1.35 0.10 seniorplatformengineer
-cv-minimal_underlined 0:060300b18051 -1.39 -0.99 2024-present
+cv-minimal_underlined 0:060300b18051 -1.39 0.01 2024-present
cv-minimal_underlined 0:f890faef907f -1.36 0.10 northwindsystems
cv-minimal_underlined 1:49faf14f1f56 -0.13 0.10 ledthereusabledocument-generatio
cv-minimal_underlined 1:6bd3f168e89e -0.13 0.10 maintenancetimeby70%byretiringpe
@@ -822,17 +822,17 @@ cv-modern_professional 1:dd20ae01c3cf 0.23 0.10 opensource:maintainerofgraphcomp
cv-modern_professional 1:33c35e9d236d 0.23 0.10 speaking:jvmsummit2024,devoxxuk2
cv-monogram_sidebar 0:093e7d5fdbaa -0.25 0.81 contact
cv-monogram_sidebar 0:89674e6f130e -0.10 -0.33 +442055551000
-cv-monogram_sidebar 0:e0f850305ded -0.08 -0.49 jordan.rivera@example.com
+cv-monogram_sidebar 0:e0f850305ded -0.08 0.06 jordan.rivera@example.com
cv-monogram_sidebar 0:fbeea547d99e -0.06 -0.16 london,uk
cv-monogram_sidebar 0:84308bea4540 -0.04 -0.06 linkedin
cv-monogram_sidebar 0:c0b0109d9439 -0.02 -0.04 github
cv-monogram_sidebar 0:68bf0109c6f8 -0.05 0.81 education
-cv-monogram_sidebar 0:0ad1957d6bab 0.05 0.73 msccomputerscience
-cv-monogram_sidebar 0:47ce0218bbea 0.08 -0.46 universityofmanchester
-cv-monogram_sidebar 0:3fa772117260 0.09 0.62 2019-2021
-cv-monogram_sidebar 0:b6749d0b616b 0.06 0.85 bscsoftwareengineering
-cv-monogram_sidebar 0:89ad9656f393 0.09 -0.42 imperialcollegelondon
-cv-monogram_sidebar 0:30f9262640a0 0.10 0.62 2015-2019
+cv-monogram_sidebar 0:0ad1957d6bab 0.05 0.28 msccomputerscience
+cv-monogram_sidebar 0:47ce0218bbea 0.08 0.09 universityofmanchester
+cv-monogram_sidebar 0:3fa772117260 0.09 0.22 2019-2021
+cv-monogram_sidebar 0:b6749d0b616b 0.06 0.30 bscsoftwareengineering
+cv-monogram_sidebar 0:89ad9656f393 0.09 0.13 imperialcollegelondon
+cv-monogram_sidebar 0:30f9262640a0 0.10 0.22 2015-2019
cv-monogram_sidebar 0:32c1cf49a2fe 0.07 0.83 skills
cv-monogram_sidebar 0:d5680c2e2769 0.22 -0.08 java21
cv-monogram_sidebar 0:19a67c2a5369 0.24 -0.02 kotlin
@@ -840,7 +840,7 @@ cv-monogram_sidebar 0:95bfacd3a466 0.26 -0.05 groovy
cv-monogram_sidebar 0:11a4a60b518b 0.28 -0.03 python
cv-monogram_sidebar 0:4ec7c53222c8 0.30 0.00 sql
cv-monogram_sidebar 0:57529f20c3ad 0.32 -0.05 pdfbox
-cv-monogram_sidebar 0:1814aacb6d5a 0.34 -0.48 apachepoi(docx/xlsx)
+cv-monogram_sidebar 0:1814aacb6d5a 0.34 0.02 apachepoi(docx/xlsx)
cv-monogram_sidebar 0:136c67657614 -0.38 2.80 jordan
cv-monogram_sidebar 0:8ba35ebe3d5c -0.37 2.83 rivera
cv-monogram_sidebar 0:2d58f0cadc2d -0.19 0.11 platformengineer
@@ -945,7 +945,7 @@ cv-nordic_clean 0:8f1010120ed6 0.15 0.10 jordanrivera
cv-nordic_clean 0:2d58f0cadc2d -0.20 0.10 platformengineer
cv-nordic_clean 0:fbeea547d99e -0.20 -0.30 london,uk
cv-nordic_clean 0:89674e6f130e -0.18 -0.65 +442055551000
-cv-nordic_clean 0:e0f850305ded -0.16 -1.02 jordan.rivera@example.com
+cv-nordic_clean 0:e0f850305ded -0.16 0.13 jordan.rivera@example.com
cv-nordic_clean 0:84308bea4540 -0.14 -0.16 linkedin
cv-nordic_clean 0:c0b0109d9439 -0.12 -0.11 github
cv-nordic_clean 0:1900eab6c028 -0.25 -1.40 profile
@@ -1006,8 +1006,8 @@ cv-orange_ops 0:6a3bd39f358d 77.92 -0.78 birmingham,westmidlands,uk
cv-orange_ops 0:84308bea4540 77.92 -1.07 linkedin
cv-panel 0:8f1010120ed6 -0.40 0.37 jordanrivera
cv-panel 0:2d58f0cadc2d 0.12 0.07 platformengineer
-cv-panel 0:89ff05847e96 0.19 -0.36 london,uk|+442055551000
-cv-panel 0:8817bf9310b6 0.21 -0.60 jordan.rivera@example.com|linked
+cv-panel 0:89ff05847e96 0.19 0.29 london,uk|+442055551000
+cv-panel 0:8817bf9310b6 0.21 0.35 jordan.rivera@example.com|linked
cv-panel 0:1900eab6c028 0.19 -0.10 profile
cv-panel 0:80cc87fdc4c4 -0.23 -0.10 platformengineerwith10+yearsbuil
cv-panel 0:c323ef4c385f -0.21 -0.10 systems.specialisedinhigh-throug
@@ -1484,7 +1484,7 @@ cv-terracotta_rail 0:bf4ab196cbb6 0.08 0.11 strongfocusonsustainability,qual
cv-terracotta_rail 0:0dfcf4444281 0.06 0.11 collaborativecommunicatorskilled
cv-terracotta_rail 0:22fdcc5cfe61 0.04 0.11 leadingmultidisciplinaryteamstoa
cv-terracotta_rail 0:c2f296ef30a3 -0.30 0.11 professionalexperience
-cv-terracotta_rail 0:086c0e036e67 -0.30 -1.14 2021–present
+cv-terracotta_rail 0:086c0e036e67 -0.30 -0.14 2021–present
cv-terracotta_rail 0:b3af3b56129a -0.33 0.06 northlinestudio,bristol,uk
cv-terracotta_rail 0:27819bba9c0f -0.54 0.06 •leaddesignpackagesfromconceptth
cv-terracotta_rail 0:d49fddeb9701 -0.57 0.05 andmixed-useprojects.
@@ -1496,7 +1496,7 @@ cv-terracotta_rail 0:894e988cdf94#1 -0.99 0.05 documentation.
cv-terracotta_rail 0:40c00971356d -0.67 0.06 •presentandcommunicatedesignprop
cv-terracotta_rail 0:a218287e182e -0.69 0.05 anddrivingprojectsforward.
cv-terracotta_rail 0:f2e40fc1edb7 -0.49 0.06 architect
-cv-terracotta_rail 0:f7d1049ec64b -0.49 -0.94 2018–2021
+cv-terracotta_rail 0:f7d1049ec64b -0.49 -0.24 2018–2021
cv-terracotta_rail 0:59e0b0e4edc9 -0.52 0.06 urbanformpartners,bath,uk
cv-terracotta_rail 0:4f2dbefcbec0 -0.74 0.06 •developedconceptandtechnicaldes
cv-terracotta_rail 0:e2a5e1a23401 -0.76 0.05 educationprojects.
@@ -1507,7 +1507,7 @@ cv-terracotta_rail 0:6b3142466e26 -0.84 0.06 •coordinatedwithcontractorsandco
cv-terracotta_rail 0:1150d4ba642c -0.86 0.05 intent.
cv-terracotta_rail 0:1150d4ba642c#1 -0.95 0.05 intent.
cv-terracotta_rail 0:2d2cd24f87d7 -0.67 0.06 partiiarchitecturalassistant
-cv-terracotta_rail 0:3b8048983ecc -0.67 -0.94 2016–2018
+cv-terracotta_rail 0:3b8048983ecc -0.67 -0.24 2016–2018
cv-terracotta_rail 0:ebd31e5e7505 -0.69 0.06 axisdesignworkshop,cardiff,uk
cv-terracotta_rail 0:e48e46c3ddf3 -0.71 0.06 •supportedseniorarchitectsacross
cv-terracotta_rail 0:810bbb784193 -0.93 0.06 •producedcadandrevitdrawings,mod
@@ -1534,10 +1534,10 @@ cv-terracotta_rail 0:208ed62d3df6 -0.75 0.10 environmentalstandardsandstrongs
cv-terracotta_rail 0:ac5e63220124 -1.06 0.10 performance.
cv-terracotta_rail 0:68bf0109c6f8 -0.89 0.11 education
cv-terracotta_rail 0:1d1dcf789873 -0.94 0.11 marcharchitecture
-cv-terracotta_rail 0:00583a6bed38 -0.94 -0.86 2014–2016
+cv-terracotta_rail 0:00583a6bed38 -0.94 -0.16 2014–2016
cv-terracotta_rail 0:5761434c99d8 -0.97 0.11 universityofsheffield
cv-terracotta_rail 0:e3f2f6a4e375 -0.97 0.11 ba(hons)architecture
-cv-terracotta_rail 0:2920a7fb42fc -0.97 -0.86 2011–2014
+cv-terracotta_rail 0:2920a7fb42fc -0.97 -0.16 2011–2014
cv-terracotta_rail 0:af625dd0aa20 -1.00 0.11 universityofthewestofengland
cv-timeline_minimal 0:8f1010120ed6 0.15 0.10 jordanrivera
cv-timeline_minimal 0:2d58f0cadc2d -0.40 0.10 platformengineer
@@ -1640,20 +1640,20 @@ cv-violet_grid 0:b96067918254 -0.26 -0.41 uxresearch
cv-violet_grid 0:9058674fd905 -0.21 -0.71 userinterviews,surveys,
cv-violet_grid 0:0cdd38014d79 -0.22 -0.70 personas,journeymaps,
cv-violet_grid 0:61e4ba28d469 -0.22 -0.66 competitiveanalysis
-cv-violet_grid 0:3de6be58cb9f -0.26 -0.99 informationarchitecture
+cv-violet_grid 0:3de6be58cb9f -0.26 0.11 informationarchitecture
cv-violet_grid 0:db5c6756c4b6 -0.21 -0.77 sitemaps,userflows,card
cv-violet_grid 0:6c7ad1834a60 -0.22 -0.77 sorting,contentstrategy
-cv-violet_grid 0:187737d59250 -0.26 -0.50 wireframing
+cv-violet_grid 0:187737d59250 -0.26 -0.05 wireframing
cv-violet_grid 0:2286e5aa328a -0.21 -0.52 lowtohigh-fidelity
-cv-violet_grid 0:5c4b21cb1af8 -0.26 -0.43 prototyping
+cv-violet_grid 0:5c4b21cb1af8 -0.26 0.02 prototyping
cv-violet_grid 0:1abfa9dc1773 -0.21 -0.69 interactiveprototypes,
cv-violet_grid 0:1ddc6c379a54 -0.22 -0.54 microinteractions,
cv-violet_grid 0:d1d99c54cf40 -0.22 -0.27 transitions
-cv-violet_grid 0:7a38f8d94009 -0.26 -0.62 usabilitytesting
+cv-violet_grid 0:7a38f8d94009 -0.26 0.13 usabilitytesting
cv-violet_grid 0:63af69ae99e6 -0.21 -0.63 testplanning,usability
cv-violet_grid 0:f67c409cc44f -0.22 -0.87 tests,heuristicevaluation,
cv-violet_grid 0:438e1d3264dd -0.22 -0.61 insights&iteration
-cv-violet_grid 0:7e02941d76f5 -0.26 -0.53 designsystems
+cv-violet_grid 0:7e02941d76f5 -0.26 0.07 designsystems
cv-violet_grid 0:a0d27161913a -0.21 -0.57 componentlibraries,
cv-violet_grid 0:82374d275507 -0.22 -0.49 designtokens,pattern
cv-violet_grid 0:caf9f906b16a -0.22 -0.45 documentation
@@ -1662,8 +1662,8 @@ cv-violet_grid 0:5cb2b3b6143c -0.18 0.10 figmafigjamadobexdsketchmironoti
cv-violet_grid 0:53e5e7c5a884 -0.20 0.10 experience
cv-violet_grid 0:5a0774260e0c -0.08 0.10 2022-present
cv-violet_grid 0:204f460767ff -0.12 0.02 seniorux/uidesigner|novafin(fint
-cv-violet_grid 0:318bba211bdc -0.07 -1.73 sanfrancisco,ca
-cv-violet_grid 0:318bba211bdc#1 8.85 -1.73 sanfrancisco,ca
+cv-violet_grid 0:318bba211bdc -0.07 -0.23 sanfrancisco,ca
+cv-violet_grid 0:318bba211bdc#1 8.85 -0.23 sanfrancisco,ca
cv-violet_grid 0:3883e88c63c3 -0.08 0.05 ledend-to-enddesignforapersonalf
cv-violet_grid 0:ccc00663f089 1.70 0.05 conducteduserresearch,builtperso
cv-violet_grid 0:7d0f908c7e5f 3.48 0.05 designedwireframes,prototypes,an
@@ -1677,7 +1677,7 @@ cv-violet_grid 0:ab80a52d70fe 12.40 0.05 partneredwithengineerstoensurefe
cv-violet_grid 0:11cbf615189b 14.19 0.05 createdreusablecomponentsandstyl
cv-violet_grid 0:7e5081a3ed48 15.98 0.10 2018-2020
cv-violet_grid 0:73f7bbf7cd60 15.94 0.02 juniorux/uidesigner|lumendigital
-cv-violet_grid 0:c6b06045332f 15.99 -1.12 austin,tx
+cv-violet_grid 0:c6b06045332f 15.99 -0.17 austin,tx
cv-violet_grid 0:5ea8234925c5 15.98 0.05 supporteddiscoveryworkshops,user
cv-violet_grid 0:46f00c76528c 17.76 0.05 designedresponsivewebsitesandlan
cv-violet_grid 0:f3cea36a9d83 19.54 0.05 collaboratedwithcopywritersandde
@@ -2586,12 +2586,12 @@ invoice-merchant 0:e2a9c81da5f6 0.06 -0.13 unitprice(usd)
invoice-merchant 0:5dfb966b2bcf 0.06 -0.06 amount(usd)
invoice-merchant 0:fe6b34582738 0.07 0.07 coastlineadvanced
invoice-merchant 0:46f3b0cea3bc 0.03 0.07 monthlysubscription
-invoice-merchant 0:71738eb7126d 0.04 -0.54 advancedplan
+invoice-merchant 0:71738eb7126d 0.04 0.01 advancedplan
invoice-merchant 0:7b969356782c 0.04 -0.25 399.00
invoice-merchant 0:7b969356782c#1 0.04 -0.25 399.00
invoice-merchant 0:0a9acaea6477 0.06 0.07 paymentprocessing
invoice-merchant 0:91809142f08f 0.01 0.07 transactionfee
-invoice-merchant 0:358a7392afe6 0.02 -0.46 asperusage
+invoice-merchant 0:358a7392afe6 0.02 0.04 asperusage
invoice-merchant 0:561b2814d3c0 0.02 -0.12 0.00
invoice-merchant 0:561b2814d3c0#1 0.02 -0.13 0.00
invoice-merchant 0:561b2814d3c0#2 -0.16 -0.12 0.00
@@ -2678,28 +2678,28 @@ invoice-metered 0:d1062d072acd 0.08 -0.36 unitprice
invoice-metered 0:cf38d95c9c6b 0.08 -0.38 amount
invoice-metered 0:f13d9c7c6f89 0.04 -0.42 computeinstances
invoice-metered 0:d08426b31521 0.01 -0.42 on-demandusage
-invoice-metered 0:05313a23f706 0.05 0.41 london(eu-west)
-invoice-metered 0:05313a23f706#1 0.03 0.41 london(eu-west)
-invoice-metered 0:05313a23f706#2 0.02 0.41 london(eu-west)
+invoice-metered 0:05313a23f706 0.05 -0.34 london(eu-west)
+invoice-metered 0:05313a23f706#1 0.03 -0.34 london(eu-west)
+invoice-metered 0:05313a23f706#2 0.02 -0.34 london(eu-west)
invoice-metered 0:6e477a22f840 0.05 -0.02 744hrs
invoice-metered 0:6e477a22f840#1 0.02 -0.02 744hrs
-invoice-metered 0:352967434b63 0.05 0.17 gbp0.0710
+invoice-metered 0:352967434b63 0.05 -0.28 gbp0.0710
invoice-metered 0:eec5f520909e 0.05 0.11 gbp52.82
invoice-metered 0:721ffa554e56 0.03 -0.42 objectstorage
invoice-metered 0:df2f05798b9e 0.00 -0.42 storedvolume
invoice-metered 0:41542149a8a5 0.03 -0.02 480gb
-invoice-metered 0:2242bd7aa8ce 0.03 0.17 gbp0.0215
+invoice-metered 0:2242bd7aa8ce 0.03 -0.28 gbp0.0215
invoice-metered 0:7c6be041f9c2 0.03 0.11 gbp10.32
invoice-metered 0:8152fd217e12 0.01 -0.42 manageddatabase
invoice-metered 0:3c8556c4aa26 -0.02 -0.42 primaryandreplica
-invoice-metered 0:76afc71f5b3e 0.02 0.17 gbp0.1650
-invoice-metered 0:baad0db43ed6 0.02 0.19 gbp122.76
+invoice-metered 0:76afc71f5b3e 0.02 -0.28 gbp0.1650
+invoice-metered 0:baad0db43ed6 0.02 -0.26 gbp122.76
invoice-metered 0:444858f5bf18 0.00 -0.42 egresstraffic
invoice-metered 0:8b72bb875a83 -0.03 -0.42 datatransferout
invoice-metered 0:8001c2743965 0.00 -0.08 global
invoice-metered 0:8001c2743965#1 -0.01 -0.08 global
invoice-metered 0:847eb4cbbb42 0.00 -0.02 310gb
-invoice-metered 0:d651e46704f7 0.00 0.17 gbp0.0900
+invoice-metered 0:d651e46704f7 0.00 -0.28 gbp0.0900
invoice-metered 0:4ecaa7313375 0.00 0.11 gbp27.90
invoice-metered 0:efa8771baad9 -0.01 -0.42 supportplan
invoice-metered 0:9e81cd6e653a -0.05 -0.42 standardresponse
@@ -2723,7 +2723,7 @@ invoice-metered 0:79fabcfaa819 -0.07 0.78 gbp214.71
invoice-metered 0:a52611c9261b -0.10 0.14 vat(20%)
invoice-metered 0:e50d8a0ea599 -0.10 0.70 gbp42.94
invoice-metered 0:57a66d21eb1b -0.23 0.14 totaldue
-invoice-metered 0:f207312dff2f -0.30 -0.85 gbp257.65
+invoice-metered 0:f207312dff2f -0.30 -0.05 gbp257.65
invoice-metered 0:b0553f008513 -0.14 0.11 paymentdueby
invoice-metered 0:e60e7ef06ccc -0.96 0.10 thankyouforrunningonnorthwindclo
invoice-metered 0:34ea2ec3fe72 -0.96 0.10 forbillingandaccountsupport,visi
@@ -2952,9 +2952,9 @@ invoice-obsidian 0:5d1d1b79af09 0.02 0.09 invoicenumber
invoice-obsidian 0:3e62d1fbe5ec 0.06 0.09 inv-2026-1048
invoice-obsidian 0:3e62d1fbe5ec#1 -0.56 0.08 inv-2026-1048
invoice-obsidian 0:8feaf8016bab 0.02 0.09 issuedate
-invoice-obsidian 0:4d49cb78b810 0.02 1.46 27august2026
+invoice-obsidian 0:4d49cb78b810 0.02 0.16 27august2026
invoice-obsidian 0:ac10f8c16320 -0.05 0.09 duedate
-invoice-obsidian 0:b55571aaabac -0.05 1.78 10september2026
+invoice-obsidian 0:b55571aaabac -0.05 0.18 10september2026
invoice-obsidian 0:fb854bac39c8 -0.12 0.09 paymentterms
invoice-obsidian 0:56a2e019b8aa -0.12 0.79 14days
invoice-obsidian 0:466e5b9cd57f -0.19 0.09 currency
@@ -3117,7 +3117,7 @@ invoice-payments 0:48617b6d05d4 -0.18 0.10 gb29hrbk40025071295408
invoice-payments 0:6929e6db6e1f -0.22 0.10 hrbkgb2l
invoice-payments 0:e4c4788968b2 0.06 -0.01 subtotal(excl.vat)
invoice-payments 0:7ed180496d4e 0.07 -0.01 vatat20%
-invoice-payments 0:751cbab81a3a 0.10 1.09 £2,186.00
+invoice-payments 0:751cbab81a3a 0.10 0.29 £2,186.00
invoice-payments 0:9a5256946abd 0.11 0.94 £437.20
invoice-payments 0:57a66d21eb1b 0.05 -0.01 totaldue
invoice-payments 0:5eb0b17220fa 0.11 -0.01 dueby14june2025
@@ -3136,7 +3136,7 @@ invoice-platform 0:9817f28442bf#1 -0.56 0.14 auroracloud
invoice-platform 0:52d6e3de4fa0 0.61 -0.21 invoice
invoice-platform 0:1cf61cdc6464 0.06 0.13 auroracloudinc.
invoice-platform 0:1cf61cdc6464#1 -0.10 0.40 auroracloudinc.
-invoice-platform 0:1cf61cdc6464#2 -0.53 -1.20 auroracloudinc.
+invoice-platform 0:1cf61cdc6464#2 -0.53 0.30 auroracloudinc.
invoice-platform 0:43315277b007 -0.40 0.13 500harbourstreet
invoice-platform 0:d9065bfb8f59 -0.40 0.13 vancouver,bcv6c2w2
invoice-platform 0:408c7c5887a0 -0.41 0.13 canada
@@ -3240,7 +3240,7 @@ invoice-platform 0:17e16a2f994d 0.00 0.68 cad195.76
invoice-platform 0:ac10f8c16320 -0.18 0.05 duedate
invoice-platform 0:d5ef7c1f9830 -0.46 0.11 thankyouforrunningonauroracloud.
invoice-platform 0:6a88f440b84e -0.46 0.11 manageyouraccountataurora.exampl
-invoice-platform 0:cf0a1e1dc87b -0.54 -4.25 500harbourstreet,vancouver,bcv6c
+invoice-platform 0:cf0a1e1dc87b -0.54 0.45 500harbourstreet,vancouver,bcv6c
invoice-platform 0:7b14b8d2c5c4 0.02 0.49 page1of1
invoice-subscription 0:435df6fdbffd 0.05 0.12 halstead
invoice-subscription 0:52d6e3de4fa0 0.02 -0.35 invoice
@@ -3255,7 +3255,7 @@ invoice-subscription 0:78acb3cb907b 0.27 0.12 www.halstead.example
invoice-subscription 0:f1c030eb4329 0.29 0.12 vatnogb418227603
invoice-subscription 0:77ead61b5875 0.29 0.09 invoiceno
invoice-subscription 0:0b0c0ee24de8 0.26 0.74 hs-inv-2026-0417
-invoice-subscription 0:0b0c0ee24de8#1 -0.07 1.23 hs-inv-2026-0417
+invoice-subscription 0:0b0c0ee24de8#1 -0.07 0.88 hs-inv-2026-0417
invoice-subscription 0:8feaf8016bab 0.34 0.09 issuedate
invoice-subscription 0:4d49cb78b810 0.32 0.62 27august2026
invoice-subscription 0:ac10f8c16320 0.39 0.09 duedate
@@ -3310,9 +3310,9 @@ invoice-subscription 0:57a66d21eb1b -0.18 0.11 totaldue
invoice-subscription 0:cf1b68cc49ef -0.11 0.94 £1,136.40
invoice-subscription 0:3c7ade679fdf 1.24 0.12 paymentdetails
invoice-subscription 0:56ed021beb35 -0.03 0.54 beneficiary
-invoice-subscription 0:0b85f3c8ed04 -0.07 0.83 halsteadsoftwareltd
+invoice-subscription 0:0b85f3c8ed04 -0.07 0.33 halsteadsoftwareltd
invoice-subscription 0:4381dc2ab142 -0.03 0.77 bank
-invoice-subscription 0:6d50b2dd724f -0.07 1.19 northgatebank
+invoice-subscription 0:6d50b2dd724f -0.07 0.84 northgatebank
invoice-subscription 0:10aeacde20ff -0.03 0.83 sortcode
invoice-subscription 0:b19b1415ef9b -0.07 0.99 40-05-30
invoice-subscription 0:f92a9c925024 -0.03 0.94 accountnumber
@@ -3359,10 +3359,10 @@ invoice-workspace 0:725a8f5062e7 -0.18 0.02 shipto
invoice-workspace 0:6d835e737af6 -0.17 0.02 unit4,riversidepark
invoice-workspace 0:555d5194cd4d -0.16 0.02 leedsls14ap
invoice-workspace 0:c9046f7a37ad 0.01 -0.25 description
-invoice-workspace 0:169240d91658 0.01 -1.05 plan/service
+invoice-workspace 0:169240d91658 0.01 -0.45 plan/service
invoice-workspace 0:32e8369a9c4f 0.01 -0.49 qty
-invoice-workspace 0:e2a9c81da5f6 0.01 -1.18 unitprice(usd)
-invoice-workspace 0:5dfb966b2bcf 0.01 -1.04 amount(usd)
+invoice-workspace 0:e2a9c81da5f6 0.01 -0.48 unitprice(usd)
+invoice-workspace 0:5dfb966b2bcf 0.01 -0.29 amount(usd)
invoice-workspace 0:2bcc555090a1 -0.09 -0.30 kestrelbusiness+
invoice-workspace 0:68fad867a983 -0.04 -0.30 workspacesubscription
invoice-workspace 0:e0b844887562 -0.03 -0.17 business+annual
@@ -3411,7 +3411,7 @@ invoice-workspace 0:b0553f008513 -0.57 0.10 paymentdueby
invoice-workspace 0:08775a92bfca 0.59 0.12 thankyouforusingkestrel.
invoice-workspace 0:18e82fbd48a3 0.66 0.12 questionsaboutthisinvoice?reachu
invoice-workspace 0:3c72ba0e1eb8 0.53 0.06 500howardstreet,6thfloor,sanfran
-invoice-workspace 0:9e2917e23527 0.52 -1.42 kestrel.example
+invoice-workspace 0:9e2917e23527 0.52 -0.12 kestrel.example
invoice-workspace 0:7b14b8d2c5c4 -0.05 -0.03 page1of1
letter-blue_banner 0:8f1010120ed6 0.42 1.95 jordanrivera
letter-blue_banner 0:8f1010120ed6#1 -0.02 0.10 jordanrivera
@@ -3436,7 +3436,7 @@ letter-boxed_sections 0:80a6a628ad52 -0.38 0.10 sincerely,
letter-centered_headline 0:8f1010120ed6 -0.50 2.44 jordanrivera
letter-centered_headline 0:8f1010120ed6#1 -0.06 0.10 jordanrivera
letter-centered_headline 0:2d58f0cadc2d -0.18 1.40 platformengineer
-letter-centered_headline 0:8d7a2cca3060 -0.23 -3.67 +442055551000|jordan.rivera@exam
+letter-centered_headline 0:8d7a2cca3060 -0.23 0.48 +442055551000|jordan.rivera@exam
letter-centered_headline 0:bef81b541365 -0.25 0.10 dearhiringteamatnorthwindsystems
letter-centered_headline 0:ab54410ead82 0.09 0.10 iamexcitedtosharemyinterestinthe
letter-centered_headline 0:9185168d2cf8 0.10 0.10 document-generationsystemsthatba
@@ -3446,7 +3446,7 @@ letter-centered_headline 0:a71e98216baa -0.12 0.10 iwouldwelcometheopportunityto
letter-centered_headline 0:80a6a628ad52 -0.09 0.10 sincerely,
letter-classic_serif 0:8f1010120ed6 -0.15 2.65 jordanrivera
letter-classic_serif 0:8f1010120ed6#1 0.02 0.10 jordanrivera
-letter-classic_serif 0:8d7a2cca3060 0.19 4.18 +442055551000|jordan.rivera@exam
+letter-classic_serif 0:8d7a2cca3060 0.19 -0.42 +442055551000|jordan.rivera@exam
letter-classic_serif 0:bef81b541365 0.20 0.10 dearhiringteamatnorthwindsystems
letter-classic_serif 0:ab54410ead82 -0.08 0.10 iamexcitedtosharemyinterestinthe
letter-classic_serif 0:9185168d2cf8 -0.10 0.10 document-generationsystemsthatba
@@ -3477,9 +3477,9 @@ letter-editorial_blue 0:80a6a628ad52 0.16 0.10 sincerely,
letter-engineering_resume 0:8f1010120ed6 0.15 0.10 jordanrivera
letter-engineering_resume 0:8f1010120ed6#1 -1.41 0.10 jordanrivera
letter-engineering_resume 0:2d58f0cadc2d -0.25 0.10 platformengineer
-letter-engineering_resume 0:fbeea547d99e -0.21 1.26 london,uk
-letter-engineering_resume 0:89674e6f130e -0.20 1.84 +442055551000
-letter-engineering_resume 0:e0f850305ded -0.19 2.65 jordan.rivera@example.com
+letter-engineering_resume 0:fbeea547d99e -0.21 0.36 london,uk
+letter-engineering_resume 0:89674e6f130e -0.20 0.34 +442055551000
+letter-engineering_resume 0:e0f850305ded -0.19 0.25 jordan.rivera@example.com
letter-engineering_resume 0:84308bea4540 -0.18 0.95 linkedin
letter-engineering_resume 0:c0b0109d9439 -0.17 0.85 github
letter-engineering_resume 0:bef81b541365 -1.40 0.10 dearhiringteamatnorthwindsystems
@@ -3526,7 +3526,7 @@ letter-modern_professional 0:80a6a628ad52 -0.13 0.10 sincerely,
letter-monogram_sidebar 0:136c67657614 -0.38 2.82 jordan
letter-monogram_sidebar 0:8ba35ebe3d5c -0.37 2.85 rivera
letter-monogram_sidebar 0:2d58f0cadc2d -0.19 0.13 platformengineer
-letter-monogram_sidebar 0:8d7a2cca3060 -0.17 -1.66 +442055551000|jordan.rivera@exam
+letter-monogram_sidebar 0:8d7a2cca3060 -0.17 0.39 +442055551000|jordan.rivera@exam
letter-monogram_sidebar 0:bef81b541365 -0.23 0.10 dearhiringteamatnorthwindsystems
letter-monogram_sidebar 0:ab54410ead82 0.12 0.10 iamexcitedtosharemyinterestinthe
letter-monogram_sidebar 0:9185168d2cf8 0.12 0.10 document-generationsystemsthatba
@@ -3540,7 +3540,7 @@ letter-nordic_clean 0:8f1010120ed6#1 -0.11 0.10 jordanrivera
letter-nordic_clean 0:2d58f0cadc2d -0.20 0.10 platformengineer
letter-nordic_clean 0:fbeea547d99e -0.20 -0.30 london,uk
letter-nordic_clean 0:89674e6f130e -0.18 -0.65 +442055551000
-letter-nordic_clean 0:e0f850305ded -0.16 -1.02 jordan.rivera@example.com
+letter-nordic_clean 0:e0f850305ded -0.16 0.13 jordan.rivera@example.com
letter-nordic_clean 0:84308bea4540 -0.14 -0.16 linkedin
letter-nordic_clean 0:c0b0109d9439 -0.12 -0.11 github
letter-nordic_clean 0:bef81b541365 -0.11 0.10 dearhiringteamatnorthwindsystems
@@ -3553,8 +3553,8 @@ letter-nordic_clean 0:80a6a628ad52 -0.14 0.10 sincerely,
letter-panel 0:8f1010120ed6 -0.40 0.37 jordanrivera
letter-panel 0:8f1010120ed6#1 0.16 0.10 jordanrivera
letter-panel 0:2d58f0cadc2d 0.12 0.07 platformengineer
-letter-panel 0:89ff05847e96 0.19 -0.36 london,uk|+442055551000
-letter-panel 0:8817bf9310b6 0.21 -0.60 jordan.rivera@example.com|linked
+letter-panel 0:89ff05847e96 0.19 0.34 london,uk|+442055551000
+letter-panel 0:8817bf9310b6 0.21 0.35 jordan.rivera@example.com|linked
letter-panel 0:bef81b541365 0.01 0.10 dearhiringteamatnorthwindsystems
letter-panel 0:ab54410ead82 -0.17 0.10 iamexcitedtosharemyinterestinthe
letter-panel 0:9185168d2cf8 -0.15 0.10 document-generationsystemsthatba
@@ -3566,7 +3566,7 @@ letter-panel 0:80a6a628ad52 0.15 0.10 sincerely,
letter-sidebar_portrait 0:8f1010120ed6 -0.73 0.14 jordanrivera
letter-sidebar_portrait 0:8f1010120ed6#1 -0.13 0.10 jordanrivera
letter-sidebar_portrait 0:2d58f0cadc2d -0.23 0.49 platformengineer
-letter-sidebar_portrait 0:8d7a2cca3060 -0.22 -3.72 +442055551000|jordan.rivera@exam
+letter-sidebar_portrait 0:8d7a2cca3060 -0.22 0.58 +442055551000|jordan.rivera@exam
letter-sidebar_portrait 0:bef81b541365 -0.28 0.10 dearhiringteamatnorthwindsystems
letter-sidebar_portrait 0:ab54410ead82 -0.51 0.10 iamexcitedtosharemyinterestinthe
letter-sidebar_portrait 0:9185168d2cf8 -0.49 0.10 document-generationsystemsthatba
@@ -3967,8 +3967,8 @@ proposal-northline 1:c74c9cd82f13 0.37 -0.28 date:
proposal-northline 1:026890c58de9 0.01 0.10 northlinestudio|northlinestudio.
receipt-modern 0:2b019b5b46da 0.31 0.10 northwindpay
receipt-modern 0:a3c304a23bb1 0.30 0.21 transferconfirmation
-receipt-modern 0:e885a92f5bb7 0.18 -2.47 generatedon09august2026
-receipt-modern 0:c288dd6df716 0.19 -2.30 referencenwp-4821-0067
+receipt-modern 0:e885a92f5bb7 0.18 0.03 generatedon09august2026
+receipt-modern 0:c288dd6df716 0.19 -0.20 referencenwp-4821-0067
receipt-modern 0:9de2a2d7cd7a 0.18 -1.40 amountcollected
receipt-modern 0:465691680b99 0.03 -1.40 £66.62
receipt-modern 0:465691680b99#1 0.29 -0.90 £66.62
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 fc4171baa..355cadc9d 100644
--- a/qa/src/test/resources/docx-fidelity/word-windows-lines.tsv
+++ b/qa/src/test/resources/docx-fidelity/word-windows-lines.tsv
@@ -6,18 +6,18 @@ cv-blue_banner 0:8d6758a29c08 -0.52 -0.08 platformengineerwith10+yearsbuil
cv-blue_banner 0:f0d993eb5f9d -0.50 -0.08 high-throughputpdfrendering,sema
cv-blue_banner 0:c2f296ef30a3 -0.23 -1.26 professionalexperience
cv-blue_banner 0:a76f47480818 -0.24 0.04 seniorplatformengineer
-cv-blue_banner 0:060300b18051 -0.18 1.34 2024-present
+cv-blue_banner 0:060300b18051 -0.18 0.14 2024-present
cv-blue_banner 0:f890faef907f -0.17 -0.08 northwindsystems
cv-blue_banner 0:e9ca73f80176 -0.48 -0.08 ledthereusabledocument-generatio
cv-blue_banner 0:4d5fc9766174 -0.46 -0.08 70%byretiringper-teampdfscriptsi
cv-blue_banner 0:daf65306b3b6 -0.16 0.04 softwareengineer
-cv-blue_banner 0:e7cc1822c6dc -0.10 1.14 2021-2024
+cv-blue_banner 0:e7cc1822c6dc -0.10 0.18 2021-2024
cv-blue_banner 0:998c1a985022 -0.21 -0.08 brightleaflabs
cv-blue_banner 0:57305305a2eb -0.49 -0.08 builtbackendservicesandproductio
cv-blue_banner 0:7b7239897a2f -0.35 -0.08 layoutengine,eliminatinglicensin
cv-blue_banner 0:41bb54fbae8d -0.17 0.04 backendengineer
-cv-blue_banner 0:3fa772117260 -0.11 1.14 2019-2021
-cv-blue_banner 0:3fa772117260#1 -0.09 1.14 2019-2021
+cv-blue_banner 0:3fa772117260 -0.11 0.18 2019-2021
+cv-blue_banner 0:3fa772117260#1 -0.09 0.18 2019-2021
cv-blue_banner 0:ef148f221e31 -0.10 -0.08 helixprintco
cv-blue_banner 0:d7119ba1b2b4 -0.05 -0.08 maintainedahigh-volumeinvoice-pr
cv-blue_banner 0:68bf0109c6f8 -0.14 -0.59 education
@@ -25,11 +25,11 @@ cv-blue_banner 0:0ad1957d6bab -0.15 0.04 msccomputerscience
cv-blue_banner 0:47ce0218bbea -0.08 -0.08 universityofmanchester
cv-blue_banner 0:2ffe115b06e5 -0.03 -0.08 distinction.thesis:composablelay
cv-blue_banner 0:b6749d0b616b -0.09 0.04 bscsoftwareengineering
-cv-blue_banner 0:30f9262640a0 -0.03 1.14 2015-2019
+cv-blue_banner 0:30f9262640a0 -0.03 0.18 2015-2019
cv-blue_banner 0:89ad9656f393 -0.02 -0.08 imperialcollegelondon
cv-blue_banner 0:baa173b4a82e 0.03 -0.08 first-classhonours.specialisatio
cv-blue_banner 0:6d3d5673c39f -0.01 0.04 oraclejavacertification
-cv-blue_banner 0:d40b22f00acb 0.05 1.14 2023-2024
+cv-blue_banner 0:d40b22f00acb 0.05 0.18 2023-2024
cv-blue_banner 0:2669e1f6d55c -0.06 -0.08 professionaltrack
cv-blue_banner 0:3857373829c1 -0.01 -0.08 java17platformdeep-dive:records,
cv-blue_banner 0:5f94c50dc5bf -0.10 -0.76 technicalskills
@@ -90,7 +90,7 @@ cv-boxed_sections 0:fb1927f88597 -0.29 -0.08 •chromeforge(java,graphcompose,p
cv-boxed_sections 0:e7e62230eb1f -0.20 -0.06 editorial-magazinedocumenttoolki
cv-boxed_sections 0:c2f296ef30a3 -0.20 0.54 professionalexperience
cv-boxed_sections 0:a76f47480818 -0.18 0.04 seniorplatformengineer
-cv-boxed_sections 0:060300b18051 -0.25 -1.13 2024-present
+cv-boxed_sections 0:060300b18051 -0.25 0.07 2024-present
cv-boxed_sections 0:f890faef907f -0.28 -0.08 northwindsystems
cv-boxed_sections 1:49faf14f1f56 -0.06 -0.08 ledthereusabledocument-generatio
cv-boxed_sections 1:6bd3f168e89e -0.01 -0.08 maintenancetimeby70%byretiringpe
@@ -111,7 +111,7 @@ cv-boxed_sections 1:dd20ae01c3cf 0.27 -0.08 opensource:maintainerofgraphcomp
cv-boxed_sections 1:33c35e9d236d 0.28 -0.08 speaking:jvmsummit2024,devoxxuk2
cv-centered_headline 0:8f1010120ed6 0.12 0.10 jordanrivera
cv-centered_headline 0:87ca614d0eab 0.09 0.41 professionaltitle
-cv-centered_headline 0:8d7a2cca3060 -0.29 -3.96 +442055551000|jordan.rivera@exam
+cv-centered_headline 0:8d7a2cca3060 -0.29 0.60 +442055551000|jordan.rivera@exam
cv-centered_headline 0:b944906ed84c -0.20 -0.04 professionalsummary
cv-centered_headline 0:f43d42692f27 -0.09 -0.04 platformengineerwith10+yearsbuil
cv-centered_headline 0:5f94c50dc5bf -0.26 -0.04 technicalskills
@@ -247,9 +247,9 @@ cv-charcoal_gold 0:b1ab1e892617 0.48 0.03 2022
cv-charcoal_gold 0:c2f5106fbd5a 0.38 0.03 topperformer
cv-charcoal_gold 0:058521fbf0d1 0.44 0.03 innovatechsystems
cv-charcoal_gold 0:3def82282260 0.40 -0.72 technicaltools
-cv-charcoal_gold 0:eb04bd42c7fb 0.46 -3.55 microsoft365|jira|confluence|asa
+cv-charcoal_gold 0:eb04bd42c7fb 0.46 -1.03 microsoft365|jira|confluence|asa
cv-classic_serif 0:8f1010120ed6 0.19 0.09 jordanrivera
-cv-classic_serif 0:8d7a2cca3060 0.27 4.03 +442055551000|jordan.rivera@exam
+cv-classic_serif 0:8d7a2cca3060 0.27 -0.53 +442055551000|jordan.rivera@exam
cv-classic_serif 0:3c69d6bb4892 0.28 1.37 professionalprofile
cv-classic_serif 0:32c1cf49a2fe 0.19 0.04 skills
cv-classic_serif 0:873a24d153c8 0.13 0.04 languages:java21,kotlin,groovy,p
@@ -260,7 +260,7 @@ cv-classic_serif 0:181ddda7f959 -0.06 0.04 testing:junit5,assertj,pdfbox-ba
cv-classic_serif 0:c40c656d9558 -0.02 0.04 distribution:mavencentral,sonaty
cv-classic_serif 0:53e5e7c5a884 0.04 0.04 experience
cv-classic_serif 0:a76f47480818 0.13 0.04 seniorplatformengineer
-cv-classic_serif 0:060300b18051 0.05 1.27 2024-present
+cv-classic_serif 0:060300b18051 0.05 0.07 2024-present
cv-classic_serif 0:f890faef907f 0.06 0.04 northwindsystems
cv-classic_serif 0:49faf14f1f56 -0.25 0.04 ledthereusabledocument-generatio
cv-classic_serif 0:6bd3f168e89e -0.18 0.04 maintenancetimeby70%byretiringpe
@@ -441,12 +441,12 @@ cv-editorial_blue 1:f20997652453 0.23 -0.04 languages:english(fluent),german
cv-editorial_blue 1:ec9d3622395e 0.18 -0.04 workeligibility:eligibletoworkin
cv-editorial_blue 1:dd20ae01c3cf 0.24 -0.04 opensource:maintainerofgraphcomp
cv-editorial_blue 1:33c35e9d236d 0.19 -0.04 speaking:jvmsummit2024,devoxxuk2
-cv-editorial_blue 1:0b516aa076e0 0.17 -0.76 referencesavailableuponrequest.
+cv-editorial_blue 1:0b516aa076e0 0.17 -0.04 referencesavailableuponrequest.
cv-engineering_resume 0:8f1010120ed6 -0.02 0.04 jordanrivera
cv-engineering_resume 0:2d58f0cadc2d -0.18 0.04 platformengineer
-cv-engineering_resume 0:fbeea547d99e -0.15 1.11 london,uk
-cv-engineering_resume 0:89674e6f130e -0.11 1.72 +442055551000
-cv-engineering_resume 0:e0f850305ded -0.07 2.57 jordan.rivera@example.com
+cv-engineering_resume 0:fbeea547d99e -0.15 0.15 london,uk
+cv-engineering_resume 0:89674e6f130e -0.11 0.16 +442055551000
+cv-engineering_resume 0:e0f850305ded -0.07 0.05 jordan.rivera@example.com
cv-engineering_resume 0:84308bea4540 -0.15 0.92 linkedin
cv-engineering_resume 0:c0b0109d9439 -0.11 0.79 github
cv-engineering_resume 0:32c1cf49a2fe -0.54 0.00 skills
@@ -683,7 +683,7 @@ cv-minimal_underlined 0:fb1927f88597 -1.00 0.00 •chromeforge(java,graphcompose
cv-minimal_underlined 0:e7e62230eb1f -0.92 0.02 editorial-magazinedocumenttoolki
cv-minimal_underlined 0:c2f296ef30a3 -0.94 0.04 professionalexperience
cv-minimal_underlined 0:a76f47480818 -1.17 0.00 seniorplatformengineer
-cv-minimal_underlined 0:060300b18051 -1.23 -1.21 2024-present
+cv-minimal_underlined 0:060300b18051 -1.23 -0.01 2024-present
cv-minimal_underlined 0:f890faef907f -1.26 0.00 northwindsystems
cv-minimal_underlined 1:49faf14f1f56 -0.10 0.00 ledthereusabledocument-generatio
cv-minimal_underlined 1:6bd3f168e89e -0.05 0.00 maintenancetimeby70%byretiringpe
@@ -822,17 +822,17 @@ cv-modern_professional 1:dd20ae01c3cf 0.32 0.00 opensource:maintainerofgraphcomp
cv-modern_professional 1:33c35e9d236d 0.35 0.00 speaking:jvmsummit2024,devoxxuk2
cv-monogram_sidebar 0:093e7d5fdbaa -0.24 -0.04 contact
cv-monogram_sidebar 0:89674e6f130e -0.09 -0.38 +442055551000
-cv-monogram_sidebar 0:e0f850305ded 0.03 -0.61 jordan.rivera@example.com
+cv-monogram_sidebar 0:e0f850305ded 0.03 -0.01 jordan.rivera@example.com
cv-monogram_sidebar 0:fbeea547d99e 0.03 -0.21 london,uk
cv-monogram_sidebar 0:84308bea4540 0.03 -0.22 linkedin
cv-monogram_sidebar 0:c0b0109d9439 0.05 -0.15 github
cv-monogram_sidebar 0:68bf0109c6f8 0.09 -0.03 education
-cv-monogram_sidebar 0:0ad1957d6bab 0.17 0.59 msccomputerscience
-cv-monogram_sidebar 0:47ce0218bbea 0.17 -0.56 universityofmanchester
-cv-monogram_sidebar 0:3fa772117260 0.28 0.49 2019-2021
-cv-monogram_sidebar 0:b6749d0b616b 0.17 0.71 bscsoftwareengineering
-cv-monogram_sidebar 0:89ad9656f393 0.20 -0.50 imperialcollegelondon
-cv-monogram_sidebar 0:30f9262640a0 0.31 0.49 2015-2019
+cv-monogram_sidebar 0:0ad1957d6bab 0.17 0.11 msccomputerscience
+cv-monogram_sidebar 0:47ce0218bbea 0.17 0.04 universityofmanchester
+cv-monogram_sidebar 0:3fa772117260 0.28 0.01 2019-2021
+cv-monogram_sidebar 0:b6749d0b616b 0.17 0.11 bscsoftwareengineering
+cv-monogram_sidebar 0:89ad9656f393 0.20 -0.02 imperialcollegelondon
+cv-monogram_sidebar 0:30f9262640a0 0.31 0.01 2015-2019
cv-monogram_sidebar 0:32c1cf49a2fe 0.16 -0.03 skills
cv-monogram_sidebar 0:d5680c2e2769 0.28 -0.13 java21
cv-monogram_sidebar 0:19a67c2a5369 0.28 -0.17 kotlin
@@ -840,7 +840,7 @@ cv-monogram_sidebar 0:95bfacd3a466 0.28 -0.21 groovy
cv-monogram_sidebar 0:11a4a60b518b 0.39 -0.11 python
cv-monogram_sidebar 0:4ec7c53222c8 0.39 -0.12 sql
cv-monogram_sidebar 0:57529f20c3ad 0.39 -0.13 pdfbox
-cv-monogram_sidebar 0:1814aacb6d5a 0.42 -0.64 apachepoi(docx/xlsx)
+cv-monogram_sidebar 0:1814aacb6d5a 0.42 -0.04 apachepoi(docx/xlsx)
cv-monogram_sidebar 0:136c67657614 0.24 0.06 jordan
cv-monogram_sidebar 0:8ba35ebe3d5c 0.19 0.12 rivera
cv-monogram_sidebar 0:2d58f0cadc2d -0.19 -0.69 platformengineer
@@ -943,7 +943,7 @@ cv-nordic_clean 0:8f1010120ed6 -0.16 0.00 jordanrivera
cv-nordic_clean 0:2d58f0cadc2d -0.20 0.00 platformengineer
cv-nordic_clean 0:fbeea547d99e -0.22 -0.33 london,uk
cv-nordic_clean 0:89674e6f130e -0.16 -0.66 +442055551000
-cv-nordic_clean 0:e0f850305ded -0.22 -1.08 jordan.rivera@example.com
+cv-nordic_clean 0:e0f850305ded -0.22 0.24 jordan.rivera@example.com
cv-nordic_clean 0:84308bea4540 -0.16 -0.22 linkedin
cv-nordic_clean 0:c0b0109d9439 -0.10 -0.21 github
cv-nordic_clean 0:1900eab6c028 -0.14 -1.48 profile
@@ -1105,8 +1105,8 @@ cv-orange_ops 0:724cc536a24c 0.96 -0.02 drivinglicence:fullukdrivinglice
cv-orange_ops 0:9f0e43069d3f 0.98 -0.02 interests:strengthtraining,motor
cv-panel 0:8f1010120ed6 0.42 0.25 jordanrivera
cv-panel 0:2d58f0cadc2d 0.20 -0.05 platformengineer
-cv-panel 0:89ff05847e96 0.20 -0.42 london,uk|+442055551000
-cv-panel 0:8817bf9310b6 0.16 -0.72 jordan.rivera@example.com|linked
+cv-panel 0:89ff05847e96 0.20 0.30 london,uk|+442055551000
+cv-panel 0:8817bf9310b6 0.16 0.36 jordan.rivera@example.com|linked
cv-panel 0:1900eab6c028 0.50 -0.20 profile
cv-panel 0:80cc87fdc4c4 -0.26 -0.20 platformengineerwith10+yearsbuil
cv-panel 0:c323ef4c385f -0.26 -0.20 systems.specialisedinhigh-throug
@@ -1582,7 +1582,7 @@ cv-terracotta_rail 0:bf4ab196cbb6 0.11 0.03 strongfocusonsustainability,qual
cv-terracotta_rail 0:0dfcf4444281 0.01 0.03 collaborativecommunicatorskilled
cv-terracotta_rail 0:22fdcc5cfe61 0.03 0.03 leadingmultidisciplinaryteamstoa
cv-terracotta_rail 0:c2f296ef30a3 -0.30 0.03 professionalexperience
-cv-terracotta_rail 0:086c0e036e67 -0.33 -1.24 2021–present
+cv-terracotta_rail 0:086c0e036e67 -0.33 -0.05 2021–present
cv-terracotta_rail 0:b3af3b56129a -0.23 0.11 northlinestudio,bristol,uk
cv-terracotta_rail 0:27819bba9c0f -0.45 0.11 •leaddesignpackagesfromconceptth
cv-terracotta_rail 0:d49fddeb9701 -0.49 0.06 andmixed-useprojects.
@@ -1594,7 +1594,7 @@ cv-terracotta_rail 0:894e988cdf94#1 -1.00 0.06 documentation.
cv-terracotta_rail 0:40c00971356d -0.63 0.11 •presentandcommunicatedesignprop
cv-terracotta_rail 0:a218287e182e -0.70 0.06 anddrivingprojectsforward.
cv-terracotta_rail 0:f2e40fc1edb7 -0.44 0.11 architect
-cv-terracotta_rail 0:f7d1049ec64b -0.44 -0.99 2018–2021
+cv-terracotta_rail 0:f7d1049ec64b -0.44 -0.15 2018–2021
cv-terracotta_rail 0:59e0b0e4edc9 -0.46 0.11 urbanformpartners,bath,uk
cv-terracotta_rail 0:4f2dbefcbec0 -0.68 0.11 •developedconceptandtechnicaldes
cv-terracotta_rail 0:e2a5e1a23401 -0.75 0.06 educationprojects.
@@ -1605,7 +1605,7 @@ cv-terracotta_rail 0:6b3142466e26 -0.82 0.11 •coordinatedwithcontractorsandco
cv-terracotta_rail 0:1150d4ba642c -0.87 0.06 intent.
cv-terracotta_rail 0:1150d4ba642c#1 -0.93 0.06 intent.
cv-terracotta_rail 0:2d2cd24f87d7 -0.62 0.11 partiiarchitecturalassistant
-cv-terracotta_rail 0:3b8048983ecc -0.62 -0.99 2016–2018
+cv-terracotta_rail 0:3b8048983ecc -0.62 -0.15 2016–2018
cv-terracotta_rail 0:ebd31e5e7505 -0.52 0.11 axisdesignworkshop,cardiff,uk
cv-terracotta_rail 0:e48e46c3ddf3 -0.62 0.11 •supportedseniorarchitectsacross
cv-terracotta_rail 0:810bbb784193 -0.86 0.11 •producedcadandrevitdrawings,mod
@@ -1632,10 +1632,10 @@ cv-terracotta_rail 0:208ed62d3df6 -0.70 0.04 environmentalstandardsandstrongs
cv-terracotta_rail 0:ac5e63220124 -0.98 0.04 performance.
cv-terracotta_rail 0:68bf0109c6f8 -0.81 0.03 education
cv-terracotta_rail 0:1d1dcf789873 -0.91 0.11 marcharchitecture
-cv-terracotta_rail 0:00583a6bed38 -0.91 -0.96 2014–2016
+cv-terracotta_rail 0:00583a6bed38 -0.91 -0.12 2014–2016
cv-terracotta_rail 0:5761434c99d8 -0.81 0.11 universityofsheffield
cv-terracotta_rail 0:e3f2f6a4e375 -0.92 0.11 ba(hons)architecture
-cv-terracotta_rail 0:2920a7fb42fc -0.92 -0.96 2011–2014
+cv-terracotta_rail 0:2920a7fb42fc -0.92 -0.12 2011–2014
cv-terracotta_rail 0:af625dd0aa20 -0.82 0.11 universityofthewestofengland
cv-timeline_minimal 0:8f1010120ed6 -0.20 -0.04 jordanrivera
cv-timeline_minimal 0:2d58f0cadc2d -0.46 -0.04 platformengineer
@@ -1738,20 +1738,20 @@ cv-violet_grid 0:b96067918254 -0.23 -0.52 uxresearch
cv-violet_grid 0:9058674fd905 -0.28 -0.84 userinterviews,surveys,
cv-violet_grid 0:0cdd38014d79 -0.36 -0.71 personas,journeymaps,
cv-violet_grid 0:61e4ba28d469 -0.28 -0.67 competitiveanalysis
-cv-violet_grid 0:3de6be58cb9f -0.23 -1.12 informationarchitecture
+cv-violet_grid 0:3de6be58cb9f -0.23 0.08 informationarchitecture
cv-violet_grid 0:db5c6756c4b6 -0.28 -0.92 sitemaps,userflows,card
cv-violet_grid 0:6c7ad1834a60 -0.36 -0.90 sorting,contentstrategy
-cv-violet_grid 0:187737d59250 -0.23 -0.58 wireframing
+cv-violet_grid 0:187737d59250 -0.23 -0.10 wireframing
cv-violet_grid 0:2286e5aa328a -0.28 -0.62 lowtohigh-fidelity
-cv-violet_grid 0:5c4b21cb1af8 -0.23 -0.47 prototyping
+cv-violet_grid 0:5c4b21cb1af8 -0.23 0.01 prototyping
cv-violet_grid 0:1abfa9dc1773 -0.28 -0.70 interactiveprototypes,
cv-violet_grid 0:1ddc6c379a54 -0.36 -0.47 microinteractions,
cv-violet_grid 0:d1d99c54cf40 -0.28 -0.29 transitions
-cv-violet_grid 0:7a38f8d94009 -0.23 -0.67 usabilitytesting
+cv-violet_grid 0:7a38f8d94009 -0.23 0.17 usabilitytesting
cv-violet_grid 0:63af69ae99e6 -0.28 -0.67 testplanning,usability
cv-violet_grid 0:f67c409cc44f -0.36 -0.75 tests,heuristicevaluation,
cv-violet_grid 0:438e1d3264dd -0.28 -0.56 insights&iteration
-cv-violet_grid 0:7e02941d76f5 -0.23 -0.62 designsystems
+cv-violet_grid 0:7e02941d76f5 -0.23 0.10 designsystems
cv-violet_grid 0:a0d27161913a -0.28 -0.60 componentlibraries,
cv-violet_grid 0:82374d275507 -0.36 -0.66 designtokens,pattern
cv-violet_grid 0:caf9f906b16a -0.28 -0.44 documentation
@@ -1760,8 +1760,8 @@ cv-violet_grid 0:5cb2b3b6143c -0.09 -0.02 figmafigjamadobexdsketchmironoti
cv-violet_grid 0:53e5e7c5a884 -0.05 -0.02 experience
cv-violet_grid 0:5a0774260e0c 0.00 -0.02 2022-present
cv-violet_grid 0:204f460767ff -0.05 -0.05 seniorux/uidesigner|novafin(fint
-cv-violet_grid 0:318bba211bdc 0.00 -1.86 sanfrancisco,ca
-cv-violet_grid 0:318bba211bdc#1 -0.04 -1.86 sanfrancisco,ca
+cv-violet_grid 0:318bba211bdc 0.00 -0.06 sanfrancisco,ca
+cv-violet_grid 0:318bba211bdc#1 -0.04 -0.06 sanfrancisco,ca
cv-violet_grid 0:3883e88c63c3 0.03 0.05 ledend-to-enddesignforapersonalf
cv-violet_grid 0:ccc00663f089 -0.02 0.05 conducteduserresearch,builtperso
cv-violet_grid 0:7d0f908c7e5f -0.07 0.05 designedwireframes,prototypes,an
@@ -1775,7 +1775,7 @@ cv-violet_grid 0:ab80a52d70fe -0.11 0.05 partneredwithengineerstoensurefe
cv-violet_grid 0:11cbf615189b -0.15 0.05 createdreusablecomponentsandstyl
cv-violet_grid 0:7e5081a3ed48 -0.17 -0.02 2018-2020
cv-violet_grid 0:73f7bbf7cd60 -0.22 -0.05 juniorux/uidesigner|lumendigital
-cv-violet_grid 0:c6b06045332f -0.05 -1.02 austin,tx
+cv-violet_grid 0:c6b06045332f -0.05 -0.06 austin,tx
cv-violet_grid 0:5ea8234925c5 -0.14 0.05 supporteddiscoveryworkshops,user
cv-violet_grid 0:46f00c76528c -0.19 0.05 designedresponsivewebsitesandlan
cv-violet_grid 0:f3cea36a9d83 -0.12 0.05 collaboratedwithcopywritersandde
@@ -2687,12 +2687,12 @@ invoice-merchant 0:e2a9c81da5f6 0.13 -0.17 unitprice(usd)
invoice-merchant 0:5dfb966b2bcf 0.13 -0.07 amount(usd)
invoice-merchant 0:fe6b34582738 0.16 0.30 coastlineadvanced
invoice-merchant 0:46f3b0cea3bc 0.05 0.30 monthlysubscription
-invoice-merchant 0:71738eb7126d 0.06 -0.46 advancedplan
+invoice-merchant 0:71738eb7126d 0.06 0.26 advancedplan
invoice-merchant 0:7b969356782c 0.06 0.00 399.00
invoice-merchant 0:7b969356782c#1 0.06 0.00 399.00
invoice-merchant 0:0a9acaea6477 0.19 0.30 paymentprocessing
invoice-merchant 0:91809142f08f 0.08 0.30 transactionfee
-invoice-merchant 0:358a7392afe6 0.09 -0.29 asperusage
+invoice-merchant 0:358a7392afe6 0.09 0.31 asperusage
invoice-merchant 0:561b2814d3c0 0.09 0.06 0.00
invoice-merchant 0:561b2814d3c0#1 0.09 0.05 0.00
invoice-merchant 0:561b2814d3c0#2 0.00 0.06 0.00
@@ -2781,28 +2781,28 @@ invoice-metered 0:d1062d072acd 0.16 0.53 unitprice
invoice-metered 0:cf38d95c9c6b 0.16 0.51 amount
invoice-metered 0:f13d9c7c6f89 0.15 0.51 computeinstances
invoice-metered 0:d08426b31521 0.13 0.51 on-demandusage
-invoice-metered 0:05313a23f706 0.21 1.28 london(eu-west)
-invoice-metered 0:05313a23f706#1 0.18 1.28 london(eu-west)
-invoice-metered 0:05313a23f706#2 0.14 1.28 london(eu-west)
+invoice-metered 0:05313a23f706 0.21 0.56 london(eu-west)
+invoice-metered 0:05313a23f706#1 0.18 0.56 london(eu-west)
+invoice-metered 0:05313a23f706#2 0.14 0.56 london(eu-west)
invoice-metered 0:6e477a22f840 0.21 0.93 744hrs
invoice-metered 0:6e477a22f840#1 0.14 0.93 744hrs
-invoice-metered 0:352967434b63 0.21 1.03 gbp0.0710
+invoice-metered 0:352967434b63 0.21 0.55 gbp0.0710
invoice-metered 0:eec5f520909e 0.21 1.01 gbp52.82
invoice-metered 0:721ffa554e56 0.13 0.51 objectstorage
invoice-metered 0:df2f05798b9e 0.10 0.51 storedvolume
invoice-metered 0:41542149a8a5 0.18 0.90 480gb
-invoice-metered 0:2242bd7aa8ce 0.18 1.03 gbp0.0215
+invoice-metered 0:2242bd7aa8ce 0.18 0.55 gbp0.0215
invoice-metered 0:7c6be041f9c2 0.18 1.01 gbp10.32
invoice-metered 0:8152fd217e12 0.08 0.51 manageddatabase
invoice-metered 0:3c8556c4aa26 0.06 0.51 primaryandreplica
-invoice-metered 0:76afc71f5b3e 0.14 1.03 gbp0.1650
-invoice-metered 0:baad0db43ed6 0.14 1.11 gbp122.76
+invoice-metered 0:76afc71f5b3e 0.14 0.55 gbp0.1650
+invoice-metered 0:baad0db43ed6 0.14 0.63 gbp122.76
invoice-metered 0:444858f5bf18 0.04 0.51 egresstraffic
invoice-metered 0:8b72bb875a83 0.02 0.51 datatransferout
invoice-metered 0:8001c2743965 0.09 0.83 global
invoice-metered 0:8001c2743965#1 0.17 0.83 global
invoice-metered 0:847eb4cbbb42 0.09 0.90 310gb
-invoice-metered 0:d651e46704f7 0.09 1.03 gbp0.0900
+invoice-metered 0:d651e46704f7 0.09 0.55 gbp0.0900
invoice-metered 0:4ecaa7313375 0.09 1.01 gbp27.90
invoice-metered 0:efa8771baad9 0.12 0.51 supportplan
invoice-metered 0:9e81cd6e653a 0.09 0.51 standardresponse
@@ -2826,7 +2826,7 @@ invoice-metered 0:79fabcfaa819 0.12 0.71 gbp214.71
invoice-metered 0:a52611c9261b 0.09 0.07 vat(20%)
invoice-metered 0:e50d8a0ea599 0.09 0.65 gbp42.94
invoice-metered 0:57a66d21eb1b -0.11 0.07 totaldue
-invoice-metered 0:f207312dff2f -0.19 -0.99 gbp257.65
+invoice-metered 0:f207312dff2f -0.19 0.09 gbp257.65
invoice-metered 0:b0553f008513 -0.02 0.12 paymentdueby
invoice-metered 0:e60e7ef06ccc -0.79 0.00 thankyouforrunningonnorthwindclo
invoice-metered 0:34ea2ec3fe72 -0.82 0.00 forbillingandaccountsupport,visi
@@ -3055,9 +3055,9 @@ invoice-obsidian 0:5d1d1b79af09 -0.01 0.11 invoicenumber
invoice-obsidian 0:3e62d1fbe5ec 0.06 0.11 inv-2026-1048
invoice-obsidian 0:3e62d1fbe5ec#1 -0.36 0.06 inv-2026-1048
invoice-obsidian 0:8feaf8016bab 0.08 0.11 issuedate
-invoice-obsidian 0:4d49cb78b810 0.08 1.38 27august2026
+invoice-obsidian 0:4d49cb78b810 0.08 -0.06 27august2026
invoice-obsidian 0:ac10f8c16320 -0.03 0.11 duedate
-invoice-obsidian 0:b55571aaabac -0.03 1.73 10september2026
+invoice-obsidian 0:b55571aaabac -0.03 -0.07 10september2026
invoice-obsidian 0:fb854bac39c8 -0.04 0.11 paymentterms
invoice-obsidian 0:56a2e019b8aa -0.04 0.74 14days
invoice-obsidian 0:466e5b9cd57f -0.17 0.11 currency
@@ -3220,7 +3220,7 @@ invoice-payments 0:48617b6d05d4 -0.10 0.05 gb29hrbk40025071295408
invoice-payments 0:6929e6db6e1f -0.19 0.05 hrbkgb2l
invoice-payments 0:e4c4788968b2 0.18 0.03 subtotal(excl.vat)
invoice-payments 0:7ed180496d4e 0.25 0.03 vatat20%
-invoice-payments 0:751cbab81a3a 0.25 1.18 £2,186.00
+invoice-payments 0:751cbab81a3a 0.25 0.22 £2,186.00
invoice-payments 0:9a5256946abd 0.32 0.93 £437.20
invoice-payments 0:57a66d21eb1b 0.17 0.03 totaldue
invoice-payments 0:5eb0b17220fa 0.15 0.03 dueby14june2025
@@ -3239,7 +3239,7 @@ invoice-platform 0:9817f28442bf#1 -0.46 0.00 auroracloud
invoice-platform 0:52d6e3de4fa0 0.18 -0.24 invoice
invoice-platform 0:1cf61cdc6464 -0.14 -0.03 auroracloudinc.
invoice-platform 0:1cf61cdc6464#1 0.02 0.71 auroracloudinc.
-invoice-platform 0:1cf61cdc6464#2 -0.43 -1.37 auroracloudinc.
+invoice-platform 0:1cf61cdc6464#2 -0.43 0.31 auroracloudinc.
invoice-platform 0:43315277b007 -0.38 -0.03 500harbourstreet
invoice-platform 0:d9065bfb8f59 -0.33 -0.03 vancouver,bcv6c2w2
invoice-platform 0:408c7c5887a0 -0.41 -0.03 canada
@@ -3343,7 +3343,7 @@ invoice-platform 0:17e16a2f994d -0.01 0.67 cad195.76
invoice-platform 0:ac10f8c16320 -0.10 0.11 duedate
invoice-platform 0:d5ef7c1f9830 -0.36 -0.02 thankyouforrunningonauroracloud.
invoice-platform 0:6a88f440b84e -0.42 -0.02 manageyouraccountataurora.exampl
-invoice-platform 0:cf0a1e1dc87b -0.47 -4.43 500harbourstreet,vancouver,bcv6c
+invoice-platform 0:cf0a1e1dc87b -0.47 0.37 500harbourstreet,vancouver,bcv6c
invoice-platform 0:7b14b8d2c5c4 0.11 0.38 page1of1
invoice-subscription 0:435df6fdbffd 0.18 0.04 halstead
invoice-subscription 0:52d6e3de4fa0 0.16 -0.43 invoice
@@ -3358,7 +3358,7 @@ invoice-subscription 0:78acb3cb907b 0.32 0.04 www.halstead.example
invoice-subscription 0:f1c030eb4329 0.43 0.04 vatnogb418227603
invoice-subscription 0:77ead61b5875 0.37 0.06 invoiceno
invoice-subscription 0:0b0c0ee24de8 0.34 0.68 hs-inv-2026-0417
-invoice-subscription 0:0b0c0ee24de8#1 0.08 1.84 hs-inv-2026-0417
+invoice-subscription 0:0b0c0ee24de8#1 0.08 1.48 hs-inv-2026-0417
invoice-subscription 0:8feaf8016bab 0.32 0.06 issuedate
invoice-subscription 0:4d49cb78b810 0.41 0.60 27august2026
invoice-subscription 0:ac10f8c16320 0.39 0.06 duedate
@@ -3413,9 +3413,9 @@ invoice-subscription 0:57a66d21eb1b -0.10 0.12 totaldue
invoice-subscription 0:cf1b68cc49ef -0.01 0.80 £1,136.40
invoice-subscription 0:3c7ade679fdf 1.33 0.04 paymentdetails
invoice-subscription 0:56ed021beb35 0.14 0.19 beneficiary
-invoice-subscription 0:0b85f3c8ed04 0.08 0.68 halsteadsoftwareltd
+invoice-subscription 0:0b85f3c8ed04 0.08 0.20 halsteadsoftwareltd
invoice-subscription 0:4381dc2ab142 0.14 0.77 bank
-invoice-subscription 0:6d50b2dd724f 0.08 1.30 northgatebank
+invoice-subscription 0:6d50b2dd724f 0.08 0.94 northgatebank
invoice-subscription 0:10aeacde20ff 0.14 1.06 sortcode
invoice-subscription 0:b19b1415ef9b 0.08 1.16 40-05-30
invoice-subscription 0:f92a9c925024 0.14 1.23 accountnumber
@@ -3462,10 +3462,10 @@ invoice-workspace 0:725a8f5062e7 -0.12 0.14 shipto
invoice-workspace 0:6d835e737af6 -0.14 0.02 unit4,riversidepark
invoice-workspace 0:555d5194cd4d -0.10 0.02 leedsls14ap
invoice-workspace 0:c9046f7a37ad 0.04 0.43 description
-invoice-workspace 0:169240d91658 0.04 -0.39 plan/service
+invoice-workspace 0:169240d91658 0.04 0.21 plan/service
invoice-workspace 0:32e8369a9c4f 0.04 0.14 qty
-invoice-workspace 0:e2a9c81da5f6 0.04 -0.60 unitprice(usd)
-invoice-workspace 0:5dfb966b2bcf 0.04 -0.52 amount(usd)
+invoice-workspace 0:e2a9c81da5f6 0.04 0.24 unitprice(usd)
+invoice-workspace 0:5dfb966b2bcf 0.04 0.44 amount(usd)
invoice-workspace 0:2bcc555090a1 0.01 0.43 kestrelbusiness+
invoice-workspace 0:68fad867a983 0.06 0.43 workspacesubscription
invoice-workspace 0:e0b844887562 0.06 0.48 business+annual
@@ -3514,7 +3514,7 @@ invoice-workspace 0:b0553f008513 -0.43 0.15 paymentdueby
invoice-workspace 0:08775a92bfca 0.64 -0.01 thankyouforusingkestrel.
invoice-workspace 0:18e82fbd48a3 0.77 -0.01 questionsaboutthisinvoice?reachu
invoice-workspace 0:3c72ba0e1eb8 0.76 0.06 500howardstreet,6thfloor,sanfran
-invoice-workspace 0:9e2917e23527 0.65 -1.54 kestrel.example
+invoice-workspace 0:9e2917e23527 0.65 -0.10 kestrel.example
invoice-workspace 0:7b14b8d2c5c4 0.08 -0.16 page1of1
letter-blue_banner 0:8f1010120ed6 0.50 0.04 jordanrivera
letter-blue_banner 0:8f1010120ed6#1 0.08 0.00 jordanrivera
@@ -3539,7 +3539,7 @@ letter-boxed_sections 0:80a6a628ad52 -0.40 0.00 sincerely,
letter-centered_headline 0:8f1010120ed6 0.16 0.10 jordanrivera
letter-centered_headline 0:8f1010120ed6#1 0.00 0.00 jordanrivera
letter-centered_headline 0:2d58f0cadc2d 0.15 0.49 platformengineer
-letter-centered_headline 0:8d7a2cca3060 -0.25 -3.96 +442055551000|jordan.rivera@exam
+letter-centered_headline 0:8d7a2cca3060 -0.25 0.72 +442055551000|jordan.rivera@exam
letter-centered_headline 0:bef81b541365 -0.19 0.00 dearhiringteamatnorthwindsystems
letter-centered_headline 0:ab54410ead82 -0.02 0.00 iamexcitedtosharemyinterestinthe
letter-centered_headline 0:9185168d2cf8 -0.03 0.00 document-generationsystemsthatba
@@ -3549,7 +3549,7 @@ letter-centered_headline 0:a71e98216baa -0.07 0.00 iwouldwelcometheopportunityto
letter-centered_headline 0:80a6a628ad52 0.00 0.00 sincerely,
letter-classic_serif 0:8f1010120ed6 0.15 0.09 jordanrivera
letter-classic_serif 0:8f1010120ed6#1 0.03 0.00 jordanrivera
-letter-classic_serif 0:8d7a2cca3060 0.25 4.03 +442055551000|jordan.rivera@exam
+letter-classic_serif 0:8d7a2cca3060 0.25 -0.65 +442055551000|jordan.rivera@exam
letter-classic_serif 0:bef81b541365 0.17 0.00 dearhiringteamatnorthwindsystems
letter-classic_serif 0:ab54410ead82 -0.09 0.00 iamexcitedtosharemyinterestinthe
letter-classic_serif 0:9185168d2cf8 -0.16 0.00 document-generationsystemsthatba
@@ -3580,9 +3580,9 @@ letter-editorial_blue 0:80a6a628ad52 0.19 0.00 sincerely,
letter-engineering_resume 0:8f1010120ed6 -0.04 0.02 jordanrivera
letter-engineering_resume 0:8f1010120ed6#1 -1.36 0.00 jordanrivera
letter-engineering_resume 0:2d58f0cadc2d -0.20 0.02 platformengineer
-letter-engineering_resume 0:fbeea547d99e -0.19 1.15 london,uk
-letter-engineering_resume 0:89674e6f130e -0.15 1.76 +442055551000
-letter-engineering_resume 0:e0f850305ded -0.09 2.61 jordan.rivera@example.com
+letter-engineering_resume 0:fbeea547d99e -0.19 0.19 london,uk
+letter-engineering_resume 0:89674e6f130e -0.15 0.20 +442055551000
+letter-engineering_resume 0:e0f850305ded -0.09 0.09 jordan.rivera@example.com
letter-engineering_resume 0:84308bea4540 -0.05 0.96 linkedin
letter-engineering_resume 0:c0b0109d9439 -0.13 0.83 github
letter-engineering_resume 0:bef81b541365 -1.39 0.00 dearhiringteamatnorthwindsystems
@@ -3629,7 +3629,7 @@ letter-modern_professional 0:80a6a628ad52 -0.07 0.00 sincerely,
letter-monogram_sidebar 0:136c67657614 0.18 0.06 jordan
letter-monogram_sidebar 0:8ba35ebe3d5c 0.22 0.12 rivera
letter-monogram_sidebar 0:2d58f0cadc2d -0.20 -0.69 platformengineer
-letter-monogram_sidebar 0:8d7a2cca3060 -0.08 -1.93 +442055551000|jordan.rivera@exam
+letter-monogram_sidebar 0:8d7a2cca3060 -0.08 0.35 +442055551000|jordan.rivera@exam
letter-monogram_sidebar 0:bef81b541365 -0.10 0.00 dearhiringteamatnorthwindsystems
letter-monogram_sidebar 0:ab54410ead82 0.05 0.00 iamexcitedtosharemyinterestinthe
letter-monogram_sidebar 0:9185168d2cf8 0.02 0.00 document-generationsystemsthatba
@@ -3643,7 +3643,7 @@ letter-nordic_clean 0:8f1010120ed6#1 -0.15 0.00 jordanrivera
letter-nordic_clean 0:2d58f0cadc2d -0.18 0.00 platformengineer
letter-nordic_clean 0:fbeea547d99e -0.22 -0.33 london,uk
letter-nordic_clean 0:89674e6f130e -0.16 -0.66 +442055551000
-letter-nordic_clean 0:e0f850305ded -0.22 -1.08 jordan.rivera@example.com
+letter-nordic_clean 0:e0f850305ded -0.22 0.24 jordan.rivera@example.com
letter-nordic_clean 0:84308bea4540 -0.14 -0.22 linkedin
letter-nordic_clean 0:c0b0109d9439 -0.08 -0.21 github
letter-nordic_clean 0:bef81b541365 -0.10 0.00 dearhiringteamatnorthwindsystems
@@ -3656,8 +3656,8 @@ letter-nordic_clean 0:80a6a628ad52 -0.11 0.00 sincerely,
letter-panel 0:8f1010120ed6 0.44 0.25 jordanrivera
letter-panel 0:8f1010120ed6#1 0.21 0.00 jordanrivera
letter-panel 0:2d58f0cadc2d 0.22 -0.05 platformengineer
-letter-panel 0:89ff05847e96 0.20 -0.42 london,uk|+442055551000
-letter-panel 0:8817bf9310b6 0.16 -0.72 jordan.rivera@example.com|linked
+letter-panel 0:89ff05847e96 0.20 0.30 london,uk|+442055551000
+letter-panel 0:8817bf9310b6 0.16 0.36 jordan.rivera@example.com|linked
letter-panel 0:bef81b541365 0.06 0.00 dearhiringteamatnorthwindsystems
letter-panel 0:ab54410ead82 -0.17 0.00 iamexcitedtosharemyinterestinthe
letter-panel 0:9185168d2cf8 -0.17 0.00 document-generationsystemsthatba
@@ -3669,7 +3669,7 @@ letter-panel 0:80a6a628ad52 0.25 0.00 sincerely,
letter-sidebar_portrait 0:8f1010120ed6 0.04 -0.01 jordanrivera
letter-sidebar_portrait 0:8f1010120ed6#1 -0.11 0.00 jordanrivera
letter-sidebar_portrait 0:2d58f0cadc2d -0.19 -0.33 platformengineer
-letter-sidebar_portrait 0:8d7a2cca3060 -0.29 -3.96 +442055551000|jordan.rivera@exam
+letter-sidebar_portrait 0:8d7a2cca3060 -0.29 0.72 +442055551000|jordan.rivera@exam
letter-sidebar_portrait 0:bef81b541365 -0.29 0.00 dearhiringteamatnorthwindsystems
letter-sidebar_portrait 0:ab54410ead82 -0.52 0.00 iamexcitedtosharemyinterestinthe
letter-sidebar_portrait 0:9185168d2cf8 -0.55 0.00 document-generationsystemsthatba
@@ -4071,8 +4071,8 @@ proposal-northline 1:c74c9cd82f13 0.50 -0.27 date:
proposal-northline 1:026890c58de9 0.10 0.00 northlinestudio|northlinestudio.
receipt-modern 0:2b019b5b46da 0.29 -0.04 northwindpay
receipt-modern 0:a3c304a23bb1 0.23 0.00 transferconfirmation
-receipt-modern 0:e885a92f5bb7 0.17 -2.63 generatedon09august2026
-receipt-modern 0:c288dd6df716 0.11 -2.48 referencenwp-4821-0067
+receipt-modern 0:e885a92f5bb7 0.17 0.01 generatedon09august2026
+receipt-modern 0:c288dd6df716 0.11 -0.08 referencenwp-4821-0067
receipt-modern 0:9de2a2d7cd7a 0.21 -1.52 amountcollected
receipt-modern 0:465691680b99 0.17 -1.52 £66.62
receipt-modern 0:465691680b99#1 0.35 -0.92 £66.62
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 16c9209e8..8670808c5 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
@@ -5163,9 +5163,19 @@ private void measureAtWordsSize(XWPFParagraph para, ParagraphNode node, double r
}
}
+ /** How wide the page set a line: its spans', text, pictures and tracking, in points. */
+ private static double askedWidth(com.demcha.compose.document.layout.payloads.ParagraphLine line) {
+ double asked = 0;
+ for (com.demcha.compose.document.layout.payloads.ParagraphSpan span : line.spans()) {
+ asked += Math.max(0, span.width());
+ }
+ return asked;
+ }
+
/**
* The measure a paragraph of one line needs in Word, in points: its box, or its line as wide
- * as Word sets it and a few hundredths more ({@link #ONE_LINE_FACE_SLACK}), whichever is
+ * as Word sets it — or as the page does, where Word sets it narrower and its letters are
+ * spaced back to it — and a few hundredths more ({@link #ONE_LINE_FACE_SLACK}), whichever is
* wider.
*/
private double oneLinesMeasure(ParagraphNode node, double room) {
@@ -5176,7 +5186,10 @@ private double oneLinesMeasure(ParagraphNode node, double room) {
&& span instanceof com.demcha.compose.document.layout.payloads.ParagraphTextSpan run
&& run.textStyle() != null && run.textStyle().size() > 0);
if (text) {
- set = Math.max(set, widthAtWordsSize(line));
+ // As wide as the page's line at least: a centred or right-aligned line of its own
+ // that Word sets narrower has its letters spaced back to it
+ // (setTheLineAsWideAsThePage), and needs the room past it as much as any.
+ set = Math.max(set, Math.max(widthAtWordsSize(line), askedWidth(line)));
}
}
return Math.max(room, set * (1 + ONE_LINE_FACE_SLACK));
@@ -5783,11 +5796,74 @@ private void writeParagraph(XWPFDocument document, ParagraphNode node) {
// the page's line does (see DocxStackedLines).
DocxStackedLines.Line stacked = stackedLineHeights.get(node);
writeParagraphRuns(para, node, rightToLeft, stacked == null ? 0 : stacked.topAbove(), stacked == null);
+ if (!rightToLeft) {
+ setTheLineAsWideAsThePage(para, node);
+ }
closeAnchor(para, anchor);
lastWrittenNode = node;
lastWrittenParagraph = para;
}
+ /**
+ * Spaces a centred or right-aligned line's letters so Word sets it as wide as the page does.
+ *
+ * Word states a type size in half points, so it sets a size the page gives to the tenth a
+ * little larger or smaller, and a line that much wider or narrower. A line set from its left
+ * keeps its first letter where the page puts it; a centred line moves it by half the
+ * difference and a right-aligned one by all of it: {@code CenteredHeadline}'s 8.3pt contact
+ * line, set at 8.5, stood 10pt wider in Word, its first letter 4pt left of the page's, and
+ * {@code ClassicSerif}'s 8.7pt one 4pt right. The size is left as Word sets it; the
+ * difference is spread over the letters as character spacing ({@code w:spacing}, in
+ * twentieths of a point), so the line's ends stand within a fraction of a point of the
+ * page's. Only a line of its own: a paragraph that wraps breaks at the page's
+ * words through its measure ({@link #measureAtWordsSize}), and its lines' ends are the
+ * measure's. Under a point it is left alone: {@code ObsidianInvoice}'s 9.34pt amounts, set
+ * at 9.5 and half a point wider by the size alone, measured as wide as the page's in Word,
+ * and spacing them moved them half a point off.
+ */
+ private void setTheLineAsWideAsThePage(XWPFParagraph para, ParagraphNode node) {
+ if (node.align() != TextAlign.CENTER && node.align() != TextAlign.RIGHT || layout.lineCount(node) != 1) {
+ return;
+ }
+ List lines = layout.lines(node);
+ if (lines.size() != 1) {
+ return;
+ }
+ double difference = askedWidth(lines.get(0)) - widthAtWordsSize(lines.get(0));
+ int letters = 0;
+ // Every run, an internal link's included, which the paragraph's own list leaves out.
+ List runs = runsIn(para).stream().map(run -> new XWPFRun(run, para)).toList();
+ for (XWPFRun run : runs) {
+ String text = run.text();
+ letters += text == null ? 0 : text.codePointCount(0, text.length());
+ if (run.getCharacterSpacing() != 0) {
+ // Tracked already: Word rounds it its own way and spaces the last letter too,
+ // which the size alone does not tell — TealPulse's tracked tagline, spaced on it,
+ // stood a point further off than it was.
+ return;
+ }
+ }
+ if (letters == 0 || !(Math.abs(difference) >= LINE_WIDTH_CLEARANCE)) {
+ return;
+ }
+ // In twentieths of a point, to the nearest: measured on the corpus, the twentieth brought
+ // 75 lines in Word nearer the page and none further, whole tenths 64. A line widened back
+ // to the page's has the page's room and a few hundredths more (oneLinesMeasure).
+ int twentieths = (int) Math.round(difference / letters * 20.0);
+ if (twentieths == 0) {
+ return;
+ }
+ for (XWPFRun run : runs) {
+ String text = run.text();
+ if (text != null && !text.isEmpty()) {
+ run.setCharacterSpacing(run.getCharacterSpacing() + twentieths);
+ }
+ }
+ }
+
+ /** How far a line of its own may stand from the page's width before its letters are spaced, in points. */
+ private static final double LINE_WIDTH_CLEARANCE = 1.0;
+
/**
* Indents a paragraph's lines by the blank prefix the page sets them after.
*
diff --git a/render-docx/src/test/java/com/demcha/compose/document/backend/semantic/docx/DocxWordSizeMeasureTest.java b/render-docx/src/test/java/com/demcha/compose/document/backend/semantic/docx/DocxWordSizeMeasureTest.java
index 81536eae2..f43d83459 100644
--- a/render-docx/src/test/java/com/demcha/compose/document/backend/semantic/docx/DocxWordSizeMeasureTest.java
+++ b/render-docx/src/test/java/com/demcha/compose/document/backend/semantic/docx/DocxWordSizeMeasureTest.java
@@ -75,6 +75,49 @@ void aCentredLineIsLeftWhereThePageSetsIt() throws Exception {
assertThat(indentOf(7.8, TextAlign.CENTER, LONG)).isZero();
}
+ @Test
+ void aCentredOrRightAlignedLineOfItsOwnIsSpacedToThePagesWidth() throws Exception {
+ // CenteredHeadline's 8.3pt contact line, set at 8.5, stood 10pt wider in Word, its first
+ // letter 4pt left of the page's. The difference is spread over its letters.
+ // Word spaces letters in whole tenths, to the nearest: narrowed at 8.3pt (set at 8.5),
+ // widened at 6.2pt (set at 6).
+ int letters = SHORT.codePointCount(0, SHORT.length());
+ for (double size : new double[] {8.3, 6.2}) {
+ double asked = widthAlone(SHORT, size);
+ double wordSize = Math.round(size * 2) / 2.0;
+ int expected = (int) Math.round((asked - asked * wordSize / size) / letters * 20);
+ assertThat(expected).as("spaced at %spt", size).isNotZero();
+ for (TextAlign align : new TextAlign[] {TextAlign.CENTER, TextAlign.RIGHT}) {
+ try (XWPFDocument document = DocxExports.withLayout(400, 400, 20, page -> page
+ .addParagraph(p -> p.text(SHORT).textStyle(DocumentTextStyle.DEFAULT.withSize(size)).align(align)))) {
+ assertThat(text(document, "Platform engineer").getRuns())
+ .as("each run spaced the page's width less Word's, a letter at a time, %s at %spt", align, size)
+ .allSatisfy(run -> assertThat(run.getCharacterSpacing()).isEqualTo(expected));
+ }
+ }
+ }
+ }
+
+ @Test
+ void aLineSetFromItsLeftATrackedLineAndANearOneAreNotSpaced() throws Exception {
+ // From its left, the line's first letter is the page's. A tracked line Word sets in
+ // twentieths and spaces after its last letter too, which the size alone does not tell;
+ // a difference under a point is within what Word rounds its glyphs by.
+ DocumentTextStyle tracked = DocumentTextStyle.DEFAULT.withSize(8.3)
+ .withLetterSpacing(com.demcha.compose.document.style.DocumentLetterSpacing.points(1));
+ try (XWPFDocument left = DocxExports.withLayout(400, 400, 20, page -> page
+ .addParagraph(p -> p.text(SHORT).textStyle(DocumentTextStyle.DEFAULT.withSize(8.3)).align(TextAlign.LEFT)));
+ XWPFDocument spaced = DocxExports.withLayout(400, 400, 20, page -> page
+ .addParagraph(p -> p.text(SHORT).textStyle(tracked).align(TextAlign.CENTER)));
+ XWPFDocument near = DocxExports.withLayout(400, 400, 20, page -> page
+ .addParagraph(p -> p.text("Platform engineer").textStyle(DocumentTextStyle.DEFAULT.withSize(8.4))
+ .align(TextAlign.CENTER)))) {
+ assertThat(text(left, "Platform engineer").getRuns()).allSatisfy(run -> assertThat(run.getCharacterSpacing()).isZero());
+ assertThat(text(spaced, "Platform engineer").getRuns()).allSatisfy(run -> assertThat(run.getCharacterSpacing()).isEqualTo(20));
+ assertThat(text(near, "Platform engineer").getRuns()).allSatisfy(run -> assertThat(run.getCharacterSpacing()).isZero());
+ }
+ }
+
@Test
void aShortCentredLineIsLeftWhereThePageSetsIt() throws Exception {
// Its box has room for it as Word sets it, so neither edge moves.
@@ -88,6 +131,21 @@ void aShortCentredLineIsLeftWhereThePageSetsIt() throws Exception {
}
}
+ @Test
+ void aCentredLineWordSetsNarrowerIsGivenRoomForThePagesWidth() throws Exception {
+ // Its letters are spaced back to the page's width, which needs the few hundredths of
+ // room past it as much as a line Word sets wider: without them, 7.2pt set at 7 and spaced
+ // back to its box broke a word onto a second line.
+ double room = fillingRoom(HEADING, 7.2);
+ try (XWPFDocument document = filledBy(HEADING, 7.2, TextAlign.CENTER, room)) {
+ XWPFParagraph heading = text(document, "INFORMATION");
+ double measure = room - (leftIndent(heading) + rightIndent(heading)) / 20.0;
+
+ assertThat(measure).as("the page's line and a few hundredths more")
+ .isGreaterThan(widthAlone(HEADING, 7.2) * 1.02);
+ }
+ }
+
@Test
void aCentredLineThatFillsItsBoxIsGivenRoomAtBothEdgesAlike() throws Exception {
// VioletGrid's 6.8pt "INFORMATION ARCHITECTURE" fills its tile; set at 7pt it broke in two.
From daa34432ae514dca7f57667c49665362ed6189b8 Mon Sep 17 00:00:00 2001
From: DemchaAV
Date: Sun, 4 Oct 2026 14:42:45 +0100
Subject: [PATCH 2/2] fix(docx): reckon a spaced line's room from the width
Word ends up setting it at
twentiethsToThePage decides once, from the layout's line, whether and how
far a centred or right-aligned line's letters are spaced, and the room
oneLinesMeasure gives it follows: the page's width where it is spaced,
its own at Word's size where not. Reckoned from Word's wider width, a line
condensed back kept room it no longer needed, which in a cell comes off
the right and moved a right-aligned line right. A line with a picture or
a shape is left alone; the runs are wrapped through the non-deprecated
XWPFRun constructor. The Linux LibreOffice baseline is this branch's CI
measure.
---
CHANGELOG.md | 18 +--
.../architecture/backend-capability-matrix.md | 2 +-
docs/recipes/docx-export.md | 19 +--
.../docx-fidelity/libreoffice-linux-lines.tsv | 150 +++++++++---------
.../semantic/docx/DocxSemanticBackend.java | 104 ++++++------
.../docx/DocxWordSizeMeasureTest.java | 27 +++-
6 files changed, 176 insertions(+), 144 deletions(-)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 23321aeb6..8ed0fdb2e 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -10,16 +10,16 @@ follow semantic versioning; release dates are ISO 8601.
- **A centred or right-aligned DOCX line of its own stands as wide as the page sets it.**
Word states a type size in half points, and a line set at a size the page gives to the tenth
- is that much wider or narrower; a centred line moves its first letter by half the difference
- and a right-aligned one by all of it: `CenteredHeadline`'s 8.3pt contact line, set at 8.5,
- stood 10pt wider in Word, its first letter 4pt left of the page's, and `ClassicSerif`'s
- 8.7pt one 4pt right. Where the difference is a point or more, its letters are now spaced by
+ is that much wider or narrower, a centred or right-aligned one moving its first letter with
+ it: `CenteredHeadline`'s 8.3pt contact line, set at 8.5, stood 10pt wider in Word and its
+ first letter 4pt left of the page's, and `ClassicSerif`'s 8.7pt one 4pt right. Where a
+ left-to-right line of text alone differs by a point or more, its letters are now spaced by
it (`w:spacing`), the size left as Word sets it, so the line's ends stand within a fraction
- of a point of the page's, and the line is given room for the page's width as one Word sets
- wider is; a line already tracked, and a difference under a point, are left as they are. Lines
- more than 2pt off across fall from 23 to 12 in Word (74 lines nearer the page, one 0.02pt
- further) and from 29 to 18 in LibreOffice on Windows (70 nearer, five at most 0.11pt
- further); nothing moves down.
+ of a point of the page's, and its room is reckoned from the page's width; a line that is
+ tracked or carries a picture or a shape, and a difference under a point, are left as they
+ are, room included. Lines more than 2pt off across fall from 23 to 12 in Word (74 lines
+ nearer the page, one 0.02pt further) and from 29 to 18 in LibreOffice on Windows (70 nearer,
+ five at most 0.11pt further) and 310 to 301 on Linux; nothing moves down.
- **A nested list item with no marker stands where the page sets its text in DOCX.** In a
list of paragraphs — rich items — a nested item was written two spaces a level in, with no
indent: the project descriptions `Panel`, `Executive`, `ModernProfessional`,
diff --git a/docs/architecture/backend-capability-matrix.md b/docs/architecture/backend-capability-matrix.md
index edaa8bf4a..53deb938c 100644
--- a/docs/architecture/backend-capability-matrix.md
+++ b/docs/architecture/backend-capability-matrix.md
@@ -62,7 +62,7 @@ Payload records live in `core` under
| Capability (payload) | PDF (fixed) | PPTX (fixed) | DOCX (semantic) |
|---|---|---|---|
-| Paragraph — pre-wrapped lines, runs, alignment (`ParagraphFragmentPayload`) | ✅ `PdfParagraphFragmentRenderHandler` | ✅ `PptxParagraphFragmentRenderHandler` (one absolute, wrap-disabled frame per measured line) | ⚠️ semantic paragraphs (`DocxSemanticBackend`) — each run keeps its own style, falling back to the paragraph's when it has none; a centred or right-aligned line of its own that Word sets a point or more wider or narrower at its half-point size has its letters spaced by the difference (`w:spacing`), unless it is tracked already; a `linkTarget` becomes a `w:hyperlink`, with a relationship for an address or `w:anchor` for one of the document's own anchors, and a run's own link wins over the paragraph's; a paragraph seated off its baseline (`TextVerticalAlign`) has its runs raised or lowered in the line (`w:position`) by the PDF backend's own correction (`ParagraphSeating`), one shift for the paragraph where the page seats each line by its own; Word and LibreOffice stand an exact line's baseline four fifths of the way down it whatever the face, where the page sets it the face's ascent down, so a paragraph whose face puts the two half a point or more apart — Spectral's, not Lato's — has its text moved to the page's baseline in the same position, matched at its middle line (not yet a list item's or a table text cell's; a picture among it moves with it in Word and stays on its own baseline in LibreOffice); lines a container stacks over one another tighter than their face each end halfway between their letters and the next line's (Word draws an exact line's text on screen only inside the line; its PDF export does not cut it), and the last layer of a shape container on one page, where its line runs past the foot, ends at the foot or below its letters; letters two lines share are split halfway so the page does not move, and a stack that holds a picture keeps its lines' own heights; a `bulletOffset` of spaces becomes the paragraph's indent (`w:ind` left, hanging or first line, by `indentStrategy`) in the flow and in cells, not yet over the flow, in an overlay's left-and-right pair or in a header or footer; one with letters in it is not written, its wrapped lines still set after the spaces that cover it |
+| Paragraph — pre-wrapped lines, runs, alignment (`ParagraphFragmentPayload`) | ✅ `PdfParagraphFragmentRenderHandler` | ✅ `PptxParagraphFragmentRenderHandler` (one absolute, wrap-disabled frame per measured line) | ⚠️ semantic paragraphs (`DocxSemanticBackend`) — each run keeps its own style, falling back to the paragraph's when it has none; a centred or right-aligned left-to-right line of its own, of text alone and untracked, that Word sets a point or more wider or narrower at its half-point size has its letters spaced by the difference (`w:spacing`) and its room reckoned from the page's width; a `linkTarget` becomes a `w:hyperlink`, with a relationship for an address or `w:anchor` for one of the document's own anchors, and a run's own link wins over the paragraph's; a paragraph seated off its baseline (`TextVerticalAlign`) has its runs raised or lowered in the line (`w:position`) by the PDF backend's own correction (`ParagraphSeating`), one shift for the paragraph where the page seats each line by its own; Word and LibreOffice stand an exact line's baseline four fifths of the way down it whatever the face, where the page sets it the face's ascent down, so a paragraph whose face puts the two half a point or more apart — Spectral's, not Lato's — has its text moved to the page's baseline in the same position, matched at its middle line (not yet a list item's or a table text cell's; a picture among it moves with it in Word and stays on its own baseline in LibreOffice); lines a container stacks over one another tighter than their face each end halfway between their letters and the next line's (Word draws an exact line's text on screen only inside the line; its PDF export does not cut it), and the last layer of a shape container on one page, where its line runs past the foot, ends at the foot or below its letters; letters two lines share are split halfway so the page does not move, and a stack that holds a picture keeps its lines' own heights; a `bulletOffset` of spaces becomes the paragraph's indent (`w:ind` left, hanging or first line, by `indentStrategy`) in the flow and in cells, not yet over the flow, in an overlay's left-and-right pair or in a header or footer; one with letters in it is not written, its wrapped lines still set after the spaces that cover it |
| List hanging indent — a marker column and a content column (`ListBuilder.hangingIndent(true)`, `markerGap(...)`) | ✅ marker and content emitted as separate `ParagraphFragmentPayload` fragments at the resolved `markerX` / `contentX` | ✅ the same fragments — the fixed-layout pipeline resolves the geometry before either backend sees it | ⚠️ the top level only. `DocxSemanticBackend` exports a list as a real Word list — `numbering.xml`, `w:numPr` per item, the level carrying the marker — or, with rich items or a drawn marker, as paragraphs; content and nesting are unaffected. With the flag, the top level's marker column is the layout's — the marker's width and `markerGap`, the text and its wrapped lines where the page sets them — where the gap covers what Word may set the marker wider: a picture at its written size, its edges included, or text in the page's face (embedded, or a standard one Word sets in the same widths) grown to its half-point size, half a point clear. A Word list's level then indents and hangs by that column; a list of paragraphs writes the marker, a tab to a stop there, and hangs the item there. Word places content at absolute indents and has no relative-advance primitive, so without the layout's measure the gap could not be honoured; a Word list without the flag that the layout placed and that does not nest takes the page's column too, the spaces the page sets its wrapped lines after, its marker followed by a space (`w:suff`) and an item that wraps measured at Word's half-point size; a list that nests items, a list built as a tree of items (laid out flattened), and a marker the gap does not clear keep the stated column (180 twips, plus 120 per nesting level) — except, in a list of paragraphs, a nested rich item with no marker, which stands where the layout set its text, its measure weighed at Word's half-point sizes, where the layout's items are matched to the list's; a list that nests only such items sets its top level at the page's column too |
| Inline code/badge chips (`InlineBackground` on text spans) | ✅ `PdfParagraphFragmentRenderHandler` | ✅ `PptxParagraphFragmentRenderHandler` | ⚠️ `DocxSemanticBackend` — the fill becomes the run's own `w:shd`, in a paragraph and in a list item alike, so a badge still reads as a badge. What Word has no way to say is the shape: shading covers the glyph box, so the corner radius and the padding that widens the run on the page are not in the file, and the export records both. A `w:shd` fill is opaque, so a translucent chip is flattened first against what this export wrote underneath it — the paragraph's shading, the cell's, or the page — so the chip agrees with the file it is in, which on a white page is the colour the PDF shows. It stops being translucent, and that is recorded with the rest |
| Inline images (`ParagraphImageSpan`) | ✅ `PdfParagraphFragmentRenderHandler` | ✅ `PptxParagraphFragmentRenderHandler` | ✅ `DocxSemanticBackend.writeInlinePicture` (a picture in its own run where it sits among the words, at its size, inside the run's or the paragraph's link; raised or lowered by `w:position` to where the page's alignment and `baselineOffset` put it, from the layout's measure of the paragraph's first line — in a list, the list's text on a line as tall as the item's own tallest picture; LibreOffice ignores `w:position` on a picture and stands it on the baseline, so a picture the export draws itself (icon, emoji, shape) that the page raises carries the rise as transparent rows and needs no `w:position`, while one the page lowers stands in LibreOffice higher than on the page by as much as the page lowers it — up to the text's descent for a centred icon as tall as its line; the editor clips a picture to an exact line height, so a paragraph holding a picture that leaves its text — past the ascent or the descent, in Word's placement or on the baseline — has its lines written at least the height the picture reaches, grown by the editor rather than clipped, every line of the paragraph since Word has one line height for it, and each as tall as the editor's font makes it — for 14pt text about 2.5pt taller than the page's in LibreOffice; a picture inside its text in both editors keeps the exact height; a paragraph of one line of text in a Word paragraph of its own, with room above for its pictures' reach, keeps an exact line at the page's height of it, the pictures set in it where the page puts them in Word and what their ink reaches past it taken from the gaps around it, and in LibreOffice a lowered picture there stands higher and loses what passes the line's top; its description is the text it stands for or empty) |
diff --git a/docs/recipes/docx-export.md b/docs/recipes/docx-export.md
index 2e099f393..021159b93 100644
--- a/docs/recipes/docx-export.md
+++ b/docs/recipes/docx-export.md
@@ -327,16 +327,17 @@ it. In a table cell, where Word draws no text past the cell's left edge, the lef
gives no more than it has, and the right edge gives the rest, the line moving by that much.
Any other centred or right-aligned paragraph keeps the page's measure: in one of several
lines a wider measure takes another word onto a line, and a short line's box already has
-room for it. A centred or right-aligned paragraph of one line moves its first letter by the
-line's difference — by half of it, or all of it — so where Word sets it a point or more
-wider or narrower than the page, its letters are spaced by the difference (`w:spacing`, in
+room for it. A centred or right-aligned paragraph of one line moves its first letter with
+the line's width, so where Word sets a left-to-right one of text alone a point or more wider
+or narrower than the page, its letters are spaced by the difference (`w:spacing`, in
twentieths of a point, the size left as Word sets it) and its ends stand within a fraction of
-a point of the page's; such a line is given room for the page's width and a few hundredths
-more, as one Word sets wider is. A line that already carries tracking is left as it is: Word
-rounds tracking its own way and spaces the last letter too, which the size does not tell. So is a difference under a point — measured, `ObsidianInvoice`'s 9.34pt
-amounts, half a point wider by the size alone, came out as wide as the page's in Word. Text
-typed into a spaced line takes the same spacing. A list's items, a line pair's line, text over the flow and a header's or
-footer's line keep the page's measure too.
+a point of the page's; its room is then reckoned from the page's width, a few hundredths past
+it. A line that carries tracking, or a picture or a shape, is left as it is, and so is its
+room: Word rounds tracking its own way and spaces the last letter too, which the size does
+not tell. So is a difference under a point — measured, `ObsidianInvoice`'s 9.34pt amounts,
+half a point wider by the size alone, came out as wide as the page's in Word. Text typed into
+a spaced line takes the same spacing. A list's items, a line pair's line, text over the flow
+and a header's or footer's line keep the page's measure too.
## Named styles, so the document can be restyled
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 7a07db705..1e9b8a555 100644
--- a/qa/src/test/resources/docx-fidelity/libreoffice-linux-lines.tsv
+++ b/qa/src/test/resources/docx-fidelity/libreoffice-linux-lines.tsv
@@ -6,18 +6,18 @@ cv-blue_banner 0:8d6758a29c08 -0.51 0.10 platformengineerwith10+yearsbuil
cv-blue_banner 0:f0d993eb5f9d -0.50 0.10 high-throughputpdfrendering,sema
cv-blue_banner 0:c2f296ef30a3 -0.19 -0.46 professionalexperience
cv-blue_banner 0:a76f47480818 -0.25 0.10 seniorplatformengineer
-cv-blue_banner 0:060300b18051 -0.20 1.42 2024-present
+cv-blue_banner 0:060300b18051 -0.20 0.32 2024-present
cv-blue_banner 0:f890faef907f -0.20 0.10 northwindsystems
cv-blue_banner 0:e9ca73f80176 -0.49 0.10 ledthereusabledocument-generatio
cv-blue_banner 0:4d5fc9766174 -0.48 0.10 70%byretiringper-teampdfscriptsi
cv-blue_banner 0:daf65306b3b6 -0.22 0.10 softwareengineer
-cv-blue_banner 0:e7cc1822c6dc -0.17 1.17 2021-2024
+cv-blue_banner 0:e7cc1822c6dc -0.17 0.37 2021-2024
cv-blue_banner 0:998c1a985022 -0.17 0.10 brightleaflabs
cv-blue_banner 0:57305305a2eb -0.46 0.10 builtbackendservicesandproductio
cv-blue_banner 0:7b7239897a2f -0.45 0.10 layoutengine,eliminatinglicensin
cv-blue_banner 0:41bb54fbae8d -0.19 0.10 backendengineer
-cv-blue_banner 0:3fa772117260 -0.14 1.17 2019-2021
-cv-blue_banner 0:3fa772117260#1 -0.13 1.17 2019-2021
+cv-blue_banner 0:3fa772117260 -0.14 0.37 2019-2021
+cv-blue_banner 0:3fa772117260#1 -0.13 0.37 2019-2021
cv-blue_banner 0:ef148f221e31 -0.14 0.10 helixprintco
cv-blue_banner 0:d7119ba1b2b4 -0.13 0.10 maintainedahigh-volumeinvoice-pr
cv-blue_banner 0:68bf0109c6f8 -0.12 0.24 education
@@ -25,11 +25,11 @@ cv-blue_banner 0:0ad1957d6bab -0.18 0.10 msccomputerscience
cv-blue_banner 0:47ce0218bbea -0.13 0.10 universityofmanchester
cv-blue_banner 0:2ffe115b06e5 -0.12 0.10 distinction.thesis:composablelay
cv-blue_banner 0:b6749d0b616b -0.16 0.10 bscsoftwareengineering
-cv-blue_banner 0:30f9262640a0 -0.11 1.17 2015-2019
+cv-blue_banner 0:30f9262640a0 -0.11 0.37 2015-2019
cv-blue_banner 0:89ad9656f393 -0.11 0.10 imperialcollegelondon
cv-blue_banner 0:baa173b4a82e -0.10 0.10 first-classhonours.specialisatio
cv-blue_banner 0:6d3d5673c39f -0.14 0.10 oraclejavacertification
-cv-blue_banner 0:d40b22f00acb -0.09 1.17 2023-2024
+cv-blue_banner 0:d40b22f00acb -0.09 0.37 2023-2024
cv-blue_banner 0:2669e1f6d55c -0.09 0.10 professionaltrack
cv-blue_banner 0:3857373829c1 -0.08 0.10 java17platformdeep-dive:records,
cv-blue_banner 0:5f94c50dc5bf -0.07 -0.01 technicalskills
@@ -90,7 +90,7 @@ cv-boxed_sections 0:fb1927f88597 -0.32 0.10 •chromeforge(java,graphcompose,p
cv-boxed_sections 0:e7e62230eb1f -0.32 0.11 editorial-magazinedocumenttoolki
cv-boxed_sections 0:c2f296ef30a3 -0.30 6.46 professionalexperience
cv-boxed_sections 0:a76f47480818 -0.31 0.10 seniorplatformengineer
-cv-boxed_sections 0:060300b18051 -0.34 -0.99 2024-present
+cv-boxed_sections 0:060300b18051 -0.34 0.01 2024-present
cv-boxed_sections 0:f890faef907f -0.32 0.10 northwindsystems
cv-boxed_sections 1:49faf14f1f56 -0.13 0.10 ledthereusabledocument-generatio
cv-boxed_sections 1:6bd3f168e89e -0.13 0.10 maintenancetimeby70%byretiringpe
@@ -111,7 +111,7 @@ cv-boxed_sections 1:dd20ae01c3cf 0.25 0.10 opensource:maintainerofgraphcomp
cv-boxed_sections 1:33c35e9d236d 0.25 0.10 speaking:jvmsummit2024,devoxxuk2
cv-centered_headline 0:8f1010120ed6 -0.50 2.39 jordanrivera
cv-centered_headline 0:87ca614d0eab -0.18 1.36 professionaltitle
-cv-centered_headline 0:8d7a2cca3060 -0.23 -3.87 +442055551000|jordan.rivera@exam
+cv-centered_headline 0:8d7a2cca3060 -0.23 0.28 +442055551000|jordan.rivera@exam
cv-centered_headline 0:b944906ed84c -0.28 0.10 professionalsummary
cv-centered_headline 0:f43d42692f27 0.06 0.10 platformengineerwith10+yearsbuil
cv-centered_headline 0:5f94c50dc5bf -0.25 0.10 technicalskills
@@ -247,9 +247,9 @@ cv-charcoal_gold 0:b1ab1e892617 0.36 0.00 2022
cv-charcoal_gold 0:c2f5106fbd5a 0.33 0.00 topperformer
cv-charcoal_gold 0:058521fbf0d1 0.37 0.00 innovatechsystems
cv-charcoal_gold 0:3def82282260 0.30 -0.71 technicaltools
-cv-charcoal_gold 0:eb04bd42c7fb 0.36 -3.39 microsoft365|jira|confluence|asa
+cv-charcoal_gold 0:eb04bd42c7fb 0.36 -1.19 microsoft365|jira|confluence|asa
cv-classic_serif 0:8f1010120ed6 -0.15 2.60 jordanrivera
-cv-classic_serif 0:8d7a2cca3060 0.19 4.08 +442055551000|jordan.rivera@exam
+cv-classic_serif 0:8d7a2cca3060 0.19 -0.52 +442055551000|jordan.rivera@exam
cv-classic_serif 0:3c69d6bb4892 0.24 20.27 professionalprofile
cv-classic_serif 0:32c1cf49a2fe 0.14 0.10 skills
cv-classic_serif 0:873a24d153c8 0.11 0.10 languages:java21,kotlin,groovy,p
@@ -260,7 +260,7 @@ cv-classic_serif 0:181ddda7f959 0.01 0.10 testing:junit5,assertj,pdfbox-ba
cv-classic_serif 0:c40c656d9558 -0.01 0.10 distribution:mavencentral,sonaty
cv-classic_serif 0:53e5e7c5a884 0.00 0.10 experience
cv-classic_serif 0:a76f47480818 0.01 0.10 seniorplatformengineer
-cv-classic_serif 0:060300b18051 -0.02 1.36 2024-present
+cv-classic_serif 0:060300b18051 -0.02 0.26 2024-present
cv-classic_serif 0:f890faef907f -0.01 0.10 northwindsystems
cv-classic_serif 0:49faf14f1f56 -0.24 0.10 ledthereusabledocument-generatio
cv-classic_serif 0:6bd3f168e89e -0.25 0.10 maintenancetimeby70%byretiringpe
@@ -441,12 +441,12 @@ cv-editorial_blue 1:f20997652453 0.19 0.10 languages:english(fluent),german
cv-editorial_blue 1:ec9d3622395e 0.20 0.10 workeligibility:eligibletoworkin
cv-editorial_blue 1:dd20ae01c3cf 0.20 0.10 opensource:maintainerofgraphcomp
cv-editorial_blue 1:33c35e9d236d 0.21 0.10 speaking:jvmsummit2024,devoxxuk2
-cv-editorial_blue 1:0b516aa076e0 0.18 -0.67 referencesavailableuponrequest.
+cv-editorial_blue 1:0b516aa076e0 0.18 0.13 referencesavailableuponrequest.
cv-engineering_resume 0:8f1010120ed6 0.15 15.10 jordanrivera
cv-engineering_resume 0:2d58f0cadc2d -0.25 15.10 platformengineer
-cv-engineering_resume 0:fbeea547d99e -0.21 16.21 london,uk
-cv-engineering_resume 0:89674e6f130e -0.20 16.84 +442055551000
-cv-engineering_resume 0:e0f850305ded -0.19 17.60 jordan.rivera@example.com
+cv-engineering_resume 0:fbeea547d99e -0.21 15.31 london,uk
+cv-engineering_resume 0:89674e6f130e -0.20 15.34 +442055551000
+cv-engineering_resume 0:e0f850305ded -0.19 15.20 jordan.rivera@example.com
cv-engineering_resume 0:84308bea4540 -0.18 15.95 linkedin
cv-engineering_resume 0:c0b0109d9439 -0.17 15.85 github
cv-engineering_resume 0:32c1cf49a2fe -0.50 0.10 skills
@@ -683,7 +683,7 @@ cv-minimal_underlined 0:fb1927f88597 -1.10 0.10 •chromeforge(java,graphcompose
cv-minimal_underlined 0:e7e62230eb1f -1.09 0.11 editorial-magazinedocumenttoolki
cv-minimal_underlined 0:c2f296ef30a3 -1.06 0.10 professionalexperience
cv-minimal_underlined 0:a76f47480818 -1.35 0.10 seniorplatformengineer
-cv-minimal_underlined 0:060300b18051 -1.39 -0.99 2024-present
+cv-minimal_underlined 0:060300b18051 -1.39 0.01 2024-present
cv-minimal_underlined 0:f890faef907f -1.36 0.10 northwindsystems
cv-minimal_underlined 1:49faf14f1f56 -0.13 0.10 ledthereusabledocument-generatio
cv-minimal_underlined 1:6bd3f168e89e -0.13 0.10 maintenancetimeby70%byretiringpe
@@ -822,17 +822,17 @@ cv-modern_professional 1:dd20ae01c3cf 0.23 0.10 opensource:maintainerofgraphcomp
cv-modern_professional 1:33c35e9d236d 0.23 0.10 speaking:jvmsummit2024,devoxxuk2
cv-monogram_sidebar 0:093e7d5fdbaa -0.25 0.81 contact
cv-monogram_sidebar 0:89674e6f130e -0.10 -0.33 +442055551000
-cv-monogram_sidebar 0:e0f850305ded -0.08 -0.49 jordan.rivera@example.com
+cv-monogram_sidebar 0:e0f850305ded -0.08 0.06 jordan.rivera@example.com
cv-monogram_sidebar 0:fbeea547d99e -0.06 -0.16 london,uk
cv-monogram_sidebar 0:84308bea4540 -0.04 -0.11 linkedin
cv-monogram_sidebar 0:c0b0109d9439 -0.02 -0.09 github
cv-monogram_sidebar 0:68bf0109c6f8 -0.05 0.81 education
-cv-monogram_sidebar 0:0ad1957d6bab 0.05 0.73 msccomputerscience
-cv-monogram_sidebar 0:47ce0218bbea 0.08 -0.46 universityofmanchester
-cv-monogram_sidebar 0:3fa772117260 0.09 0.57 2019-2021
-cv-monogram_sidebar 0:b6749d0b616b 0.06 0.85 bscsoftwareengineering
-cv-monogram_sidebar 0:89ad9656f393 0.09 -0.42 imperialcollegelondon
-cv-monogram_sidebar 0:30f9262640a0 0.10 0.57 2015-2019
+cv-monogram_sidebar 0:0ad1957d6bab 0.05 0.23 msccomputerscience
+cv-monogram_sidebar 0:47ce0218bbea 0.08 0.09 universityofmanchester
+cv-monogram_sidebar 0:3fa772117260 0.09 0.17 2019-2021
+cv-monogram_sidebar 0:b6749d0b616b 0.06 0.25 bscsoftwareengineering
+cv-monogram_sidebar 0:89ad9656f393 0.09 0.08 imperialcollegelondon
+cv-monogram_sidebar 0:30f9262640a0 0.10 0.17 2015-2019
cv-monogram_sidebar 0:32c1cf49a2fe 0.07 0.78 skills
cv-monogram_sidebar 0:d5680c2e2769 0.22 -0.08 java21
cv-monogram_sidebar 0:19a67c2a5369 0.24 -0.02 kotlin
@@ -840,7 +840,7 @@ cv-monogram_sidebar 0:95bfacd3a466 0.26 -0.05 groovy
cv-monogram_sidebar 0:11a4a60b518b 0.28 -0.08 python
cv-monogram_sidebar 0:4ec7c53222c8 0.30 0.00 sql
cv-monogram_sidebar 0:57529f20c3ad 0.32 -0.10 pdfbox
-cv-monogram_sidebar 0:1814aacb6d5a 0.34 -0.48 apachepoi(docx/xlsx)
+cv-monogram_sidebar 0:1814aacb6d5a 0.34 0.02 apachepoi(docx/xlsx)
cv-monogram_sidebar 0:136c67657614 -0.38 2.80 jordan
cv-monogram_sidebar 0:8ba35ebe3d5c -0.37 2.83 rivera
cv-monogram_sidebar 0:2d58f0cadc2d -0.19 0.11 platformengineer
@@ -945,7 +945,7 @@ cv-nordic_clean 0:8f1010120ed6 0.15 0.10 jordanrivera
cv-nordic_clean 0:2d58f0cadc2d -0.20 0.10 platformengineer
cv-nordic_clean 0:fbeea547d99e -0.20 -0.35 london,uk
cv-nordic_clean 0:89674e6f130e -0.18 -0.65 +442055551000
-cv-nordic_clean 0:e0f850305ded -0.16 -1.02 jordan.rivera@example.com
+cv-nordic_clean 0:e0f850305ded -0.16 0.13 jordan.rivera@example.com
cv-nordic_clean 0:84308bea4540 -0.14 -0.21 linkedin
cv-nordic_clean 0:c0b0109d9439 -0.12 -0.16 github
cv-nordic_clean 0:1900eab6c028 -0.25 8.60 profile
@@ -1107,8 +1107,8 @@ cv-orange_ops 0:724cc536a24c 0.85 0.02 drivinglicence:fullukdrivinglice
cv-orange_ops 0:9f0e43069d3f 0.86 0.02 interests:strengthtraining,motor
cv-panel 0:8f1010120ed6 -0.40 8.42 jordanrivera
cv-panel 0:2d58f0cadc2d 0.12 8.12 platformengineer
-cv-panel 0:89ff05847e96 0.19 7.69 london,uk|+442055551000
-cv-panel 0:8817bf9310b6 0.21 7.40 jordan.rivera@example.com|linked
+cv-panel 0:89ff05847e96 0.19 8.34 london,uk|+442055551000
+cv-panel 0:8817bf9310b6 0.21 8.40 jordan.rivera@example.com|linked
cv-panel 0:1900eab6c028 0.19 7.95 profile
cv-panel 0:80cc87fdc4c4 -0.23 7.95 platformengineerwith10+yearsbuil
cv-panel 0:c323ef4c385f -0.21 7.95 systems.specialisedinhigh-throug
@@ -1585,7 +1585,7 @@ cv-terracotta_rail 0:bf4ab196cbb6 0.08 0.11 strongfocusonsustainability,qual
cv-terracotta_rail 0:0dfcf4444281 0.06 0.11 collaborativecommunicatorskilled
cv-terracotta_rail 0:22fdcc5cfe61 0.04 0.11 leadingmultidisciplinaryteamstoa
cv-terracotta_rail 0:c2f296ef30a3 -0.30 0.11 professionalexperience
-cv-terracotta_rail 0:086c0e036e67 -0.30 -1.14 2021–present
+cv-terracotta_rail 0:086c0e036e67 -0.30 -0.14 2021–present
cv-terracotta_rail 0:b3af3b56129a -0.33 0.06 northlinestudio,bristol,uk
cv-terracotta_rail 0:27819bba9c0f -0.54 0.06 •leaddesignpackagesfromconceptth
cv-terracotta_rail 0:d49fddeb9701 -0.57 0.05 andmixed-useprojects.
@@ -1597,7 +1597,7 @@ cv-terracotta_rail 0:894e988cdf94#1 -0.99 0.05 documentation.
cv-terracotta_rail 0:40c00971356d -0.67 0.06 •presentandcommunicatedesignprop
cv-terracotta_rail 0:a218287e182e -0.69 0.05 anddrivingprojectsforward.
cv-terracotta_rail 0:f2e40fc1edb7 -0.49 0.06 architect
-cv-terracotta_rail 0:f7d1049ec64b -0.49 -0.94 2018–2021
+cv-terracotta_rail 0:f7d1049ec64b -0.49 -0.24 2018–2021
cv-terracotta_rail 0:59e0b0e4edc9 -0.52 0.06 urbanformpartners,bath,uk
cv-terracotta_rail 0:4f2dbefcbec0 -0.74 0.06 •developedconceptandtechnicaldes
cv-terracotta_rail 0:e2a5e1a23401 -0.76 0.05 educationprojects.
@@ -1608,7 +1608,7 @@ cv-terracotta_rail 0:6b3142466e26 -0.84 0.06 •coordinatedwithcontractorsandco
cv-terracotta_rail 0:1150d4ba642c -0.86 0.05 intent.
cv-terracotta_rail 0:1150d4ba642c#1 -0.95 0.05 intent.
cv-terracotta_rail 0:2d2cd24f87d7 -0.67 0.06 partiiarchitecturalassistant
-cv-terracotta_rail 0:3b8048983ecc -0.67 -0.94 2016–2018
+cv-terracotta_rail 0:3b8048983ecc -0.67 -0.24 2016–2018
cv-terracotta_rail 0:ebd31e5e7505 -0.69 0.06 axisdesignworkshop,cardiff,uk
cv-terracotta_rail 0:e48e46c3ddf3 -0.71 0.06 •supportedseniorarchitectsacross
cv-terracotta_rail 0:810bbb784193 -0.93 0.06 •producedcadandrevitdrawings,mod
@@ -1635,10 +1635,10 @@ cv-terracotta_rail 0:208ed62d3df6 -0.75 0.10 environmentalstandardsandstrongs
cv-terracotta_rail 0:ac5e63220124 -1.06 0.10 performance.
cv-terracotta_rail 0:68bf0109c6f8 -0.89 0.11 education
cv-terracotta_rail 0:1d1dcf789873 -0.94 0.11 marcharchitecture
-cv-terracotta_rail 0:00583a6bed38 -0.94 -0.86 2014–2016
+cv-terracotta_rail 0:00583a6bed38 -0.94 -0.16 2014–2016
cv-terracotta_rail 0:5761434c99d8 -0.97 0.11 universityofsheffield
cv-terracotta_rail 0:e3f2f6a4e375 -0.97 0.11 ba(hons)architecture
-cv-terracotta_rail 0:2920a7fb42fc -0.97 -0.86 2011–2014
+cv-terracotta_rail 0:2920a7fb42fc -0.97 -0.16 2011–2014
cv-terracotta_rail 0:af625dd0aa20 -1.00 0.11 universityofthewestofengland
cv-timeline_minimal 0:8f1010120ed6 0.15 0.10 jordanrivera
cv-timeline_minimal 0:2d58f0cadc2d -0.40 0.10 platformengineer
@@ -1741,20 +1741,20 @@ cv-violet_grid 0:b96067918254 -0.26 -0.41 uxresearch
cv-violet_grid 0:9058674fd905 -0.21 -0.71 userinterviews,surveys,
cv-violet_grid 0:0cdd38014d79 -0.22 -0.70 personas,journeymaps,
cv-violet_grid 0:61e4ba28d469 -0.22 -0.71 competitiveanalysis
-cv-violet_grid 0:3de6be58cb9f -0.26 -0.99 informationarchitecture
+cv-violet_grid 0:3de6be58cb9f -0.26 0.11 informationarchitecture
cv-violet_grid 0:db5c6756c4b6 -0.21 -0.77 sitemaps,userflows,card
cv-violet_grid 0:6c7ad1834a60 -0.22 -0.77 sorting,contentstrategy
-cv-violet_grid 0:187737d59250 -0.26 -0.50 wireframing
+cv-violet_grid 0:187737d59250 -0.26 -0.05 wireframing
cv-violet_grid 0:2286e5aa328a -0.21 -0.52 lowtohigh-fidelity
-cv-violet_grid 0:5c4b21cb1af8 -0.26 -0.43 prototyping
+cv-violet_grid 0:5c4b21cb1af8 -0.26 0.02 prototyping
cv-violet_grid 0:1abfa9dc1773 -0.21 -0.69 interactiveprototypes,
cv-violet_grid 0:1ddc6c379a54 -0.22 -0.54 microinteractions,
cv-violet_grid 0:d1d99c54cf40 -0.22 -0.27 transitions
-cv-violet_grid 0:7a38f8d94009 -0.26 -0.62 usabilitytesting
+cv-violet_grid 0:7a38f8d94009 -0.26 0.13 usabilitytesting
cv-violet_grid 0:63af69ae99e6 -0.21 -0.68 testplanning,usability
cv-violet_grid 0:f67c409cc44f -0.22 -0.87 tests,heuristicevaluation,
cv-violet_grid 0:438e1d3264dd -0.22 -0.61 insights&iteration
-cv-violet_grid 0:7e02941d76f5 -0.26 -0.53 designsystems
+cv-violet_grid 0:7e02941d76f5 -0.26 0.07 designsystems
cv-violet_grid 0:a0d27161913a -0.21 -0.57 componentlibraries,
cv-violet_grid 0:82374d275507 -0.22 -0.54 designtokens,pattern
cv-violet_grid 0:caf9f906b16a -0.22 -0.45 documentation
@@ -1763,8 +1763,8 @@ cv-violet_grid 0:5cb2b3b6143c -0.18 0.10 figmafigjamadobexdsketchmironoti
cv-violet_grid 0:53e5e7c5a884 -0.20 0.10 experience
cv-violet_grid 0:5a0774260e0c -0.08 0.10 2022-present
cv-violet_grid 0:204f460767ff -0.12 0.02 seniorux/uidesigner|novafin(fint
-cv-violet_grid 0:318bba211bdc -0.07 -1.78 sanfrancisco,ca
-cv-violet_grid 0:318bba211bdc#1 8.85 -1.78 sanfrancisco,ca
+cv-violet_grid 0:318bba211bdc -0.07 -0.28 sanfrancisco,ca
+cv-violet_grid 0:318bba211bdc#1 8.85 -0.28 sanfrancisco,ca
cv-violet_grid 0:3883e88c63c3 -0.08 0.05 ledend-to-enddesignforapersonalf
cv-violet_grid 0:ccc00663f089 1.70 0.05 conducteduserresearch,builtperso
cv-violet_grid 0:7d0f908c7e5f 3.48 0.05 designedwireframes,prototypes,an
@@ -1778,7 +1778,7 @@ cv-violet_grid 0:ab80a52d70fe 12.40 0.05 partneredwithengineerstoensurefe
cv-violet_grid 0:11cbf615189b 14.19 0.05 createdreusablecomponentsandstyl
cv-violet_grid 0:7e5081a3ed48 15.98 0.10 2018-2020
cv-violet_grid 0:73f7bbf7cd60 15.94 0.02 juniorux/uidesigner|lumendigital
-cv-violet_grid 0:c6b06045332f 15.99 -1.17 austin,tx
+cv-violet_grid 0:c6b06045332f 15.99 -0.17 austin,tx
cv-violet_grid 0:5ea8234925c5 15.98 0.05 supporteddiscoveryworkshops,user
cv-violet_grid 0:46f00c76528c 17.76 0.05 designedresponsivewebsitesandlan
cv-violet_grid 0:f3cea36a9d83 19.54 0.05 collaboratedwithcopywritersandde
@@ -2689,12 +2689,12 @@ invoice-merchant 0:e2a9c81da5f6 0.06 -0.13 unitprice(usd)
invoice-merchant 0:5dfb966b2bcf 0.06 -0.06 amount(usd)
invoice-merchant 0:fe6b34582738 -0.18 0.07 coastlineadvanced
invoice-merchant 0:46f3b0cea3bc -0.22 0.07 monthlysubscription
-invoice-merchant 0:71738eb7126d -0.21 -0.54 advancedplan
+invoice-merchant 0:71738eb7126d -0.21 0.01 advancedplan
invoice-merchant 0:7b969356782c -0.21 -0.25 399.00
invoice-merchant 0:7b969356782c#1 -0.21 -0.25 399.00
invoice-merchant 0:0a9acaea6477 -0.69 0.07 paymentprocessing
invoice-merchant 0:91809142f08f -0.74 0.07 transactionfee
-invoice-merchant 0:358a7392afe6 -0.73 -0.46 asperusage
+invoice-merchant 0:358a7392afe6 -0.73 0.04 asperusage
invoice-merchant 0:561b2814d3c0 -0.73 -0.17 0.00
invoice-merchant 0:561b2814d3c0#1 -0.73 -0.18 0.00
invoice-merchant 0:561b2814d3c0#2 -1.91 -0.17 0.00
@@ -2783,28 +2783,28 @@ invoice-metered 0:d1062d072acd 0.08 0.64 unitprice
invoice-metered 0:cf38d95c9c6b 0.08 0.62 amount
invoice-metered 0:f13d9c7c6f89 -0.91 0.58 computeinstances
invoice-metered 0:d08426b31521 -0.94 0.58 on-demandusage
-invoice-metered 0:05313a23f706 -0.90 1.41 london(eu-west)
-invoice-metered 0:05313a23f706#1 -2.82 1.41 london(eu-west)
-invoice-metered 0:05313a23f706#2 -4.73 1.41 london(eu-west)
+invoice-metered 0:05313a23f706 -0.90 0.66 london(eu-west)
+invoice-metered 0:05313a23f706#1 -2.82 0.66 london(eu-west)
+invoice-metered 0:05313a23f706#2 -4.73 0.66 london(eu-west)
invoice-metered 0:6e477a22f840 -0.90 0.98 744hrs
invoice-metered 0:6e477a22f840#1 -4.73 0.98 744hrs
-invoice-metered 0:352967434b63 -0.90 1.17 gbp0.0710
+invoice-metered 0:352967434b63 -0.90 0.72 gbp0.0710
invoice-metered 0:eec5f520909e -0.90 1.11 gbp52.82
invoice-metered 0:721ffa554e56 -2.82 0.58 objectstorage
invoice-metered 0:df2f05798b9e -2.85 0.58 storedvolume
invoice-metered 0:41542149a8a5 -2.82 0.93 480gb
-invoice-metered 0:2242bd7aa8ce -2.82 1.17 gbp0.0215
+invoice-metered 0:2242bd7aa8ce -2.82 0.72 gbp0.0215
invoice-metered 0:7c6be041f9c2 -2.82 1.11 gbp10.32
invoice-metered 0:8152fd217e12 -4.74 0.58 manageddatabase
invoice-metered 0:3c8556c4aa26 -4.77 0.58 primaryandreplica
-invoice-metered 0:76afc71f5b3e -4.73 1.17 gbp0.1650
-invoice-metered 0:baad0db43ed6 -4.73 1.19 gbp122.76
+invoice-metered 0:76afc71f5b3e -4.73 0.72 gbp0.1650
+invoice-metered 0:baad0db43ed6 -4.73 0.74 gbp122.76
invoice-metered 0:444858f5bf18 -6.65 0.58 egresstraffic
invoice-metered 0:8b72bb875a83 -6.68 0.58 datatransferout
invoice-metered 0:8001c2743965 -6.65 0.92 global
invoice-metered 0:8001c2743965#1 -8.56 0.92 global
invoice-metered 0:847eb4cbbb42 -6.65 0.93 310gb
-invoice-metered 0:d651e46704f7 -6.65 1.17 gbp0.0900
+invoice-metered 0:d651e46704f7 -6.65 0.72 gbp0.0900
invoice-metered 0:4ecaa7313375 -6.65 1.11 gbp27.90
invoice-metered 0:efa8771baad9 -8.56 0.58 supportplan
invoice-metered 0:9e81cd6e653a -8.60 0.58 standardresponse
@@ -2828,7 +2828,7 @@ invoice-metered 0:79fabcfaa819 -9.57 0.73 gbp214.71
invoice-metered 0:a52611c9261b -9.60 0.14 vat(20%)
invoice-metered 0:e50d8a0ea599 -9.60 0.70 gbp42.94
invoice-metered 0:57a66d21eb1b -9.73 0.14 totaldue
-invoice-metered 0:f207312dff2f -9.80 -0.85 gbp257.65
+invoice-metered 0:f207312dff2f -9.80 -0.05 gbp257.65
invoice-metered 0:b0553f008513 -9.64 0.11 paymentdueby
invoice-metered 0:e60e7ef06ccc -10.46 0.10 thankyouforrunningonnorthwindclo
invoice-metered 0:34ea2ec3fe72 -10.46 0.10 forbillingandaccountsupport,visi
@@ -3057,9 +3057,9 @@ invoice-obsidian 0:5d1d1b79af09 0.02 0.09 invoicenumber
invoice-obsidian 0:3e62d1fbe5ec 0.06 0.09 inv-2026-1048
invoice-obsidian 0:3e62d1fbe5ec#1 -1.46 0.08 inv-2026-1048
invoice-obsidian 0:8feaf8016bab 0.02 0.09 issuedate
-invoice-obsidian 0:4d49cb78b810 0.02 1.41 27august2026
+invoice-obsidian 0:4d49cb78b810 0.02 0.11 27august2026
invoice-obsidian 0:ac10f8c16320 -0.05 0.09 duedate
-invoice-obsidian 0:b55571aaabac -0.05 1.73 10september2026
+invoice-obsidian 0:b55571aaabac -0.05 0.13 10september2026
invoice-obsidian 0:fb854bac39c8 -0.12 0.09 paymentterms
invoice-obsidian 0:56a2e019b8aa -0.12 0.79 14days
invoice-obsidian 0:466e5b9cd57f -0.19 0.09 currency
@@ -3222,7 +3222,7 @@ invoice-payments 0:48617b6d05d4 -5.73 0.10 gb29hrbk40025071295408
invoice-payments 0:6929e6db6e1f -5.77 0.10 hrbkgb2l
invoice-payments 0:e4c4788968b2 -5.49 -0.01 subtotal(excl.vat)
invoice-payments 0:7ed180496d4e -5.48 -0.01 vatat20%
-invoice-payments 0:751cbab81a3a -5.45 1.09 £2,186.00
+invoice-payments 0:751cbab81a3a -5.45 0.29 £2,186.00
invoice-payments 0:9a5256946abd -5.44 0.89 £437.20
invoice-payments 0:57a66d21eb1b -5.50 -0.01 totaldue
invoice-payments 0:5eb0b17220fa -5.44 -0.01 dueby14june2025
@@ -3241,7 +3241,7 @@ invoice-platform 0:9817f28442bf#1 -1.41 0.14 auroracloud
invoice-platform 0:52d6e3de4fa0 0.61 -0.26 invoice
invoice-platform 0:1cf61cdc6464 0.06 0.13 auroracloudinc.
invoice-platform 0:1cf61cdc6464#1 -0.35 0.40 auroracloudinc.
-invoice-platform 0:1cf61cdc6464#2 -1.38 -1.25 auroracloudinc.
+invoice-platform 0:1cf61cdc6464#2 -1.38 0.25 auroracloudinc.
invoice-platform 0:43315277b007 -0.40 0.13 500harbourstreet
invoice-platform 0:d9065bfb8f59 -0.40 0.13 vancouver,bcv6c2w2
invoice-platform 0:408c7c5887a0 -0.41 0.13 canada
@@ -3345,7 +3345,7 @@ invoice-platform 0:17e16a2f994d -0.25 0.63 cad195.76
invoice-platform 0:ac10f8c16320 -0.43 0.05 duedate
invoice-platform 0:d5ef7c1f9830 -1.31 0.11 thankyouforrunningonauroracloud.
invoice-platform 0:6a88f440b84e -1.31 0.11 manageyouraccountataurora.exampl
-invoice-platform 0:cf0a1e1dc87b -1.39 -4.25 500harbourstreet,vancouver,bcv6c
+invoice-platform 0:cf0a1e1dc87b -1.39 0.45 500harbourstreet,vancouver,bcv6c
invoice-platform 0:7b14b8d2c5c4 0.02 0.44 page1of1
invoice-subscription 0:435df6fdbffd 0.05 0.12 halstead
invoice-subscription 0:52d6e3de4fa0 0.02 -0.40 invoice
@@ -3360,7 +3360,7 @@ invoice-subscription 0:78acb3cb907b 0.27 0.12 www.halstead.example
invoice-subscription 0:f1c030eb4329 0.29 0.12 vatnogb418227603
invoice-subscription 0:77ead61b5875 0.29 0.09 invoiceno
invoice-subscription 0:0b0c0ee24de8 0.26 0.69 hs-inv-2026-0417
-invoice-subscription 0:0b0c0ee24de8#1 -0.07 1.23 hs-inv-2026-0417
+invoice-subscription 0:0b0c0ee24de8#1 -0.07 0.88 hs-inv-2026-0417
invoice-subscription 0:8feaf8016bab 0.34 0.09 issuedate
invoice-subscription 0:4d49cb78b810 0.32 0.62 27august2026
invoice-subscription 0:ac10f8c16320 0.39 0.09 duedate
@@ -3415,9 +3415,9 @@ invoice-subscription 0:57a66d21eb1b -0.18 0.11 totaldue
invoice-subscription 0:cf1b68cc49ef -0.11 0.89 £1,136.40
invoice-subscription 0:3c7ade679fdf 1.24 0.12 paymentdetails
invoice-subscription 0:56ed021beb35 -0.03 0.49 beneficiary
-invoice-subscription 0:0b85f3c8ed04 -0.07 0.83 halsteadsoftwareltd
+invoice-subscription 0:0b85f3c8ed04 -0.07 0.33 halsteadsoftwareltd
invoice-subscription 0:4381dc2ab142 -0.03 0.72 bank
-invoice-subscription 0:6d50b2dd724f -0.07 1.19 northgatebank
+invoice-subscription 0:6d50b2dd724f -0.07 0.84 northgatebank
invoice-subscription 0:10aeacde20ff -0.03 0.83 sortcode
invoice-subscription 0:b19b1415ef9b -0.07 0.94 40-05-30
invoice-subscription 0:f92a9c925024 -0.03 0.94 accountnumber
@@ -3464,10 +3464,10 @@ invoice-workspace 0:725a8f5062e7 -0.18 0.02 shipto
invoice-workspace 0:6d835e737af6 -0.17 0.02 unit4,riversidepark
invoice-workspace 0:555d5194cd4d -0.16 0.02 leedsls14ap
invoice-workspace 0:c9046f7a37ad 0.01 0.45 description
-invoice-workspace 0:169240d91658 0.01 -0.40 plan/service
+invoice-workspace 0:169240d91658 0.01 0.20 plan/service
invoice-workspace 0:32e8369a9c4f 0.01 0.21 qty
-invoice-workspace 0:e2a9c81da5f6 0.01 -0.48 unitprice(usd)
-invoice-workspace 0:5dfb966b2bcf 0.01 -0.39 amount(usd)
+invoice-workspace 0:e2a9c81da5f6 0.01 0.22 unitprice(usd)
+invoice-workspace 0:5dfb966b2bcf 0.01 0.36 amount(usd)
invoice-workspace 0:2bcc555090a1 -0.09 0.40 kestrelbusiness+
invoice-workspace 0:68fad867a983 -0.04 0.40 workspacesubscription
invoice-workspace 0:e0b844887562 -0.03 0.53 business+annual
@@ -3516,7 +3516,7 @@ invoice-workspace 0:b0553f008513 -0.77 0.10 paymentdueby
invoice-workspace 0:08775a92bfca 0.19 0.12 thankyouforusingkestrel.
invoice-workspace 0:18e82fbd48a3 0.26 0.12 questionsaboutthisinvoice?reachu
invoice-workspace 0:3c72ba0e1eb8 0.13 0.06 500howardstreet,6thfloor,sanfran
-invoice-workspace 0:9e2917e23527 0.12 -1.42 kestrel.example
+invoice-workspace 0:9e2917e23527 0.12 -0.12 kestrel.example
invoice-workspace 0:7b14b8d2c5c4 -0.05 -0.13 page1of1
letter-blue_banner 0:8f1010120ed6 0.42 1.90 jordanrivera
letter-blue_banner 0:8f1010120ed6#1 -0.02 0.10 jordanrivera
@@ -3541,7 +3541,7 @@ letter-boxed_sections 0:80a6a628ad52 -0.38 0.10 sincerely,
letter-centered_headline 0:8f1010120ed6 -0.50 2.39 jordanrivera
letter-centered_headline 0:8f1010120ed6#1 -0.06 0.10 jordanrivera
letter-centered_headline 0:2d58f0cadc2d -0.18 1.40 platformengineer
-letter-centered_headline 0:8d7a2cca3060 -0.23 -3.87 +442055551000|jordan.rivera@exam
+letter-centered_headline 0:8d7a2cca3060 -0.23 0.28 +442055551000|jordan.rivera@exam
letter-centered_headline 0:bef81b541365 -0.25 0.10 dearhiringteamatnorthwindsystems
letter-centered_headline 0:ab54410ead82 0.09 0.10 iamexcitedtosharemyinterestinthe
letter-centered_headline 0:9185168d2cf8 0.10 0.10 document-generationsystemsthatba
@@ -3551,7 +3551,7 @@ letter-centered_headline 0:a71e98216baa -0.12 0.10 iwouldwelcometheopportunityto
letter-centered_headline 0:80a6a628ad52 -0.09 0.10 sincerely,
letter-classic_serif 0:8f1010120ed6 -0.15 2.60 jordanrivera
letter-classic_serif 0:8f1010120ed6#1 0.02 0.10 jordanrivera
-letter-classic_serif 0:8d7a2cca3060 0.19 4.08 +442055551000|jordan.rivera@exam
+letter-classic_serif 0:8d7a2cca3060 0.19 -0.52 +442055551000|jordan.rivera@exam
letter-classic_serif 0:bef81b541365 0.20 0.10 dearhiringteamatnorthwindsystems
letter-classic_serif 0:ab54410ead82 -0.08 0.10 iamexcitedtosharemyinterestinthe
letter-classic_serif 0:9185168d2cf8 -0.10 0.10 document-generationsystemsthatba
@@ -3582,9 +3582,9 @@ letter-editorial_blue 0:80a6a628ad52 0.16 0.10 sincerely,
letter-engineering_resume 0:8f1010120ed6 0.15 15.10 jordanrivera
letter-engineering_resume 0:8f1010120ed6#1 -1.41 0.10 jordanrivera
letter-engineering_resume 0:2d58f0cadc2d -0.25 15.10 platformengineer
-letter-engineering_resume 0:fbeea547d99e -0.21 16.21 london,uk
-letter-engineering_resume 0:89674e6f130e -0.20 16.84 +442055551000
-letter-engineering_resume 0:e0f850305ded -0.19 17.60 jordan.rivera@example.com
+letter-engineering_resume 0:fbeea547d99e -0.21 15.31 london,uk
+letter-engineering_resume 0:89674e6f130e -0.20 15.34 +442055551000
+letter-engineering_resume 0:e0f850305ded -0.19 15.20 jordan.rivera@example.com
letter-engineering_resume 0:84308bea4540 -0.18 15.95 linkedin
letter-engineering_resume 0:c0b0109d9439 -0.17 15.85 github
letter-engineering_resume 0:bef81b541365 -1.40 0.10 dearhiringteamatnorthwindsystems
@@ -3631,7 +3631,7 @@ letter-modern_professional 0:80a6a628ad52 -0.13 0.10 sincerely,
letter-monogram_sidebar 0:136c67657614 -0.38 2.82 jordan
letter-monogram_sidebar 0:8ba35ebe3d5c -0.37 2.85 rivera
letter-monogram_sidebar 0:2d58f0cadc2d -0.19 0.13 platformengineer
-letter-monogram_sidebar 0:8d7a2cca3060 -0.17 -1.86 +442055551000|jordan.rivera@exam
+letter-monogram_sidebar 0:8d7a2cca3060 -0.17 0.24 +442055551000|jordan.rivera@exam
letter-monogram_sidebar 0:bef81b541365 -0.23 0.10 dearhiringteamatnorthwindsystems
letter-monogram_sidebar 0:ab54410ead82 0.12 0.10 iamexcitedtosharemyinterestinthe
letter-monogram_sidebar 0:9185168d2cf8 0.12 0.10 document-generationsystemsthatba
@@ -3645,7 +3645,7 @@ letter-nordic_clean 0:8f1010120ed6#1 -0.11 0.10 jordanrivera
letter-nordic_clean 0:2d58f0cadc2d -0.20 0.10 platformengineer
letter-nordic_clean 0:fbeea547d99e -0.20 -0.35 london,uk
letter-nordic_clean 0:89674e6f130e -0.18 -0.65 +442055551000
-letter-nordic_clean 0:e0f850305ded -0.16 -1.02 jordan.rivera@example.com
+letter-nordic_clean 0:e0f850305ded -0.16 0.13 jordan.rivera@example.com
letter-nordic_clean 0:84308bea4540 -0.14 -0.21 linkedin
letter-nordic_clean 0:c0b0109d9439 -0.12 -0.16 github
letter-nordic_clean 0:bef81b541365 -0.11 0.10 dearhiringteamatnorthwindsystems
@@ -3658,8 +3658,8 @@ letter-nordic_clean 0:80a6a628ad52 -0.14 0.10 sincerely,
letter-panel 0:8f1010120ed6 -0.40 8.42 jordanrivera
letter-panel 0:8f1010120ed6#1 0.16 0.10 jordanrivera
letter-panel 0:2d58f0cadc2d 0.12 8.12 platformengineer
-letter-panel 0:89ff05847e96 0.19 7.69 london,uk|+442055551000
-letter-panel 0:8817bf9310b6 0.21 7.40 jordan.rivera@example.com|linked
+letter-panel 0:89ff05847e96 0.19 8.39 london,uk|+442055551000
+letter-panel 0:8817bf9310b6 0.21 8.40 jordan.rivera@example.com|linked
letter-panel 0:bef81b541365 0.01 0.10 dearhiringteamatnorthwindsystems
letter-panel 0:ab54410ead82 -0.17 0.10 iamexcitedtosharemyinterestinthe
letter-panel 0:9185168d2cf8 -0.15 0.10 document-generationsystemsthatba
@@ -3671,7 +3671,7 @@ letter-panel 0:80a6a628ad52 0.15 0.10 sincerely,
letter-sidebar_portrait 0:8f1010120ed6 -0.73 0.14 jordanrivera
letter-sidebar_portrait 0:8f1010120ed6#1 -0.13 0.10 jordanrivera
letter-sidebar_portrait 0:2d58f0cadc2d -0.23 0.49 platformengineer
-letter-sidebar_portrait 0:8d7a2cca3060 -0.22 -3.82 +442055551000|jordan.rivera@exam
+letter-sidebar_portrait 0:8d7a2cca3060 -0.22 0.48 +442055551000|jordan.rivera@exam
letter-sidebar_portrait 0:bef81b541365 -0.28 0.10 dearhiringteamatnorthwindsystems
letter-sidebar_portrait 0:ab54410ead82 -0.51 0.10 iamexcitedtosharemyinterestinthe
letter-sidebar_portrait 0:9185168d2cf8 -0.49 0.10 document-generationsystemsthatba
@@ -4072,8 +4072,8 @@ proposal-northline 1:c74c9cd82f13 0.37 10.77 date:
proposal-northline 1:026890c58de9 0.01 0.10 northlinestudio|northlinestudio.
receipt-modern 0:2b019b5b46da 0.31 0.10 northwindpay
receipt-modern 0:a3c304a23bb1 0.30 0.16 transferconfirmation
-receipt-modern 0:e885a92f5bb7 0.18 -2.52 generatedon09august2026
-receipt-modern 0:c288dd6df716 0.19 -2.30 referencenwp-4821-0067
+receipt-modern 0:e885a92f5bb7 0.18 -0.02 generatedon09august2026
+receipt-modern 0:c288dd6df716 0.19 -0.20 referencenwp-4821-0067
receipt-modern 0:9de2a2d7cd7a 0.18 14.60 amountcollected
receipt-modern 0:465691680b99 0.03 14.60 £66.62
receipt-modern 0:465691680b99#1 0.29 -0.90 £66.62
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 8670808c5..9fc7c250f 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
@@ -5186,15 +5186,58 @@ private double oneLinesMeasure(ParagraphNode node, double room) {
&& span instanceof com.demcha.compose.document.layout.payloads.ParagraphTextSpan run
&& run.textStyle() != null && run.textStyle().size() > 0);
if (text) {
- // As wide as the page's line at least: a centred or right-aligned line of its own
- // that Word sets narrower has its letters spaced back to it
- // (setTheLineAsWideAsThePage), and needs the room past it as much as any.
- set = Math.max(set, Math.max(widthAtWordsSize(line), askedWidth(line)));
+ // As wide as Word ends up setting it: the page's width where its letters are
+ // spaced to it (setTheLineAsWideAsThePage), its own at Word's size where not.
+ set = Math.max(set, twentiethsToThePage(node) != 0 ? askedWidth(line) : widthAtWordsSize(line));
}
}
return Math.max(room, set * (1 + ONE_LINE_FACE_SLACK));
}
+ /**
+ * How far apart to space a centred or right-aligned line's letters so Word sets it as wide
+ * as the page does, in twentieths of a point; 0 where it is left as Word sets it.
+ *
+ * Only a left-to-right line of its own, which Word sets a point or more wider or narrower
+ * at its size to the half point ({@link #LINE_WIDTH_CLEARANCE}), with text and nothing else
+ * in it, none of it tracked. Under a point the size alone mispredicts what Word sets: measured,
+ * {@code ObsidianInvoice}'s 9.34pt amounts, half a point wider by it, came out as wide as the
+ * page's in Word, and spacing them moved them half a point off. Word sets tracking its own way
+ * and spaces the last letter too, which the size does not tell: spaced on its tracking,
+ * {@code TealPulse}'s tagline stood a point further off than it was. A picture or a shape is
+ * written at its own size, and the run beside it may be closed up to make room for it
+ * ({@link #giveBackWidth}). Twentieths, to the nearest: on the corpus they put 74 lines in
+ * Word nearer the page where whole tenths put 64.
+ */
+ private int twentiethsToThePage(ParagraphNode node) {
+ if (node.align() != TextAlign.CENTER && node.align() != TextAlign.RIGHT
+ || ParagraphDirection.resolve(node) == TextDirection.RTL || layout.lineCount(node) != 1) {
+ return 0;
+ }
+ List lines = layout.lines(node);
+ if (lines.size() != 1) {
+ return 0;
+ }
+ int letters = 0;
+ for (com.demcha.compose.document.layout.payloads.ParagraphSpan span : lines.get(0).spans()) {
+ if (!(span instanceof com.demcha.compose.document.layout.payloads.ParagraphTextSpan text)) {
+ if (span.width() > 0) {
+ return 0;
+ }
+ continue;
+ }
+ if (text.textStyle() == null || text.textStyle().letterSpacing() != 0) {
+ return 0;
+ }
+ letters += text.text() == null ? 0 : text.text().codePointCount(0, text.text().length());
+ }
+ double difference = askedWidth(lines.get(0)) - widthAtWordsSize(lines.get(0));
+ if (letters == 0 || !(Math.abs(difference) >= LINE_WIDTH_CLEARANCE)) {
+ return 0;
+ }
+ return (int) Math.round(difference / letters * 20.0);
+ }
+
/**
* How wide Word sets a line the page laid out: its text grown or shrunk to Word's size, its
* tracking, pictures and shapes as the page has them.
@@ -5809,59 +5852,30 @@ private void writeParagraph(XWPFDocument document, ParagraphNode node) {
*
* Word states a type size in half points, so it sets a size the page gives to the tenth a
* little larger or smaller, and a line that much wider or narrower. A line set from its left
- * keeps its first letter where the page puts it; a centred line moves it by half the
- * difference and a right-aligned one by all of it: {@code CenteredHeadline}'s 8.3pt contact
- * line, set at 8.5, stood 10pt wider in Word, its first letter 4pt left of the page's, and
- * {@code ClassicSerif}'s 8.7pt one 4pt right. The size is left as Word sets it; the
- * difference is spread over the letters as character spacing ({@code w:spacing}, in
- * twentieths of a point), so the line's ends stand within a fraction of a point of the
- * page's. Only a line of its own: a paragraph that wraps breaks at the page's
- * words through its measure ({@link #measureAtWordsSize}), and its lines' ends are the
- * measure's. Under a point it is left alone: {@code ObsidianInvoice}'s 9.34pt amounts, set
- * at 9.5 and half a point wider by the size alone, measured as wide as the page's in Word,
- * and spacing them moved them half a point off.
+ * keeps its first letter where the page puts it; a centred or right-aligned one moves it:
+ * {@code CenteredHeadline}'s 8.3pt contact line, set at 8.5, stood 10pt wider in Word and its
+ * first letter 4pt left of the page's, and {@code ClassicSerif}'s 8.7pt one 4pt right. The
+ * size is left as Word sets it; the difference is spread over the letters as character
+ * spacing ({@code w:spacing}), so the line's ends stand within a fraction of a point of the
+ * page's. Which lines, and by how much, is {@link #twentiethsToThePage}'s; their measure
+ * reckons with it ({@link #oneLinesMeasure}).
*/
private void setTheLineAsWideAsThePage(XWPFParagraph para, ParagraphNode node) {
- if (node.align() != TextAlign.CENTER && node.align() != TextAlign.RIGHT || layout.lineCount(node) != 1) {
- return;
- }
- List lines = layout.lines(node);
- if (lines.size() != 1) {
- return;
- }
- double difference = askedWidth(lines.get(0)) - widthAtWordsSize(lines.get(0));
- int letters = 0;
- // Every run, an internal link's included, which the paragraph's own list leaves out.
- List runs = runsIn(para).stream().map(run -> new XWPFRun(run, para)).toList();
- for (XWPFRun run : runs) {
- String text = run.text();
- letters += text == null ? 0 : text.codePointCount(0, text.length());
- if (run.getCharacterSpacing() != 0) {
- // Tracked already: Word rounds it its own way and spaces the last letter too,
- // which the size alone does not tell — TealPulse's tracked tagline, spaced on it,
- // stood a point further off than it was.
- return;
- }
- }
- if (letters == 0 || !(Math.abs(difference) >= LINE_WIDTH_CLEARANCE)) {
- return;
- }
- // In twentieths of a point, to the nearest: measured on the corpus, the twentieth brought
- // 75 lines in Word nearer the page and none further, whole tenths 64. A line widened back
- // to the page's has the page's room and a few hundredths more (oneLinesMeasure).
- int twentieths = (int) Math.round(difference / letters * 20.0);
+ int twentieths = twentiethsToThePage(node);
if (twentieths == 0) {
return;
}
+ // Every run, an internal link's included, which the paragraph's own list leaves out.
+ List runs = runsIn(para).stream()
+ .map(run -> new XWPFRun(run, (IRunBody) para)).toList();
for (XWPFRun run : runs) {
String text = run.text();
if (text != null && !text.isEmpty()) {
- run.setCharacterSpacing(run.getCharacterSpacing() + twentieths);
+ run.setCharacterSpacing(twentieths);
}
}
}
- /** How far a line of its own may stand from the page's width before its letters are spaced, in points. */
private static final double LINE_WIDTH_CLEARANCE = 1.0;
/**
diff --git a/render-docx/src/test/java/com/demcha/compose/document/backend/semantic/docx/DocxWordSizeMeasureTest.java b/render-docx/src/test/java/com/demcha/compose/document/backend/semantic/docx/DocxWordSizeMeasureTest.java
index f43d83459..c4bdee062 100644
--- a/render-docx/src/test/java/com/demcha/compose/document/backend/semantic/docx/DocxWordSizeMeasureTest.java
+++ b/render-docx/src/test/java/com/demcha/compose/document/backend/semantic/docx/DocxWordSizeMeasureTest.java
@@ -79,8 +79,8 @@ void aCentredLineIsLeftWhereThePageSetsIt() throws Exception {
void aCentredOrRightAlignedLineOfItsOwnIsSpacedToThePagesWidth() throws Exception {
// CenteredHeadline's 8.3pt contact line, set at 8.5, stood 10pt wider in Word, its first
// letter 4pt left of the page's. The difference is spread over its letters.
- // Word spaces letters in whole tenths, to the nearest: narrowed at 8.3pt (set at 8.5),
- // widened at 6.2pt (set at 6).
+ // In twentieths of a point, to the nearest: narrowed at 8.3pt (set at 8.5), widened at
+ // 6.2pt (set at 6).
int letters = SHORT.codePointCount(0, SHORT.length());
for (double size : new double[] {8.3, 6.2}) {
double asked = widthAlone(SHORT, size);
@@ -100,9 +100,9 @@ void aCentredOrRightAlignedLineOfItsOwnIsSpacedToThePagesWidth() throws Exceptio
@Test
void aLineSetFromItsLeftATrackedLineAndANearOneAreNotSpaced() throws Exception {
- // From its left, the line's first letter is the page's. A tracked line Word sets in
- // twentieths and spaces after its last letter too, which the size alone does not tell;
- // a difference under a point is within what Word rounds its glyphs by.
+ // From its left, the line's first letter is the page's. Word sets a tracked line's
+ // tracking its own way and spaces after its last letter too, which the size alone does
+ // not tell; under a point, the size alone mispredicted what Word set on the corpus.
DocumentTextStyle tracked = DocumentTextStyle.DEFAULT.withSize(8.3)
.withLetterSpacing(com.demcha.compose.document.style.DocumentLetterSpacing.points(1));
try (XWPFDocument left = DocxExports.withLayout(400, 400, 20, page -> page
@@ -131,6 +131,23 @@ void aShortCentredLineIsLeftWhereThePageSetsIt() throws Exception {
}
}
+ @Test
+ void aLineSpacedBackFromWiderIsGivenRoomForThePagesWidthOnly() throws Exception {
+ // Word sets 8.3pt at 8.5, wider; spaced back to the page's width, the line needs room for
+ // that and no more: in a cell, room the left edge cannot give comes off the right and
+ // would move a right-aligned line right.
+ double room = fillingRoom(HEADING, 8.3);
+ try (XWPFDocument document = filledBy(HEADING, 8.3, TextAlign.RIGHT, room)) {
+ XWPFParagraph heading = text(document, "INFORMATION");
+ double measure = room - (leftIndent(heading) + rightIndent(heading)) / 20.0;
+
+ double asked = widthAlone(HEADING, 8.3);
+ assertThat(measure).as("the page's line and a few hundredths, not Word's wider one")
+ .isCloseTo(asked * 1.03, org.assertj.core.data.Offset.offset(0.2))
+ .isLessThan(asked * 8.5 / 8.3 * 1.03 - 1);
+ }
+ }
+
@Test
void aCentredLineWordSetsNarrowerIsGivenRoomForThePagesWidth() throws Exception {
// Its letters are spaced back to the page's width, which needs the few hundredths of