Skip to content
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

Open
wants to merge 38 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 6 commits
Commits
Show all changes
38 commits
Select commit Hold shift + click to select a range
0aef943
Feat(Table cell vertical alignment) - for text, stack, columns, list,…
May 17, 2022
a0781c5
Fix: CRLF
May 17, 2022
640a9d1
Revert "Fix: CRLF"
May 17, 2022
f528db6
fix indentation
sirfullTom May 17, 2022
acdfca7
init sfs repo
sirfullTom May 17, 2022
7df5ef2
updated the build
sirfullTom May 18, 2022
5787e4a
Fix : function name & lineHeight
May 18, 2022
ad3e102
Merge branch 'feature/table_cell_vertical_alignment' of https://githu…
May 18, 2022
ecba23b
use paddingTop in processTableVerticalAlignment
sirfullTom Oct 25, 2022
0795850
Merge pull request #1 from SirFull/fix/padding-vertical-alignment
t0m-4 Oct 25, 2022
a46dee1
updated version
sirfullTom Oct 25, 2022
22607e0
Merge branch 'master' into release/vertical_alignment
sirfullTom Mar 23, 2023
e86a99e
build 0.3.0-beta.5
sirfullTom Mar 23, 2023
aec02a6
Merge branch 'release/vertical_alignment' into feature/table_cell_ver…
sirfullTom Mar 23, 2023
20092c6
update package name to remove @sirfull
sirfullTom Mar 23, 2023
8549dba
update oiriginal repo url
sirfullTom Mar 23, 2023
a2c12da
merge
t0m-4 Sep 6, 2024
859b34d
build
t0m-4 Sep 6, 2024
b7f8e5d
fix merge
t0m-4 Sep 6, 2024
2ea57b6
fix
t0m-4 Sep 6, 2024
16137d4
fix eslint
t0m-4 Sep 6, 2024
c7d31e6
fix eslitn verticalAlignment sample
t0m-4 Sep 6, 2024
6d77717
fix pr comments
t0m-4 Sep 9, 2024
997703e
merge
t0m-4 Sep 9, 2024
b201a40
reinit package json
t0m-4 Sep 9, 2024
15f666a
remove npmrc
t0m-4 Sep 9, 2024
f215a2a
build & exemples
t0m-4 Sep 9, 2024
f104326
up with last src repo
t0m-4 Sep 9, 2024
4b97f4e
up build & exemples
t0m-4 Sep 9, 2024
9dba972
publis package yml
t0m-4 Sep 9, 2024
d51d9c4
publish package
t0m-4 Sep 9, 2024
066d51b
stretch working
t0m-4 Sep 11, 2024
dc43438
fix
t0m-4 Sep 11, 2024
14de809
working with copy
sirfullTom Sep 11, 2024
de4063b
stretch heights on table working
sirfullTom Sep 12, 2024
1cb4475
fix lint
sirfullTom Sep 12, 2024
3546ce5
cleaning
sirfullTom Sep 12, 2024
fe745c1
cleaning
sirfullTom Sep 12, 2024
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
292 changes: 292 additions & 0 deletions examples/verticalAlignment.js

Large diffs are not rendered by default.

23 changes: 22 additions & 1 deletion src/DocPreprocessor.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,8 @@ class DocPreprocessor {
return this.preprocessNode(docStructure);
}

preprocessNode(node) {
// begin - Vertical alignment
checkNode(node) {
// expand shortcuts and casting values
if (Array.isArray(node)) {
node = { stack: node };
Expand All @@ -32,6 +33,14 @@ class DocPreprocessor {
} else if ('text' in node) { // cast value in text property
node.text = convertValueToString(node.text);
}
return node;
};
// end - Vertical alignment

preprocessNode(node) {
// begin - Vertical alignment
node = this.checkNode(node);
// end - Vertical alignment

if (node.columns) {
return this.preprocessColumns(node);
Expand Down Expand Up @@ -68,6 +77,10 @@ class DocPreprocessor {
let columns = node.columns;

for (let i = 0, l = columns.length; i < l; i++) {
// begin - Vertical alignment
columns[i] = this.checkNode(columns[i]);
columns[i].__nodeRef = node.__nodeRef ?? node;
// end - Vertical alignment
columns[i] = this.preprocessNode(columns[i]);
}

Expand All @@ -78,6 +91,10 @@ class DocPreprocessor {
let items = node.stack;

for (let i = 0, l = items.length; i < l; i++) {
// begin - Vertical alignment
items[i] = this.checkNode(items[i]);
items[i].__nodeRef = node.__nodeRef ?? node;
// end - Vertical alignment
items[i] = this.preprocessNode(items[i]);
}

Expand All @@ -88,6 +105,10 @@ class DocPreprocessor {
let items = node.ul || node.ol;

for (let i = 0, l = items.length; i < l; i++) {
// begin - Vertical alignment
items[i] = this.checkNode(items[i]);
items[i].__nodeRef = node.__nodeRef ?? node;
// end - Vertical alignment
items[i] = this.preprocessNode(items[i]);
}

Expand Down
6 changes: 6 additions & 0 deletions src/ElementWriter.js
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,9 @@ class ElementWriter extends EventEmitter {
this.alignCanvas(node);

node.canvas.forEach(function (vector) {
// begin - Vertical alignment
vector.__nodeRef = node.__nodeRef ?? node;
// end - Vertical alignment
let position = this.addVector(vector, false, false, index);
positions.push(position);
if (index !== undefined) {
Expand Down Expand Up @@ -188,6 +191,9 @@ class ElementWriter extends EventEmitter {
let vector = qr._canvas[i];
vector.x += qr.x;
vector.y += qr.y;
// begin - Vertical alignment
vector.__nodeRef = qr.__nodeRef ?? qr;
// end - Vertical alignment
this.addVector(vector, true, true, index);
}

Expand Down
52 changes: 52 additions & 0 deletions src/LayoutBuilder.js
Original file line number Diff line number Diff line change
Expand Up @@ -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 => {
Expand Down Expand Up @@ -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
Expand All @@ -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);
Expand All @@ -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;
Copy link

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

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
this.__nodesHierarchy[this.__nodesHierarchy.length - 1].__contentHeight += lastNode.__contentHeight;
this.__nodesHierarchy.length > 0 && (this.__nodesHierarchy[this.__nodesHierarchy.length - 1].__contentHeight += lastNode.__contentHeight);

Suggested check

// end - Vertical alignment
}

processRow(columns, widths, gaps, tableBody, tableRow, height) {
Expand Down Expand Up @@ -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;
Expand All @@ -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);
Expand All @@ -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;
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

another missing check for this.__nodesHierarchy.length > 0

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
this.__nodesHierarchy[this.__nodesHierarchy.length - 1].__contentHeight += lastNode.__contentHeight;
this.__nodesHierarchy.length > 0 && (this.__nodesHierarchy[this.__nodesHierarchy.length - 1].__contentHeight += lastNode.__contentHeight);

Suggested check

// end - Vertical alignment
}

// tables
Expand Down Expand Up @@ -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;
}
Expand Down Expand Up @@ -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) {
Expand Down
95 changes: 95 additions & 0 deletions src/TableProcessor.js
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;

Expand Down Expand Up @@ -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) {
Expand Down Expand Up @@ -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) {
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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);

Choose a reason for hiding this comment

The 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();
}
Expand Down Expand Up @@ -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
}
}

Expand Down