Since 0.3.0, table cell background fills that are drawn inside an unbreakable block
(dontBreakRows: true rows, unbreakable stacks) are painted in reverse of the order in
which they were drawn. When those fills overlap, the wrong one ends up on top.
The case that surfaces this in practice is an outer table with a layout.fillColor
(zebra striping) whose rows use dontBreakRows: true and contain a nested table with its
own cell fillColor: the outer row fill paints over the nested cell fill. The nested
table's borders and text still render correctly, since those are not background-band
items, so the result is a bordered box with the wrong fill colour.
Minimal example (runnable on the playground)
{
content: [{
table: {
dontBreakRows: true,
widths: ['*', 'auto'],
body: [[
{ text: 'row with zebra fill' },
{ table: { widths: ['auto'], body: [[{ text: 'X', fillColor: '#1C488A', color: '#FFFFFF' }]] } },
]],
},
layout: { fillColor: () => '#EAF1FC' },
}],
}
Expected: the nested cell is a dark blue (#1C488A) box with a white X, sitting on
the light blue (#EAF1FC) row fill. This is what 0.2.12 produces.
Actual (0.3.x, including current master): the nested cell's dark blue fill is covered
by the light blue row fill, so the box appears light blue and the white X is almost
invisible against it.
Removing dontBreakRows: true makes it render correctly, which is consistent with the
fills only being reordered when they pass through an unbreakable block.
Root cause
TableProcessor marks cell fill vectors drawn while a transaction is open with
_isFillColorFromUnbreakable. On commit, ElementWriter.addFragment splices each marked
fill into the page's background band so that lines and text paint above it:
const endOfBackgroundItemsIndex = ctx.backgroundLength[ctx.page];
page.items.splice(endOfBackgroundItemsIndex, 0, { type: 'vector', item: v });
The index is fixed for every fill in the block, so each successive splice inserts the new
fill in front of the previously inserted one and the block's fills end up in reverse
draw order. In 0.2.x addFragment pushed the block's items in order, which preserved it.
Versions: correct in 0.2.12, wrong in 0.3.x up to and including 0.3.11 and current master.
A pull request follows.
Since 0.3.0, table cell background fills that are drawn inside an unbreakable block
(
dontBreakRows: truerows,unbreakablestacks) are painted in reverse of the order inwhich they were drawn. When those fills overlap, the wrong one ends up on top.
The case that surfaces this in practice is an outer table with a
layout.fillColor(zebra striping) whose rows use
dontBreakRows: trueand contain a nested table with itsown cell
fillColor: the outer row fill paints over the nested cell fill. The nestedtable's borders and text still render correctly, since those are not background-band
items, so the result is a bordered box with the wrong fill colour.
Minimal example (runnable on the playground)
Expected: the nested cell is a dark blue (
#1C488A) box with a whiteX, sitting onthe light blue (
#EAF1FC) row fill. This is what 0.2.12 produces.Actual (0.3.x, including current master): the nested cell's dark blue fill is covered
by the light blue row fill, so the box appears light blue and the white
Xis almostinvisible against it.
Removing
dontBreakRows: truemakes it render correctly, which is consistent with thefills only being reordered when they pass through an unbreakable block.
Root cause
TableProcessormarks cell fill vectors drawn while a transaction is open with_isFillColorFromUnbreakable. On commit,ElementWriter.addFragmentsplices each markedfill into the page's background band so that lines and text paint above it:
The index is fixed for every fill in the block, so each successive splice inserts the new
fill in front of the previously inserted one and the block's fills end up in reverse
draw order. In 0.2.x
addFragmentpushed the block's items in order, which preserved it.Versions: correct in 0.2.12, wrong in 0.3.x up to and including 0.3.11 and current master.
A pull request follows.