Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 19 additions & 2 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,23 @@ follow semantic versioning; release dates are ISO 8601.

### Public API

- **A picture, a shape or a barcode composed in a table cell keeps its own size.** The layout gave
a node composed in a table cell (`DocumentTableCell.node`) the cell's inner width, and the
height it measured. In a 120pt column padded 4pt, the page drew:
- a picture fitted to 100pt in a box 112pt wide, so 112×56 for 100×50;
- a 60pt picture stretched to 112×60;
- a 20pt circle as an ellipse 112pt wide.

The DOCX export wrote the pictures at their own size, and drew the circle as the page did.
- A node of a size of its own is now laid out at the width it measured, as the flow and a row
slot lay one out: a picture, a shape, a circle, a barcode, a line, a path or a polygon. It is
held to the cell's inner width where it measures wider, and starts at the cell's content edge
as before. A link on it covers it rather than the cell's width.
- Each of the three now stands where Word and LibreOffice draw it.
- Text aligns in the cell's inner width as before: a paragraph, a list, a page number or a page
reference. A composite is laid out in the cell's inner box as before.
- The DOCX export draws a circle or a shape there at its own size, as the page now does, and
measures a shape's corners against its own width.
- **A DOCX export keeps the space above a picture the layout moves to a new page.** Word drops a
paragraph's space above at the top of a page, a picture's paragraph's too. Measured in Word and
LibreOffice on Windows, a 100pt picture opening its page stood at the page's top in both: 20pt
Expand Down Expand Up @@ -315,8 +332,8 @@ follow semantic versioning; release dates are ISO 8601.
tell that box apart from the node. A box whose corners round by radii that differ only past
half its shorter side — 10 and 12 on a box 20 tall — is drawn at 10 on the page and in Word
alike, and its note no longer says its corners are drawn at the largest. A shape composed in a
table cell is laid out at the cell's width, which the report does not know, so its radii are
held to half its height: one in a cell narrower than it is tall can still be named. None of the
table cell is held to half its own shorter side, though its cell, which the report does not
know, may lay it out narrower: one in a cell narrower than it can still be named. None of the
corpus's 62 DOCX changes, and none of its 1332 notes: no corpus document holds either case.
- **A DOCX export ends and turns a drawing's outline as the page strokes it.** An outline was
written with no cap and no join, which Word 16 and LibreOffice draw with flat ends and round
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -636,8 +636,9 @@ public static List<LayoutFragment> emitTableFragments(PreparedNode<TableNode> pr
: null)));
// Composed cells: dispatch each prepared child's fragments
// at the cell's absolute table-local position. The child's
// FragmentPlacement is constructed from the cell's width,
// height, and position relative to the placement path.
// FragmentPlacement is constructed from the cell's box, its
// position relative to the placement path, and the child's own
// size (emitComposedCellFragments).
List<TableResolvedCell> rowCells = layout.rows().get(rowIndex);
for (TableResolvedCell cell : rowCells) {
int columnIndex = locateCellColumn(layout, rowIndex, cell);
Expand Down Expand Up @@ -739,8 +740,17 @@ private static List<LayoutFragment> emitComposedCellFragments(
// quantities, which is what keeps the reserved row and the drawn content
// the same size.
DocumentInsets contentMargin = child.node().margin();
double childWidth = Math.max(0.0,
// A composite child is given the cell's inner box to lay itself out in, as a row slot gives
// one its slot, and so is text, which aligns in the width it is given: a page number
// measures as wide as its digits, and set right it stands at the cell's right edge. A
// leaf of a size of its own is laid out at the width it measured, as the flow and a row
// slot place one. Given the cell's width, a picture fitted to 100pt in a 120pt column was
// drawn in a box 112pt wide, and a 20pt circle as an ellipse 112pt wide.
double innerWidth = Math.max(0.0,
cellWidth - padding.horizontal() - contentMargin.horizontal());
double childWidth = child.isComposite() || alignsInTheWidthItIsGiven(child.node())
? innerWidth
: Math.min(innerWidth, Math.max(0.0, child.measureResult().width()));
double childHeight = Math.max(0.0, child.measureResult().height());
// Where in the cell's box the child sits. Follows resolveTextLines on
// the vertical axis: same default (centre, from
Expand All @@ -749,8 +759,9 @@ private static List<LayoutFragment> emitComposedCellFragments(
// cell land on the same line.
//
// Two deliberate differences. The horizontal half of the anchor is not
// read: a composed child is laid out at the cell's inner width, so
// there is no slack across for it to sit in, where a text line has its
// read: a composite child and text lay themselves out in the cell's
// inner box, and a leaf of a size of its own starts at the cell's
// content edge, as a row slot starts one, where a text line has its
// own measured width. And slack is clamped at zero rather than allowed
// to go negative — an over-tall child keeps starting at the bottom and
// overflowing upward instead of being pushed below its own cell. No
Expand Down Expand Up @@ -817,6 +828,15 @@ private static List<LayoutFragment> emitComposedCellFragments(
return translated;
}

/**
* Whether a leaf sets its text in the width it is given rather than drawing at a size of its
* own: a paragraph, a list, a page number and a page reference, each by its alignment.
*/
private static boolean alignsInTheWidthItIsGiven(DocumentNode node) {
return node instanceof ParagraphNode || node instanceof ListNode
|| node instanceof PageReferenceNode || node instanceof PageFieldNode;
}

private static List<LayoutFragment> emitDecorationFragment(Color fillColor,
Stroke stroke,
double cornerRadius,
Expand Down
4 changes: 2 additions & 2 deletions docs/recipes/docx-export.md
Original file line number Diff line number Diff line change
Expand Up @@ -1189,8 +1189,8 @@ named in the report:
anchor points at none, and a page reference to it is a fixed number. A gradient fill,
unequal corners (drawn at the largest radius; corners whose radii differ only past half the
box's shorter side, where the page holds them, are drawn alike and not named — a shape
composed in a table cell, laid out at the cell's width, is held to half its height, so one in
a cell narrower than it is tall can still be named) and a line's dash pattern (drawn solid)
composed in a table cell is held to half its own shorter side, so one in a cell narrower than
it can still be named) and a line's dash pattern (drawn solid)
are not carried either.
A rule — a line or a thin bar written as a paragraph border — keeps its anchor; its note
names a lost outline entry, caps (a border ends flat) or a bar's link, a line's link being
Expand Down
7 changes: 7 additions & 0 deletions docs/recipes/tables.md
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,13 @@ paragraphs in the cell, not just the section's background. The row stays
atomic: a composed cell does not split across a page break, so keep the
child shorter than one page's content area.

Text in a composed cell aligns in the cell's inner width, and a section,
a row or any other composite lays itself out in the cell's inner box. A
picture, a shape, a barcode or any
other node of a size of its own keeps that size, held to the cell's
inner width where it is wider, and starts at the cell's left content
edge: the cell's horizontal anchor is not read for composed content.

## Row span — merge a cell vertically

<!-- claim: capability=table.row-span -->
Expand Down
22 changes: 11 additions & 11 deletions knowledge/claims/index.json
Original file line number Diff line number Diff line change
Expand Up @@ -202,7 +202,7 @@
"TableBuilder.headerRow": [
{
"page": "docs/recipes/tables.md",
"line": 207,
"line": 214,
"heading": "Repeated header on page break",
"surface": "authoring",
"stability": "stable"
Expand All @@ -211,7 +211,7 @@
"TableBuilder.repeatHeader": [
{
"page": "docs/recipes/tables.md",
"line": 206,
"line": 213,
"heading": "Repeated header on page break",
"surface": "authoring",
"stability": "stable"
Expand All @@ -220,7 +220,7 @@
"TableBuilder.totalRow": [
{
"page": "docs/recipes/tables.md",
"line": 169,
"line": 176,
"heading": "Totals row — bold + subtle fill",
"surface": "authoring",
"stability": "stable"
Expand All @@ -229,7 +229,7 @@
"TableBuilder.zebra": [
{
"page": "docs/recipes/tables.md",
"line": 128,
"line": 135,
"heading": "Zebra — alternating row fills",
"surface": "authoring",
"stability": "stable"
Expand Down Expand Up @@ -275,21 +275,21 @@
"table.repeat-header-across-pages": [
{
"page": "docs/recipes/tables.md",
"line": 205,
"line": 212,
"heading": "Repeated header on page break"
}
],
"table.row-span": [
{
"page": "docs/recipes/tables.md",
"line": 87,
"line": 94,
"heading": "Row span — merge a cell vertically"
}
],
"table.zebra-striping": [
{
"page": "docs/recipes/tables.md",
"line": 127,
"line": 134,
"heading": "Zebra — alternating row fills"
}
]
Expand Down Expand Up @@ -370,31 +370,31 @@
"table.explicit-row-style-beats-zebra": [
{
"page": "docs/recipes/tables.md",
"line": 129,
"line": 136,
"heading": "Zebra — alternating row fills",
"proof": "test:TableBuilderZebraAndTotalsTest"
}
],
"table.header-repeats-on-every-continuation-page": [
{
"page": "docs/recipes/tables.md",
"line": 208,
"line": 215,
"heading": "Repeated header on page break",
"proof": "test:TableBuilderRepeatHeaderTest"
}
],
"table.repeat-header-defaults-to-zero": [
{
"page": "docs/recipes/tables.md",
"line": 209,
"line": 216,
"heading": "Repeated header on page break",
"proof": "test:TableBuilderRepeatHeaderTest"
}
],
"table.spanning-cell-height-is-sum-of-covered-rows": [
{
"page": "docs/recipes/tables.md",
"line": 88,
"line": 95,
"heading": "Row span — merge a cell vertically",
"proof": "test:TableBuilderRowSpanTest"
}
Expand Down
8 changes: 4 additions & 4 deletions knowledge/proofs/index.json
Original file line number Diff line number Diff line change
Expand Up @@ -121,14 +121,14 @@
"kind": "behavior",
"value": "table.header-repeats-on-every-continuation-page",
"page": "docs/recipes/tables.md",
"line": 208,
"line": 215,
"heading": "Repeated header on page break"
},
{
"kind": "behavior",
"value": "table.repeat-header-defaults-to-zero",
"page": "docs/recipes/tables.md",
"line": 209,
"line": 216,
"heading": "Repeated header on page break"
}
]
Expand All @@ -141,7 +141,7 @@
"kind": "behavior",
"value": "table.spanning-cell-height-is-sum-of-covered-rows",
"page": "docs/recipes/tables.md",
"line": 88,
"line": 95,
"heading": "Row span — merge a cell vertically"
}
]
Expand All @@ -154,7 +154,7 @@
"kind": "behavior",
"value": "table.explicit-row-style-beats-zebra",
"page": "docs/recipes/tables.md",
"line": 129,
"line": 136,
"heading": "Zebra — alternating row fills"
}
]
Expand Down
Loading
Loading