row span summary
This commit is contained in:
parent
4c054b3215
commit
17bf4aaf99
111
table.go
111
table.go
|
|
@ -116,8 +116,17 @@ func NewTableGenerator(config *TableConfig) *TableGenerator {
|
||||||
|
|
||||||
func (t *TableData) Check() error {
|
func (t *TableData) Check() error {
|
||||||
headerLen := len(t.Headers)
|
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 {
|
for _, row := range t.Rows {
|
||||||
if headerLen != len(row) {
|
rowLen := len(row)
|
||||||
|
if rowLen != headerLen && rowLen != 2 {
|
||||||
return fmt.Errorf("header length does not match row length")
|
return fmt.Errorf("header length does not match row length")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -150,11 +159,34 @@ func (g *TableGenerator) calculateColumnWidths(data *TableData) []float64 {
|
||||||
|
|
||||||
// 检查每行内容的宽度
|
// 检查每行内容的宽度
|
||||||
for _, row := range data.Rows {
|
for _, row := range data.Rows {
|
||||||
for j, cell := range row {
|
if len(row) == 2 && colCount > 2 {
|
||||||
width, _ := g.dc.MeasureString(cell)
|
// 第一列占一列
|
||||||
|
width, _ := g.dc.MeasureString(row[0])
|
||||||
totalWidth := width + g.scaled(g.config.Padding)*2
|
totalWidth := width + g.scaled(g.config.Padding)*2
|
||||||
if totalWidth > colWidths[j] {
|
if totalWidth > colWidths[0] {
|
||||||
colWidths[j] = math.Min(totalWidth, g.scaled(g.config.MaxColWidth))
|
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
|
||||||
|
if totalWidth > colWidths[j] {
|
||||||
|
colWidths[j] = math.Min(totalWidth, g.scaled(g.config.MaxColWidth))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -174,14 +206,34 @@ func (g *TableGenerator) calculateRowHeights(data *TableData, colWidths []float6
|
||||||
// 计算每行内容的高度
|
// 计算每行内容的高度
|
||||||
for i, row := range data.Rows {
|
for i, row := range data.Rows {
|
||||||
maxHeight := 0.0
|
maxHeight := 0.0
|
||||||
for j, cell := range row {
|
if len(row) == 2 && len(data.Headers) > 2 {
|
||||||
// 测量文本在限定宽度内的高度
|
// 第一列占一列
|
||||||
lines := g.wrapText(cell, colWidths[j]-g.scaled(g.config.Padding)*2)
|
lines := g.wrapText(row[0], colWidths[0]-g.scaled(g.config.Padding)*2)
|
||||||
lineHeight := g.getLineHeight()
|
lineHeight := g.getLineHeight()
|
||||||
cellHeight := float64(len(lines))*lineHeight + g.scaled(g.config.Padding)*2
|
cellHeight := float64(len(lines))*lineHeight + g.scaled(g.config.Padding)*2
|
||||||
if cellHeight > maxHeight {
|
if cellHeight > maxHeight {
|
||||||
maxHeight = cellHeight
|
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)
|
||||||
|
lineHeight := g.getLineHeight()
|
||||||
|
cellHeight := float64(len(lines))*lineHeight + g.scaled(g.config.Padding)*2
|
||||||
|
if cellHeight > maxHeight {
|
||||||
|
maxHeight = cellHeight
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
rowHeights[i+1] = maxHeight
|
rowHeights[i+1] = maxHeight
|
||||||
}
|
}
|
||||||
|
|
@ -354,8 +406,19 @@ func (g *TableGenerator) Generate(data *TableData, filename string) error {
|
||||||
currentX := margin
|
currentX := margin
|
||||||
rowHeight := rowHeights[i]
|
rowHeight := rowHeights[i]
|
||||||
|
|
||||||
|
isTwoColRow := i > 0 && len(data.Rows[i-1]) == 2 && len(data.Headers) > 2
|
||||||
|
|
||||||
for j := 0; j < len(colWidths); j++ {
|
for j := 0; j < len(colWidths); j++ {
|
||||||
|
if isTwoColRow && j >= 2 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
colWidth := colWidths[j]
|
colWidth := colWidths[j]
|
||||||
|
if isTwoColRow && j == 1 {
|
||||||
|
for k := 2; k < len(colWidths); k++ {
|
||||||
|
colWidth += colWidths[k]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// 设置单元格背景色
|
// 设置单元格背景色
|
||||||
if i == 0 {
|
if i == 0 {
|
||||||
|
|
@ -374,7 +437,15 @@ func (g *TableGenerator) Generate(data *TableData, filename string) error {
|
||||||
cellText = data.Headers[j]
|
cellText = data.Headers[j]
|
||||||
dc.SetColor(g.config.HeaderTextColor)
|
dc.SetColor(g.config.HeaderTextColor)
|
||||||
} else {
|
} else {
|
||||||
cellText = data.Rows[i-1][j]
|
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)
|
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)
|
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.SetColor(g.config.LineColor)
|
||||||
dc.SetLineWidth(g.scaled(g.config.LineWidth))
|
dc.SetLineWidth(g.scaled(g.config.LineWidth))
|
||||||
|
|
||||||
|
|
@ -425,8 +496,22 @@ func (g *TableGenerator) drawGridLines(dc *gg.Context, colWidths, rowHeights []f
|
||||||
// 绘制垂直线
|
// 绘制垂直线
|
||||||
currentX := margin
|
currentX := margin
|
||||||
for j := 0; j <= len(colWidths); j++ {
|
for j := 0; j <= len(colWidths); j++ {
|
||||||
dc.DrawLine(currentX, startY, currentX, startY+tableHeight)
|
if j == 0 || j == len(colWidths) || len(data.Headers) <= 2 {
|
||||||
dc.Stroke()
|
// 最左/最右的线直接画到底,或者列数<=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) {
|
if j < len(colWidths) {
|
||||||
currentX += colWidths[j]
|
currentX += colWidths[j]
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue