从 wps 复制文档,获取到如下 html 表格, 计算表格每列的宽度后,插入 colgroup>col[width="61"]
标签用于控制表格每列宽度。

<table>
<tbody>
<tr>
<td width="61" colspan="1" rowspan="1"></td>
<td width="723" colspan="7" rowspan="1"></td>
</tr>
<tr>
<td width="61" colspan="1" rowspan="1"></td>
<td width="723" colspan="7" rowspan="1"></td>
</tr>
<tr>
<td width="61" colspan="1" rowspan="1"></td>
<td width="723" colspan="7" rowspan="1"></td>
</tr>
<tr>
<td width="61" colspan="1" rowspan="2"></td>
<td width="91" colspan="1" rowspan="1"></td>
<td width="106" colspan="1" rowspan="1"></td>
<td width="47" colspan="1" rowspan="1"></td>
<td width="84" colspan="1" rowspan="1"></td>
<td width="394" colspan="3" rowspan="1"></td>
</tr>
<tr>
<td width="91" colspan="1" rowspan="1"></td>
<td width="106" colspan="1" rowspan="1"></td>
<td width="47" colspan="1" rowspan="1"></td>
<td width="84" colspan="1" rowspan="1"></td>
<td width="394" colspan="3" rowspan="1"></td>
</tr>
<tr>
<td width="61" colspan="1" rowspan="2"></td>
<td width="723" colspan="7" rowspan="1"></td>
</tr>
<tr>
<td width="723" colspan="7" rowspan="1"></td>
</tr>
<tr>
<td width="61" colspan="1" rowspan="1"></td>
<td width="337" colspan="5" rowspan="1"></td>
<td width="65" colspan="1" rowspan="1"></td>
<td width="320" colspan="1" rowspan="1"></td>
</tr>
</tbody>
</table>
转化为下面的数据结构
type TdAttr = { width: number; colspan: number; rowspan: number }
const tdAttr: TdAttr[][] = [
[
{ width: 61, colspan: 1, rowspan: 1 },
{ width: 723, colspan: 7, rowspan: 1 }
],
[
{ width: 61, colspan: 1, rowspan: 1 },
{ width: 723, colspan: 7, rowspan: 1 }
],
[
{ width: 61, colspan: 1, rowspan: 1 },
{ width: 723, colspan: 7, rowspan: 1 }
],
[
{ width: 61, colspan: 1, rowspan: 2 },
{ width: 91, colspan: 1, rowspan: 1 },
{ width: 106, colspan: 1, rowspan: 1 },
{ width: 47, colspan: 1, rowspan: 1 },
{ width: 84, colspan: 1, rowspan: 1 },
{ width: 394, colspan: 3, rowspan: 1 }
],
[
{ width: 91, colspan: 1, rowspan: 1 },
{ width: 106, colspan: 1, rowspan: 1 },
{ width: 47, colspan: 1, rowspan: 1 },
{ width: 84, colspan: 1, rowspan: 1 },
{ width: 394, colspan: 3, rowspan: 1 }
],
[
{ width: 61, colspan: 1, rowspan: 2 },
{ width: 723, colspan: 7, rowspan: 1 }
],
[
{ width: 723, colspan: 7, rowspan: 1 }
],
[
{ width: 61, colspan: 1, rowspan: 1 },
{ width: 337, colspan: 5, rowspan: 1 },
{ width: 65, colspan: 1, rowspan: 1 },
{ width: 320, colspan: 1, rowspan: 1 }
]
]
算法如下
function calculateColumnWidths(tds: TdAttr[][]) {
// 列数
const columnCount = tds[0].reduce((acc, cell) => acc + cell.colspan, 0)
// 定义 dp[i][j] 表示 i 到 j 列的宽度,初始值为 0。(从 0 开始计数列数)
const dp = Array.from({ length: columnCount }, () => new Array<number>(columnCount).fill(0))
// 用于记录每列被跨行占据的行数
const occupied = new Array(columnCount).fill(0)
// 填充 dp
for (const row of tds) {
let currentColumnIndex = 0
const minRowspan = Math.min(...row.map(it => it.rowspan))
for (const cell of row) {
while (currentColumnIndex < columnCount && occupied[currentColumnIndex] > 0) {
occupied[currentColumnIndex] -= minRowspan
currentColumnIndex++
}
if (cell.rowspan > minRowspan) {
for (let i = 0; i < cell.colspan; i++) {
occupied[currentColumnIndex + i] = cell.rowspan - minRowspan
}
}
if (currentColumnIndex + cell.colspan - 1 >= columnCount) break
dp[currentColumnIndex][currentColumnIndex + cell.colspan - 1] = cell.width
currentColumnIndex += cell.colspan
}
}
// 计算 dp
for (let k = 0; k < (columnCount * columnCount) / 2; k++) {
for (let len = 1; len < columnCount; len++) {
for (let i = 0; i + len < columnCount; i++) {
let j = i + len
if (dp[i][j] == 0 && dp[i][j - 1] != 0 && dp[j][j] != 0) {
dp[i][j] = dp[i][j - 1] + dp[j][j]
}
if (dp[i][j - 1] == 0 && dp[i][j] != 0 && dp[j][j] != 0) {
dp[i][j - 1] = dp[i][j] - dp[j][j]
}
if (dp[j][j] == 0 && dp[i][j] != 0 && dp[i][j - 1] != 0) {
dp[j][j] = dp[i][j] - dp[i][j - 1]
}
}
}
}
return dp.map((it, index) => it[index])
}