From 2db73634468d9fe8a328007a8dc881f4f6fa71a2 Mon Sep 17 00:00:00 2001 From: Taiki Noda Date: Mon, 23 Mar 2026 19:26:09 +0900 Subject: [PATCH 1/5] fix: propagate overflow from horizontal layout for multi-page tables --- .github/ISSUE_TEMPLATE/bug_report.yml | 2 +- CHANGELOG.md | 4 +- _benchmark/go.mod | 12 +-- _benchmark/go.sum | 30 +++--- _examples/builder/33_multi_page_table_test.go | 100 ++++++++++++++++++ _validation/go.mod | 4 +- _validation/go.sum | 8 +- document/layout/block.go | 41 +++++++ 8 files changed, 171 insertions(+), 30 deletions(-) create mode 100644 _examples/builder/33_multi_page_table_test.go diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml index debb2ad..9ce51e4 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.yml +++ b/.github/ISSUE_TEMPLATE/bug_report.yml @@ -63,7 +63,7 @@ body: id: gpdf-version attributes: label: gpdf Version - placeholder: "v1.0.2" + placeholder: "v1.0.3" validations: required: true diff --git a/CHANGELOG.md b/CHANGELOG.md index c0c518b..674a7f8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -102,8 +102,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/). - Reed-Solomon coefficient order in QR code encoder - binary.Write return value handling for errcheck lint -[Unreleased]: https://github.com/gpdf-dev/gpdf/compare/v1.0.2...HEAD -[1.0.2]: https://github.com/gpdf-dev/gpdf/compare/v1.0.0...v1.0.2 +[Unreleased]: https://github.com/gpdf-dev/gpdf/compare/v1.0.3...HEAD +[1.0.2]: https://github.com/gpdf-dev/gpdf/compare/v1.0.0...v1.0.3 [1.0.0]: https://github.com/gpdf-dev/gpdf/compare/v0.9.0...v1.0.0 [0.9.0]: https://github.com/gpdf-dev/gpdf/compare/v0.8.0...v0.9.0 [0.8.0]: https://github.com/gpdf-dev/gpdf/compare/v0.5.0...v0.8.0 diff --git a/_benchmark/go.mod b/_benchmark/go.mod index d0e979b..b493340 100644 --- a/_benchmark/go.mod +++ b/_benchmark/go.mod @@ -5,24 +5,24 @@ go 1.24 replace github.com/gpdf-dev/gpdf => ../ require ( - github.com/go-pdf/fpdf v1.0.2 + github.com/go-pdf/fpdf v1.0.3 github.com/johnfercher/maroto/v2 v2.3.3 - github.com/gpdf-dev/gpdf v1.0.2 + github.com/gpdf-dev/gpdf v1.0.3 github.com/signintech/gopdf v0.36.0 golang.org/x/image v0.18.0 ) require ( - github.com/boombuler/barcode v1.0.2 // indirect + github.com/boombuler/barcode v1.0.3 // indirect github.com/f-amaral/go-async v0.3.0 // indirect github.com/google/uuid v1.5.0 // indirect - github.com/hhrutter/lzw v1.0.2 // indirect - github.com/hhrutter/tiff v1.0.2 // indirect + github.com/hhrutter/lzw v1.0.3 // indirect + github.com/hhrutter/tiff v1.0.3 // indirect github.com/johnfercher/go-tree v1.0.5 // indirect github.com/mattn/go-runewidth v0.0.15 // indirect github.com/pdfcpu/pdfcpu v0.6.0 // indirect github.com/phpdave11/gofpdf v1.4.3 // indirect - github.com/phpdave11/gofpdi v1.0.25 // indirect + github.com/phpdave11/gofpdi v1.0.35 // indirect github.com/pkg/errors v0.9.1 // indirect github.com/rivo/uniseg v0.4.4 // indirect golang.org/x/text v0.22.0 // indirect diff --git a/_benchmark/go.sum b/_benchmark/go.sum index c24384d..b2e8c36 100644 --- a/_benchmark/go.sum +++ b/_benchmark/go.sum @@ -1,38 +1,38 @@ -github.com/boombuler/barcode v1.0.2/go.mod h1:paBWMcWSl3LHKBqUq+rly7CNSldXjb2rDl3JlRe0mD8= -github.com/boombuler/barcode v1.0.2 h1:NDBbPmhS+EqABEs5Kg3n/5ZNjy73Pz7SIV+KCeqyXcs= -github.com/boombuler/barcode v1.0.2/go.mod h1:paBWMcWSl3LHKBqUq+rly7CNSldXjb2rDl3JlRe0mD8= +github.com/boombuler/barcode v1.0.3/go.mod h1:paBWMcWSl3LHKBqUq+rly7CNSldXjb2rDl3JlRe0mD8= +github.com/boombuler/barcode v1.0.3 h1:NDBbPmhS+EqABEs5Kg3n/5ZNjy73Pz7SIV+KCeqyXcs= +github.com/boombuler/barcode v1.0.3/go.mod h1:paBWMcWSl3LHKBqUq+rly7CNSldXjb2rDl3JlRe0mD8= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/f-amaral/go-async v0.3.0 h1:h4kLsX7aKfdWaHvV0lf+/EE3OIeCzyeDYJDb/vDZUyg= github.com/f-amaral/go-async v0.3.0/go.mod h1:Hz5Qr6DAWpbTTUjytnrg1WIsDgS7NtOei5y8SipYS7U= -github.com/go-pdf/fpdf v1.0.2 h1:PPvSaUuo1iMi9KkaAn90NuKi+P4gwMedWPHhj8YlJQw= -github.com/go-pdf/fpdf v1.0.2/go.mod h1:oO8N111TkmKb9D7VvWGLvLJlaZUQVPM+6V42pp3iV4Y= +github.com/go-pdf/fpdf v1.0.3 h1:PPvSaUuo1iMi9KkaAn90NuKi+P4gwMedWPHhj8YlJQw= +github.com/go-pdf/fpdf v1.0.3/go.mod h1:oO8N111TkmKb9D7VvWGLvLJlaZUQVPM+6V42pp3iV4Y= github.com/google/uuid v1.5.0 h1:1p67kYwdtXjb0gL0BPiP1Av9wiZPo5A8z2cWkTZ+eyU= github.com/google/uuid v1.5.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/hhrutter/lzw v1.0.2 h1:laL89Llp86W3rRs83LvKbwYRx6INE8gDn0XNb1oXtm0= -github.com/hhrutter/lzw v1.0.2/go.mod h1:2HC6DJSn/n6iAZfgM3Pg+cP1KxeWc3ezG8bBqW5+WEo= -github.com/hhrutter/tiff v1.0.2 h1:MIus8caHU5U6823gx7C6jrfoEvfSTGtEFRiM8/LOzC0= -github.com/hhrutter/tiff v1.0.2/go.mod h1:zU/dNgDm0cMIa8y8YwcYBeuEEveI4B0owqHyiPpJPHc= +github.com/hhrutter/lzw v1.0.3 h1:laL89Llp86W3rRs83LvKbwYRx6INE8gDn0XNb1oXtm0= +github.com/hhrutter/lzw v1.0.3/go.mod h1:2HC6DJSn/n6iAZfgM3Pg+cP1KxeWc3ezG8bBqW5+WEo= +github.com/hhrutter/tiff v1.0.3 h1:MIus8caHU5U6823gx7C6jrfoEvfSTGtEFRiM8/LOzC0= +github.com/hhrutter/tiff v1.0.3/go.mod h1:zU/dNgDm0cMIa8y8YwcYBeuEEveI4B0owqHyiPpJPHc= github.com/johnfercher/go-tree v1.0.5 h1:zpgVhJsChavzhKdxhQiCJJzcSY3VCT9oal2JoA2ZevY= github.com/johnfercher/go-tree v1.0.5/go.mod h1:DUO6QkXIFh1K7jeGBIkLCZaeUgnkdQAsB64FDSoHswg= github.com/johnfercher/maroto/v2 v2.3.3 h1:oeXsBnoecaMgRDwN0Cstjoe4rug3lKpOanuxuHKPqQE= github.com/johnfercher/maroto/v2 v2.3.3/go.mod h1:KNv102TwUrlVgZGukzlIbhkG6l/WaCD6pzu6aWGVjBI= -github.com/jung-kurt/gofpdf v1.0.2/go.mod h1:7Id9E/uU8ce6rXgefFLlgrJj/GYY22cpxn+r32jIOes= +github.com/jung-kurt/gofpdf v1.0.3/go.mod h1:7Id9E/uU8ce6rXgefFLlgrJj/GYY22cpxn+r32jIOes= github.com/mattn/go-runewidth v0.0.15 h1:UNAjwbU9l54TA3KzvqLGxwWjHmMgBUVhBiTjelZgg3U= github.com/mattn/go-runewidth v0.0.15/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= github.com/pdfcpu/pdfcpu v0.6.0 h1:z4kARP5bcWa39TTYMcN/kjBnm7MvhTWjXgeYmkdAGMI= github.com/pdfcpu/pdfcpu v0.6.0/go.mod h1:kmpD0rk8YnZj0l3qSeGBlAB+XszHUgNv//ORH/E7EYo= github.com/phpdave11/gofpdf v1.4.3 h1:M/zHvS8FO3zh9tUd2RCOPEjyuVcs281FCyF22Qlz/IA= github.com/phpdave11/gofpdf v1.4.3/go.mod h1:MAwzoUIgD3J55u0rxIG2eu37c+XWhBtXSpPAhnQXf/o= -github.com/phpdave11/gofpdi v1.0.24-0.20211212211723-1f10f9844311/go.mod h1:vBmVV0Do6hSBHC8uKUQ71JGW+ZGQq74llk/7bXwjDoI= -github.com/phpdave11/gofpdi v1.0.25 h1:iJazY1BQ07I9s7N5EWjBO1YbhmKfHGxNligUv/Rw4Lc= -github.com/phpdave11/gofpdi v1.0.25/go.mod h1:vBmVV0Do6hSBHC8uKUQ71JGW+ZGQq74llk/7bXwjDoI= +github.com/phpdave11/gofpdi v1.0.34-0.20211212211723-1f10f9844311/go.mod h1:vBmVV0Do6hSBHC8uKUQ71JGW+ZGQq74llk/7bXwjDoI= +github.com/phpdave11/gofpdi v1.0.35 h1:iJazY1BQ07I9s7N5EWjBO1YbhmKfHGxNligUv/Rw4Lc= +github.com/phpdave11/gofpdi v1.0.35/go.mod h1:vBmVV0Do6hSBHC8uKUQ71JGW+ZGQq74llk/7bXwjDoI= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= -github.com/pmezard/go-difflib v1.0.2 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= -github.com/pmezard/go-difflib v1.0.2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/pmezard/go-difflib v1.0.3 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.3/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= github.com/rivo/uniseg v0.4.4 h1:8TfxU8dW6PdqD27gjM8MVNuicgxIjxpm4K7x4jp8sis= github.com/rivo/uniseg v0.4.4/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= diff --git a/_examples/builder/33_multi_page_table_test.go b/_examples/builder/33_multi_page_table_test.go new file mode 100644 index 0000000..c926074 --- /dev/null +++ b/_examples/builder/33_multi_page_table_test.go @@ -0,0 +1,100 @@ +package builder_test + +import ( + "fmt" + "testing" + + "github.com/gpdf-dev/gpdf/_examples/testutil" + "github.com/gpdf-dev/gpdf/document" + "github.com/gpdf-dev/gpdf/pdf" + "github.com/gpdf-dev/gpdf/template" +) + +// TestExample_33_MultiPageTable verifies whether a large table automatically +// splits across multiple pages when its content exceeds the available space. +func TestExample_33_MultiPageTable(t *testing.T) { + doc := template.New( + template.WithPageSize(document.A4), + template.WithMargins(document.UniformEdges(document.Mm(15))), + ) + + // Header/footer to verify they repeat on every page. + doc.Header(func(p *template.PageBuilder) { + p.AutoRow(func(r *template.RowBuilder) { + r.Col(6, func(c *template.ColBuilder) { + c.Text("Multi-Page Table Test", template.Bold(), template.FontSize(9)) + }) + r.Col(6, func(c *template.ColBuilder) { + c.Text("Header", template.AlignRight(), template.FontSize(9), + template.TextColor(pdf.Gray(0.5))) + }) + }) + p.AutoRow(func(r *template.RowBuilder) { + r.Col(12, func(c *template.ColBuilder) { + c.Line() + c.Spacer(document.Mm(3)) + }) + }) + }) + + doc.Footer(func(p *template.PageBuilder) { + p.AutoRow(func(r *template.RowBuilder) { + r.Col(12, func(c *template.ColBuilder) { + c.Spacer(document.Mm(3)) + c.Line() + }) + }) + p.AutoRow(func(r *template.RowBuilder) { + r.Col(12, func(c *template.ColBuilder) { + c.Text("Footer - Page Break Test", + template.AlignCenter(), template.FontSize(8), template.TextColor(pdf.Gray(0.5))) + }) + }) + }) + + // Generate 100 rows — far more than a single A4 page can hold. + rows := make([][]string, 100) + for i := range rows { + rows[i] = []string{ + fmt.Sprintf("%d", i+1), + fmt.Sprintf("Product-%04d", i+1), + fmt.Sprintf("Category %c", 'A'+rune(i%5)), + fmt.Sprintf("$%d.%02d", 10+i*3, (i*7)%100), + fmt.Sprintf("%d", 100+i*10), + } + } + + page := doc.AddPage() + + page.AutoRow(func(r *template.RowBuilder) { + r.Col(12, func(c *template.ColBuilder) { + c.Text("Large Table — Auto Page Break Test", template.FontSize(16), template.Bold()) + c.Spacer(document.Mm(5)) + }) + }) + + page.AutoRow(func(r *template.RowBuilder) { + r.Col(12, func(c *template.ColBuilder) { + c.Table( + []string{"#", "Product", "Category", "Price", "Stock"}, + rows, + template.ColumnWidths(8, 30, 22, 20, 20), + template.TableHeaderStyle( + template.TextColor(pdf.White), + template.BgColor(pdf.RGBHex(0x1A237E)), + ), + template.TableStripe(pdf.RGBHex(0xF5F5F5)), + ) + }) + }) + + data, err := doc.Generate() + if err != nil { + t.Fatalf("Generate failed: %v", err) + } + testutil.AssertValidPDF(t, data) + testutil.WritePDF(t, "33_multi_page_table.pdf", data) + + // Log the PDF size so we can check it produced meaningful output. + t.Logf("Generated PDF: %d bytes", len(data)) +} diff --git a/_validation/go.mod b/_validation/go.mod index 26a11bd..272c2e8 100644 --- a/_validation/go.mod +++ b/_validation/go.mod @@ -10,8 +10,8 @@ require ( ) require ( - github.com/hhrutter/lzw v1.0.2 // indirect - github.com/hhrutter/tiff v1.0.2 // indirect + github.com/hhrutter/lzw v1.0.3 // indirect + github.com/hhrutter/tiff v1.0.3 // indirect github.com/mattn/go-runewidth v0.0.16 // indirect github.com/pkg/errors v0.9.1 // indirect github.com/rivo/uniseg v0.4.7 // indirect diff --git a/_validation/go.sum b/_validation/go.sum index 7482956..604f923 100644 --- a/_validation/go.sum +++ b/_validation/go.sum @@ -1,7 +1,7 @@ -github.com/hhrutter/lzw v1.0.2 h1:laL89Llp86W3rRs83LvKbwYRx6INE8gDn0XNb1oXtm0= -github.com/hhrutter/lzw v1.0.2/go.mod h1:2HC6DJSn/n6iAZfgM3Pg+cP1KxeWc3ezG8bBqW5+WEo= -github.com/hhrutter/tiff v1.0.2 h1:MIus8caHU5U6823gx7C6jrfoEvfSTGtEFRiM8/LOzC0= -github.com/hhrutter/tiff v1.0.2/go.mod h1:zU/dNgDm0cMIa8y8YwcYBeuEEveI4B0owqHyiPpJPHc= +github.com/hhrutter/lzw v1.0.3 h1:laL89Llp86W3rRs83LvKbwYRx6INE8gDn0XNb1oXtm0= +github.com/hhrutter/lzw v1.0.3/go.mod h1:2HC6DJSn/n6iAZfgM3Pg+cP1KxeWc3ezG8bBqW5+WEo= +github.com/hhrutter/tiff v1.0.3 h1:MIus8caHU5U6823gx7C6jrfoEvfSTGtEFRiM8/LOzC0= +github.com/hhrutter/tiff v1.0.3/go.mod h1:zU/dNgDm0cMIa8y8YwcYBeuEEveI4B0owqHyiPpJPHc= github.com/mattn/go-runewidth v0.0.16 h1:E5ScNMtiwvlvB5paMFdw9p4kSQzbXFikJ5SQO6TULQc= github.com/mattn/go-runewidth v0.0.16/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= github.com/pdfcpu/pdfcpu v0.9.1 h1:q8/KlBdHjkE7ZJU4ofhKG5Rjf7M6L324CVM6BMDySao= diff --git a/document/layout/block.go b/document/layout/block.go index f1d7926..31df9b4 100644 --- a/document/layout/block.go +++ b/document/layout/block.go @@ -330,8 +330,10 @@ func (bl *BlockLayout) layoutHorizontal(node document.DocumentNode, constraints } var placed []PlacedNode + childResults := make([]Result, len(children)) cursorX := 0.0 maxHeight := 0.0 + hasOverflow := false for i, child := range children { if child == nil { @@ -345,6 +347,10 @@ func (bl *BlockLayout) layoutHorizontal(node document.DocumentNode, constraints } childResult := bl.layoutChild(child, childConstraints) + childResults[i] = childResult + if childResult.Overflow != nil { + hasOverflow = true + } placed = append(placed, PlacedNode{ Node: child, @@ -373,6 +379,40 @@ func (bl *BlockLayout) layoutHorizontal(node document.DocumentNode, constraints stretchPlacedNodes(placed, finalContentHeight) totalHeight := margin.Top + borderWidths.Top + padding.Top + finalContentHeight + padding.Bottom + borderWidths.Bottom + margin.Bottom + + // Build overflow: if any column overflowed, create a new horizontal box + // mirroring the column structure with overflow content (or empty boxes for + // columns that fit entirely). + var overflow document.DocumentNode + if hasOverflow { + overflowChildren := make([]document.DocumentNode, len(children)) + for i, child := range children { + if child == nil { + continue + } + if childResults[i].Overflow != nil { + // Column had overflow — carry it forward. + overflowChildren[i] = childResults[i].Overflow + } else { + // Column fit entirely — use an empty placeholder with the same width. + placeholder := &document.Box{} + if box, ok := child.(*document.Box); ok { + placeholder.BoxStyle.Width = box.BoxStyle.Width + } + overflowChildren[i] = placeholder + } + } + overflow = &document.Box{ + Content: overflowChildren, + BoxStyle: document.BoxStyle{ + Direction: document.DirectionHorizontal, + Margin: style.Margin, + Padding: style.Padding, + Border: style.Border, + }, + } + } + return Result{ Bounds: document.Rectangle{ X: 0, @@ -381,6 +421,7 @@ func (bl *BlockLayout) layoutHorizontal(node document.DocumentNode, constraints Height: totalHeight, }, Children: placed, + Overflow: overflow, } } From 32a879e5cb0ca2949a4ad129b29536db0e85bde6 Mon Sep 17 00:00:00 2001 From: Taiki Noda Date: Mon, 23 Mar 2026 19:26:55 +0900 Subject: [PATCH 2/5] fix: propagate overflow from horizontal layout for multi-page tables --- CHANGELOG.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 674a7f8..7a368a9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/). ## [Unreleased] +### Fixed +- Multi-page table support — tables inside Row/Col now automatically split across pages + - `layoutHorizontal` propagates child overflow to the paginator + - Table headers repeat on each continuation page (existing `layoutTable` logic) + ## [1.0.2] - 2026-03-23 ### Added From 766125c5887e24346b018eec3f460858ae81388d Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Mon, 23 Mar 2026 10:27:43 +0000 Subject: [PATCH 3/5] chore: bump version to v1.0.3 --- internal/buildinfo/version.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/buildinfo/version.go b/internal/buildinfo/version.go index dca7ff8..9925f24 100644 --- a/internal/buildinfo/version.go +++ b/internal/buildinfo/version.go @@ -3,4 +3,4 @@ package buildinfo // Version is the library version. It is the single source of truth used by // the public gpdf.Version constant and the default PDF Producer metadata. -const Version = "1.0.1" +const Version = "1.0.3" From 629f95cfe99640c9c5c44bd04b6563a85ca9cd55 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Mon, 23 Mar 2026 10:29:08 +0000 Subject: [PATCH 4/5] update coverage badge to 86.3% --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index e67916a..24a7295 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ [![Go Reference](https://pkg.go.dev/badge/github.com/gpdf-dev/gpdf.svg)](https://pkg.go.dev/github.com/gpdf-dev/gpdf) [![CI](https://github.com/gpdf-dev/gpdf/actions/workflows/check-code.yml/badge.svg)](https://github.com/gpdf-dev/gpdf/actions/workflows/check-code.yml) -![coverage](https://img.shields.io/badge/coverage-86.5%25-green) +![coverage](https://img.shields.io/badge/coverage-86.3%25-green) [![Go Report Card](https://goreportcard.com/badge/github.com/gpdf-dev/gpdf)](https://goreportcard.com/report/github.com/gpdf-dev/gpdf) [![Go Version](https://img.shields.io/badge/Go-%3E%3D1.22-blue)](https://go.dev/) [![Website](https://img.shields.io/badge/Website-gpdf.dev-blue)](https://gpdf.dev/) From 5f9a0531069bdb2429a048f6ad9f286778f15166 Mon Sep 17 00:00:00 2001 From: Taiki Noda Date: Mon, 23 Mar 2026 19:31:13 +0900 Subject: [PATCH 5/5] refactor: extract buildHorizontalOverflow to reduce cyclomatic complexity --- document/layout/block.go | 66 +++++++++++++++++++++------------------- 1 file changed, 34 insertions(+), 32 deletions(-) diff --git a/document/layout/block.go b/document/layout/block.go index 31df9b4..2c460f7 100644 --- a/document/layout/block.go +++ b/document/layout/block.go @@ -380,38 +380,7 @@ func (bl *BlockLayout) layoutHorizontal(node document.DocumentNode, constraints totalHeight := margin.Top + borderWidths.Top + padding.Top + finalContentHeight + padding.Bottom + borderWidths.Bottom + margin.Bottom - // Build overflow: if any column overflowed, create a new horizontal box - // mirroring the column structure with overflow content (or empty boxes for - // columns that fit entirely). - var overflow document.DocumentNode - if hasOverflow { - overflowChildren := make([]document.DocumentNode, len(children)) - for i, child := range children { - if child == nil { - continue - } - if childResults[i].Overflow != nil { - // Column had overflow — carry it forward. - overflowChildren[i] = childResults[i].Overflow - } else { - // Column fit entirely — use an empty placeholder with the same width. - placeholder := &document.Box{} - if box, ok := child.(*document.Box); ok { - placeholder.BoxStyle.Width = box.BoxStyle.Width - } - overflowChildren[i] = placeholder - } - } - overflow = &document.Box{ - Content: overflowChildren, - BoxStyle: document.BoxStyle{ - Direction: document.DirectionHorizontal, - Margin: style.Margin, - Padding: style.Padding, - Border: style.Border, - }, - } - } + overflow := buildHorizontalOverflow(children, childResults, hasOverflow, style) return Result{ Bounds: document.Rectangle{ @@ -425,6 +394,39 @@ func (bl *BlockLayout) layoutHorizontal(node document.DocumentNode, constraints } } +// buildHorizontalOverflow creates an overflow horizontal box when any column +// child overflowed. Columns that fit entirely get an empty placeholder with +// the same width; columns with overflow carry their overflow content. +func buildHorizontalOverflow(children []document.DocumentNode, childResults []Result, hasOverflow bool, style document.Style) document.DocumentNode { + if !hasOverflow { + return nil + } + overflowChildren := make([]document.DocumentNode, len(children)) + for i, child := range children { + if child == nil { + continue + } + if childResults[i].Overflow != nil { + overflowChildren[i] = childResults[i].Overflow + } else { + placeholder := &document.Box{} + if box, ok := child.(*document.Box); ok { + placeholder.BoxStyle.Width = box.BoxStyle.Width + } + overflowChildren[i] = placeholder + } + } + return &document.Box{ + Content: overflowChildren, + BoxStyle: document.BoxStyle{ + Direction: document.DirectionHorizontal, + Margin: style.Margin, + Padding: style.Padding, + Border: style.Border, + }, + } +} + // resolveChildWidths determines the width of each child node for // horizontal layout. Children with an explicit BoxStyle.Width are // resolved against parentWidth. The remaining space is divided equally