-
Notifications
You must be signed in to change notification settings - Fork 2.1k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Feature/table cell vertical alignment #2436
base: master
Are you sure you want to change the base?
Changes from 6 commits
0aef943
a0781c5
640a9d1
f528db6
acdfca7
7df5ef2
5787e4a
ad3e102
ecba23b
0795850
a46dee1
22607e0
e86a99e
aec02a6
20092c6
8549dba
a2c12da
859b34d
b7f8e5d
2ea57b6
16137d4
c7d31e6
6d77717
997703e
b201a40
15f666a
f215a2a
f104326
4b97f4e
9dba972
d51d9c4
066d51b
dc43438
14de809
de4063b
1cb4475
3546ce5
fe745c1
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Large diffs are not rendered by default.
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
|
@@ -140,6 +140,9 @@ class LayoutBuilder { | |||||
|
||||||
this.docPreprocessor = new DocPreprocessor(); | ||||||
this.docMeasure = new DocMeasure(pdfDocument, styleDictionary, defaultStyle, this.svgMeasure, this.tableLayouts); | ||||||
// begin - Vertical alignment | ||||||
this.__nodesHierarchy = []; | ||||||
// end - Vertical alignment | ||||||
|
||||||
function resetXYs(result) { | ||||||
result.linearNodeList.forEach(node => { | ||||||
|
@@ -457,12 +460,21 @@ class LayoutBuilder { | |||||
|
||||||
// vertical container | ||||||
processVerticalContainer(node) { | ||||||
// begin - Vertical alignment | ||||||
this.__nodesHierarchy.push(node); | ||||||
node.__contentHeight = 0; | ||||||
// end - Vertical alignment | ||||||
|
||||||
node.stack.forEach(item => { | ||||||
this.processNode(item); | ||||||
addAll(node.positions, item.positions); | ||||||
|
||||||
//TODO: paragraph gap | ||||||
}, this); | ||||||
// begin - Vertical alignment | ||||||
const lastNode = this.__nodesHierarchy.pop(); | ||||||
this.__nodesHierarchy.length > 0 && (this.__nodesHierarchy[this.__nodesHierarchy.length - 1].__contentHeight += lastNode.__contentHeight); | ||||||
// end - Vertical alignment | ||||||
} | ||||||
|
||||||
// columns | ||||||
|
@@ -474,6 +486,10 @@ class LayoutBuilder { | |||||
if (gaps) { | ||||||
availableWidth -= (gaps.length - 1) * columnNode._gap; | ||||||
} | ||||||
// begin - Vertical alignment | ||||||
columnNode.__contentHeight = 0; | ||||||
this.__nodesHierarchy.push(columnNode); | ||||||
// end - Vertical alignment | ||||||
|
||||||
ColumnCalculator.buildColumnWidths(columns, availableWidth); | ||||||
let result = this.processRow(columns, columns, gaps); | ||||||
|
@@ -493,6 +509,11 @@ class LayoutBuilder { | |||||
|
||||||
return gaps; | ||||||
} | ||||||
// begin - Vertical alignment | ||||||
const lastNode = this.__nodesHierarchy.pop(); | ||||||
lastNode.__contentHeight = Math.max(...columns.map(c => c.__contentHeight)); | ||||||
this.__nodesHierarchy[this.__nodesHierarchy.length - 1].__contentHeight += lastNode.__contentHeight; | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
Suggested check |
||||||
// end - Vertical alignment | ||||||
} | ||||||
|
||||||
processRow(columns, widths, gaps, tableBody, tableRow, height) { | ||||||
|
@@ -582,18 +603,29 @@ class LayoutBuilder { | |||||
|
||||||
if (marker.canvas) { | ||||||
let vector = marker.canvas[0]; | ||||||
// begin - Vertical alignment | ||||||
vector.__nodeRef = line.__nodeRef ?? line; | ||||||
vector._height = marker._maxHeight; | ||||||
// end - Vertical alignment | ||||||
|
||||||
offsetVector(vector, -marker._minWidth, 0); | ||||||
this.writer.addVector(vector); | ||||||
} else if (marker._inlines) { | ||||||
let markerLine = new Line(this.pageSize.width); | ||||||
// begin - Vertical alignment | ||||||
markerLine.__nodeRef = line.__nodeRef ?? line; | ||||||
// end - Vertical alignment | ||||||
markerLine.addInline(marker._inlines[0]); | ||||||
markerLine.x = -marker._minWidth; | ||||||
markerLine.y = line.getAscenderHeight() - markerLine.getAscenderHeight(); | ||||||
this.writer.addLine(markerLine, true); | ||||||
} | ||||||
} | ||||||
}; | ||||||
// begin - Vertical alignment | ||||||
this.__nodesHierarchy.push(node); | ||||||
node.__contentHeight = 0; | ||||||
// end - Vertical alignment | ||||||
|
||||||
let items = orderedList ? node.ol : node.ul; | ||||||
let gapSize = node._gapSize; | ||||||
|
@@ -605,6 +637,9 @@ class LayoutBuilder { | |||||
this.writer.addListener('lineAdded', addMarkerToFirstLeaf); | ||||||
|
||||||
items.forEach(item => { | ||||||
// begin - Vertical alignment | ||||||
item.__nodeRef = node.__nodeRef ?? node; | ||||||
// end - Vertical alignment | ||||||
nextMarker = item.listMarker; | ||||||
this.processNode(item); | ||||||
addAll(node.positions, item.positions); | ||||||
|
@@ -613,6 +648,10 @@ class LayoutBuilder { | |||||
this.writer.removeListener('lineAdded', addMarkerToFirstLeaf); | ||||||
|
||||||
this.writer.context().addMargin(-gapSize.width); | ||||||
// begin - Vertical alignment | ||||||
const lastNode = this.__nodesHierarchy.pop(); | ||||||
this.__nodesHierarchy[this.__nodesHierarchy.length - 1].__contentHeight += lastNode.__contentHeight; | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. another missing check for There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
Suggested check |
||||||
// end - Vertical alignment | ||||||
} | ||||||
|
||||||
// tables | ||||||
|
@@ -649,7 +688,13 @@ class LayoutBuilder { | |||||
|
||||||
// leafs (texts) | ||||||
processLeaf(node) { | ||||||
// begin - Vertical alignment | ||||||
node = this.docPreprocessor.checkNode(node); | ||||||
// end - Vertical alignment | ||||||
let line = this.buildNextLine(node); | ||||||
// begin - Vertical alignment | ||||||
line && (line.__nodeRef = node.__nodeRef ?? node); | ||||||
// end - Vertical alignment | ||||||
if (line && (node.tocItem || node.id)) { | ||||||
line._node = node; | ||||||
} | ||||||
|
@@ -688,9 +733,16 @@ class LayoutBuilder { | |||||
node.positions.push(positions); | ||||||
line = this.buildNextLine(node); | ||||||
if (line) { | ||||||
// begin - Vertical alignment | ||||||
line.__nodeRef = node.__nodeRef ?? node; | ||||||
// end - Vertical alignment | ||||||
currentHeight += line.getHeight(); | ||||||
} | ||||||
} | ||||||
// begin - Vertical alignment | ||||||
node.__contentHeight = currentHeight; | ||||||
this.__nodesHierarchy.length > 0 && (this.__nodesHierarchy[this.__nodesHierarchy.length - 1].__contentHeight += currentHeight); | ||||||
// end - Vertical alignment | ||||||
} | ||||||
|
||||||
processToc(node) { | ||||||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,12 +1,18 @@ | ||
import ColumnCalculator from './columnCalculator'; | ||
import { isNumber } from './helpers/variableType'; | ||
// begin - Vertical alignment | ||
import { offsetVector } from './helpers/tools'; | ||
// end - Vertical alignment | ||
|
||
class TableProcessor { | ||
constructor(tableNode) { | ||
this.tableNode = tableNode; | ||
} | ||
|
||
beginTable(writer) { | ||
// begin - Vertical alignment | ||
this.tableNode.table.__rowsHeight = []; | ||
// end - Vertical alignment | ||
const getTableInnerContentWidth = () => { | ||
let width = 0; | ||
|
||
|
@@ -113,6 +119,9 @@ class TableProcessor { | |
prepareCellBorders(this.tableNode.table.body); | ||
|
||
this.drawHorizontalLine(0, writer); | ||
// begin - Vertical alignment | ||
this.tableNode.__height = writer.context().y; | ||
// end - Vertical alignment | ||
} | ||
|
||
onRowBreak(rowIndex, writer) { | ||
|
@@ -140,6 +149,9 @@ class TableProcessor { | |
writer.context().availableHeight -= this.reservedAtBottom; | ||
|
||
writer.context().moveDown(this.rowPaddingTop); | ||
// begin - Vertical alignment | ||
this.tableNode.table.__rowsHeight[rowIndex] = { top: this.rowTopY, height: 0 }; | ||
// end - Vertical alignment | ||
} | ||
|
||
drawHorizontalLine(lineIndex, writer, overrideY) { | ||
|
@@ -240,6 +252,9 @@ class TableProcessor { | |
lineWidth: lineWidth, | ||
dash: dash, | ||
lineColor: borderColor | ||
// begin - Vertical alignment | ||
, __tableRef: this.tableNode.table | ||
// end - Vertical alignment | ||
}, false, overrideY); | ||
currentLine = null; | ||
borderColor = null; | ||
|
@@ -321,13 +336,90 @@ class TableProcessor { | |
lineWidth: width, | ||
dash: dash, | ||
lineColor: borderColor | ||
// begin - Vertical alignment | ||
, __tableRef: this.tableNode.table | ||
// end - Vertical alignment | ||
}, false, true); | ||
cellBefore = null; | ||
currentCell = null; | ||
borderColor = null; | ||
} | ||
|
||
// begin - Vertical alignment | ||
getCellContentHeight(cell, items) { | ||
let contentHeight = 0; | ||
cell._maxHeight && (contentHeight = cell._maxHeight); // for canvas | ||
// for forced multiline text, text with lineHeight, ul, ol | ||
!cell.lineHeight && cell.__contentHeight && (contentHeight = cell.__contentHeight); | ||
!contentHeight && (contentHeight = items.reduce((p, v) => { | ||
const item = v.item.inlines ? (v.item.inlines[0] ?? null) : v.item; | ||
const lineHeight = v.item.__nodeRef?.lineHeight ?? (v.item._height ?? v.item.h); | ||
let height = item.height ?? (item.h ?? 0); | ||
(v.type === 'vector') || (cell.ol && !v.item.lastLineInParagraph) && (height = 0); // for ol with counter | ||
return p + (height / (lineHeight ?? 1)) | ||
},0)); | ||
!contentHeight && cell._height && (contentHeight = cell._height); // for text, image, svg, qr | ||
return contentHeight; | ||
}; | ||
|
||
processTableVerticalAlignment(writer, table) { | ||
const getCells = (node) => node.table ? node.table.body.flat().map(getCells).flat() : node; | ||
const getNestedTables = (node) => node.table ? [node, ...node.table.body.flat().map(getNestedTables).filter(Boolean).flat()] : null; | ||
// for all rows in table | ||
table.body.forEach((row, rowIndex) => { | ||
// filter only cells with vertical alignment (middle / bottom) | ||
!Array.isArray(row) && row.columns && (row = row.columns); | ||
row.filter(cell => cell.verticalAlign && ['middle', 'bottom'].indexOf(cell.verticalAlign) > -1).forEach(cell => { | ||
let nestedTables; | ||
if (!cell._span) { | ||
let cellHeight = 0; | ||
if (cell.rowSpan && cell.rowSpan > 1) { | ||
const heights = table.__rowsHeight.slice(rowIndex, rowIndex + cell.rowSpan); | ||
cellHeight = heights.reduce((previousValue, value) => previousValue + value.height, 0); | ||
} else { | ||
cellHeight = table.__rowsHeight[rowIndex].height; | ||
} | ||
if (cellHeight) { | ||
const pageItems = writer._context.pages.flatMap(x => x.items); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Note for people porting this to the 0.2 branch: This line needs to be const pageItems = writer.writer.context.pages.flatMap(x => x.items); |
||
let items = pageItems.filter(i => i.item.__nodeRef === cell || i.item === cell); | ||
let itemHeight = 0; | ||
if (items.length === 0 && cell.table) { | ||
itemHeight = cell.table.__rowsHeight.reduce((p, v) => p + v.height, 0); | ||
nestedTables = getNestedTables(cell); | ||
items = pageItems.filter(i => getCells(cell).indexOf(i.item.__nodeRef) > -1 || | ||
i.item.__tableRef && nestedTables.some(nt => nt.table === i.item.__tableRef)); | ||
} else if (cell.stack) { | ||
const tables = cell.stack.filter(x => x.table); | ||
nestedTables = getNestedTables(tables[0]); | ||
itemHeight = tables.reduce((p, v) => p + v.__height, 0) + | ||
cell.stack.flatMap(x => x.__contentHeight).filter(Boolean).reduce((p, v) => p + v, 0); | ||
items = [...items, pageItems.filter(i => getCells(tables[0]).indexOf(i.item.__nodeRef) > -1 || | ||
i.item.__tableRef && nestedTables.some(nt => nt.table === i.item.__tableRef))].flat(); | ||
} else { | ||
itemHeight = this.getCellContentHeight(cell, items); | ||
} | ||
items.forEach(x => { | ||
const offsetTop = cell.verticalAlign === 'bottom' | ||
? cellHeight - itemHeight - 3 | ||
: (cellHeight - itemHeight) / 2; | ||
if (x && x.item) { | ||
x.item.type && offsetVector(x.item, 0, Math.max(0, offsetTop) - 1.5); | ||
!x.item.type && (x.item.y += Math.max(0, offsetTop) - 1.5); | ||
} | ||
}); | ||
} | ||
} | ||
}); | ||
}); | ||
} | ||
// end - Vertical alignment | ||
|
||
endTable(writer) { | ||
// begin - Vertical alignment | ||
this.processTableVerticalAlignment(writer, this.tableNode.table); | ||
this.tableNode.__height = writer.context().y - this.tableNode.__height + | ||
Math.ceil(this.layout.hLineWidth(0, this.tableNode)) * this.tableNode.table.body.length; | ||
// end - Vertical alignment | ||
if (this.cleanUpRepeatables) { | ||
writer.popFromRepeatables(); | ||
} | ||
|
@@ -552,6 +644,9 @@ class TableProcessor { | |
this.cleanUpRepeatables = true; | ||
this.headerRepeatable = null; | ||
} | ||
// begin - Vertical alignment | ||
this.tableNode.table.__rowsHeight[rowIndex].height = endingY - this.tableNode.table.__rowsHeight[rowIndex].top; | ||
// end - Vertical alignment | ||
} | ||
} | ||
|
||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
there is a missing check for
this.__nodesHierarchy.length > 0