row span summary

This commit is contained in:
jiangyong 2026-04-03 16:30:08 +08:00
parent 4c054b3215
commit 17bf4aaf99
1 changed files with 98 additions and 13 deletions

View File

@ -116,12 +116,21 @@ func NewTableGenerator(config *TableConfig) *TableGenerator {
func (t *TableData) Check() error {
headerLen := len(t.Headers)
if headerLen <= 2 {
for _, row := range t.Rows {
if headerLen != len(row) {
return fmt.Errorf("header length does not match row length")
}
}
return nil
}
for _, row := range t.Rows {
rowLen := len(row)
if rowLen != headerLen && rowLen != 2 {
return fmt.Errorf("header length does not match row length")
}
}
return nil
}
// 应用缩放因子
@ -150,6 +159,28 @@ func (g *TableGenerator) calculateColumnWidths(data *TableData) []float64 {
// 检查每行内容的宽度
for _, row := range data.Rows {
if len(row) == 2 && colCount > 2 {
// 第一列占一列
width, _ := g.dc.MeasureString(row[0])
totalWidth := width + g.scaled(g.config.Padding)*2
if totalWidth > colWidths[0] {
colWidths[0] = math.Min(totalWidth, g.scaled(g.config.MaxColWidth))
}
// 第二列跨剩余所有列
width, _ = g.dc.MeasureString(row[1])
totalWidth = width + g.scaled(g.config.Padding)*2
remainingWidth := 0.0
for k := 1; k < colCount; k++ {
remainingWidth += colWidths[k]
}
if totalWidth > remainingWidth {
diff := totalWidth - remainingWidth
perCol := diff / float64(colCount-1)
for k := 1; k < colCount; k++ {
colWidths[k] += perCol
}
}
} else {
for j, cell := range row {
width, _ := g.dc.MeasureString(cell)
totalWidth := width + g.scaled(g.config.Padding)*2
@ -158,6 +189,7 @@ func (g *TableGenerator) calculateColumnWidths(data *TableData) []float64 {
}
}
}
}
return colWidths
}
@ -174,6 +206,25 @@ func (g *TableGenerator) calculateRowHeights(data *TableData, colWidths []float6
// 计算每行内容的高度
for i, row := range data.Rows {
maxHeight := 0.0
if len(row) == 2 && len(data.Headers) > 2 {
// 第一列占一列
lines := g.wrapText(row[0], colWidths[0]-g.scaled(g.config.Padding)*2)
lineHeight := g.getLineHeight()
cellHeight := float64(len(lines))*lineHeight + g.scaled(g.config.Padding)*2
if cellHeight > maxHeight {
maxHeight = cellHeight
}
// 第二列跨剩余所有列
remainingWidth := 0.0
for k := 1; k < len(colWidths); k++ {
remainingWidth += colWidths[k]
}
lines = g.wrapText(row[1], remainingWidth-g.scaled(g.config.Padding)*2)
cellHeight = float64(len(lines))*lineHeight + g.scaled(g.config.Padding)*2
if cellHeight > maxHeight {
maxHeight = cellHeight
}
} else {
for j, cell := range row {
// 测量文本在限定宽度内的高度
lines := g.wrapText(cell, colWidths[j]-g.scaled(g.config.Padding)*2)
@ -183,6 +234,7 @@ func (g *TableGenerator) calculateRowHeights(data *TableData, colWidths []float6
maxHeight = cellHeight
}
}
}
rowHeights[i+1] = maxHeight
}
@ -354,8 +406,19 @@ func (g *TableGenerator) Generate(data *TableData, filename string) error {
currentX := margin
rowHeight := rowHeights[i]
isTwoColRow := i > 0 && len(data.Rows[i-1]) == 2 && len(data.Headers) > 2
for j := 0; j < len(colWidths); j++ {
if isTwoColRow && j >= 2 {
continue
}
colWidth := colWidths[j]
if isTwoColRow && j == 1 {
for k := 2; k < len(colWidths); k++ {
colWidth += colWidths[k]
}
}
// 设置单元格背景色
if i == 0 {
@ -373,8 +436,16 @@ func (g *TableGenerator) Generate(data *TableData, filename string) error {
if i == 0 {
cellText = data.Headers[j]
dc.SetColor(g.config.HeaderTextColor)
} else {
if isTwoColRow {
if j == 0 {
cellText = data.Rows[i-1][0]
} else {
cellText = data.Rows[i-1][1]
}
} else {
cellText = data.Rows[i-1][j]
}
dc.SetColor(g.config.TextColor)
}
@ -399,7 +470,7 @@ func (g *TableGenerator) Generate(data *TableData, filename string) error {
}
// 绘制网格线(使用缩放后的线宽)
g.drawGridLines(dc, colWidths, rowHeights, margin, tableStartY, tableWidth, tableHeight)
g.drawGridLines(dc, colWidths, rowHeights, margin, tableStartY, tableWidth, tableHeight, data)
// 绘制外边框(使用缩放后的边框宽度)
g.drawOuterBorder(dc, margin, tableStartY, tableWidth, tableHeight)
@ -408,7 +479,7 @@ func (g *TableGenerator) Generate(data *TableData, filename string) error {
}
// 绘制网格线(高清优化)
func (g *TableGenerator) drawGridLines(dc *gg.Context, colWidths, rowHeights []float64, margin, startY, tableWidth, tableHeight float64) {
func (g *TableGenerator) drawGridLines(dc *gg.Context, colWidths, rowHeights []float64, margin, startY, tableWidth, tableHeight float64, data *TableData) {
dc.SetColor(g.config.LineColor)
dc.SetLineWidth(g.scaled(g.config.LineWidth))
@ -425,8 +496,22 @@ func (g *TableGenerator) drawGridLines(dc *gg.Context, colWidths, rowHeights []f
// 绘制垂直线
currentX := margin
for j := 0; j <= len(colWidths); j++ {
if j == 0 || j == len(colWidths) || len(data.Headers) <= 2 {
// 最左/最右的线直接画到底,或者列数<=2时不需要跳过
dc.DrawLine(currentX, startY, currentX, startY+tableHeight)
dc.Stroke()
} else {
// 内部垂直线需要跳过2列合并行的内部
currentY2 := startY
for i := 0; i < len(rowHeights); i++ {
isTwoColRow := i > 0 && len(data.Rows[i-1]) == 2 && len(data.Headers) > 2
if !(isTwoColRow && j >= 2) {
dc.DrawLine(currentX, currentY2, currentX, currentY2+rowHeights[i])
dc.Stroke()
}
currentY2 += rowHeights[i]
}
}
if j < len(colWidths) {
currentX += colWidths[j]
}