Skip to content

Render styled XLSX grids and charts in Go - #176

Merged
shps951023 merged 3 commits into
mainfrom
fix/go-xlsx-text-overflow-pagination
Sep 14, 2026
Merged

shps951023 merged 3 commits into
mainfrom
fix/go-xlsx-text-overflow-pagination

Conversation

@shps951023

@shps951023 shps951023 commented Sep 14, 2026

Copy link
Copy Markdown
Member

Summary

  • render XLSX cell fills, borders, dimensions, merged ranges, number formats, and supported uncached formulas in Go
  • preserve Calibri-like text metrics, vertical alignment, and default row-height behavior
  • render embedded clustered-column charts across worksheet pagination bands
  • refresh the canonical Go issue/XLSX benchmark evidence

Closes #175

Validation

  • go vet ./...
  • go test ./...
  • go test -race ./...
  • dotnet build src/MiniPdf/MiniPdf.csproj --configuration Release
  • full Go issue/XLSX benchmark: 24/24 candidates valid, no missing Microsoft 365 or LibreOffice references
  • Business expense budget1: 4/4 pages, text similarity 1.0000, overall 0.9670 in the canonical three-page comparison and 0.9734 across all four pages
  • canonical target PNG matches the latest focused build by SHA-256

This follows merged PR #174; the branch contains only the post-merge Go rendering work and benchmark evidence relative to main.

Summary by CodeRabbit

  • New Features

    • Added enhanced XLSX-to-PDF rendering for styled cells, merged ranges, borders, fills, alignment, number formats, page settings, and formulas.
    • Added support for clustered column charts, including titles, labels, legends, axes, and series colors.
    • Improved text layout with horizontal scaling and more accurate font metrics.
    • Added automatic worksheet pagination and printable grid sizing.
  • Bug Fixes

    • Corrected formula rendering so calculated results appear without displaying formula expressions.
    • Improved conversion accuracy for supported XLSX documents, including previously incomplete pages and visual content.

Copilot AI lite review requested due to automatic review settings September 14, 2026 14:57
@coderabbitai

coderabbitai Bot commented Sep 14, 2026

Copy link
Copy Markdown

Review Change StackReview Change Stack

Caution

Review failed

The pull request is closed.

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Advanced

Run ID: a873ce54-68df-42cf-95b8-b4b482663338

📥 Commits

Reviewing files that changed from the base of the PR and between 6216961 and 725ecde.

⛔ Files ignored due to path filters (9)
  • artifacts/go-benchmark/issue/xlsx/report/images/Business expense budget1_p1_heatmap.png is excluded by !**/*.png
  • artifacts/go-benchmark/issue/xlsx/report/images/Business expense budget1_p1_minipdf.png is excluded by !**/*.png
  • artifacts/go-benchmark/issue/xlsx/report/images/Business expense budget1_p2_heatmap.png is excluded by !**/*.png
  • artifacts/go-benchmark/issue/xlsx/report/images/Business expense budget1_p2_minipdf.png is excluded by !**/*.png
  • artifacts/go-benchmark/issue/xlsx/report/images/Business expense budget1_p3_heatmap.png is excluded by !**/*.png
  • artifacts/go-benchmark/issue/xlsx/report/images/Business expense budget1_p3_minipdf.png is excluded by !**/*.png
  • artifacts/go-benchmark/issue/xlsx/report/side-by-side/Business expense budget1_p1_go_minipdf_vs_microsoft_365_excel_reference_vs_libreoffice.png is excluded by !**/*.png
  • artifacts/go-benchmark/issue/xlsx/report/side-by-side/Business expense budget1_p2_go_minipdf_vs_microsoft_365_excel_reference_vs_libreoffice.png is excluded by !**/*.png
  • artifacts/go-benchmark/issue/xlsx/report/side-by-side/Business expense budget1_p3_go_minipdf_vs_microsoft_365_excel_reference_vs_libreoffice.png is excluded by !**/*.png
📒 Files selected for processing (8)
  • artifacts/go-benchmark/issue/xlsx/report/benchmark_coverage.json
  • artifacts/go-benchmark/issue/xlsx/report/comparison_report.json
  • artifacts/go-benchmark/issue/xlsx/report/comparison_report.md
  • minipdf-go/office_test.go
  • minipdf-go/pdf.go
  • minipdf-go/xlsx.go
  • minipdf-go/xlsx_chart.go
  • minipdf-go/xlsx_render.go

📝 Walkthrough

Walkthrough

Changes

XLSX styled rendering and chart support

Layer / File(s) Summary
Conversion routing and cell decoding
minipdf-go/xlsx.go
Styled worksheets with fully uncached formulas use the grid renderer. Formula text is kept separate from displayed cell values.
Styled worksheet rendering
minipdf-go/xlsx_render.go, minipdf-go/pdf.go
The renderer supports styles, dimensions, merges, formulas, pagination, alignment, numeric formatting, and horizontal text scaling.
Embedded chart rendering
minipdf-go/xlsx_chart.go
The converter discovers clustered charts, resolves their data, extends worksheet bounds, and renders chart elements into PDF pages.
Rendering validation and benchmark results
minipdf-go/office_test.go, artifacts/go-benchmark/issue/xlsx/report/*
Tests cover styled cells, formulas, dimensions, alignment, and charts. The benchmark reports four pages, identical text, and a 0.967 overall score for the target workbook.

Estimated code review effort: 4 (Complex) | ~45 minutes

Change: Feature · Severity of issue fixed: Medium

Sequence Diagram(s)

sequenceDiagram
  participant XLSXConverter
  participant XLSXRenderer
  participant ChartParser
  participant PDF
  XLSXConverter->>XLSXRenderer: route styled worksheet
  XLSXRenderer->>ChartParser: load worksheet chart relationships
  ChartParser-->>XLSXRenderer: return chart data and anchors
  XLSXRenderer->>PDF: render worksheet grid and charts
Loading
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/go-xlsx-text-overflow-pagination

Comment @coderabbitai help to get the list of available commands.

@shps951023
shps951023 merged commit ae9671f into main Sep 14, 2026
4 of 5 checks passed

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🟡 Changes recommended

Unresolved moderate issues remain across worksheet selection, formulas, chart rendering, pagination, merges, and number formatting.

Get a fresh assessment by requesting another Copilot review.

Pull request overview

This PR adds styled XLSX grid and clustered-chart rendering in Go, including formulas, formatting, pagination, and benchmark updates.

Changes:

  • Added style-aware worksheet and chart rendering.
  • Added formula, merge, dimension, and number-format support.
  • Added regression tests and refreshed benchmark evidence.
File summaries
File Description
minipdf-go/xlsx.go Selects XLSX rendering paths.
minipdf-go/xlsx_render.go Parses and renders styled worksheet grids.
minipdf-go/xlsx_chart.go Parses and renders embedded charts.
minipdf-go/pdf.go Adds horizontal text scaling.
minipdf-go/office_test.go Adds XLSX rendering tests.
artifacts/go-benchmark/issue/xlsx/report/comparison_report.md Updates benchmark report.
artifacts/go-benchmark/issue/xlsx/report/comparison_report.json Updates comparison metrics.
artifacts/go-benchmark/issue/xlsx/report/benchmark_coverage.json Refreshes benchmark coverage data.
Review details

Suppressed comments (4)

minipdf-go/xlsx_chart.go:397

  • The parsed series.categories are never used here; only the number of values is read. Consequently the rendered chart has no category labels, such as Day 1 through Day 5 in the canonical chart fixture, even after chart dispatch is fixed. Emit the category values under each group and clip them to the chart band.
	categories := 0
	for _, series := range chart.series {
		categories = max(categories, len(series.values))
	}

minipdf-go/xlsx_chart.go:413

  • A zero or negative series value is forced to a positive 0.5-point bar, and the axis maximum is initialized at zero, so clustered-column charts with negative or zero data are rendered with incorrect bars and no negative range. Compute the data range and draw bars around a zero baseline instead of clamping every value to a positive height.
				height := max(0.5, series.values[category]/axisMaximum*plotHeight)
				barX := plotLeft + float64(category)*groupWidth + groupPadding + float64(seriesIndex)*barWidth
				clip.rect(barX, plotBottom, barWidth, height, colors[seriesIndex%len(colors)])

minipdf-go/xlsx_chart.go:133

  • A worksheet can legally contain a drawing made only of shapes and have no drawing relationship part. In that case files.read(drawingRelationshipsName) returns an error and aborts conversion, even though there are no charts to render. Treat a missing drawing .rels part as an unsupported drawing with zero charts, while still propagating errors for a part that exists but cannot be read.
	drawingRelationshipsName := xlsxRelationshipPartName(drawingName)
	drawingRelationshipsData, err := files.read(drawingRelationshipsName)
	if err != nil {
		return nil, err

minipdf-go/xlsx_render.go:487

  • These loops mark every cell in every merge before checking whether the merge intersects the current band. A large valid merged range, including a whole-sheet merge, can therefore cause enormous work and allocation on every page; clip the merge to rows and columns before populating merged.
	for _, merge := range grid.merges {
		for row := merge.startRow; row <= merge.endRow; row++ {
			for column := merge.startColumn; column <= merge.endColumn; column++ {
				merged[[2]int{row, column}] = true
			}
  • Files reviewed: 8/17 changed files
  • Comments generated: 6
  • Review effort level: Lite

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread minipdf-go/xlsx.go
Comment on lines +35 to +39
if needsCalculatedXLSXGrid(worksheetXML) {
needsGridRendering = true
break
}
}
Comment thread minipdf-go/xlsx_chart.go
Comment on lines +160 to +164
width = grid.columnOffset(anchor.To.Column) - grid.columnOffset(anchor.From.Column)
}
if height <= 0 {
height = grid.rowOffset(anchor.To.Row) - grid.rowOffset(anchor.From.Row)
}
Comment thread minipdf-go/xlsx_render.go
Comment on lines +132 to +142
formulaCount := 0
for _, cell := range worksheet.Cells {
if cell.Formula == nil {
continue
}
formulaCount++
if cell.Value != nil && strings.TrimSpace(*cell.Value) != "" {
return false
}
}
return formulaCount > 0
Comment thread minipdf-go/xlsx_render.go
Comment on lines +430 to +432
if grid.maxRow == 0 || grid.maxColumn == 0 {
document.AddPage(grid.pageSize.Width, grid.pageSize.Height)
return
Comment thread minipdf-go/xlsx_render.go
endColumn := min(merge.endColumn, columns.end)
startRow := max(merge.startRow, rows.start)
endRow := min(merge.endRow, rows.end)
renderXLSXCell(page, cell, xPositions[startColumn], yPositions[endRow+1], xPositions[endColumn+1]-xPositions[startColumn], yPositions[startRow]-yPositions[endRow+1], styleAt(styles, cell.style), merge.startColumn >= columns.start && merge.startColumn <= columns.end)
Comment thread minipdf-go/xlsx_render.go
Comment on lines +709 to +711
func formatXLSXValue(value string, numeric bool, numberFormat int) string {
if !numeric || numberFormat != 4 {
return value
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Improve Go XLSX styled grid and chart rendering

2 participants