Skip to content

fix(docx): name in the report what the export draws as a picture or draws without - #900

Merged
DemchaAV merged 1 commit into
2.5-devfrom
fix/docx-silent-losses
Oct 14, 2026
Merged

DemchaAV merged 1 commit into
2.5-devfrom
fix/docx-silent-losses

Conversation

@DemchaAV

Copy link
Copy Markdown
Owner

Why

A DOCX export's report is meant to name everything the export does not write as authored. Some of what it draws was not named at all, and some notes said more than was lost:

  • Inline shapes and icons. A dot, an arrow or a checkbox among the words, and an SVG icon that states no text, were drawn into pictures with no note. DocxShapePictures said Word cannot hold a drawing in a line. It can (wp:inline round a wps:wsp); the export simply does not write one.
  • Unequal corners. A box drawn as a shape whose corners have different radii is drawn at the largest radius. That is a shape container's outline, or a section's or a container's paint laid over the flow, and none of them was named. A ShapeNode's unequal corners were already named.
  • A path in a table cell. Its caps and joins were lost in silence.
  • A drawn path's note. It named a gradient paint, a dash pattern, caps and joins whenever the path lost any one of them. All 50 such notes in the corpus are on paths with no gradient and no dash.
  • Defaults that do not match. The outline is written as <a:ln> with no cap and no join. Measured with a 10pt stroke, Word and LibreOffice then draw flat ends, as the page's butt default does, and round joins, where the page mitres: the point of a V stood 2.76pt short in both. So a round join is carried, and the page's default mitre is not.
  • A path painted only with a gradient. It was dropped because "its geometry has no semantic Word analogue". In fact it is drawn as custom geometry, and its gradient is what the export cannot draw.
  • Page numbers counted from elsewhere. The note said Word's fields number the pages from 1. A section's w:pgNumType could start them elsewhere; the export does not write one.

What changed

  • writeInlinePicture / reportInlinePicture. Each InlineShapeRun is reported as an inline shape, and each InlineSvgRun without text as an inline icon, both APPROXIMATED. An icon with text keeps its note, which names the text as the picture's description.

    • A page zone's line is written into each kind of header Word is given. Its runs report on the first write only, tracked by the zone content's identity in zoneLinesReported, as zonePartsReported already does for shapes. warnDroppedInlineRuns follows the same rule.
  • Corners. drawOwnFragments reads the radii off what it drew. roundsCornersUnequally asks for a ShapeFragmentPayload with non-uniform radii whose box is drawn (DocxDrawings.drawsItsBox), so a box drawn as its side borders alone has no corners to name.

    • A ShapeNode's corners stay with the rest of what it is drawn without.
    • A shape container composed in a table cell, which has no fragment of its own, is checked by cornersDiffer in drawOutlineOf. That applies only when its table draws it and its fill or stroke shows.
  • pathStyleLost, replacing losesStyle, names only what the path loses:

    • a gradient paint;
    • a dash, when the path is stroked;
    • a cap that is not butt, where a subpath stays open (hasOpenEnds);
    • a join that is not round, where two segments meet (joinsSegments).

    carriedWithout gains a PathNode branch, so a path composed in a table cell names its caps and joins. The table's note names the gradient and the dash, as it does for every drawing in its cells.

  • Other notes:

    • droppedBecause: a path painted only with a gradient is dropped for that, as a ShapeNode already was.
    • A drawn line with a cap other than butt now says it ends flat, rather than that the editor ends it its own way.
    • bandPageClasses: the page-number note says the export writes the fields without the offset.
  • Javadoc and ledger. The DocxShapePictures, InlineSvgRasters and DocxInlineShapeTest Javadoc no longer say Word has no drawing in a line. DocxNodeFieldLedgerTest records ShapeContainerNode.outline and .layers as reported, and a line's butt cap as ending flat.

  • Docs. docs/recipes/docx-export.md and the capability matrix state the measured defaults, what each note names, and that the page-number offset is not written rather than impossible.

Verification

  • ./mvnw -B -ntp clean verify → BUILD SUCCESS: render-docx 1573 tests, qa 1820.

  • render-docx: +11 tests. Two were renamed, and their assertions changed with the behaviour.

    • Inline pictures. anSvgIconAndAShapeInTheLineAreNamedPicturesInTheReport checks both notes word for word, with their severity. anAuthorsPictureInTheLineIsNotNamedAnIcon checks that an author's own picture gets neither.

    • A page zone. aShapeAndAnIconAmongAPageZonesWordsAreReportedOnceThoughWrittenIntoTwoFooters checks the zone is in two footers, with one note each.

    • Corners that differ:

      • aDrawnContainerWithCornersOfDifferentRadiiSaysTheyAreDrawnAtTheLargest;
      • aSectionDrawnOverTheFlowWithCornersOfDifferentRadiiSaysTheyAreDrawnAtTheLargest;
      • aTileComposedInATableCellSaysItsCornersAreDrawnAtTheLargest.
    • No corners note:

      • aContainerWithEqualCornersSaysNothingOfThem;
      • aSectionDrawnOverTheFlowAsItsBorderAloneHasNoCornersToName;
      • anUnpaintedTileComposedInATableCellSaysNothingOfItsCorners.

      A ShapeNode's corners are named once.

    • Paths. aDrawnPathNamesOnlyWhatItLoses covers:

      • dash only and round caps only;
      • all four, and a gradient stroke;
      • a mitred V, which names its joins, and a round-joined one, which names nothing;
      • a two-stroke cross, which names its caps and not its joins;
      • a closed outline;
      • a plain segment, and a path the page only fills.

      aPathComposedInATableCellNamesTheCapsAndJoinsItsTableDoesNot covers a cell path.

    • Other notes. aBuiltInPathNothingShowsIsDroppedForItsGradient pins the reason. numberingTheExportWritesWithoutItsOffsetIsReported pins the page-number note, and that no w:pgNumType is written.

  • Sabotage. Each of 20 parts undone fails at least one of these tests: every note, every guard (paint, stroke, open end, joined segments, drawn box, zone line), the shape's corners named twice, and both old wordings.

  • Corpus. All 62 corpus documents were exported against 2.5-dev, and no document.xml changes. Their reports go from 969 notes to 1340:

    Notes Change
    278 inline shapes, 85 icons without text named as pictures
    8 paths in table cells name their caps
    50 drawn path notes that named all four name caps (37), joins (1), caps and joins (1), or nothing (11)

    No other note changes.

  • Measurement. A probe document held a 10pt butt line, a mitred V and a butt line node, and the same three set square and round. It was exported, converted with Word 16 and LibreOffice on Windows, and the ink measured per colour at 8 px a point. With no cap and no join written, both editors drew each end where the page's butt end stands, and every join round.

Lane: shared-engine (render-docx export report). No DOCX bytes change.

…raws without

An inline shape and an SVG icon with no text are reported as pictures, once
for a page zone's line. Corners of different radii drawn at the largest are
named wherever a box is drawn. A path's note names only what it loses,
against the measured editor defaults: no cap ends flat, no join is round.
A path painted only with a gradient is dropped for that, and the page-number
note no longer says Word's fields cannot be offset.
@DemchaAV
DemchaAV merged commit 1712236 into 2.5-dev Oct 14, 2026
13 checks passed
@DemchaAV
DemchaAV deleted the fix/docx-silent-losses branch October 14, 2026 17:59
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant