diff --git a/CHANGELOG.md b/CHANGELOG.md index 89c2d7770..da9e9ca52 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,24 @@ follow semantic versioning; release dates are ISO 8601. ### Public API +- **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`, + `MinimalUnderlined` and `BoxedSections` set under each project's name started up to 3.4pt + short of the page's and wrapped back 6 to 7.9pt short, in Word; they stand within 0.07pt + now. A nested rich item with no marker now stands, every line of it, as far past the list's + edge as the layout set its text, where the layout's items are matched to the list's, and its + measure is weighed at Word's half-point sizes as a paragraph's is, so it breaks at the page's + words. A list that nests only such items sets its top level at the page's column too, its + marker a tab ahead of its text: kept at a space, the project's name started the gap less a + space left of its description. A nested item with a marker, one of plain text, one the + layout sets no further in than the list's edge, and every item of a list the layout did not + place or whose items it does not report one by one — an item split across pages — keep the + spaces, and so does the top level of a list that nests any of them. Lines more than 2pt off + across fall from 49 to 23 in Word, and in LibreOffice from 54 to 29 on Windows and 335 to + 310 on Linux; in LibreOffice the foot of `MinimalUnderlined`'s first page, 14pt high, comes + within 1.4pt of the page's, and lines more than 2pt off down fall from 127 to 118 on Windows + and 245 to 236 on Linux. - **A DOCX list without `hangingIndent` wraps where the page does.** The page sets an item's first line as the marker, a space and the text, and every wrapped line after the spaces that cover the marker and that space; the Word list's levels were a stated 9pt column, a tab diff --git a/docs/architecture/backend-capability-matrix.md b/docs/architecture/backend-capability-matrix.md index 0fdf905f5..c7111669a 100644 --- a/docs/architecture/backend-capability-matrix.md +++ b/docs/architecture/backend-capability-matrix.md @@ -63,7 +63,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 | -| 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) | +| 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) | | Inline vector shapes (`ParagraphShapeSpan`) | ✅ `PdfParagraphFragmentRenderHandler` | ⚠️ `PptxParagraphFragmentRenderHandler` + `PptxInlineGeometry` (distinct per-corner radii render with the top-left radius — single-adjust preset) | ⚠️ `DocxSemanticBackend.writeInlinePicture` + `DocxShapePictures` (a transparent PNG drawn by the shared `InlineSvgRasters` from the outline, fill and stroke — every outline kind, each layer centred in the run's box — placed as an inline picture is; the picture takes as far as the stroked ink reaches past the outline — half the stroke on an edge, more at a sharp corner's miter — and a pixel on each side, measured side by side, and is lowered by what it takes below, so no edge is cut and a shape takes that much more room in the line; a list marker that draws a disc is its picture; at the top level of a `hangingIndent(true)` list that does not nest it is followed by a tab to where the layout starts the item's text, the item's lines hanging there, when the picture clears that stop, else by a space) | diff --git a/docs/recipes/docx-export.md b/docs/recipes/docx-export.md index 1c4b715a3..104cde11d 100644 --- a/docs/recipes/docx-export.md +++ b/docs/recipes/docx-export.md @@ -407,7 +407,16 @@ items is laid out flattened, its markers in its text, and keeps the stated colum A list that nests items keeps stated columns throughout — its nested levels are not measured, and one kept stated could stand left of its parent's text. So does any other list the layout did not place: the column is then a stated constant, 180 twips plus 120 for each -nesting level, chosen near the single space the old text form left. +nesting level, chosen near the single space the old text form left. In a list of paragraphs, +a nested rich item with no marker — a description under a name — is the exception: every line +of it stands where the layout set its text, as far past the list's edge, rather than two +spaces a level in, and its measure is weighed at Word's half-point sizes as a paragraph's is, +so it breaks at the page's words. A list that nests only such items sets its top level at the +page's column too, its marker a tab ahead of its text, so a name and the description under it +start together. A nested item with a marker, one of plain text, one the layout sets no further +in than the list's edge, and every item of a list the layout did not place or whose items it +does not report one by one — an item split across pages — keep the spaces, and so does the +top level of a list that nests any of them. ## What a panel keeps and loses 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 56a4b7288..7a07db705 100644 --- a/qa/src/test/resources/docx-fidelity/libreoffice-linux-lines.tsv +++ b/qa/src/test/resources/docx-fidelity/libreoffice-linux-lines.tsv @@ -79,15 +79,15 @@ cv-boxed_sections 0:2669e1f6d55c -0.36 0.10 professionaltrack cv-boxed_sections 0:3857373829c1 -0.35 0.10 java17platformdeep-dive:records, cv-boxed_sections 0:2577c0f557b2 -0.33 6.18 projects cv-boxed_sections 0:b2b029995203 -0.36 0.10 •graphcompose(java21,pdfbox,mave -cv-boxed_sections 0:67593a76ef70 -0.66 -1.64 declarativejavapdflayoutengine.s -cv-boxed_sections 0:b9292cbd1454 -0.65 -6.24 pipelinesforhiringtoolsandbillin +cv-boxed_sections 0:67593a76ef70 -0.66 0.11 declarativejavapdflayoutengine.s +cv-boxed_sections 0:b9292cbd1454 -0.65 0.11 pipelinesforhiringtoolsandbillin cv-boxed_sections 0:1b50bb81df5a -0.35 0.10 •templatestudio(kotlin,composede -cv-boxed_sections 0:836e76daecbf -0.34 -1.64 internaltoolforevaluatingcv,prop +cv-boxed_sections 0:836e76daecbf -0.34 0.11 internaltoolforevaluatingcv,prop cv-boxed_sections 0:25cdca2beb8b -0.34 0.10 •layoutlint(java21,javaparser,sp -cv-boxed_sections 0:7488006d7864 -0.63 -1.64 staticanalyserthatflagsfragileau -cv-boxed_sections 0:c4d0c415b4f8 -0.63 -6.24 production. +cv-boxed_sections 0:7488006d7864 -0.63 0.11 staticanalyserthatflagsfragileau +cv-boxed_sections 0:c4d0c415b4f8 -0.63 0.11 production. cv-boxed_sections 0:fb1927f88597 -0.32 0.10 •chromeforge(java,graphcompose,p -cv-boxed_sections 0:e7e62230eb1f -0.32 -1.64 editorial-magazinedocumenttoolki +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 @@ -528,16 +528,16 @@ cv-executive 0:2669e1f6d55c -0.26 0.10 professionaltrack cv-executive 0:3857373829c1 -0.26 0.10 java17platformdeep-dive:records, cv-executive 0:2577c0f557b2 -0.07 0.10 projects cv-executive 0:b2b029995203 -0.28 0.10 •graphcompose(java21,pdfbox,mave -cv-executive 0:cf13c8a0c3d0 -0.53 -3.18 declarativejavapdflayoutengine.s -cv-executive 0:cfef54184ea6 -0.53 -7.78 proposalpipelinesforhiringtoolsa +cv-executive 0:cf13c8a0c3d0 -0.53 0.12 declarativejavapdflayoutengine.s +cv-executive 0:cfef54184ea6 -0.53 0.12 proposalpipelinesforhiringtoolsa cv-executive 0:1b50bb81df5a -0.28 0.10 •templatestudio(kotlin,composede -cv-executive 0:7127a108c33c -0.53 -3.18 internaltoolforevaluatingcv,prop -cv-executive 0:6b844249c695 -0.53 -7.78 freezing. +cv-executive 0:7127a108c33c -0.53 0.12 internaltoolforevaluatingcv,prop +cv-executive 0:6b844249c695 -0.53 0.12 freezing. cv-executive 0:25cdca2beb8b -0.28 0.10 •layoutlint(java21,javaparser,sp -cv-executive 0:f5fa2add7746 -0.53 -3.18 staticanalyserthatflagsfragileau -cv-executive 0:9f5414016bd1 -0.53 -7.78 toproduction. +cv-executive 0:f5fa2add7746 -0.53 0.12 staticanalyserthatflagsfragileau +cv-executive 0:9f5414016bd1 -0.53 0.12 toproduction. cv-executive 0:fb1927f88597 -0.28 0.10 •chromeforge(java,graphcompose,p -cv-executive 0:e7e62230eb1f -0.28 -3.18 editorial-magazinedocumenttoolki +cv-executive 0:e7e62230eb1f -0.28 0.12 editorial-magazinedocumenttoolki cv-executive 0:c2f296ef30a3 -0.09 0.10 professionalexperience cv-executive 0:a76f47480818 -0.30 0.10 seniorplatformengineer cv-executive 0:060300b18051 -0.30 0.17 2024-present @@ -671,18 +671,20 @@ cv-minimal_underlined 0:2669e1f6d55c -0.87 0.10 professionaltrack cv-minimal_underlined 0:3857373829c1 -0.86 0.10 java17platformdeep-dive:records, cv-minimal_underlined 0:2577c0f557b2 -0.83 0.10 projects cv-minimal_underlined 0:b2b029995203 -1.14 0.10 •graphcompose(java21,pdfbox,mave -cv-minimal_underlined 0:67593a76ef70 -1.44 -1.64 declarativejavapdflayoutengine.s -cv-minimal_underlined 0:b9292cbd1454 -1.43 -6.24 pipelinesforhiringtoolsandbillin +cv-minimal_underlined 0:67593a76ef70 -1.44 0.11 declarativejavapdflayoutengine.s +cv-minimal_underlined 0:b9292cbd1454 -1.43 0.11 pipelinesforhiringtoolsandbillin cv-minimal_underlined 0:1b50bb81df5a -1.13 0.10 •templatestudio(kotlin,composede -cv-minimal_underlined 0:25cdca2beb8b -13.91 0.10 •layoutlint(java21,javaparser,sp -cv-minimal_underlined 0:7488006d7864 -14.21 -1.64 staticanalyserthatflagsfragileau -cv-minimal_underlined 0:c4d0c415b4f8 -14.20 -6.24 production. -cv-minimal_underlined 0:fb1927f88597 -13.90 0.10 •chromeforge(java,graphcompose,p -cv-minimal_underlined 0:e7e62230eb1f -13.89 -1.64 editorial-magazinedocumenttoolki -cv-minimal_underlined 0:c2f296ef30a3 -13.86 0.10 professionalexperience -cv-minimal_underlined 0:a76f47480818 -14.15 0.10 seniorplatformengineer -cv-minimal_underlined 0:060300b18051 -14.19 -0.99 2024-present -cv-minimal_underlined 0:f890faef907f -14.16 0.10 northwindsystems +cv-minimal_underlined 0:7127a108c33c -1.42 0.11 internaltoolforevaluatingcv,prop +cv-minimal_underlined 0:6b844249c695 -1.42 0.11 freezing. +cv-minimal_underlined 0:25cdca2beb8b -1.11 0.10 •layoutlint(java21,javaparser,sp +cv-minimal_underlined 0:7488006d7864 -1.41 0.11 staticanalyserthatflagsfragileau +cv-minimal_underlined 0:c4d0c415b4f8 -1.40 0.11 production. +cv-minimal_underlined 0:fb1927f88597 -1.10 0.10 •chromeforge(java,graphcompose,p +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: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 cv-minimal_underlined 1:daf65306b3b6 0.20 0.10 softwareengineer @@ -787,17 +789,17 @@ cv-modern_professional 0:2669e1f6d55c -0.17 0.10 professionaltrack cv-modern_professional 0:3857373829c1 -0.11 0.10 java17platformdeep-dive:records, cv-modern_professional 0:2577c0f557b2 0.02 0.10 projects cv-modern_professional 0:b2b029995203 -0.11 0.10 •graphcompose(java21,pdfbox,mave -cv-modern_professional 0:dd39d2ff3520 -0.41 -0.35 declarativejavapdflayoutengine.s -cv-modern_professional 0:9ecaa6df6d41 -0.41 -5.90 invoice/proposalpipelinesforhiri +cv-modern_professional 0:dd39d2ff3520 -0.41 0.10 declarativejavapdflayoutengine.s +cv-modern_professional 0:9ecaa6df6d41 -0.41 0.10 invoice/proposalpipelinesforhiri cv-modern_professional 0:1b50bb81df5a -0.11 0.10 •templatestudio(kotlin,composede -cv-modern_professional 0:18eec9e76e2d -0.41 -0.35 internaltoolforevaluatingcv,prop -cv-modern_professional 0:6bc2807453c6 -0.41 -5.90 baselinefreezing. +cv-modern_professional 0:18eec9e76e2d -0.41 0.10 internaltoolforevaluatingcv,prop +cv-modern_professional 0:6bc2807453c6 -0.41 0.10 baselinefreezing. cv-modern_professional 0:25cdca2beb8b -0.11 0.10 •layoutlint(java21,javaparser,sp -cv-modern_professional 0:cc2850ddd8ba -0.41 -0.35 staticanalyserthatflagsfragileau -cv-modern_professional 0:7b985a1d2b08 -0.41 -5.90 shiptoproduction. +cv-modern_professional 0:cc2850ddd8ba -0.41 0.10 staticanalyserthatflagsfragileau +cv-modern_professional 0:7b985a1d2b08 -0.41 0.10 shiptoproduction. cv-modern_professional 0:fb1927f88597 -0.11 0.10 •chromeforge(java,graphcompose,p -cv-modern_professional 0:cda7db6c6ea6 -0.41 -0.35 editorial-magazinedocumenttoolki -cv-modern_professional 0:fe63fe18585a -0.41 -5.90 callouts. +cv-modern_professional 0:cda7db6c6ea6 -0.41 0.10 editorial-magazinedocumenttoolki +cv-modern_professional 0:fe63fe18585a -0.41 0.10 callouts. cv-modern_professional 0:c2f296ef30a3 0.03 0.10 professionalexperience cv-modern_professional 0:a76f47480818 -0.11 0.10 seniorplatformengineer cv-modern_professional 0:060300b18051 -0.10 0.15 2024-present @@ -1163,19 +1165,20 @@ cv-panel 0:090e2184c17b 0.33 0.16 pdfs/year)andauthoredthecomplian cv-panel 0:6f071fdf7529 0.35 0.16 gatedeverytemplatechange. cv-panel 0:2577c0f557b2 0.58 0.16 projects cv-panel 0:b2b029995203 0.36 0.16 •graphcompose(java21,pdfbox,mave +cv-panel 0:81c8132f9792 0.24 0.16 systems.(opensource) cv-panel 0:753f46e4e01b 0.26 0.16 •templatestudio(kotlin,composede -cv-panel 0:6eb35b5b9a91 0.28 -7.64 diff) -cv-panel 0:d919bd2d50b6 0.30 -3.04 internaltoolforevaluatingcv,prop -cv-panel 0:245b9e56b9de 0.32 -7.64 across14designpresets.pngdiffing -cv-panel 0:6bc2807453c6 0.34 -7.64 baselinefreezing. +cv-panel 0:6eb35b5b9a91 0.28 0.16 diff) +cv-panel 0:d919bd2d50b6 0.30 0.16 internaltoolforevaluatingcv,prop +cv-panel 0:245b9e56b9de 0.32 0.16 across14designpresets.pngdiffing +cv-panel 0:6bc2807453c6 0.34 0.16 baselinefreezing. cv-panel 0:25cdca2beb8b 0.56 0.16 •layoutlint(java21,javaparser,sp -cv-panel 0:30e034b9c7b2 0.38 -3.04 staticanalyserthatflagsfragileau -cv-panel 0:e7ad19d64668 0.40 -7.64 nestedrows,untypedoffsets,implic -cv-panel 0:95ac1a62c76f 0.42 -7.64 theyshiptoproduction. +cv-panel 0:30e034b9c7b2 0.38 0.16 staticanalyserthatflagsfragileau +cv-panel 0:e7ad19d64668 0.40 0.16 nestedrows,untypedoffsets,implic +cv-panel 0:95ac1a62c76f 0.42 0.16 theyshiptoproduction. cv-panel 0:fb1927f88597 0.64 0.16 •chromeforge(java,graphcompose,p -cv-panel 0:bb865a4ccac1 0.46 -3.04 editorial-magazinedocumenttoolki -cv-panel 0:8a31ccd889ee 0.48 -7.64 cinematiccovers,pullquotes,multi -cv-panel 0:fe63fe18585a 0.50 -7.64 callouts. +cv-panel 0:bb865a4ccac1 0.46 0.16 editorial-magazinedocumenttoolki +cv-panel 0:8a31ccd889ee 0.48 0.16 cinematiccovers,pullquotes,multi +cv-panel 0:fe63fe18585a 0.50 0.16 callouts. cv-professional_sidebar 0:093e7d5fdbaa -0.23 0.10 contact cv-professional_sidebar 0:7f459725e1f6 -0.03 0.00 +12345678900 cv-professional_sidebar 0:52bf11c1afe0 -0.08 0.00 your.email@example.com diff --git a/qa/src/test/resources/docx-fidelity/libreoffice-linux.tsv b/qa/src/test/resources/docx-fidelity/libreoffice-linux.tsv index 5d81c6796..0333713a2 100644 --- a/qa/src/test/resources/docx-fidelity/libreoffice-linux.tsv +++ b/qa/src/test/resources/docx-fidelity/libreoffice-linux.tsv @@ -10,14 +10,14 @@ cv-editorial_blue 2 2 82 82 0.08 0.30 0 cv-engineering_resume 1 1 57 57 0.60 1.62 0 cv-executive 2 2 60 60 0.26 0.50 0 cv-midnight_navy 1 1 86 84 0.28 0.87 0 -cv-minimal_underlined 2 2 59 57 0.71 13.90 9 +cv-minimal_underlined 2 2 59 59 0.72 1.39 0 cv-mint_editorial 2 2 59 59 0.27 0.60 0 cv-modern_professional 2 2 61 59 0.12 0.41 0 cv-monogram_sidebar 1 1 53 50 0.25 0.62 0 cv-navy_sidebar 1 1 71 71 0.20 0.45 0 cv-nordic_clean 1 1 63 58 0.22 0.52 0 cv-orange_ops 1 1 108 106 0.26 0.53 0 -cv-panel 1 1 79 73 0.27 0.49 0 +cv-panel 1 1 79 74 0.27 0.49 0 cv-professional_sidebar 1 1 68 64 0.36 0.58 0 cv-serif_headline 1 1 103 103 0.44 0.88 0 cv-sidebar_portrait 1 1 49 49 0.17 0.31 0 diff --git a/qa/src/test/resources/docx-fidelity/libreoffice-windows-lines.tsv b/qa/src/test/resources/docx-fidelity/libreoffice-windows-lines.tsv index 3287e6c01..5bfa1bc50 100644 --- a/qa/src/test/resources/docx-fidelity/libreoffice-windows-lines.tsv +++ b/qa/src/test/resources/docx-fidelity/libreoffice-windows-lines.tsv @@ -79,15 +79,15 @@ cv-boxed_sections 0:2669e1f6d55c -0.36 0.10 professionaltrack cv-boxed_sections 0:3857373829c1 -0.35 0.10 java17platformdeep-dive:records, cv-boxed_sections 0:2577c0f557b2 -0.33 1.18 projects cv-boxed_sections 0:b2b029995203 -0.36 0.10 •graphcompose(java21,pdfbox,mave -cv-boxed_sections 0:67593a76ef70 -0.66 -1.69 declarativejavapdflayoutengine.s -cv-boxed_sections 0:b9292cbd1454 -0.65 -6.24 pipelinesforhiringtoolsandbillin +cv-boxed_sections 0:67593a76ef70 -0.66 0.11 declarativejavapdflayoutengine.s +cv-boxed_sections 0:b9292cbd1454 -0.65 0.11 pipelinesforhiringtoolsandbillin cv-boxed_sections 0:1b50bb81df5a -0.35 0.10 •templatestudio(kotlin,composede -cv-boxed_sections 0:836e76daecbf -0.34 -1.69 internaltoolforevaluatingcv,prop +cv-boxed_sections 0:836e76daecbf -0.34 0.11 internaltoolforevaluatingcv,prop cv-boxed_sections 0:25cdca2beb8b -0.34 0.10 •layoutlint(java21,javaparser,sp -cv-boxed_sections 0:7488006d7864 -0.63 -1.69 staticanalyserthatflagsfragileau -cv-boxed_sections 0:c4d0c415b4f8 -0.63 -6.24 production. +cv-boxed_sections 0:7488006d7864 -0.63 0.11 staticanalyserthatflagsfragileau +cv-boxed_sections 0:c4d0c415b4f8 -0.63 0.11 production. cv-boxed_sections 0:fb1927f88597 -0.32 0.10 •chromeforge(java,graphcompose,p -cv-boxed_sections 0:e7e62230eb1f -0.32 -1.69 editorial-magazinedocumenttoolki +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 @@ -528,16 +528,16 @@ cv-executive 0:2669e1f6d55c -0.26 0.10 professionaltrack cv-executive 0:3857373829c1 -0.26 0.10 java17platformdeep-dive:records, cv-executive 0:2577c0f557b2 -0.07 0.10 projects cv-executive 0:b2b029995203 -0.28 0.10 •graphcompose(java21,pdfbox,mave -cv-executive 0:cf13c8a0c3d0 -0.53 -3.18 declarativejavapdflayoutengine.s -cv-executive 0:cfef54184ea6 -0.53 -7.78 proposalpipelinesforhiringtoolsa +cv-executive 0:cf13c8a0c3d0 -0.53 0.12 declarativejavapdflayoutengine.s +cv-executive 0:cfef54184ea6 -0.53 0.12 proposalpipelinesforhiringtoolsa cv-executive 0:1b50bb81df5a -0.28 0.10 •templatestudio(kotlin,composede -cv-executive 0:7127a108c33c -0.53 -3.18 internaltoolforevaluatingcv,prop -cv-executive 0:6b844249c695 -0.53 -7.78 freezing. +cv-executive 0:7127a108c33c -0.53 0.12 internaltoolforevaluatingcv,prop +cv-executive 0:6b844249c695 -0.53 0.12 freezing. cv-executive 0:25cdca2beb8b -0.28 0.10 •layoutlint(java21,javaparser,sp -cv-executive 0:f5fa2add7746 -0.53 -3.18 staticanalyserthatflagsfragileau -cv-executive 0:9f5414016bd1 -0.53 -7.78 toproduction. +cv-executive 0:f5fa2add7746 -0.53 0.12 staticanalyserthatflagsfragileau +cv-executive 0:9f5414016bd1 -0.53 0.12 toproduction. cv-executive 0:fb1927f88597 -0.28 0.10 •chromeforge(java,graphcompose,p -cv-executive 0:e7e62230eb1f -0.28 -3.18 editorial-magazinedocumenttoolki +cv-executive 0:e7e62230eb1f -0.28 0.12 editorial-magazinedocumenttoolki cv-executive 0:c2f296ef30a3 -0.09 0.10 professionalexperience cv-executive 0:a76f47480818 -0.30 0.10 seniorplatformengineer cv-executive 0:060300b18051 -0.30 0.22 2024-present @@ -671,18 +671,20 @@ cv-minimal_underlined 0:2669e1f6d55c -0.87 0.10 professionaltrack cv-minimal_underlined 0:3857373829c1 -0.86 0.10 java17platformdeep-dive:records, cv-minimal_underlined 0:2577c0f557b2 -0.83 0.10 projects cv-minimal_underlined 0:b2b029995203 -1.14 0.10 •graphcompose(java21,pdfbox,mave -cv-minimal_underlined 0:67593a76ef70 -1.44 -1.69 declarativejavapdflayoutengine.s -cv-minimal_underlined 0:b9292cbd1454 -1.43 -6.24 pipelinesforhiringtoolsandbillin +cv-minimal_underlined 0:67593a76ef70 -1.44 0.11 declarativejavapdflayoutengine.s +cv-minimal_underlined 0:b9292cbd1454 -1.43 0.11 pipelinesforhiringtoolsandbillin cv-minimal_underlined 0:1b50bb81df5a -1.13 0.10 •templatestudio(kotlin,composede -cv-minimal_underlined 0:25cdca2beb8b -13.91 0.10 •layoutlint(java21,javaparser,sp -cv-minimal_underlined 0:7488006d7864 -14.21 -1.69 staticanalyserthatflagsfragileau -cv-minimal_underlined 0:c4d0c415b4f8 -14.20 -6.24 production. -cv-minimal_underlined 0:fb1927f88597 -13.90 0.10 •chromeforge(java,graphcompose,p -cv-minimal_underlined 0:e7e62230eb1f -13.89 -1.69 editorial-magazinedocumenttoolki -cv-minimal_underlined 0:c2f296ef30a3 -13.86 0.10 professionalexperience -cv-minimal_underlined 0:a76f47480818 -14.15 0.10 seniorplatformengineer -cv-minimal_underlined 0:060300b18051 -14.19 -0.99 2024-present -cv-minimal_underlined 0:f890faef907f -14.16 0.10 northwindsystems +cv-minimal_underlined 0:7127a108c33c -1.42 0.11 internaltoolforevaluatingcv,prop +cv-minimal_underlined 0:6b844249c695 -1.42 0.11 freezing. +cv-minimal_underlined 0:25cdca2beb8b -1.11 0.10 •layoutlint(java21,javaparser,sp +cv-minimal_underlined 0:7488006d7864 -1.41 0.11 staticanalyserthatflagsfragileau +cv-minimal_underlined 0:c4d0c415b4f8 -1.40 0.11 production. +cv-minimal_underlined 0:fb1927f88597 -1.10 0.10 •chromeforge(java,graphcompose,p +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: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 cv-minimal_underlined 1:daf65306b3b6 0.20 0.10 softwareengineer @@ -787,17 +789,17 @@ cv-modern_professional 0:2669e1f6d55c -0.17 0.10 professionaltrack cv-modern_professional 0:3857373829c1 -0.11 0.10 java17platformdeep-dive:records, cv-modern_professional 0:2577c0f557b2 0.02 0.10 projects cv-modern_professional 0:b2b029995203 -0.11 0.10 •graphcompose(java21,pdfbox,mave -cv-modern_professional 0:dd39d2ff3520 -0.41 -0.35 declarativejavapdflayoutengine.s -cv-modern_professional 0:9ecaa6df6d41 -0.41 -5.90 invoice/proposalpipelinesforhiri +cv-modern_professional 0:dd39d2ff3520 -0.41 0.10 declarativejavapdflayoutengine.s +cv-modern_professional 0:9ecaa6df6d41 -0.41 0.10 invoice/proposalpipelinesforhiri cv-modern_professional 0:1b50bb81df5a -0.11 0.10 •templatestudio(kotlin,composede -cv-modern_professional 0:18eec9e76e2d -0.41 -0.35 internaltoolforevaluatingcv,prop -cv-modern_professional 0:6bc2807453c6 -0.41 -5.90 baselinefreezing. +cv-modern_professional 0:18eec9e76e2d -0.41 0.10 internaltoolforevaluatingcv,prop +cv-modern_professional 0:6bc2807453c6 -0.41 0.10 baselinefreezing. cv-modern_professional 0:25cdca2beb8b -0.11 0.10 •layoutlint(java21,javaparser,sp -cv-modern_professional 0:cc2850ddd8ba -0.41 -0.35 staticanalyserthatflagsfragileau -cv-modern_professional 0:7b985a1d2b08 -0.41 -5.90 shiptoproduction. +cv-modern_professional 0:cc2850ddd8ba -0.41 0.10 staticanalyserthatflagsfragileau +cv-modern_professional 0:7b985a1d2b08 -0.41 0.10 shiptoproduction. cv-modern_professional 0:fb1927f88597 -0.11 0.10 •chromeforge(java,graphcompose,p -cv-modern_professional 0:cda7db6c6ea6 -0.41 -0.35 editorial-magazinedocumenttoolki -cv-modern_professional 0:fe63fe18585a -0.41 -5.90 callouts. +cv-modern_professional 0:cda7db6c6ea6 -0.41 0.10 editorial-magazinedocumenttoolki +cv-modern_professional 0:fe63fe18585a -0.41 0.10 callouts. cv-modern_professional 0:c2f296ef30a3 0.03 0.10 professionalexperience cv-modern_professional 0:a76f47480818 -0.11 0.10 seniorplatformengineer cv-modern_professional 0:060300b18051 -0.10 0.20 2024-present @@ -1062,19 +1064,20 @@ cv-panel 0:090e2184c17b 0.33 0.16 pdfs/year)andauthoredthecomplian cv-panel 0:6f071fdf7529 0.35 0.16 gatedeverytemplatechange. cv-panel 0:2577c0f557b2 0.58 0.16 projects cv-panel 0:b2b029995203 0.36 0.16 •graphcompose(java21,pdfbox,mave +cv-panel 0:81c8132f9792 0.24 0.16 systems.(opensource) cv-panel 0:753f46e4e01b 0.26 0.16 •templatestudio(kotlin,composede -cv-panel 0:6eb35b5b9a91 0.28 -7.64 diff) -cv-panel 0:d919bd2d50b6 0.30 -3.04 internaltoolforevaluatingcv,prop -cv-panel 0:245b9e56b9de 0.32 -7.64 across14designpresets.pngdiffing -cv-panel 0:6bc2807453c6 0.34 -7.64 baselinefreezing. +cv-panel 0:6eb35b5b9a91 0.28 0.16 diff) +cv-panel 0:d919bd2d50b6 0.30 0.16 internaltoolforevaluatingcv,prop +cv-panel 0:245b9e56b9de 0.32 0.16 across14designpresets.pngdiffing +cv-panel 0:6bc2807453c6 0.34 0.16 baselinefreezing. cv-panel 0:25cdca2beb8b 0.56 0.16 •layoutlint(java21,javaparser,sp -cv-panel 0:30e034b9c7b2 0.38 -3.04 staticanalyserthatflagsfragileau -cv-panel 0:e7ad19d64668 0.40 -7.64 nestedrows,untypedoffsets,implic -cv-panel 0:95ac1a62c76f 0.42 -7.64 theyshiptoproduction. +cv-panel 0:30e034b9c7b2 0.38 0.16 staticanalyserthatflagsfragileau +cv-panel 0:e7ad19d64668 0.40 0.16 nestedrows,untypedoffsets,implic +cv-panel 0:95ac1a62c76f 0.42 0.16 theyshiptoproduction. cv-panel 0:fb1927f88597 0.64 0.16 •chromeforge(java,graphcompose,p -cv-panel 0:bb865a4ccac1 0.46 -3.04 editorial-magazinedocumenttoolki -cv-panel 0:8a31ccd889ee 0.48 -7.64 cinematiccovers,pullquotes,multi -cv-panel 0:fe63fe18585a 0.50 -7.64 callouts. +cv-panel 0:bb865a4ccac1 0.46 0.16 editorial-magazinedocumenttoolki +cv-panel 0:8a31ccd889ee 0.48 0.16 cinematiccovers,pullquotes,multi +cv-panel 0:fe63fe18585a 0.50 0.16 callouts. cv-professional_sidebar 0:093e7d5fdbaa -0.23 0.10 contact cv-professional_sidebar 0:7f459725e1f6 -0.03 -0.04 +12345678900 cv-professional_sidebar 0:52bf11c1afe0 -0.08 -0.04 your.email@example.com diff --git a/qa/src/test/resources/docx-fidelity/libreoffice-windows.tsv b/qa/src/test/resources/docx-fidelity/libreoffice-windows.tsv index c85cb30a6..da7ee94ec 100644 --- a/qa/src/test/resources/docx-fidelity/libreoffice-windows.tsv +++ b/qa/src/test/resources/docx-fidelity/libreoffice-windows.tsv @@ -10,14 +10,14 @@ cv-editorial_blue 2 2 82 82 0.08 0.30 0 cv-engineering_resume 1 1 57 57 0.60 1.62 0 cv-executive 2 2 60 60 0.26 0.50 0 cv-midnight_navy 1 1 86 84 0.28 0.87 0 -cv-minimal_underlined 2 2 59 57 0.71 13.90 9 +cv-minimal_underlined 2 2 59 59 0.72 1.39 0 cv-mint_editorial 2 2 59 59 0.27 0.60 0 cv-modern_professional 2 2 61 59 0.12 0.41 0 cv-monogram_sidebar 1 1 53 50 0.25 0.62 0 cv-navy_sidebar 1 1 71 71 0.20 0.45 0 cv-nordic_clean 1 1 63 58 0.20 0.57 0 cv-orange_ops 1 2 108 5 77.92 77.92 5 -cv-panel 1 1 79 73 0.27 0.49 0 +cv-panel 1 1 79 74 0.27 0.49 0 cv-professional_sidebar 1 1 68 64 0.36 0.58 0 cv-serif_headline 1 1 103 103 0.44 0.88 0 cv-sidebar_portrait 1 1 49 49 0.17 0.31 0 diff --git a/qa/src/test/resources/docx-fidelity/word-windows-lines.tsv b/qa/src/test/resources/docx-fidelity/word-windows-lines.tsv index cf6b31c58..fc4171baa 100644 --- a/qa/src/test/resources/docx-fidelity/word-windows-lines.tsv +++ b/qa/src/test/resources/docx-fidelity/word-windows-lines.tsv @@ -79,15 +79,15 @@ cv-boxed_sections 0:2669e1f6d55c -0.34 -0.08 professionaltrack cv-boxed_sections 0:3857373829c1 -0.28 -0.08 java17platformdeep-dive:records, cv-boxed_sections 0:2577c0f557b2 -0.27 0.15 projects cv-boxed_sections 0:b2b029995203 -0.32 -0.08 •graphcompose(java21,pdfbox,mave -cv-boxed_sections 0:67593a76ef70 -0.59 -1.86 declarativejavapdflayoutengine.s -cv-boxed_sections 0:b9292cbd1454 -0.55 -6.42 pipelinesforhiringtoolsandbillin +cv-boxed_sections 0:67593a76ef70 -0.59 -0.06 declarativejavapdflayoutengine.s +cv-boxed_sections 0:b9292cbd1454 -0.55 -0.06 pipelinesforhiringtoolsandbillin cv-boxed_sections 0:1b50bb81df5a -0.31 -0.08 •templatestudio(kotlin,composede -cv-boxed_sections 0:836e76daecbf -0.23 -1.86 internaltoolforevaluatingcv,prop +cv-boxed_sections 0:836e76daecbf -0.23 -0.06 internaltoolforevaluatingcv,prop cv-boxed_sections 0:25cdca2beb8b -0.26 -0.08 •layoutlint(java21,javaparser,sp -cv-boxed_sections 0:7488006d7864 -0.54 -1.86 staticanalyserthatflagsfragileau -cv-boxed_sections 0:c4d0c415b4f8 -0.49 -6.42 production. +cv-boxed_sections 0:7488006d7864 -0.54 -0.06 staticanalyserthatflagsfragileau +cv-boxed_sections 0:c4d0c415b4f8 -0.49 -0.06 production. cv-boxed_sections 0:fb1927f88597 -0.29 -0.08 •chromeforge(java,graphcompose,p -cv-boxed_sections 0:e7e62230eb1f -0.20 -1.86 editorial-magazinedocumenttoolki +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 @@ -528,16 +528,16 @@ cv-executive 0:2669e1f6d55c -0.17 -0.04 professionaltrack cv-executive 0:3857373829c1 -0.15 -0.04 java17platformdeep-dive:records, cv-executive 0:2577c0f557b2 0.37 -0.04 projects cv-executive 0:b2b029995203 -0.19 -0.04 •graphcompose(java21,pdfbox,mave -cv-executive 0:cf13c8a0c3d0 -0.43 -3.36 declarativejavapdflayoutengine.s -cv-executive 0:cfef54184ea6 -0.48 -7.92 proposalpipelinesforhiringtoolsa +cv-executive 0:cf13c8a0c3d0 -0.43 0.00 declarativejavapdflayoutengine.s +cv-executive 0:cfef54184ea6 -0.48 0.00 proposalpipelinesforhiringtoolsa cv-executive 0:1b50bb81df5a -0.20 -0.04 •templatestudio(kotlin,composede -cv-executive 0:7127a108c33c -0.44 -3.36 internaltoolforevaluatingcv,prop -cv-executive 0:6b844249c695 -0.46 -7.92 freezing. +cv-executive 0:7127a108c33c -0.44 0.00 internaltoolforevaluatingcv,prop +cv-executive 0:6b844249c695 -0.46 0.00 freezing. cv-executive 0:25cdca2beb8b -0.18 -0.04 •layoutlint(java21,javaparser,sp -cv-executive 0:f5fa2add7746 -0.42 -3.36 staticanalyserthatflagsfragileau -cv-executive 0:9f5414016bd1 -0.47 -7.92 toproduction. +cv-executive 0:f5fa2add7746 -0.42 0.00 staticanalyserthatflagsfragileau +cv-executive 0:9f5414016bd1 -0.47 0.00 toproduction. cv-executive 0:fb1927f88597 -0.19 -0.04 •chromeforge(java,graphcompose,p -cv-executive 0:e7e62230eb1f -0.19 -3.36 editorial-magazinedocumenttoolki +cv-executive 0:e7e62230eb1f -0.19 0.00 editorial-magazinedocumenttoolki cv-executive 0:c2f296ef30a3 0.45 -0.04 professionalexperience cv-executive 0:a76f47480818 -0.15 -0.04 seniorplatformengineer cv-executive 0:060300b18051 -0.15 0.06 2024-present @@ -671,16 +671,16 @@ cv-minimal_underlined 0:2669e1f6d55c -0.83 0.00 professionaltrack cv-minimal_underlined 0:3857373829c1 -0.77 0.00 java17platformdeep-dive:records, cv-minimal_underlined 0:2577c0f557b2 -0.67 0.04 projects cv-minimal_underlined 0:b2b029995203 -1.08 0.00 •graphcompose(java21,pdfbox,mave -cv-minimal_underlined 0:67593a76ef70 -1.35 -1.78 declarativejavapdflayoutengine.s -cv-minimal_underlined 0:b9292cbd1454 -1.31 -6.34 pipelinesforhiringtoolsandbillin +cv-minimal_underlined 0:67593a76ef70 -1.35 0.02 declarativejavapdflayoutengine.s +cv-minimal_underlined 0:b9292cbd1454 -1.31 0.02 pipelinesforhiringtoolsandbillin cv-minimal_underlined 0:1b50bb81df5a -1.10 0.00 •templatestudio(kotlin,composede -cv-minimal_underlined 0:7127a108c33c -1.35 -1.78 internaltoolforevaluatingcv,prop -cv-minimal_underlined 0:6b844249c695 -1.30 -6.34 freezing. +cv-minimal_underlined 0:7127a108c33c -1.35 0.02 internaltoolforevaluatingcv,prop +cv-minimal_underlined 0:6b844249c695 -1.30 0.02 freezing. cv-minimal_underlined 0:25cdca2beb8b -0.98 0.00 •layoutlint(java21,javaparser,sp -cv-minimal_underlined 0:7488006d7864 -1.25 -1.78 staticanalyserthatflagsfragileau -cv-minimal_underlined 0:c4d0c415b4f8 -1.21 -6.34 production. +cv-minimal_underlined 0:7488006d7864 -1.25 0.02 staticanalyserthatflagsfragileau +cv-minimal_underlined 0:c4d0c415b4f8 -1.21 0.02 production. cv-minimal_underlined 0:fb1927f88597 -1.00 0.00 •chromeforge(java,graphcompose,p -cv-minimal_underlined 0:e7e62230eb1f -0.92 -1.78 editorial-magazinedocumenttoolki +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 @@ -789,17 +789,17 @@ cv-modern_professional 0:2669e1f6d55c -0.13 0.00 professionaltrack cv-modern_professional 0:3857373829c1 0.00 0.00 java17platformdeep-dive:records, cv-modern_professional 0:2577c0f557b2 0.10 0.00 projects cv-modern_professional 0:b2b029995203 -0.04 0.00 •graphcompose(java21,pdfbox,mave -cv-modern_professional 0:dd39d2ff3520 -0.41 -0.48 declarativejavapdflayoutengine.s -cv-modern_professional 0:9ecaa6df6d41 -0.21 -6.00 invoice/proposalpipelinesforhiri +cv-modern_professional 0:dd39d2ff3520 -0.41 0.00 declarativejavapdflayoutengine.s +cv-modern_professional 0:9ecaa6df6d41 -0.21 0.00 invoice/proposalpipelinesforhiri cv-modern_professional 0:1b50bb81df5a -0.04 0.00 •templatestudio(kotlin,composede -cv-modern_professional 0:18eec9e76e2d -0.41 -0.48 internaltoolforevaluatingcv,prop -cv-modern_professional 0:6bc2807453c6 -0.21 -6.00 baselinefreezing. +cv-modern_professional 0:18eec9e76e2d -0.41 0.00 internaltoolforevaluatingcv,prop +cv-modern_professional 0:6bc2807453c6 -0.21 0.00 baselinefreezing. cv-modern_professional 0:25cdca2beb8b -0.04 0.00 •layoutlint(java21,javaparser,sp -cv-modern_professional 0:cc2850ddd8ba -0.41 -0.48 staticanalyserthatflagsfragileau -cv-modern_professional 0:7b985a1d2b08 -0.21 -6.00 shiptoproduction. +cv-modern_professional 0:cc2850ddd8ba -0.41 0.00 staticanalyserthatflagsfragileau +cv-modern_professional 0:7b985a1d2b08 -0.21 0.00 shiptoproduction. cv-modern_professional 0:fb1927f88597 -0.04 0.00 •chromeforge(java,graphcompose,p -cv-modern_professional 0:cda7db6c6ea6 -0.41 -0.48 editorial-magazinedocumenttoolki -cv-modern_professional 0:fe63fe18585a -0.21 -6.00 callouts. +cv-modern_professional 0:cda7db6c6ea6 -0.41 0.00 editorial-magazinedocumenttoolki +cv-modern_professional 0:fe63fe18585a -0.21 0.00 callouts. cv-modern_professional 0:c2f296ef30a3 0.14 0.00 professionalexperience cv-modern_professional 0:a76f47480818 0.08 0.00 seniorplatformengineer cv-modern_professional 0:060300b18051 0.08 0.17 2024-present @@ -1163,19 +1163,20 @@ cv-panel 0:090e2184c17b 0.35 0.05 pdfs/year)andauthoredthecomplian cv-panel 0:6f071fdf7529 0.35 0.05 gatedeverytemplatechange. cv-panel 0:2577c0f557b2 0.95 0.05 projects cv-panel 0:b2b029995203 0.43 0.05 •graphcompose(java21,pdfbox,mave +cv-panel 0:81c8132f9792 0.31 0.07 systems.(opensource) cv-panel 0:753f46e4e01b 0.34 0.05 •templatestudio(kotlin,composede -cv-panel 0:6eb35b5b9a91 0.34 -7.75 diff) -cv-panel 0:d919bd2d50b6 0.34 -3.17 internaltoolforevaluatingcv,prop -cv-panel 0:245b9e56b9de 0.34 -7.75 across14designpresets.pngdiffing -cv-panel 0:6bc2807453c6 0.46 -7.75 baselinefreezing. +cv-panel 0:6eb35b5b9a91 0.34 0.07 diff) +cv-panel 0:d919bd2d50b6 0.34 0.07 internaltoolforevaluatingcv,prop +cv-panel 0:245b9e56b9de 0.34 0.07 across14designpresets.pngdiffing +cv-panel 0:6bc2807453c6 0.46 0.07 baselinefreezing. cv-panel 0:25cdca2beb8b 0.70 0.05 •layoutlint(java21,javaparser,sp -cv-panel 0:30e034b9c7b2 0.46 -3.17 staticanalyserthatflagsfragileau -cv-panel 0:e7ad19d64668 0.46 -7.75 nestedrows,untypedoffsets,implic -cv-panel 0:95ac1a62c76f 0.46 -7.75 theyshiptoproduction. +cv-panel 0:30e034b9c7b2 0.46 0.07 staticanalyserthatflagsfragileau +cv-panel 0:e7ad19d64668 0.46 0.07 nestedrows,untypedoffsets,implic +cv-panel 0:95ac1a62c76f 0.46 0.07 theyshiptoproduction. cv-panel 0:fb1927f88597 0.70 0.05 •chromeforge(java,graphcompose,p -cv-panel 0:bb865a4ccac1 0.58 -3.17 editorial-magazinedocumenttoolki -cv-panel 0:8a31ccd889ee 0.58 -7.75 cinematiccovers,pullquotes,multi -cv-panel 0:fe63fe18585a 0.58 -7.75 callouts. +cv-panel 0:bb865a4ccac1 0.58 0.07 editorial-magazinedocumenttoolki +cv-panel 0:8a31ccd889ee 0.58 0.07 cinematiccovers,pullquotes,multi +cv-panel 0:fe63fe18585a 0.58 0.07 callouts. cv-professional_sidebar 0:093e7d5fdbaa -0.17 -0.04 contact cv-professional_sidebar 0:7f459725e1f6 0.05 -0.15 +12345678900 cv-professional_sidebar 0:52bf11c1afe0 -0.07 -0.19 your.email@example.com diff --git a/qa/src/test/resources/docx-fidelity/word-windows.tsv b/qa/src/test/resources/docx-fidelity/word-windows.tsv index 2e0fb9033..810331312 100644 --- a/qa/src/test/resources/docx-fidelity/word-windows.tsv +++ b/qa/src/test/resources/docx-fidelity/word-windows.tsv @@ -17,7 +17,7 @@ cv-monogram_sidebar 1 1 53 50 0.28 0.67 0 cv-navy_sidebar 1 1 71 69 0.13 0.36 0 cv-nordic_clean 1 1 63 58 0.22 0.61 0 cv-orange_ops 1 1 108 106 0.29 0.71 0 -cv-panel 1 1 79 73 0.34 0.67 0 +cv-panel 1 1 79 74 0.34 0.67 0 cv-professional_sidebar 1 1 68 64 0.30 0.49 0 cv-serif_headline 1 1 103 103 0.50 1.03 0 cv-sidebar_portrait 1 1 49 49 0.15 0.29 0 diff --git a/render-docx/src/main/java/com/demcha/compose/document/backend/semantic/docx/DocxLayoutMetrics.java b/render-docx/src/main/java/com/demcha/compose/document/backend/semantic/docx/DocxLayoutMetrics.java index 1a119fa71..0b6787630 100644 --- a/render-docx/src/main/java/com/demcha/compose/document/backend/semantic/docx/DocxLayoutMetrics.java +++ b/render-docx/src/main/java/com/demcha/compose/document/backend/semantic/docx/DocxLayoutMetrics.java @@ -373,13 +373,14 @@ OptionalDouble placedWidth(DocumentNode node) { *
A list lays each item out as a fragment of its own. A list whose markers stand in a * column of their own lays the marker out as one more fragment, level with its item's * text and as tall: the two are one item, its text's lines the item's, and the marker's - * single line does not make the item a wrapped one.
+ * single line does not make the item a wrapped one. The text is the fragment to the right, + * or the later where the two start together — a marker that draws nothing, with no gap. * * @param list a list node - * @return every item's lines, empty when the list laid out nothing + * @return every item's text, empty when the list laid out nothing */ - ListMoved to the page's place, the item has the page's measure exactly, and Word sets a + * size the page gives to the tenth at the half point: measured at the page's place alone, + * {@code Panel}'s description of its magazine toolkit broke its first line a word sooner in + * Word than the page does. Its lines are + * weighed as a paragraph's are ({@link #wordsMeasure(List, double)}), and the right indent + * gives the difference or takes it.
+ * + * @return whether it was moved + */ + private boolean indentAsTheItemIs(XWPFParagraph para, DocxLayoutMetrics.ItemText laidOut) { + double past = laidOut == null || !Double.isFinite(listTextLeft) ? Double.NaN : laidOut.x() - listTextLeft; + if (!(past > 0.05)) { + return false; + } + CTPPr properties = para.getCTP().isSetPPr() ? para.getCTP().getPPr() : para.getCTP().addNewPPr(); + CTInd indent = properties.isSetInd() ? properties.getInd() : properties.addNewInd(); + // From the insets, as applyInset writes them, not from the indent they left: in a cell + // that is never below its edge, and an item past it there still stands its distance in. + indent.setLeft(BigInteger.valueOf(leftIndentTwips(insetLeft + cellTextShift + past))); + double room = availableWidth() - past; + double measure = Double.isFinite(room) && room > 0 ? wordsMeasure(laidOut.lines(), room) : Double.NaN; + if (Double.isFinite(measure) && Math.abs(measure - room) >= 0.05) { + long right = twipsOf(indent.isSetRight() ? indent.getRight() : null); + indent.setRight(BigInteger.valueOf(right - Math.round((measure - room) * POINT_TO_TWIP))); + } + return true; + } + + /** + * Puts the list's gap between the lines of the item just started, if it wraps. + * + * @return how the layout set the item, or {@code null} where its items are not matched + */ + private DocxLayoutMetrics.ItemText applyItemLineGap(XWPFParagraph item) { + DocxLayoutMetrics.ItemText laidOut = listItemLines.poll(); + if (laidOut != null) { + applyLineGap(item, listLineGap, laidOut.lines().size()); } - return lines; + return laidOut; } /** How many items a list writes, nested ones included. */ @@ -3053,7 +3104,8 @@ private void writeListLine(XWPFDocument document, DocumentTextStyle style, spaceBeforeTheNextItem(); XWPFParagraph para = newBodyParagraph(document); applyLineHeight(para, lineHeight); - ListA nested rich item with no marker stands where the layout set its text + * ({@link #indentAsTheItemIs}), where the layout's items are matched to the list's. A list + * that nests only those sets its top level at the page's column too: kept at a space, the + * name of {@code writeBulletedPair}'s project started the gap less a space left of the + * description set under it.
+ */ + private boolean nestsBesideItsText(com.demcha.compose.document.node.ListNode list) { + if (!nests(list)) { + return false; + } + if (!listItemsMatched) { + return true; + } + java.util.ArrayDeque