diff --git a/README.md b/README.md index 1c399a2a..ab38fd93 100644 --- a/README.md +++ b/README.md @@ -66,99 +66,173 @@ with the code to generate the PDFs. #### Code ```go +// Billing example package main import ( "fmt" + "github.com/johnfercher/maroto/pkg/color" "github.com/johnfercher/maroto/pkg/consts" "github.com/johnfercher/maroto/pkg/pdf" "github.com/johnfercher/maroto/pkg/props" "os" + "time" ) func main() { - m := pdf.NewMaroto(consts.Portrait, consts.Letter) - - m.Row(40, func() { - m.Col(func() { - _ = m.FileImage("internal/assets/images/biplane.jpg", props.Rect{ - Center: true, - Percent: 80, - }) - }) - m.Col(func() { - m.Text("Gopher International Shipping, Inc.", props.Text{ - Top: 15, - Size: 20, - Extrapolate: true, + begin := time.Now() + + darkGrayColor := getDarkGrayColor() + grayColor := getGrayColor() + whiteColor := color.NewWhite() + header := getHeader() + contents := getContents() + + m := pdf.NewMaroto(consts.Portrait, consts.A4) + m.SetPageMargins(10, 15, 10) + //m.SetBorder(true) + + m.RegisterHeader(func() { + m.Row(20, func() { + m.Col(3, func() { + _ = m.FileImage("internal/assets/images/biplane.jpg", props.Rect{ + Center: true, + Percent: 80, + }) }) - m.Text("1000 Shipping Gopher Golang TN 3691234 GopherLand (GL)", props.Text{ - Size: 12, - Top: 21, + + m.ColSpace(6) + + m.Col(3, func() { + m.Text("AnyCompany Name Inc. 851 Any Street Name, Suite 120, Any City, CA 45123.", props.Text{ + Size: 8, + Align: consts.Right, + Extrapolate: false, + }) + m.Text("Tel: 55 024 12345-1234", props.Text{ + Top: 12, + Style: consts.BoldItalic, + Size: 8, + Align: consts.Right, + }) + m.Text("www.mycompany.com", props.Text{ + Top: 15, + Style: consts.BoldItalic, + Size: 8, + Align: consts.Right, + }) }) }) - m.ColSpace() }) - m.Line(10) - - m.Row(40, func() { - m.Col(func() { - m.Text("João Sant'Ana 100 Main Street Stringfield TN 39021 United Stats (USA)", props.Text{ - Size: 15, - Top: 14, + m.RegisterFooter(func() { + m.Row(20, func() { + m.Col(12, func() { + m.Text("Tel: 55 024 12345-1234", props.Text{ + Top: 13, + Style: consts.BoldItalic, + Size: 8, + Align: consts.Left, + }) + m.Text("www.mycompany.com", props.Text{ + Top: 16, + Style: consts.BoldItalic, + Size: 8, + Align: consts.Left, + }) }) }) - m.ColSpace() - m.Col(func() { - m.QrCode("https://github.com/johnfercher/maroto", props.Rect{ - Center: true, - Percent: 75, + }) + + m.Row(10, func() { + m.Col(12, func() { + m.Text("Invoice ABC123456789", props.Text{ + Top: 3, + Style: consts.Bold, + Align: consts.Center, }) }) }) - m.Line(10) + m.SetBackgroundColor(darkGrayColor) - m.Row(100, func() { - m.Col(func() { - _ = m.Barcode("https://github.com/johnfercher/maroto", props.Barcode{ - Center: true, - Percent: 70, - }) - m.Text("https://github.com/johnfercher/maroto", props.Text{ - Size: 20, + m.Row(7, func() { + m.Col(3, func() { + m.Text("Transactions", props.Text{ + Top: 1.5, + Size: 9, + Style: consts.Bold, Align: consts.Center, - Top: 80, }) }) + m.ColSpace(9) }) - m.SetBorder(true) + m.SetBackgroundColor(whiteColor) + + m.TableList(header, contents, props.TableList{ + HeaderProp: props.TableListContent{ + Size: 9, + GridSizes: []uint{3, 4, 2, 3}, + }, + ContentProp: props.TableListContent{ + Size: 8, + GridSizes: []uint{3, 4, 2, 3}, + }, + Align: consts.Center, + AlternatedBackground: &grayColor, + HeaderContentSpace: 1, + Line: false, + }) - m.Row(40, func() { - m.Col(func() { - m.Text("CODE: 123412351645231245564 DATE: 20-07-1994 20:20:33", props.Text{ - Size: 15, - Top: 19, + m.Row(20, func() { + m.ColSpace(7) + m.Col(2, func() { + m.Text("Total:", props.Text{ + Top: 5, + Style: consts.Bold, + Size: 8, + Align: consts.Right, }) }) - m.Col(func() { - m.Text("CA", props.Text{ - Top: 30, - Size: 85, + m.Col(3, func() { + m.Text("R$ 2.567,00", props.Text{ + Top: 5, + Style: consts.Bold, + Size: 8, Align: consts.Center, }) }) }) - m.SetBorder(false) + m.Row(15, func() { + m.Col(6, func() { + _ = m.Barcode("5123.151231.512314.1251251.123215", props.Barcode{ + Percent: 0, + Proportion: props.Proportion{ + Width: 20, + Height: 2, + }, + }) + m.Text("5123.151231.512314.1251251.123215", props.Text{ + Top: 12, + Family: "", + Style: consts.Bold, + Size: 9, + Align: consts.Center, + }) + }) + m.ColSpace(6) + }) - err := m.OutputFileAndClose("internal/examples/pdfs/zpl.pdf") + err := m.OutputFileAndClose("internal/examples/pdfs/billing.pdf") if err != nil { fmt.Println("Could not save PDF:", err) os.Exit(1) } + + end := time.Now() + fmt.Println(end.Sub(begin)) } ``` diff --git a/docs/doc.go b/docs/doc.go index ac56070d..2dd5eeb5 100644 --- a/docs/doc.go +++ b/docs/doc.go @@ -42,7 +42,7 @@ The following Go Code generates a simple PDF file. m := pdf.NewMaroto(consts.Portrait, consts.Letter) m.Row(10, func() { - m.Col(func() { + m.Col(12, func() { m.Text(props.Text{ Size: 18, Style: consts.Bold, diff --git a/internal/cell.go b/internal/cell.go new file mode 100644 index 00000000..8cd88d1e --- /dev/null +++ b/internal/cell.go @@ -0,0 +1,9 @@ +package internal + +// Cell represents a cell inside the PDF +type Cell struct { + X float64 + Y float64 + Width float64 + Height float64 +} diff --git a/internal/code.go b/internal/code.go index da426369..70e49b89 100644 --- a/internal/code.go +++ b/internal/code.go @@ -10,8 +10,8 @@ import ( // Code is the abstraction which deals of how to add QrCodes or Barcode in a PDF type Code interface { - AddQr(code string, marginTop float64, indexCol float64, qtdCols float64, colHeight float64, prop props.Rect) - AddBar(code string, marginTop float64, indexCol float64, qtdCols float64, colHeight float64, prop props.Barcode) (err error) + AddQr(code string, cell Cell, prop props.Rect) + AddBar(code string, cell Cell, prop props.Barcode) (err error) } type code struct { @@ -28,39 +28,36 @@ func NewCode(pdf gofpdf.Pdf, math Math) *code { } // AddQr create a QrCode inside a cell -func (s *code) AddQr(code string, marginTop float64, indexCol float64, qtdCols float64, colHeight float64, prop props.Rect) { +func (s *code) AddQr(code string, cell Cell, prop props.Rect) { key := barcode.RegisterQR(s.pdf, code, qr.H, qr.Unicode) - actualWidthPerCol := s.math.GetWidthPerCol(qtdCols) - var x, y, w, h float64 if prop.Center { - x, y, w, h = s.math.GetRectCenterColProperties(actualWidthPerCol, actualWidthPerCol, qtdCols, colHeight, indexCol, prop.Percent) + x, y, w, h = s.math.GetRectCenterColProperties(cell.Width, cell.Width, cell.Width, cell.Height, cell.X, prop.Percent) } else { - x, y, w, h = s.math.GetRectNonCenterColProperties(actualWidthPerCol, actualWidthPerCol, qtdCols, colHeight, indexCol, prop) + x, y, w, h = s.math.GetRectNonCenterColProperties(cell.Width, cell.Width, cell.Width, cell.Height, cell.X, prop) } - barcode.Barcode(s.pdf, key, x, y+marginTop, w, h, false) + barcode.Barcode(s.pdf, key, x, y+cell.Y, w, h, false) } // AddBar create a Barcode inside a cell -func (s *code) AddBar(code string, marginTop float64, indexCol float64, qtdCols float64, colHeight float64, prop props.Barcode) (err error) { +func (s *code) AddBar(code string, cell Cell, prop props.Barcode) (err error) { bcode, err := code128.Encode(code) if err != nil { return } - actualWidthPerCol := s.math.GetWidthPerCol(qtdCols) heightPercentFromWidth := prop.Proportion.Height / prop.Proportion.Width var x, y, w, h float64 if prop.Center { - x, y, w, h = s.math.GetRectCenterColProperties(actualWidthPerCol, actualWidthPerCol*heightPercentFromWidth, qtdCols, colHeight, indexCol, prop.Percent) + x, y, w, h = s.math.GetRectCenterColProperties(cell.Width, cell.Width*heightPercentFromWidth, cell.Width, cell.Height, cell.X, prop.Percent) } else { rectProps := props.Rect{Left: prop.Left, Top: prop.Top, Center: prop.Center, Percent: prop.Percent} - x, y, w, h = s.math.GetRectNonCenterColProperties(actualWidthPerCol, actualWidthPerCol*heightPercentFromWidth, qtdCols, colHeight, indexCol, rectProps) + x, y, w, h = s.math.GetRectNonCenterColProperties(cell.Width, cell.Width*heightPercentFromWidth, cell.Width, cell.Height, cell.X, rectProps) } - barcode.Barcode(s.pdf, barcode.Register(bcode), x, y+marginTop, w, h, false) + barcode.Barcode(s.pdf, barcode.Register(bcode), x, y+cell.Y, w, h, false) return } diff --git a/internal/code_test.go b/internal/code_test.go index 898da75f..383d92d9 100644 --- a/internal/code_test.go +++ b/internal/code_test.go @@ -40,23 +40,19 @@ func TestCode_AddBar(t *testing.T) { }, func() *mocks.Math { math := &mocks.Math{} - math.On("GetWidthPerCol", mock.Anything).Return(50.0) math.On("GetRectNonCenterColProperties", mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(100.0, 20.0, 33.0, 0.0) return math }, func(t *testing.T, pdf *mocks.Pdf) { pdf.AssertNumberOfCalls(t, "GetImageInfo", 1) - pdf.AssertCalled(t, "GetImageInfo", "barcode-Code 128AnyCode-1E+023E+01") + pdf.AssertCalled(t, "GetImageInfo", "barcode-Code 128AnyCode-1E+022.2E+01") pdf.AssertNumberOfCalls(t, "Image", 1) - pdf.AssertCalled(t, "Image", "", 100, 30, 33, 0) + pdf.AssertCalled(t, "Image", "", 100, 22, 33, 0) }, func(t *testing.T, math *mocks.Math) { - math.AssertNumberOfCalls(t, "GetWidthPerCol", 1) - math.AssertCalled(t, "GetWidthPerCol", 5.0) - math.AssertNumberOfCalls(t, "GetRectNonCenterColProperties", 1) - math.AssertCalled(t, "GetRectNonCenterColProperties", 50, 28, 5, 40, 2, props.Rect{Center: false, Left: 10, Top: 10}) + math.AssertCalled(t, "GetRectNonCenterColProperties", 5, 2, 5, 40, 10, props.Rect{Center: false, Left: 10, Top: 10}) }, func(t *testing.T, err error) { assert.Nil(t, err) @@ -74,23 +70,19 @@ func TestCode_AddBar(t *testing.T) { }, func() *mocks.Math { math := &mocks.Math{} - math.On("GetWidthPerCol", mock.Anything).Return(50.0) math.On("GetRectCenterColProperties", mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(100.0, 20.0, 33.0, 0.0) return math }, func(t *testing.T, pdf *mocks.Pdf) { pdf.AssertNumberOfCalls(t, "GetImageInfo", 1) - pdf.AssertCalled(t, "GetImageInfo", "barcode-Code 128AnyCode-1E+023E+01") + pdf.AssertCalled(t, "GetImageInfo", "barcode-Code 128AnyCode-1E+022.2E+01") pdf.AssertNumberOfCalls(t, "Image", 1) - pdf.AssertCalled(t, "Image", "", 100, 30, 33, 0) + pdf.AssertCalled(t, "Image", "", 100, 22, 33, 0) }, func(t *testing.T, math *mocks.Math) { - math.AssertNumberOfCalls(t, "GetWidthPerCol", 1) - math.AssertCalled(t, "GetWidthPerCol", 5.0) - math.AssertNumberOfCalls(t, "GetRectCenterColProperties", 1) - math.AssertCalled(t, "GetRectCenterColProperties", 50, 50, 5, 40, 2, 100) + math.AssertCalled(t, "GetRectCenterColProperties", 5, 5, 5, 40, 10, 100) }, func(t *testing.T, err error) { assert.Nil(t, err) @@ -108,7 +100,6 @@ func TestCode_AddBar(t *testing.T) { }, func() *mocks.Math { math := &mocks.Math{} - math.On("GetWidthPerCol", mock.Anything).Return(50.0) math.On("GetRectCenterColProperties", mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(100.0, 20.0, 33.0, 0.0) return math }, @@ -117,7 +108,6 @@ func TestCode_AddBar(t *testing.T) { pdf.AssertNotCalled(t, "Image") }, func(t *testing.T, math *mocks.Math) { - math.AssertNotCalled(t, "GetWidthPerCol") math.AssertNotCalled(t, "GetRectCenterColProperties") }, func(t *testing.T, err error) { @@ -133,9 +123,15 @@ func TestCode_AddBar(t *testing.T) { math := c.math() code := internal.NewCode(pdf, math) + cell := internal.Cell{ + X: 10, + Y: 2, + Width: 5, + Height: 40, + } // Act - err := code.AddBar(c.code, 10, 2, 5, 40, c.prop) + err := code.AddBar(c.code, cell, c.prop) // Assert c.assertPdf(t, pdf) @@ -165,7 +161,6 @@ func TestCode_AddQr(t *testing.T) { }, func() *mocks.Math { math := &mocks.Math{} - math.On("GetWidthPerCol", mock.Anything).Return(50.0) math.On("GetRectCenterColProperties", mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(100.0, 20.0, 33.0, 0.0) return math }, @@ -177,11 +172,8 @@ func TestCode_AddQr(t *testing.T) { pdf.AssertCalled(t, "Image", "", 100, 30, 33, 0) }, func(t *testing.T, math *mocks.Math) { - math.AssertNumberOfCalls(t, "GetWidthPerCol", 1) - math.AssertCalled(t, "GetWidthPerCol", 5.0) - math.AssertNumberOfCalls(t, "GetRectCenterColProperties", 1) - math.AssertCalled(t, "GetRectCenterColProperties", 50, 50, 5, 40, 2, 100) + math.AssertCalled(t, "GetRectCenterColProperties", 5, 5, 5, 40, 2, 100) }, props.Rect{Center: true, Percent: 100}, }, @@ -196,7 +188,6 @@ func TestCode_AddQr(t *testing.T) { }, func() *mocks.Math { math := &mocks.Math{} - math.On("GetWidthPerCol", mock.Anything).Return(50.0) math.On("GetRectNonCenterColProperties", mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(100.0, 20.0, 33.0, 0.0) return math }, @@ -208,11 +199,8 @@ func TestCode_AddQr(t *testing.T) { pdf.AssertCalled(t, "Image", "", 100, 30, 33, 0) }, func(t *testing.T, math *mocks.Math) { - math.AssertNumberOfCalls(t, "GetWidthPerCol", 1) - math.AssertCalled(t, "GetWidthPerCol", 5.0) - math.AssertNumberOfCalls(t, "GetRectNonCenterColProperties", 1) - math.AssertCalled(t, "GetRectNonCenterColProperties", 50, 50, 5, 40, 2, props.Rect{Center: false, Left: 10, Top: 10}) + math.AssertCalled(t, "GetRectNonCenterColProperties", 5, 5, 5, 40, 2, props.Rect{Center: false, Left: 10, Top: 10}) }, props.Rect{Center: false, Left: 10, Top: 10}, }, @@ -224,9 +212,15 @@ func TestCode_AddQr(t *testing.T) { math := c.math() code := internal.NewCode(pdf, math) + cell := internal.Cell{ + X: 2, + Y: 10, + Width: 5, + Height: 40, + } // Act - code.AddQr(c.code, 10, 2, 5, 40, c.prop) + code.AddQr(c.code, cell, c.prop) // Assert c.assertPdf(t, pdf) diff --git a/internal/examples/barcodegrid/main.go b/internal/examples/barcodegrid/main.go new file mode 100644 index 00000000..4626ec60 --- /dev/null +++ b/internal/examples/barcodegrid/main.go @@ -0,0 +1,103 @@ +package main + +import ( + "fmt" + "github.com/johnfercher/maroto/pkg/consts" + "github.com/johnfercher/maroto/pkg/pdf" + "github.com/johnfercher/maroto/pkg/props" + "os" + "time" +) + +func main() { + begin := time.Now() + m := pdf.NewMaroto(consts.Portrait, consts.Letter) + m.SetBorder(true) + + m.Row(40, func() { + m.Col(2, func() { + m.Barcode("https://github.com/johnfercher/maroto", props.Barcode{ + Percent: 50, + }) + }) + m.Col(4, func() { + m.Barcode("https://github.com/johnfercher/maroto", props.Barcode{ + Percent: 75, + }) + }) + m.Col(6, func() { + m.Barcode("https://github.com/johnfercher/maroto", props.Barcode{ + Percent: 100, + }) + }) + }) + + m.Row(40, func() { + m.Col(2, func() { + m.Barcode("https://github.com/johnfercher/maroto", props.Barcode{ + Center: true, + Percent: 50, + }) + }) + m.Col(4, func() { + m.Barcode("https://github.com/johnfercher/maroto", props.Barcode{ + Center: true, + Percent: 75, + }) + }) + m.Col(6, func() { + m.Barcode("https://github.com/johnfercher/maroto", props.Barcode{ + Center: true, + Percent: 100, + }) + }) + }) + + m.Row(40, func() { + m.Col(6, func() { + m.Barcode("https://github.com/johnfercher/maroto", props.Barcode{ + Percent: 50, + }) + }) + m.Col(4, func() { + m.Barcode("https://github.com/johnfercher/maroto", props.Barcode{ + Percent: 75, + }) + }) + m.Col(2, func() { + m.Barcode("https://github.com/johnfercher/maroto", props.Barcode{ + Percent: 100, + }) + }) + }) + + m.Row(40, func() { + m.Col(6, func() { + m.Barcode("https://github.com/johnfercher/maroto", props.Barcode{ + Center: true, + Percent: 50, + }) + }) + m.Col(4, func() { + m.Barcode("https://github.com/johnfercher/maroto", props.Barcode{ + Center: true, + Percent: 75, + }) + }) + m.Col(2, func() { + m.Barcode("https://github.com/johnfercher/maroto", props.Barcode{ + Center: true, + Percent: 100, + }) + }) + }) + + err := m.OutputFileAndClose("internal/examples/pdfs/barcodegrid.pdf") + if err != nil { + fmt.Println("Could not save PDF:", err) + os.Exit(1) + } + + end := time.Now() + fmt.Println(end.Sub(begin)) +} diff --git a/internal/examples/billing/main.go b/internal/examples/billing/main.go index da4324af..0d9716fb 100644 --- a/internal/examples/billing/main.go +++ b/internal/examples/billing/main.go @@ -13,19 +13,11 @@ import ( func main() { begin := time.Now() - darkGrayColor := color.Color{ - Red: 144, - Green: 144, - Blue: 144, - } - - grayColor := color.Color{ - Red: 200, - Green: 200, - Blue: 200, - } - + darkGrayColor := getDarkGrayColor() + grayColor := getGrayColor() whiteColor := color.NewWhite() + header := getHeader() + contents := getContents() m := pdf.NewMaroto(consts.Portrait, consts.A4) m.SetPageMargins(10, 15, 10) @@ -33,42 +25,40 @@ func main() { m.RegisterHeader(func() { m.Row(20, func() { - m.Col(func() { + m.Col(3, func() { _ = m.FileImage("internal/assets/images/biplane.jpg", props.Rect{ Center: true, Percent: 80, }) }) - m.ColSpaces(2) - m.Col(func() { + + m.ColSpace(6) + + m.Col(3, func() { m.Text("AnyCompany Name Inc. 851 Any Street Name, Suite 120, Any City, CA 45123.", props.Text{ - Top: 4, Size: 8, Align: consts.Right, Extrapolate: false, }) m.Text("Tel: 55 024 12345-1234", props.Text{ - Top: 16, + Top: 12, Style: consts.BoldItalic, Size: 8, Align: consts.Right, }) m.Text("www.mycompany.com", props.Text{ - Top: 19, + Top: 15, Style: consts.BoldItalic, Size: 8, Align: consts.Right, }) }) }) - m.Row(5, func() { - m.ColSpace() - }) }) m.RegisterFooter(func() { m.Row(20, func() { - m.Col(func() { + m.Col(12, func() { m.Text("Tel: 55 024 12345-1234", props.Text{ Top: 13, Style: consts.BoldItalic, @@ -86,9 +76,9 @@ func main() { }) m.Row(10, func() { - m.Col(func() { + m.Col(12, func() { m.Text("Invoice ABC123456789", props.Text{ - Top: 6, + Top: 3, Style: consts.Bold, Align: consts.Center, }) @@ -96,56 +86,29 @@ func main() { }) m.SetBackgroundColor(darkGrayColor) + m.Row(7, func() { - m.Col(func() { + m.Col(3, func() { m.Text("Transactions", props.Text{ - Top: 4.5, + Top: 1.5, Size: 9, Style: consts.Bold, Align: consts.Center, }) }) - m.ColSpaces(3) + m.ColSpace(9) }) - m.SetBackgroundColor(whiteColor) - header := []string{"", "Product", "Quantity", "Price"} - contents := [][]string{ - {"", "Swamp", "12", "R$ 4,00"}, - {"", "Sorin, A Planeswalker", "4", "R$ 90,00"}, - {"", "Tassa", "4", "R$ 30,00"}, - {"", "Skinrender", "4", "R$ 9,00"}, - {"", "Island", "12", "R$ 4,00"}, - {"", "Mountain", "12", "R$ 4,00"}, - {"", "Plain", "12", "R$ 4,00"}, - {"", "Black Lotus", "1", "R$ 1.000,00"}, - {"", "Time Walk", "1", "R$ 1.000,00"}, - {"", "Emberclave", "4", "R$ 44,00"}, - {"", "Anax", "4", "R$ 32,00"}, - {"", "Murderous Rider", "4", "R$ 22,00"}, - {"", "Gray Merchant of Asphodel", "4", "R$ 2,00"}, - {"", "Ajani's Pridemate", "4", "R$ 2,00"}, - {"", "Renan, Chatuba", "4", "R$ 19,00"}, - {"", "Tymarett", "4", "R$ 13,00"}, - {"", "Doom Blade", "4", "R$ 5,00"}, - {"", "Dark Lord", "3", "R$ 7,00"}, - {"", "Memory of Thanatos", "3", "R$ 32,00"}, - {"", "Poring", "4", "R$ 1,00"}, - {"", "Deviling", "4", "R$ 99,00"}, - {"", "Seiya", "4", "R$ 45,00"}, - {"", "Harry Potter", "4", "R$ 62,00"}, - {"", "Goku", "4", "R$ 77,00"}, - {"", "Phreoni", "4", "R$ 22,00"}, - {"", "Katheryn High Wizard", "4", "R$ 25,00"}, - {"", "Lord Seyren", "4", "R$ 55,00"}, - } + m.SetBackgroundColor(whiteColor) m.TableList(header, contents, props.TableList{ - HeaderProp: props.Font{ - Size: 9, + HeaderProp: props.TableListContent{ + Size: 9, + GridSizes: []uint{3, 4, 2, 3}, }, - ContentProp: props.Font{ - Size: 8, + ContentProp: props.TableListContent{ + Size: 8, + GridSizes: []uint{3, 4, 2, 3}, }, Align: consts.Center, AlternatedBackground: &grayColor, @@ -154,8 +117,8 @@ func main() { }) m.Row(20, func() { - m.ColSpaces(2) - m.Col(func() { + m.ColSpace(7) + m.Col(2, func() { m.Text("Total:", props.Text{ Top: 5, Style: consts.Bold, @@ -163,7 +126,7 @@ func main() { Align: consts.Right, }) }) - m.Col(func() { + m.Col(3, func() { m.Text("R$ 2.567,00", props.Text{ Top: 5, Style: consts.Bold, @@ -174,7 +137,7 @@ func main() { }) m.Row(15, func() { - m.Col(func() { + m.Col(6, func() { _ = m.Barcode("5123.151231.512314.1251251.123215", props.Barcode{ Percent: 0, Proportion: props.Proportion{ @@ -190,7 +153,7 @@ func main() { Align: consts.Center, }) }) - m.ColSpace() + m.ColSpace(6) }) err := m.OutputFileAndClose("internal/examples/pdfs/billing.pdf") @@ -202,3 +165,55 @@ func main() { end := time.Now() fmt.Println(end.Sub(begin)) } + +func getHeader() []string { + return []string{"", "Product", "Quantity", "Price"} +} + +func getContents() [][]string { + return [][]string{ + {"", "Swamp", "12", "R$ 4,00"}, + {"", "Sorin, A Planeswalker", "4", "R$ 90,00"}, + {"", "Tassa", "4", "R$ 30,00"}, + {"", "Skinrender", "4", "R$ 9,00"}, + {"", "Island", "12", "R$ 4,00"}, + {"", "Mountain", "12", "R$ 4,00"}, + {"", "Plain", "12", "R$ 4,00"}, + {"", "Black Lotus", "1", "R$ 1.000,00"}, + {"", "Time Walk", "1", "R$ 1.000,00"}, + {"", "Emberclave", "4", "R$ 44,00"}, + {"", "Anax", "4", "R$ 32,00"}, + {"", "Murderous Rider", "4", "R$ 22,00"}, + {"", "Gray Merchant of Asphodel", "4", "R$ 2,00"}, + {"", "Ajani's Pridemate", "4", "R$ 2,00"}, + {"", "Renan, Chatuba", "4", "R$ 19,00"}, + {"", "Tymarett", "4", "R$ 13,00"}, + {"", "Doom Blade", "4", "R$ 5,00"}, + {"", "Dark Lord", "3", "R$ 7,00"}, + {"", "Memory of Thanatos", "3", "R$ 32,00"}, + {"", "Poring", "4", "R$ 1,00"}, + {"", "Deviling", "4", "R$ 99,00"}, + {"", "Seiya", "4", "R$ 45,00"}, + {"", "Harry Potter", "4", "R$ 62,00"}, + {"", "Goku", "4", "R$ 77,00"}, + {"", "Phreoni", "4", "R$ 22,00"}, + {"", "Katheryn High Wizard", "4", "R$ 25,00"}, + {"", "Lord Seyren", "4", "R$ 55,00"}, + } +} + +func getDarkGrayColor() color.Color { + return color.Color{ + Red: 144, + Green: 144, + Blue: 144, + } +} + +func getGrayColor() color.Color { + return color.Color{ + Red: 200, + Green: 200, + Blue: 200, + } +} diff --git a/internal/examples/certificate/main.go b/internal/examples/certificate/main.go index e887db50..56eb0c31 100644 --- a/internal/examples/certificate/main.go +++ b/internal/examples/certificate/main.go @@ -15,21 +15,21 @@ func main() { //m.SetBorder(true) m.Row(20, func() { - m.Col(func() { + m.Col(4, func() { _ = m.FileImage("internal/assets/images/frontpage.png", props.Rect{ Percent: 88, Center: true, }) }) - m.Col(func() { + m.Col(4, func() { m.Text("Golang Certificate", props.Text{ - Top: 12, + Top: 6, Align: consts.Center, Size: 20, Style: consts.BoldItalic, }) }) - m.Col(func() { + m.Col(4, func() { _ = m.FileImage("internal/assets/images/frontpage.png", props.Rect{ Percent: 90, Center: true, @@ -38,25 +38,25 @@ func main() { }) m.Row(130, func() { - m.Col(func() { + m.Col(12, func() { text := "Lorem Ipsum is simply dummy textá of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum." m.Text(text, props.Text{ Size: 13, Align: consts.Center, - Top: 60, + Top: 50, VerticalPadding: 2.0, }) }) }) m.Row(25, func() { - m.Col(func() { + m.Col(4, func() { m.Signature("Gopher Senior") }) - m.Col(func() { + m.Col(4, func() { m.Signature("Gopheroid") }) - m.Col(func() { + m.Col(4, func() { m.Signature("Sign Here") }) }) diff --git a/internal/examples/imagegrid/main.go b/internal/examples/imagegrid/main.go new file mode 100644 index 00000000..c5011ac3 --- /dev/null +++ b/internal/examples/imagegrid/main.go @@ -0,0 +1,147 @@ +package main + +import ( + "fmt" + "github.com/johnfercher/maroto/pkg/consts" + "github.com/johnfercher/maroto/pkg/pdf" + "github.com/johnfercher/maroto/pkg/props" + "os" + "time" +) + +func main() { + begin := time.Now() + m := pdf.NewMaroto(consts.Portrait, consts.Letter) + m.SetBorder(true) + + m.Row(40, func() { + m.Col(2, func() { + _ = m.FileImage("internal/assets/images/biplane.jpg", props.Rect{ + Center: true, + Percent: 80, + }) + }) + m.Col(4, func() { + _ = m.FileImage("internal/assets/images/biplane.jpg", props.Rect{ + Center: true, + Percent: 80, + }) + }) + m.Col(6, func() { + _ = m.FileImage("internal/assets/images/biplane.jpg", props.Rect{ + Center: true, + Percent: 80, + }) + }) + }) + + m.Row(40, func() { + m.Col(2, func() { + _ = m.FileImage("internal/assets/images/biplane.jpg", props.Rect{ + Center: false, + Percent: 50, + Left: 10, + }) + }) + m.Col(4, func() { + _ = m.FileImage("internal/assets/images/biplane.jpg", props.Rect{ + Center: false, + Percent: 50, + Top: 10, + }) + }) + m.Col(6, func() { + _ = m.FileImage("internal/assets/images/biplane.jpg", props.Rect{ + Center: false, + Percent: 50, + Left: 15, + Top: 15, + }) + }) + }) + + m.Row(40, func() { + m.Col(8, func() { + _ = m.FileImage("internal/assets/images/biplane.jpg", props.Rect{ + Center: true, + Percent: 80, + }) + }) + m.Col(4, func() { + _ = m.FileImage("internal/assets/images/biplane.jpg", props.Rect{ + Center: true, + Percent: 80, + }) + }) + }) + + m.Row(40, func() { + m.Col(6, func() { + _ = m.FileImage("internal/assets/images/frontpage.png", props.Rect{ + Center: false, + Percent: 80, + Top: 5, + Left: 10, + }) + }) + m.Col(4, func() { + _ = m.FileImage("internal/assets/images/frontpage.png", props.Rect{ + Center: false, + Percent: 80, + Top: 5, + }) + }) + m.Col(2, func() { + _ = m.FileImage("internal/assets/images/frontpage.png", props.Rect{ + Center: false, + Percent: 80, + Left: 5, + }) + }) + }) + + m.Row(40, func() { + m.Col(6, func() { + _ = m.FileImage("internal/assets/images/frontpage.png", props.Rect{ + Center: true, + Percent: 50, + }) + }) + m.Col(4, func() { + _ = m.FileImage("internal/assets/images/frontpage.png", props.Rect{ + Center: true, + Percent: 50, + }) + }) + m.Col(2, func() { + _ = m.FileImage("internal/assets/images/frontpage.png", props.Rect{ + Center: true, + Percent: 50, + }) + }) + }) + + m.Row(40, func() { + m.Col(4, func() { + _ = m.FileImage("internal/assets/images/frontpage.png", props.Rect{ + Center: true, + Percent: 80, + }) + }) + m.Col(8, func() { + _ = m.FileImage("internal/assets/images/frontpage.png", props.Rect{ + Center: true, + Percent: 80, + }) + }) + }) + + err := m.OutputFileAndClose("internal/examples/pdfs/imagegrid.pdf") + if err != nil { + fmt.Println("Could not save PDF:", err) + os.Exit(1) + } + + end := time.Now() + fmt.Println(end.Sub(begin)) +} diff --git a/internal/examples/pdfs/barcodegrid.pdf b/internal/examples/pdfs/barcodegrid.pdf new file mode 100644 index 00000000..1c1b87af Binary files /dev/null and b/internal/examples/pdfs/barcodegrid.pdf differ diff --git a/internal/examples/pdfs/billing.pdf b/internal/examples/pdfs/billing.pdf index c591e5ee..02cd05f2 100644 Binary files a/internal/examples/pdfs/billing.pdf and b/internal/examples/pdfs/billing.pdf differ diff --git a/internal/examples/pdfs/certificate.pdf b/internal/examples/pdfs/certificate.pdf index 9eb716ce..eb1c7b4a 100644 Binary files a/internal/examples/pdfs/certificate.pdf and b/internal/examples/pdfs/certificate.pdf differ diff --git a/internal/examples/pdfs/imagegrid.pdf b/internal/examples/pdfs/imagegrid.pdf new file mode 100644 index 00000000..a658f4b7 Binary files /dev/null and b/internal/examples/pdfs/imagegrid.pdf differ diff --git a/internal/examples/pdfs/qrgrid.pdf b/internal/examples/pdfs/qrgrid.pdf new file mode 100644 index 00000000..7d60da47 Binary files /dev/null and b/internal/examples/pdfs/qrgrid.pdf differ diff --git a/internal/examples/pdfs/sample1.pdf b/internal/examples/pdfs/sample1.pdf index a48d4497..f96da552 100644 Binary files a/internal/examples/pdfs/sample1.pdf and b/internal/examples/pdfs/sample1.pdf differ diff --git a/internal/examples/pdfs/textgrid.pdf b/internal/examples/pdfs/textgrid.pdf new file mode 100644 index 00000000..2075b66b Binary files /dev/null and b/internal/examples/pdfs/textgrid.pdf differ diff --git a/internal/examples/pdfs/zpl.pdf b/internal/examples/pdfs/zpl.pdf index 523b94ea..aad361e1 100644 Binary files a/internal/examples/pdfs/zpl.pdf and b/internal/examples/pdfs/zpl.pdf differ diff --git a/internal/examples/qrgrid/main.go b/internal/examples/qrgrid/main.go new file mode 100644 index 00000000..e2dbf10d --- /dev/null +++ b/internal/examples/qrgrid/main.go @@ -0,0 +1,103 @@ +package main + +import ( + "fmt" + "github.com/johnfercher/maroto/pkg/consts" + "github.com/johnfercher/maroto/pkg/pdf" + "github.com/johnfercher/maroto/pkg/props" + "os" + "time" +) + +func main() { + begin := time.Now() + m := pdf.NewMaroto(consts.Portrait, consts.Letter) + m.SetBorder(true) + + m.Row(40, func() { + m.Col(2, func() { + m.QrCode("https://github.com/johnfercher/maroto", props.Rect{ + Percent: 50, + }) + }) + m.Col(4, func() { + m.QrCode("https://github.com/johnfercher/maroto", props.Rect{ + Percent: 75, + }) + }) + m.Col(6, func() { + m.QrCode("https://github.com/johnfercher/maroto", props.Rect{ + Percent: 100, + }) + }) + }) + + m.Row(40, func() { + m.Col(2, func() { + m.QrCode("https://github.com/johnfercher/maroto", props.Rect{ + Center: true, + Percent: 50, + }) + }) + m.Col(4, func() { + m.QrCode("https://github.com/johnfercher/maroto", props.Rect{ + Center: true, + Percent: 75, + }) + }) + m.Col(6, func() { + m.QrCode("https://github.com/johnfercher/maroto", props.Rect{ + Center: true, + Percent: 100, + }) + }) + }) + + m.Row(40, func() { + m.Col(6, func() { + m.QrCode("https://github.com/johnfercher/maroto", props.Rect{ + Percent: 50, + }) + }) + m.Col(4, func() { + m.QrCode("https://github.com/johnfercher/maroto", props.Rect{ + Percent: 75, + }) + }) + m.Col(2, func() { + m.QrCode("https://github.com/johnfercher/maroto", props.Rect{ + Percent: 100, + }) + }) + }) + + m.Row(40, func() { + m.Col(6, func() { + m.QrCode("https://github.com/johnfercher/maroto", props.Rect{ + Center: true, + Percent: 50, + }) + }) + m.Col(4, func() { + m.QrCode("https://github.com/johnfercher/maroto", props.Rect{ + Center: true, + Percent: 75, + }) + }) + m.Col(2, func() { + m.QrCode("https://github.com/johnfercher/maroto", props.Rect{ + Center: true, + Percent: 100, + }) + }) + }) + + err := m.OutputFileAndClose("internal/examples/pdfs/qrgrid.pdf") + if err != nil { + fmt.Println("Could not save PDF:", err) + os.Exit(1) + } + + end := time.Now() + fmt.Println(end.Sub(begin)) +} diff --git a/internal/examples/sample1/main.go b/internal/examples/sample1/main.go index 31ab5ee7..cb945aff 100644 --- a/internal/examples/sample1/main.go +++ b/internal/examples/sample1/main.go @@ -24,29 +24,30 @@ func main() { os.Exit(1) } - base64 := base64.StdEncoding.EncodeToString(byteSlices) - headerSmall, smallContent := getSmallContent() headerMedium, mediumContent := getMediumContent() + base64 := base64.StdEncoding.EncodeToString(byteSlices) + m.RegisterHeader(func() { m.Row(20, func() { - m.Col(func() { + m.Col(3, func() { m.Base64Image(base64, consts.Jpg, props.Rect{ Center: true, Percent: 70, }) }) - m.ColSpaces(2) + m.ColSpace(3) - m.Col(func() { + m.Col(3, func() { m.QrCode("https://github.com/johnfercher/maroto", props.Rect{ + Center: true, Percent: 75, }) }) - m.Col(func() { + m.Col(3, func() { id := "https://github.com/johnfercher/maroto" _ = m.Barcode(id, props.Barcode{ Center: true, @@ -56,7 +57,7 @@ func main() { m.Text(id, props.Text{ Size: 7, Align: consts.Center, - Top: 16, + Top: 14, }) }) }) @@ -64,30 +65,28 @@ func main() { m.Line(1.0) m.Row(12, func() { - m.Col(func() { + m.Col(3, func() { _ = m.FileImage("internal/assets/images/gopherbw.png", props.Rect{ Center: true, }) }) - m.ColSpace() - - m.Col(func() { + m.Col(6, func() { m.Text("Packages Report: Daily", props.Text{ - Top: 4, + Top: 1, + Align: consts.Center, }) m.Text("Type: Small, Medium", props.Text{ - Top: 10, + Top: 7, + Align: consts.Center, }) }) - m.ColSpace() - - m.Col(func() { + m.Col(3, func() { m.Text("20/07/1994", props.Text{ Size: 10, Style: consts.BoldItalic, - Top: 7.5, + Top: 4, Family: consts.Helvetica, }) }) @@ -96,28 +95,27 @@ func main() { m.Line(1.0) m.Row(22, func() { - m.Col(func() { + m.Col(0, func() { m.Text(fmt.Sprintf("Small: %d, Medium %d", len(smallContent), len(mediumContent)), props.Text{ Size: 15, Style: consts.Bold, Align: consts.Center, - Top: 9, + Top: 4, }) m.Text("Brasil / São Paulo", props.Text{ Size: 12, Align: consts.Center, - Top: 17, + Top: 12, }) }) }) m.Line(1.0) - }) m.RegisterFooter(func() { m.Row(40, func() { - m.Col(func() { + m.Col(4, func() { m.Signature("Signature 1", props.Font{ Family: consts.Courier, Style: consts.BoldItalic, @@ -125,19 +123,19 @@ func main() { }) }) - m.Col(func() { + m.Col(4, func() { m.Signature("Signature 2") }) - m.Col(func() { + m.Col(4, func() { m.Signature("Signature 3") }) }) }) m.Row(15, func() { - m.Col(func() { - m.Text("Small Packages / 39u.", props.Text{ + m.Col(12, func() { + m.Text(fmt.Sprintf("Small Packages / %du.", len(smallContent)), props.Text{ Top: 8, Style: consts.Bold, }) @@ -145,6 +143,12 @@ func main() { }) m.TableList(headerSmall, smallContent, props.TableList{ + ContentProp: props.TableListContent{ + GridSizes: []uint{3, 6, 3}, + }, + HeaderProp: props.TableListContent{ + GridSizes: []uint{3, 6, 3}, + }, AlternatedBackground: &color.Color{ Red: 200, Green: 200, @@ -153,8 +157,8 @@ func main() { }) m.Row(15, func() { - m.Col(func() { - m.Text("Medium Packages / 22u.", props.Text{ + m.Col(12, func() { + m.Text(fmt.Sprintf("Medium Packages / %du.", len(mediumContent)), props.Text{ Top: 8, Style: consts.Bold, }) @@ -162,16 +166,18 @@ func main() { }) m.TableList(headerMedium, mediumContent, props.TableList{ - Align: consts.Center, - Line: true, - HeaderProp: props.Font{ - Family: consts.Courier, - Style: consts.BoldItalic, + ContentProp: props.TableListContent{ + Family: consts.Courier, + Style: consts.Italic, + GridSizes: []uint{5, 5, 2}, }, - ContentProp: props.Font{ - Family: consts.Courier, - Style: consts.Italic, + HeaderProp: props.TableListContent{ + GridSizes: []uint{5, 5, 2}, + Family: consts.Courier, + Style: consts.BoldItalic, }, + Align: consts.Center, + Line: true, }) err = m.OutputFileAndClose("internal/examples/pdfs/sample1.pdf") @@ -185,29 +191,39 @@ func main() { } func getSmallContent() ([]string, [][]string) { - header := []string{"Origin", "Destiny", "", "Cost"} + header := []string{"Origin", "Destiny", "Cost"} contents := [][]string{} - contents = append(contents, []string{"São Paulo", "Rio de Janeiro", "", "R$ 20,00"}) - contents = append(contents, []string{"São Carlos", "Petrópolis", "", "R$ 25,00"}) - contents = append(contents, []string{"São José do Vale do Rio Preto", "Osasco", "", "R$ 20,00"}) - contents = append(contents, []string{"Osasco", "São Paulo", "", "R$ 5,00"}) - contents = append(contents, []string{"Congonhas", "Fortaleza", "", "R$ 100,00"}) - contents = append(contents, []string{"Natal", "Santo André", "", "R$ 200,00"}) - contents = append(contents, []string{"Rio Grande do Norte", "Sorocaba", "", "R$ 44,00"}) - contents = append(contents, []string{"Campinas", "Recife", "", "R$ 56,00"}) - contents = append(contents, []string{"São Vicente", "Juiz de Fora", "", "R$ 35,00"}) - contents = append(contents, []string{"Taubaté", "Rio de Janeiro", "", "R$ 82,00"}) - contents = append(contents, []string{"Suzano", "Petrópolis", "", "R$ 62,00"}) - contents = append(contents, []string{"Jundiaí", "Florianópolis", "", "R$ 21,00"}) - contents = append(contents, []string{"Natal", "Jundiaí", "", "R$ 12,00"}) - contents = append(contents, []string{"Niterói", "Itapevi", "", "R$ 21,00"}) - contents = append(contents, []string{"São Paulo", "Rio de Janeiro", "", "R$ 31,00"}) - contents = append(contents, []string{"São Carlos", "Petrópolis", "", "R$ 42,00"}) - contents = append(contents, []string{"Florianópolis", "Osasco", "", "R$ 19,00"}) - contents = append(contents, []string{"Rio Grande do Norte", "Sorocaba", "", "R$ 42,00"}) - contents = append(contents, []string{"Campinas", "Recife", "", "R$ 58,00"}) - contents = append(contents, []string{"Florianópolis", "Osasco", "", "R$ 21,00"}) + contents = append(contents, []string{"São Paulo", "Rio de Janeiro", "R$ 20,00"}) + contents = append(contents, []string{"São Carlos", "Petrópolis", "R$ 25,00"}) + contents = append(contents, []string{"São José do Vale do Rio Preto", "Osasco", "R$ 20,00"}) + contents = append(contents, []string{"Osasco", "São Paulo", "R$ 5,00"}) + contents = append(contents, []string{"Congonhas", "Fortaleza", "R$ 100,00"}) + contents = append(contents, []string{"Natal", "Santo André", "R$ 200,00"}) + contents = append(contents, []string{"Rio Grande do Norte", "Sorocaba", "R$ 44,00"}) + contents = append(contents, []string{"Campinas", "Recife", "R$ 56,00"}) + contents = append(contents, []string{"São Vicente", "Juiz de Fora", "R$ 35,00"}) + contents = append(contents, []string{"Taubaté", "Rio de Janeiro", "R$ 82,00"}) + contents = append(contents, []string{"Suzano", "Petrópolis", "R$ 62,00"}) + contents = append(contents, []string{"Jundiaí", "Florianópolis", "R$ 21,00"}) + contents = append(contents, []string{"Natal", "Jundiaí", "R$ 12,00"}) + contents = append(contents, []string{"Niterói", "Itapevi", "R$ 21,00"}) + contents = append(contents, []string{"São Paulo", "Rio de Janeiro", "R$ 31,00"}) + contents = append(contents, []string{"São Carlos", "Petrópolis", "R$ 42,00"}) + contents = append(contents, []string{"Florianópolis", "Osasco", "R$ 19,00"}) + contents = append(contents, []string{"Rio Grande do Norte", "Sorocaba", "R$ 42,00"}) + contents = append(contents, []string{"Campinas", "Recife", "R$ 58,00"}) + contents = append(contents, []string{"Florianópolis", "Osasco", "R$ 21,00"}) + contents = append(contents, []string{"Campinas", "Recife", "R$ 56,00"}) + contents = append(contents, []string{"São Vicente", "Juiz de Fora", "R$ 35,00"}) + contents = append(contents, []string{"Taubaté", "Rio de Janeiro", "R$ 82,00"}) + contents = append(contents, []string{"Suzano", "Petrópolis", "R$ 62,00"}) + contents = append(contents, []string{"Jundiaí", "Florianópolis", "R$ 21,00"}) + contents = append(contents, []string{"Natal", "Jundiaí", "R$ 12,00"}) + contents = append(contents, []string{"Niterói", "Itapevi", "R$ 21,00"}) + contents = append(contents, []string{"São Paulo", "Rio de Janeiro", "R$ 31,00"}) + contents = append(contents, []string{"São Carlos", "Petrópolis", "R$ 42,00"}) + contents = append(contents, []string{"Florianópolis", "Osasco", "R$ 19,00"}) return header, contents } @@ -216,6 +232,7 @@ func getMediumContent() ([]string, [][]string) { header := []string{"Origin", "Destiny", "Cost per Hour"} contents := [][]string{} + contents = append(contents, []string{"São José do Vale do Rio Preto", "Osasco", "R$ 12,00"}) contents = append(contents, []string{"Niterói", "Itapevi", "R$ 2,10"}) contents = append(contents, []string{"São Paulo", "Rio de Janeiro", "R$ 3,10"}) contents = append(contents, []string{"São Carlos", "Petrópolis", "R$ 4,20"}) @@ -226,13 +243,20 @@ func getMediumContent() ([]string, [][]string) { contents = append(contents, []string{"Rio Grande do Norte", "Sorocaba", "R$ 4,20"}) contents = append(contents, []string{"Campinas", "Recife", "R$ 5,80"}) contents = append(contents, []string{"São Vicente", "Juiz de Fora", "R$ 3,90"}) + contents = append(contents, []string{"Jundiaí", "Florianópolis", "R$ 2,30"}) + contents = append(contents, []string{"Natal", "Jundiaí", "R$ 1,10"}) + contents = append(contents, []string{"Natal", "Santo André", "R$ 19,80"}) + contents = append(contents, []string{"Rio Grande do Norte", "Sorocaba", "R$ 4,20"}) + contents = append(contents, []string{"Campinas", "Recife", "R$ 5,80"}) + contents = append(contents, []string{"São Vicente", "Juiz de Fora", "R$ 3,90"}) contents = append(contents, []string{"Taubaté", "Rio de Janeiro", "R$ 7,70"}) contents = append(contents, []string{"Suzano", "Petrópolis", "R$ 6,40"}) contents = append(contents, []string{"Jundiaí", "Florianópolis", "R$ 2,00"}) - contents = append(contents, []string{"Natal", "Jundiaí", "R$ 1,80"}) - contents = append(contents, []string{"Niterói", "Itapevi", "R$ 2,40"}) - contents = append(contents, []string{"Jundiaí", "Florianópolis", "R$ 2,30"}) - contents = append(contents, []string{"Natal", "Jundiaí", "R$ 1,10"}) + contents = append(contents, []string{"Florianópolis", "Osasco", "R$ 1,90"}) + contents = append(contents, []string{"Osasco", "São Paulo", "R$ 0,70"}) + contents = append(contents, []string{"Congonhas", "São José do Vale do Rio Preto", "R$ 11,30"}) + contents = append(contents, []string{"Natal", "Santo André", "R$ 19,80"}) + contents = append(contents, []string{"Rio Grande do Norte", "Sorocaba", "R$ 4,20"}) return header, contents } diff --git a/internal/examples/textgrid/main.go b/internal/examples/textgrid/main.go new file mode 100644 index 00000000..8e5987d1 --- /dev/null +++ b/internal/examples/textgrid/main.go @@ -0,0 +1,36 @@ +package main + +import ( + "fmt" + "github.com/johnfercher/maroto/pkg/consts" + "github.com/johnfercher/maroto/pkg/pdf" + "os" + "time" +) + +func main() { + begin := time.Now() + m := pdf.NewMaroto(consts.Portrait, consts.Letter) + m.SetBorder(true) + + m.Row(40, func() { + m.Col(2, func() { + m.Text("Any Text1") + }) + m.Col(4, func() { + m.Text("Any Text2") + }) + m.Col(6, func() { + m.Text("Any Text3") + }) + }) + + err := m.OutputFileAndClose("internal/examples/pdfs/textgrid.pdf") + if err != nil { + fmt.Println("Could not save PDF:", err) + os.Exit(1) + } + + end := time.Now() + fmt.Println(end.Sub(begin)) +} diff --git a/internal/examples/zpl/main.go b/internal/examples/zpl/main.go index 5a9dc8e5..0a5dea53 100644 --- a/internal/examples/zpl/main.go +++ b/internal/examples/zpl/main.go @@ -15,37 +15,37 @@ func main() { //m.SetBorder(true) m.Row(40, func() { - m.Col(func() { + m.Col(4, func() { _ = m.FileImage("internal/assets/images/biplane.jpg", props.Rect{ Center: true, Percent: 80, }) }) - m.Col(func() { + m.Col(4, func() { m.Text("Gopher International Shipping, Inc.", props.Text{ - Top: 15, + Top: 12, Size: 20, Extrapolate: true, }) m.Text("1000 Shipping Gopher Golang TN 3691234 GopherLand (GL)", props.Text{ Size: 12, - Top: 21, + Top: 22, }) }) - m.ColSpace() + m.ColSpace(4) }) m.Line(10) m.Row(40, func() { - m.Col(func() { + m.Col(4, func() { m.Text("João Sant'Ana 100 Main Street Stringfield TN 39021 United Stats (USA)", props.Text{ Size: 15, - Top: 14, + Top: 12, }) }) - m.ColSpace() - m.Col(func() { + m.ColSpace(4) + m.Col(4, func() { m.QrCode("https://github.com/johnfercher/maroto", props.Rect{ Center: true, Percent: 75, @@ -56,7 +56,7 @@ func main() { m.Line(10) m.Row(100, func() { - m.Col(func() { + m.Col(12, func() { _ = m.Barcode("https://github.com/johnfercher/maroto", props.Barcode{ Center: true, Percent: 70, @@ -64,7 +64,7 @@ func main() { m.Text("https://github.com/johnfercher/maroto", props.Text{ Size: 20, Align: consts.Center, - Top: 80, + Top: 65, }) }) }) @@ -72,15 +72,15 @@ func main() { m.SetBorder(true) m.Row(40, func() { - m.Col(func() { + m.Col(6, func() { m.Text("CODE: 123412351645231245564 DATE: 20-07-1994 20:20:33", props.Text{ Size: 15, - Top: 19, + Top: 14, }) }) - m.Col(func() { + m.Col(6, func() { m.Text("CA", props.Text{ - Top: 30, + Top: 1, Size: 85, Align: consts.Center, }) diff --git a/internal/image.go b/internal/image.go index 09f7bad4..c9ee2dc9 100644 --- a/internal/image.go +++ b/internal/image.go @@ -12,8 +12,8 @@ import ( // Image is the abstraction which deals of how to add images in a PDF type Image interface { - AddFromFile(path string, marginTop float64, indexCol float64, qtdCols float64, colHeight float64, prop props.Rect) (err error) - AddFromBase64(b64 string, marginTop float64, indexCol float64, qtdCols float64, colHeight float64, prop props.Rect, extension consts.Extension) (err error) + AddFromFile(path string, cell Cell, prop props.Rect) (err error) + AddFromBase64(stringBase64 string, cell Cell, prop props.Rect, extension consts.Extension) (err error) } type image struct { @@ -30,7 +30,7 @@ func NewImage(pdf gofpdf.Pdf, math Math) *image { } // AddFromFile open an image from disk and add to PDF -func (s *image) AddFromFile(path string, marginTop float64, indexCol float64, qtdCols float64, colHeight float64, prop props.Rect) error { +func (s *image) AddFromFile(path string, cell Cell, prop props.Rect) error { info := s.pdf.RegisterImageOptions(path, gofpdf.ImageOptions{ ReadDpi: false, ImageType: "", @@ -40,15 +40,15 @@ func (s *image) AddFromFile(path string, marginTop float64, indexCol float64, qt return errors.New("Could not register image options, maybe path/name is wrong") } - s.addImageToPdf(path, info, marginTop, qtdCols, colHeight, indexCol, prop) + s.addImageToPdf(path, info, cell, prop) return nil } // AddFromBase64 use a base64 string to add to PDF -func (s *image) AddFromBase64(b64 string, marginTop float64, indexCol float64, qtdCols float64, colHeight float64, prop props.Rect, extension consts.Extension) error { +func (s *image) AddFromBase64(stringBase64 string, cell Cell, prop props.Rect, extension consts.Extension) error { imageId, _ := uuid.NewRandom() - ss, _ := base64.StdEncoding.DecodeString(b64) + ss, _ := base64.StdEncoding.DecodeString(stringBase64) info := s.pdf.RegisterImageOptionsReader( imageId.String(), @@ -63,16 +63,16 @@ func (s *image) AddFromBase64(b64 string, marginTop float64, indexCol float64, q return errors.New("Could not register image options, maybe path/name is wrong") } - s.addImageToPdf(imageId.String(), info, marginTop, qtdCols, colHeight, indexCol, prop) + s.addImageToPdf(imageId.String(), info, cell, prop) return nil } -func (s *image) addImageToPdf(imageLabel string, info *gofpdf.ImageInfoType, marginTop, qtdCols, colHeight, indexCol float64, prop props.Rect) { +func (s *image) addImageToPdf(imageLabel string, info *gofpdf.ImageInfoType, cell Cell, prop props.Rect) { var x, y, w, h float64 if prop.Center { - x, y, w, h = s.math.GetRectCenterColProperties(info.Width(), info.Height(), qtdCols, colHeight, indexCol, prop.Percent) + x, y, w, h = s.math.GetRectCenterColProperties(info.Width(), info.Height(), cell.Width, cell.Height, cell.X, prop.Percent) } else { - x, y, w, h = s.math.GetRectNonCenterColProperties(info.Width(), info.Height(), qtdCols, colHeight, indexCol, prop) + x, y, w, h = s.math.GetRectNonCenterColProperties(info.Width(), info.Height(), cell.Width, cell.Height, cell.X, prop) } - s.pdf.Image(imageLabel, x, y+marginTop, w, h, false, "", 0, "") + s.pdf.Image(imageLabel, x, y+cell.Y, w, h, false, "", 0, "") } diff --git a/internal/image_test.go b/internal/image_test.go index 1f74ba0f..91751a43 100644 --- a/internal/image_test.go +++ b/internal/image_test.go @@ -165,9 +165,15 @@ func TestImage_AddFromFile(t *testing.T) { math := c.math() image := internal.NewImage(pdf, math) + cell := internal.Cell{ + X: 1.0, + Y: 10.0, + Width: 4.0, + Height: 5.0, + } // Act - err := image.AddFromFile("AnyPath", 10.0, 1.0, 4.0, 5.0, c.props) + err := image.AddFromFile("AnyPath", cell, c.props) // Assert c.assertPdfCalls(t, pdf) @@ -287,9 +293,15 @@ func TestImage_AddFromBase64(t *testing.T) { image := internal.NewImage(pdf, math) base64 := getBase64String() + cell := internal.Cell{ + X: 1.0, + Y: 10.0, + Width: 4.0, + Height: 5.0, + } // Act - err := image.AddFromBase64(base64, 10.0, 1.0, 4.0, 5.0, props.Rect{Center: true, Percent: 100}, consts.Jpg) + err := image.AddFromBase64(base64, cell, props.Rect{Center: true, Percent: 100}, consts.Jpg) // Assert c.assertPdfCalls(t, pdf) diff --git a/internal/math.go b/internal/math.go index a4986e59..efc1e7da 100644 --- a/internal/math.go +++ b/internal/math.go @@ -7,9 +7,8 @@ import ( // Math is the abstraction which deals with useful calc type Math interface { - GetWidthPerCol(qtdCols float64) float64 - GetRectCenterColProperties(imageWidth float64, imageHeight float64, qtdCols float64, colHeight float64, indexCol float64, percent float64) (x float64, y float64, w float64, h float64) - GetRectNonCenterColProperties(imageWidth float64, imageHeight float64, qtdCols float64, colHeight float64, indexCol float64, prop props.Rect) (x float64, y float64, w float64, h float64) + GetRectCenterColProperties(imageWidth float64, imageHeight float64, colWidth float64, colHeight float64, xColOffset float64, percent float64) (x float64, y float64, w float64, h float64) + GetRectNonCenterColProperties(imageWidth float64, imageHeight float64, colWidth float64, colHeight float64, xColOffset float64, prop props.Rect) (x float64, y float64, w float64, h float64) GetCenterCorrection(outerSize, innerSize float64) float64 } @@ -24,43 +23,34 @@ func NewMath(pdf gofpdf.Pdf) *math { } } -// GetWidthPerCol return a width which a col will have -// using margins and page size information -func (s *math) GetWidthPerCol(qtdCols float64) float64 { - width, _ := s.pdf.GetPageSize() - left, _, right, _ := s.pdf.GetMargins() - return (width - right - left) / qtdCols -} - // GetRectCenterColProperties define Width, Height, X Offset and Y Offset // to and rectangle (QrCode, Barcode, Image) be centralized inside a cell -func (s *math) GetRectCenterColProperties(imageWidth float64, imageHeight float64, qtdCols float64, colHeight float64, indexCol float64, percent float64) (x float64, y float64, w float64, h float64) { +func (s *math) GetRectCenterColProperties(imageWidth float64, imageHeight float64, colWidth float64, colHeight float64, xColOffset float64, percent float64) (x float64, y float64, w float64, h float64) { percent = percent / 100.0 - width, _ := s.pdf.GetPageSize() - left, top, right, _ := s.pdf.GetMargins() - widthPerCol := (width - right - left) / qtdCols - - proportion := imageHeight / imageWidth + left, top, _, _ := s.pdf.GetMargins() - newImageHeight := widthPerCol * proportion * percent - newImageWidth := widthPerCol * percent + imageProportion := imageHeight / imageWidth + celProportion := colHeight / colWidth - if newImageHeight > colHeight { - newImageWidth := colHeight / proportion * percent - newImageHeight := newImageWidth * proportion + if imageProportion > celProportion { + newImageWidth := colHeight / imageProportion * percent + newImageHeight := newImageWidth * imageProportion - widthCorrection := s.GetCenterCorrection(widthPerCol, newImageWidth) + widthCorrection := s.GetCenterCorrection(colWidth, newImageWidth) heightCorrection := s.GetCenterCorrection(colHeight, newImageHeight) - x = (widthPerCol * indexCol) + left + widthCorrection + x = xColOffset + left + widthCorrection y = top + heightCorrection w = newImageWidth h = newImageHeight } else { - widthCorrection := s.GetCenterCorrection(widthPerCol, newImageWidth) + newImageWidth := colWidth * percent + newImageHeight := newImageWidth * imageProportion + + widthCorrection := s.GetCenterCorrection(colWidth, newImageWidth) heightCorrection := s.GetCenterCorrection(colHeight, newImageHeight) - x = (widthPerCol * indexCol) + left + widthCorrection + x = xColOffset + left + widthCorrection y = top + heightCorrection w = newImageWidth h = newImageHeight @@ -70,27 +60,27 @@ func (s *math) GetRectCenterColProperties(imageWidth float64, imageHeight float6 } // GetRectNonCenterColProperties define Width, Height to and rectangle (QrCode, Barcode, Image) inside a cell -func (s *math) GetRectNonCenterColProperties(imageWidth float64, imageHeight float64, qtdCols float64, colHeight float64, indexCol float64, prop props.Rect) (x float64, y float64, w float64, h float64) { +func (s *math) GetRectNonCenterColProperties(imageWidth float64, imageHeight float64, colWidth float64, colHeight float64, xColOffset float64, prop props.Rect) (x float64, y float64, w float64, h float64) { percent := prop.Percent / 100.0 - width, _ := s.pdf.GetPageSize() - left, top, right, _ := s.pdf.GetMargins() - widthPerCol := (width - right - left) / qtdCols + left, top, _, _ := s.pdf.GetMargins() - proportion := imageHeight / imageWidth + imageProportion := imageHeight / imageWidth + celProportion := colHeight / colWidth - newImageHeight := widthPerCol * proportion * percent - newImageWidth := widthPerCol * percent + if imageProportion > celProportion { + newImageWidth := colHeight / imageProportion * percent + newImageHeight := newImageWidth * imageProportion - if newImageHeight > colHeight { - newImageWidth := colHeight / proportion * percent - newImageHeight := newImageWidth * proportion - x = (widthPerCol * indexCol) + prop.Left + left - y = prop.Top + top + x = xColOffset + left + prop.Left + y = top w = newImageWidth h = newImageHeight } else { - x = (widthPerCol * indexCol) + prop.Left + left - y = prop.Top + top + newImageWidth := colWidth * percent + newImageHeight := newImageWidth * imageProportion + + x = xColOffset + left + prop.Left + y = top w = newImageWidth h = newImageHeight } diff --git a/internal/math_test.go b/internal/math_test.go index 022600eb..11924cd6 100644 --- a/internal/math_test.go +++ b/internal/math_test.go @@ -16,206 +16,94 @@ func TestNewMath(t *testing.T) { assert.Equal(t, fmt.Sprintf("%T", math), "*internal.math") } -func TestMath_GetWidthPerCol(t *testing.T) { +func TestMath_GetRectCenterColProperties(t *testing.T) { cases := []struct { - name string - qtdCols float64 - pdf func() *mocks.Pdf - assertCalls func(t *testing.T, pdf *mocks.Pdf) - assertWidth func(t *testing.T, width float64) + name string + width float64 + height float64 + percent float64 + pdf func() *mocks.Pdf + assertPdfCalls func(t *testing.T, pdf *mocks.Pdf) + assertResult func(t *testing.T, x, y, w, h float64) }{ { - "1 col, margins 10 10", - 1, - func() *mocks.Pdf { - pdf := &mocks.Pdf{} - pdf.On("GetPageSize").Return(210.0, 0.0) - pdf.On("GetMargins").Return(10.0, 10.0, 10.0, 10.0) - return pdf - }, - func(t *testing.T, pdf *mocks.Pdf) { - pdf.AssertNumberOfCalls(t, "GetPageSize", 1) - pdf.AssertNumberOfCalls(t, "GetMargins", 1) - }, - func(t *testing.T, width float64) { - assert.Equal(t, int(width), 190) - }, - }, - { - "2 col, margins 10 10", - 2, - func() *mocks.Pdf { - pdf := &mocks.Pdf{} - pdf.On("GetPageSize").Return(210.0, 0.0) - pdf.On("GetMargins").Return(10.0, 10.0, 10.0, 10.0) - return pdf - }, - func(t *testing.T, pdf *mocks.Pdf) { - pdf.AssertNumberOfCalls(t, "GetPageSize", 1) - pdf.AssertNumberOfCalls(t, "GetMargins", 1) - }, - func(t *testing.T, width float64) { - assert.Equal(t, int(width), 95) - }, - }, - { - "4 col, margins 10 10", - 4, - func() *mocks.Pdf { - pdf := &mocks.Pdf{} - pdf.On("GetPageSize").Return(210.0, 0.0) - pdf.On("GetMargins").Return(10.0, 10.0, 10.0, 10.0) - return pdf - }, - func(t *testing.T, pdf *mocks.Pdf) { - pdf.AssertNumberOfCalls(t, "GetPageSize", 1) - pdf.AssertNumberOfCalls(t, "GetMargins", 1) - }, - func(t *testing.T, width float64) { - assert.Equal(t, int(width), 47) - }, - }, - { - "1 col, margins 20 20", - 1, - func() *mocks.Pdf { - pdf := &mocks.Pdf{} - pdf.On("GetPageSize").Return(210.0, 0.0) - pdf.On("GetMargins").Return(20.0, 20.0, 20.0, 20.0) - return pdf - }, - func(t *testing.T, pdf *mocks.Pdf) { - pdf.AssertNumberOfCalls(t, "GetPageSize", 1) - pdf.AssertNumberOfCalls(t, "GetMargins", 1) - }, - func(t *testing.T, width float64) { - assert.Equal(t, int(width), 170) - }, - }, - { - "2 col, margins 20 20", - 2, - func() *mocks.Pdf { - pdf := &mocks.Pdf{} - pdf.On("GetPageSize").Return(210.0, 0.0) - pdf.On("GetMargins").Return(20.0, 20.0, 20.0, 20.0) - return pdf - }, - func(t *testing.T, pdf *mocks.Pdf) { - pdf.AssertNumberOfCalls(t, "GetPageSize", 1) - pdf.AssertNumberOfCalls(t, "GetMargins", 1) - }, - func(t *testing.T, width float64) { - assert.Equal(t, int(width), 85) - }, - }, - { - "4 col, margins 20 20", - 4, + "When cel proportion is greater than col", + 20, + 26, + 100.0, func() *mocks.Pdf { pdf := &mocks.Pdf{} - pdf.On("GetPageSize").Return(210.0, 0.0) - pdf.On("GetMargins").Return(20.0, 20.0, 20.0, 20.0) + pdf.On("GetMargins").Return(12.0, 11.0, 13.0, 15.0) return pdf }, func(t *testing.T, pdf *mocks.Pdf) { - pdf.AssertNumberOfCalls(t, "GetPageSize", 1) pdf.AssertNumberOfCalls(t, "GetMargins", 1) }, - func(t *testing.T, width float64) { - assert.Equal(t, int(width), 42) + func(t *testing.T, x, y, w, h float64) { + assert.InDelta(t, x, 14.3, 0.1) + assert.InDelta(t, y, 11.0, 0.1) + assert.InDelta(t, w, 19.2, 0.1) + assert.InDelta(t, h, 25.0, 0.1) }, }, - } - - for _, c := range cases { - // Arrange - pdf := c.pdf() - math := internal.NewMath(pdf) - - // Act - width := math.GetWidthPerCol(c.qtdCols) - - // Assert - c.assertWidth(t, width) - c.assertCalls(t, pdf) - } -} - -func TestMath_GetRectCenterColProperties(t *testing.T) { - cases := []struct { - name string - width float64 - height float64 - percent float64 - pdf func() *mocks.Pdf - assertPdfCalls func(t *testing.T, pdf *mocks.Pdf) - assertResult func(t *testing.T, x, y, w, h float64) - }{ { - "When Image has width greater than height", - 300, - 200, - 100.0, + "When cel proportion is greater than col, 45 percent", + 20, + 26, + 45.0, func() *mocks.Pdf { pdf := &mocks.Pdf{} pdf.On("GetMargins").Return(12.0, 11.0, 13.0, 15.0) - pdf.On("GetPageSize").Return(201.0, 301.0) return pdf }, func(t *testing.T, pdf *mocks.Pdf) { pdf.AssertNumberOfCalls(t, "GetMargins", 1) - pdf.AssertNumberOfCalls(t, "GetPageSize", 1) }, func(t *testing.T, x, y, w, h float64) { - assert.InDelta(t, x, 82.4, 0.1) - assert.InDelta(t, y, 11.7, 0.1) - assert.InDelta(t, w, 35.2, 0.1) - assert.InDelta(t, h, 23.4, 0.1) + assert.InDelta(t, x, 19.6, 0.1) + assert.InDelta(t, y, 17.8, 0.1) + assert.InDelta(t, w, 8.6, 0.1) + assert.InDelta(t, h, 11.2, 0.1) }, }, { - "When Image has height greater than width", - 200, - 300, + "When cen proportion is less than col", + 26, + 20, 100.0, func() *mocks.Pdf { pdf := &mocks.Pdf{} pdf.On("GetMargins").Return(15.0, 12.0, 17.0, 10.0) - pdf.On("GetPageSize").Return(211.0, 233.0) return pdf }, func(t *testing.T, pdf *mocks.Pdf) { pdf.AssertNumberOfCalls(t, "GetMargins", 1) - pdf.AssertNumberOfCalls(t, "GetPageSize", 1) }, func(t *testing.T, x, y, w, h float64) { - assert.InDelta(t, x, 96.1, 0.1) - assert.InDelta(t, y, 12.0, 0.1) - assert.InDelta(t, w, 16.6, 0.1) - assert.InDelta(t, h, 25.0, 0.1) + assert.InDelta(t, x, 17.0, 0.1) + assert.InDelta(t, y, 16.8, 0.1) + assert.InDelta(t, w, 20.0, 0.1) + assert.InDelta(t, h, 15.3, 0.1) }, }, { - "When Image has height greater than width, percent 45", - 200, - 300, + "When cen proportion is less than col, 45 percent", + 26, + 20, 45.0, func() *mocks.Pdf { pdf := &mocks.Pdf{} pdf.On("GetMargins").Return(15.0, 12.0, 17.0, 10.0) - pdf.On("GetPageSize").Return(211.0, 233.0) return pdf }, func(t *testing.T, pdf *mocks.Pdf) { pdf.AssertNumberOfCalls(t, "GetMargins", 1) - pdf.AssertNumberOfCalls(t, "GetPageSize", 1) }, func(t *testing.T, x, y, w, h float64) { - assert.InDelta(t, x, 96.4, 0.1) - assert.InDelta(t, y, 12.4, 0.1) - assert.InDelta(t, w, 16.1, 0.1) - assert.InDelta(t, h, 24.1, 0.1) + assert.InDelta(t, x, 22.5, 0.1) + assert.InDelta(t, y, 21.0, 0.1) + assert.InDelta(t, w, 9.0, 0.1) + assert.InDelta(t, h, 6.9, 0.1) }, }, } @@ -227,7 +115,7 @@ func TestMath_GetRectCenterColProperties(t *testing.T) { math := internal.NewMath(pdf) // Act - x, y, w, h := math.GetRectCenterColProperties(c.width, c.height, 5, 25.0, 2, c.percent) + x, y, w, h := math.GetRectCenterColProperties(c.width, c.height, 20, 25.0, 2, c.percent) // Assert c.assertPdfCalls(t, pdf) @@ -246,9 +134,9 @@ func TestMath_GetRectNonCenterColProperties(t *testing.T) { assertResult func(t *testing.T, x, y, w, h float64) }{ { - "When Image has width greater than height", - 300, - 200, + "When cel proportion is greater than rectangle", + 20, + 26, props.Rect{ Percent: 100, Center: false, @@ -258,23 +146,46 @@ func TestMath_GetRectNonCenterColProperties(t *testing.T) { func() *mocks.Pdf { pdf := &mocks.Pdf{} pdf.On("GetMargins").Return(12.0, 11.0, 13.0, 15.0) - pdf.On("GetPageSize").Return(201.0, 301.0) return pdf }, func(t *testing.T, pdf *mocks.Pdf) { pdf.AssertNumberOfCalls(t, "GetMargins", 1) - pdf.AssertNumberOfCalls(t, "GetPageSize", 1) }, func(t *testing.T, x, y, w, h float64) { - assert.InDelta(t, x, 82.4, 0.1) + assert.InDelta(t, x, 14.0, 0.1) + assert.InDelta(t, y, 11, 0.1) + assert.InDelta(t, w, 19.2, 0.1) + assert.InDelta(t, h, 25.0, 0.1) + }, + }, + { + "When cel proportion is greater than rectangle, 45 percent", + 20, + 26, + props.Rect{ + Percent: 45, + Center: false, + Left: 0, + Top: 0, + }, + func() *mocks.Pdf { + pdf := &mocks.Pdf{} + pdf.On("GetMargins").Return(12.0, 11.0, 13.0, 15.0) + return pdf + }, + func(t *testing.T, pdf *mocks.Pdf) { + pdf.AssertNumberOfCalls(t, "GetMargins", 1) + }, + func(t *testing.T, x, y, w, h float64) { + assert.InDelta(t, x, 14.0, 0.1) assert.InDelta(t, y, 11, 0.1) - assert.InDelta(t, w, 35.2, 0.1) - assert.InDelta(t, h, 23.4, 0.1) + assert.InDelta(t, w, 8.6, 0.1) + assert.InDelta(t, h, 11.25, 0.1) }, }, { - "When Image has height greater than width", - 200, - 300, + "When cel proportion is less than rectangle", + 26, + 20, props.Rect{ Percent: 100, Center: false, @@ -284,24 +195,22 @@ func TestMath_GetRectNonCenterColProperties(t *testing.T) { func() *mocks.Pdf { pdf := &mocks.Pdf{} pdf.On("GetMargins").Return(15.0, 12.0, 17.0, 10.0) - pdf.On("GetPageSize").Return(211.0, 233.0) return pdf }, func(t *testing.T, pdf *mocks.Pdf) { pdf.AssertNumberOfCalls(t, "GetMargins", 1) - pdf.AssertNumberOfCalls(t, "GetPageSize", 1) }, func(t *testing.T, x, y, w, h float64) { - assert.InDelta(t, x, 86.6, 0.1) - assert.InDelta(t, y, 12, 0.1) - assert.InDelta(t, w, 16.6, 0.1) - assert.InDelta(t, h, 25.0, 0.1) + assert.InDelta(t, x, 17.0, 0.1) + assert.InDelta(t, y, 12.0, 0.1) + assert.InDelta(t, w, 20.0, 0.1) + assert.InDelta(t, h, 15.3, 0.1) }, }, { - "When Image has height greater than width, percent 45", - 200, - 300, + "When cel proportion is less than rectangle, 45 percent", + 26, + 20, props.Rect{ Percent: 45, Center: false, @@ -311,18 +220,16 @@ func TestMath_GetRectNonCenterColProperties(t *testing.T) { func() *mocks.Pdf { pdf := &mocks.Pdf{} pdf.On("GetMargins").Return(15.0, 12.0, 17.0, 10.0) - pdf.On("GetPageSize").Return(211.0, 233.0) return pdf }, func(t *testing.T, pdf *mocks.Pdf) { pdf.AssertNumberOfCalls(t, "GetMargins", 1) - pdf.AssertNumberOfCalls(t, "GetPageSize", 1) }, func(t *testing.T, x, y, w, h float64) { - assert.InDelta(t, x, 86.6, 0.1) - assert.InDelta(t, y, 12, 0.1) - assert.InDelta(t, w, 16.1, 0.1) - assert.InDelta(t, h, 24.1, 0.1) + assert.InDelta(t, x, 17.0, 0.1) + assert.InDelta(t, y, 12.0, 0.1) + assert.InDelta(t, w, 9.0, 0.1) + assert.InDelta(t, h, 6.9, 0.1) }, }, } @@ -334,7 +241,7 @@ func TestMath_GetRectNonCenterColProperties(t *testing.T) { math := internal.NewMath(pdf) // Act - x, y, w, h := math.GetRectNonCenterColProperties(c.width, c.height, 5, 25.0, 2, c.prop) + x, y, w, h := math.GetRectNonCenterColProperties(c.width, c.height, 20.0, 25.0, 2, c.prop) // Assert c.assertPdfCalls(t, pdf) diff --git a/internal/mocks/Image.go b/internal/mocks/Image.go index d2fb1ef6..574d60a9 100644 --- a/internal/mocks/Image.go +++ b/internal/mocks/Image.go @@ -3,7 +3,7 @@ package mocks import consts "github.com/johnfercher/maroto/pkg/consts" - +import internal "github.com/johnfercher/maroto/internal" import mock "github.com/stretchr/testify/mock" import props "github.com/johnfercher/maroto/pkg/props" @@ -12,13 +12,13 @@ type Image struct { mock.Mock } -// AddFromBase64 provides a mock function with given fields: b64, marginTop, indexCol, qtdCols, colHeight, prop, extension -func (_m *Image) AddFromBase64(b64 string, marginTop float64, indexCol float64, qtdCols float64, colHeight float64, prop props.Rect, extension consts.Extension) error { - ret := _m.Called(b64, marginTop, indexCol, qtdCols, colHeight, prop, extension) +// AddFromBase64 provides a mock function with given fields: stringBase64, cell, prop, extension +func (_m *Image) AddFromBase64(stringBase64 string, cell internal.Cell, prop props.Rect, extension consts.Extension) error { + ret := _m.Called(stringBase64, cell, prop, extension) var r0 error - if rf, ok := ret.Get(0).(func(string, float64, float64, float64, float64, props.Rect, consts.Extension) error); ok { - r0 = rf(b64, marginTop, indexCol, qtdCols, colHeight, prop, extension) + if rf, ok := ret.Get(0).(func(string, internal.Cell, props.Rect, consts.Extension) error); ok { + r0 = rf(stringBase64, cell, prop, extension) } else { r0 = ret.Error(0) } @@ -26,13 +26,13 @@ func (_m *Image) AddFromBase64(b64 string, marginTop float64, indexCol float64, return r0 } -// AddFromFile provides a mock function with given fields: path, marginTop, indexCol, qtdCols, colHeight, prop -func (_m *Image) AddFromFile(path string, marginTop float64, indexCol float64, qtdCols float64, colHeight float64, prop props.Rect) error { - ret := _m.Called(path, marginTop, indexCol, qtdCols, colHeight, prop) +// AddFromFile provides a mock function with given fields: path, cell, prop +func (_m *Image) AddFromFile(path string, cell internal.Cell, prop props.Rect) error { + ret := _m.Called(path, cell, prop) var r0 error - if rf, ok := ret.Get(0).(func(string, float64, float64, float64, float64, props.Rect) error); ok { - r0 = rf(path, marginTop, indexCol, qtdCols, colHeight, prop) + if rf, ok := ret.Get(0).(func(string, internal.Cell, props.Rect) error); ok { + r0 = rf(path, cell, prop) } else { r0 = ret.Error(0) } diff --git a/internal/mocks/code.go b/internal/mocks/code.go index d02a0fb3..7a813807 100644 --- a/internal/mocks/code.go +++ b/internal/mocks/code.go @@ -2,23 +2,22 @@ package mocks -import ( - props "github.com/johnfercher/maroto/pkg/props" - mock "github.com/stretchr/testify/mock" -) +import internal "github.com/johnfercher/maroto/internal" +import mock "github.com/stretchr/testify/mock" +import props "github.com/johnfercher/maroto/pkg/props" // Code is an autogenerated mock type for the Code type type Code struct { mock.Mock } -// AddBar provides a mock function with given fields: code, marginTop, indexCol, qtdCols, colHeight, prop -func (_m *Code) AddBar(code string, marginTop float64, indexCol float64, qtdCols float64, colHeight float64, prop props.Barcode) error { - ret := _m.Called(code, marginTop, indexCol, qtdCols, colHeight, prop) +// AddBar provides a mock function with given fields: code, cell, prop +func (_m *Code) AddBar(code string, cell internal.Cell, prop props.Barcode) error { + ret := _m.Called(code, cell, prop) var r0 error - if rf, ok := ret.Get(0).(func(string, float64, float64, float64, float64, props.Barcode) error); ok { - r0 = rf(code, marginTop, indexCol, qtdCols, colHeight, prop) + if rf, ok := ret.Get(0).(func(string, internal.Cell, props.Barcode) error); ok { + r0 = rf(code, cell, prop) } else { r0 = ret.Error(0) } @@ -26,7 +25,7 @@ func (_m *Code) AddBar(code string, marginTop float64, indexCol float64, qtdCols return r0 } -// AddQr provides a mock function with given fields: code, marginTop, indexCol, qtdCols, colHeight, prop -func (_m *Code) AddQr(code string, marginTop float64, indexCol float64, qtdCols float64, colHeight float64, prop props.Rect) { - _m.Called(code, marginTop, indexCol, qtdCols, colHeight, prop) +// AddQr provides a mock function with given fields: code, cell, prop +func (_m *Code) AddQr(code string, cell internal.Cell, prop props.Rect) { + _m.Called(code, cell, prop) } diff --git a/internal/mocks/maroto.go b/internal/mocks/maroto.go index b4c9a931..cae1fea7 100644 --- a/internal/mocks/maroto.go +++ b/internal/mocks/maroto.go @@ -57,12 +57,12 @@ func (_m *Maroto) Base64Image(base64 string, extension consts.Extension, prop .. } // Col provides a mock function with given fields: closure -func (_m *Maroto) Col(closure func()) { +func (_m *Maroto) Col(width uint, closure func()) { _m.Called(closure) } // ColSpace provides a mock function with given fields: -func (_m *Maroto) ColSpace() { +func (_m *Maroto) ColSpace(uint) { _m.Called() } diff --git a/internal/mocks/math.go b/internal/mocks/math.go index 0b2d9cd2..a059525d 100644 --- a/internal/mocks/math.go +++ b/internal/mocks/math.go @@ -27,33 +27,33 @@ func (_m *Math) GetCenterCorrection(outerSize float64, innerSize float64) float6 } // GetRectCenterColProperties provides a mock function with given fields: imageWidth, imageHeight, qtdCols, colHeight, indexCol, percent -func (_m *Math) GetRectCenterColProperties(imageWidth float64, imageHeight float64, qtdCols float64, colHeight float64, indexCol float64, percent float64) (float64, float64, float64, float64) { - ret := _m.Called(int(imageWidth), int(imageHeight), int(qtdCols), int(colHeight), int(indexCol), int(percent)) +func (_m *Math) GetRectCenterColProperties(imageWidth float64, imageHeight float64, colWidth float64, colHeight float64, xColOffset float64, percent float64) (float64, float64, float64, float64) { + ret := _m.Called(int(imageWidth), int(imageHeight), int(colWidth), int(colHeight), int(xColOffset), int(percent)) var r0 float64 if rf, ok := ret.Get(0).(func(float64, float64, float64, float64, float64, float64) float64); ok { - r0 = rf(imageWidth, imageHeight, qtdCols, colHeight, indexCol, percent) + r0 = rf(imageWidth, imageHeight, colWidth, colHeight, xColOffset, percent) } else { r0 = ret.Get(0).(float64) } var r1 float64 if rf, ok := ret.Get(1).(func(float64, float64, float64, float64, float64, float64) float64); ok { - r1 = rf(imageWidth, imageHeight, qtdCols, colHeight, indexCol, percent) + r1 = rf(imageWidth, imageHeight, colWidth, colHeight, xColOffset, percent) } else { r1 = ret.Get(1).(float64) } var r2 float64 if rf, ok := ret.Get(2).(func(float64, float64, float64, float64, float64, float64) float64); ok { - r2 = rf(imageWidth, imageHeight, qtdCols, colHeight, indexCol, percent) + r2 = rf(imageWidth, imageHeight, colWidth, colHeight, xColOffset, percent) } else { r2 = ret.Get(2).(float64) } var r3 float64 if rf, ok := ret.Get(3).(func(float64, float64, float64, float64, float64, float64) float64); ok { - r3 = rf(imageWidth, imageHeight, qtdCols, colHeight, indexCol, percent) + r3 = rf(imageWidth, imageHeight, colWidth, colHeight, xColOffset, percent) } else { r3 = ret.Get(3).(float64) } @@ -62,33 +62,33 @@ func (_m *Math) GetRectCenterColProperties(imageWidth float64, imageHeight float } // GetRectNonCenterColProperties provides a mock function with given fields: imageWidth, imageHeight, qtdCols, colHeight, indexCol, prop -func (_m *Math) GetRectNonCenterColProperties(imageWidth float64, imageHeight float64, qtdCols float64, colHeight float64, indexCol float64, prop props.Rect) (float64, float64, float64, float64) { - ret := _m.Called(int(imageWidth), int(imageHeight), int(qtdCols), int(colHeight), int(indexCol), prop) +func (_m *Math) GetRectNonCenterColProperties(imageWidth float64, imageHeight float64, colWidth float64, colHeight float64, xColOffset float64, prop props.Rect) (float64, float64, float64, float64) { + ret := _m.Called(int(imageWidth), int(imageHeight), int(colWidth), int(colHeight), int(xColOffset), prop) var r0 float64 if rf, ok := ret.Get(0).(func(float64, float64, float64, float64, float64, props.Rect) float64); ok { - r0 = rf(imageWidth, imageHeight, qtdCols, colHeight, indexCol, prop) + r0 = rf(imageWidth, imageHeight, colWidth, colHeight, xColOffset, prop) } else { r0 = ret.Get(0).(float64) } var r1 float64 if rf, ok := ret.Get(1).(func(float64, float64, float64, float64, float64, props.Rect) float64); ok { - r1 = rf(imageWidth, imageHeight, qtdCols, colHeight, indexCol, prop) + r1 = rf(imageWidth, imageHeight, colWidth, colHeight, xColOffset, prop) } else { r1 = ret.Get(1).(float64) } var r2 float64 if rf, ok := ret.Get(2).(func(float64, float64, float64, float64, float64, props.Rect) float64); ok { - r2 = rf(imageWidth, imageHeight, qtdCols, colHeight, indexCol, prop) + r2 = rf(imageWidth, imageHeight, colWidth, colHeight, xColOffset, prop) } else { r2 = ret.Get(2).(float64) } var r3 float64 if rf, ok := ret.Get(3).(func(float64, float64, float64, float64, float64, props.Rect) float64); ok { - r3 = rf(imageWidth, imageHeight, qtdCols, colHeight, indexCol, prop) + r3 = rf(imageWidth, imageHeight, colWidth, colHeight, xColOffset, prop) } else { r3 = ret.Get(3).(float64) } @@ -97,12 +97,12 @@ func (_m *Math) GetRectNonCenterColProperties(imageWidth float64, imageHeight fl } // GetWidthPerCol provides a mock function with given fields: qtdCols -func (_m *Math) GetWidthPerCol(qtdCols float64) float64 { - ret := _m.Called(qtdCols) +func (_m *Math) GetWidthPerCol(percent float64) float64 { + ret := _m.Called(percent) var r0 float64 if rf, ok := ret.Get(0).(func(float64) float64); ok { - r0 = rf(qtdCols) + r0 = rf(percent) } else { r0 = ret.Get(0).(float64) } diff --git a/internal/mocks/signature.go b/internal/mocks/signature.go index 71c5d438..2b8b5dca 100644 --- a/internal/mocks/signature.go +++ b/internal/mocks/signature.go @@ -2,15 +2,16 @@ package mocks -import "github.com/stretchr/testify/mock" -import "github.com/johnfercher/maroto/pkg/props" +import internal "github.com/johnfercher/maroto/internal" +import mock "github.com/stretchr/testify/mock" +import props "github.com/johnfercher/maroto/pkg/props" // Signature is an autogenerated mock type for the Signature type type Signature struct { mock.Mock } -// AddSpaceFor provides a mock function with given fields: label, textProp, qtdCols, marginTop, actualCol -func (_m *Signature) AddSpaceFor(label string, textProp props.Text, qtdCols float64, marginTop float64, actualCol float64) { - _m.Called(label, textProp, qtdCols, marginTop, actualCol) +// AddSpaceFor provides a mock function with given fields: label, cell, textProp +func (_m *Signature) AddSpaceFor(label string, cell internal.Cell, textProp props.Text) { + _m.Called(label, cell, textProp) } diff --git a/internal/mocks/text.go b/internal/mocks/text.go index 7cbd08dd..00d7ed3e 100644 --- a/internal/mocks/text.go +++ b/internal/mocks/text.go @@ -2,6 +2,7 @@ package mocks +import internal "github.com/johnfercher/maroto/internal" import mock "github.com/stretchr/testify/mock" import props "github.com/johnfercher/maroto/pkg/props" @@ -10,18 +11,18 @@ type Text struct { mock.Mock } -// Add provides a mock function with given fields: text, fontFamily, marginTop, actualCol, qtdCols -func (_m *Text) Add(text string, fontFamily props.Text, marginTop float64, actualCol float64, qtdCols float64) { - _m.Called(text, fontFamily, marginTop, actualCol, qtdCols) +// Add provides a mock function with given fields: text, cell, textProp +func (_m *Text) Add(text string, cell internal.Cell, textProp props.Text) { + _m.Called(text, cell, textProp) } -// GetLinesQuantity provides a mock function with given fields: text, fontFamily, qtdCols -func (_m *Text) GetLinesQuantity(text string, fontFamily props.Text, qtdCols float64) int { - ret := _m.Called(text, fontFamily, qtdCols) +// GetLinesQuantity provides a mock function with given fields: text, fontFamily, colWidth +func (_m *Text) GetLinesQuantity(text string, fontFamily props.Text, colWidth float64) int { + ret := _m.Called(text, fontFamily, colWidth) var r0 int if rf, ok := ret.Get(0).(func(string, props.Text, float64) int); ok { - r0 = rf(text, fontFamily, qtdCols) + r0 = rf(text, fontFamily, colWidth) } else { r0 = ret.Get(0).(int) } diff --git a/internal/signature.go b/internal/signature.go index 7f35417a..11e3f4a8 100644 --- a/internal/signature.go +++ b/internal/signature.go @@ -7,7 +7,7 @@ import ( // Signature is the abstraction which deals of how to add a signature space inside PDF type Signature interface { - AddSpaceFor(label string, textProp props.Text, qtdCols float64, marginTop float64, actualCol float64) + AddSpaceFor(label string, cell Cell, textProp props.Text) } type signature struct { @@ -26,11 +26,15 @@ func NewSignature(pdf gofpdf.Pdf, math Math, text Text) *signature { } // AddSpaceFor create a space for a signature inside a cell -func (s *signature) AddSpaceFor(label string, textProp props.Text, qtdCols float64, marginTop float64, actualCol float64) { - widthPerCol := s.math.GetWidthPerCol(qtdCols) - left, _, _, _ := s.pdf.GetMargins() +func (s *signature) AddSpaceFor(label string, cell Cell, textProp props.Text) { + left, top, _, _ := s.pdf.GetMargins() space := 4.0 - s.pdf.Line((widthPerCol*actualCol)+left+space, marginTop+5.0, widthPerCol*(actualCol+1)+left-space, marginTop+5.0) - s.text.Add(label, textProp, marginTop, actualCol, qtdCols) + lineCenterY := cell.Height / 1.33 + cell.Y += lineCenterY + + s.pdf.Line(cell.X+left+space, cell.Y+top, cell.X+cell.Width+left-space, cell.Y+top) + + cell.Y += 2.0 + s.text.Add(label, cell, textProp) } diff --git a/internal/signature_test.go b/internal/signature_test.go index 30017b2c..c0eae45a 100644 --- a/internal/signature_test.go +++ b/internal/signature_test.go @@ -32,13 +32,13 @@ func TestSignature_AddSpaceFor_DefaultMargins(t *testing.T) { signature := internal.NewSignature(pdf, math, text) // Act - signature.AddSpaceFor("label", props.Text{Size: 10.0}, 5, 5, 2) + signature.AddSpaceFor("label", internal.Cell{5, 5, 2, 0}, props.Text{Size: 10.0}) // Assert pdf.AssertNumberOfCalls(t, "Line", 1) - pdf.AssertCalled(t, "Line", 114.0, 10.0, 156.0, 10.0) + pdf.AssertCalled(t, "Line", 19.0, 15.0, 13.0, 15.0) text.AssertNumberOfCalls(t, "Add", 1) - text.AssertCalled(t, "Add", "label", props.Text{Size: 10.0}, 5.0, 2.0, 5.0) + text.AssertCalled(t, "Add", "label", internal.Cell{5.0, 7.0, 2.0, 0.0}, props.Text{Size: 10.0}) } func TestSignature_AddSpaceFor_NotDefaultMargins(t *testing.T) { @@ -56,11 +56,11 @@ func TestSignature_AddSpaceFor_NotDefaultMargins(t *testing.T) { signature := internal.NewSignature(pdf, math, text) // Act - signature.AddSpaceFor("label", props.Text{Size: 10.0}, 5, 5, 2) + signature.AddSpaceFor("label", internal.Cell{2, 5, 5, 0}, props.Text{Size: 10.0}) // Assert pdf.AssertNumberOfCalls(t, "Line", 1) - pdf.AssertCalled(t, "Line", 124.0, 10.0, 166.0, 10.0) + pdf.AssertCalled(t, "Line", 26.0, 15.0, 23.0, 15.0) text.AssertNumberOfCalls(t, "Add", 1) - text.AssertCalled(t, "Add", "label", props.Text{Size: 10.0}, 5.0, 2.0, 5.0) + text.AssertCalled(t, "Add", "label", internal.Cell{2.0, 7.0, 5.0, 0.0}, props.Text{Size: 10.0}) } diff --git a/internal/tablelist.go b/internal/tablelist.go index 39add10f..9dbc234d 100644 --- a/internal/tablelist.go +++ b/internal/tablelist.go @@ -2,6 +2,7 @@ package internal import ( "github.com/johnfercher/maroto/pkg/color" + "github.com/johnfercher/maroto/pkg/consts" "github.com/johnfercher/maroto/pkg/props" ) @@ -9,15 +10,20 @@ import ( type MarotoGridPart interface { // Grid System Row(height float64, closure func()) - Col(closure func()) - ColSpace() + Col(width uint, closure func()) + ColSpace(width uint) // Helpers SetBackgroundColor(color color.Color) GetCurrentOffset() float64 + GetPageSize() (width float64, height float64) + GetPageMargins() (left float64, top float64, right float64, bottom float64) // Outside Col/Row Components Line(spaceHeight float64) + + // Inside Col/Row Components + Text(text string, prop ...props.Text) } // TableList is the abstraction to create a table with header and contents @@ -62,60 +68,40 @@ func (s *tableList) Create(header []string, contents [][]string, prop ...props.T tableProp = prop[0] } - tableProp.MakeValid() - - qtdCols := float64(len(header)) - - headerTextProp := tableProp.HeaderProp.ToTextProp(tableProp.Align, 0.0, false, 1.0) - headerHeight := s.calcLinesHeight(header, headerTextProp, qtdCols) + tableProp.MakeValid(header, contents) + headerHeight := s.calcLinesHeight(header, tableProp.HeaderProp, tableProp.Align) // Draw header - s.pdf.Row(headerHeight, func() { - headerMarginTop := 2.0 - + s.pdf.Row(headerHeight+1, func() { for i, h := range header { hs := h - is := i - - s.pdf.Col(func() { - if headerMarginTop > headerHeight { - headerMarginTop = headerHeight - } + s.pdf.Col(tableProp.HeaderProp.GridSizes[i], func() { reason := hs - - sumOyYOffesets := headerMarginTop + s.pdf.GetCurrentOffset() + 2.5 - - s.text.Add(reason, headerTextProp, sumOyYOffesets, float64(is), qtdCols) + s.pdf.Text(reason, tableProp.HeaderProp.ToTextProp(tableProp.Align, 0, false, 0.0)) }) } }) // Define space between header and contents s.pdf.Row(tableProp.HeaderContentSpace, func() { - s.pdf.ColSpace() + s.pdf.ColSpace(0) }) - contentMarginTop := 2.0 - // Draw contents for index, content := range contents { - contentTextProp := tableProp.ContentProp.ToTextProp(tableProp.Align, 0.0, false, 1.0) - contentHeight := s.calcLinesHeight(content, contentTextProp, qtdCols) + contentHeight := s.calcLinesHeight(content, tableProp.ContentProp, tableProp.Align) if tableProp.AlternatedBackground != nil && index%2 == 0 { s.pdf.SetBackgroundColor(*tableProp.AlternatedBackground) } - s.pdf.Row(contentHeight, func() { - for j, c := range content { + s.pdf.Row(contentHeight+1, func() { + for i, c := range content { cs := c - js := j - hs := float64(len(header)) - sumOyYOffesets := contentMarginTop + s.pdf.GetCurrentOffset() + 2.0 - s.pdf.Col(func() { - s.text.Add(cs, contentTextProp, sumOyYOffesets, float64(js), hs) + s.pdf.Col(tableProp.ContentProp.GridSizes[i], func() { + s.pdf.Text(cs, tableProp.ContentProp.ToTextProp(tableProp.Align, 0, false, 0.0)) }) } }) @@ -130,11 +116,20 @@ func (s *tableList) Create(header []string, contents [][]string, prop ...props.T } } -func (s *tableList) calcLinesHeight(textList []string, textProp props.Text, qtdCols float64) float64 { +func (s *tableList) calcLinesHeight(textList []string, contentProp props.TableListContent, align consts.Align) float64 { maxLines := 1.0 - for _, text := range textList { - qtdLines := float64(s.text.GetLinesQuantity(text, textProp, qtdCols)) + left, _, right, _ := s.pdf.GetPageMargins() + width, _ := s.pdf.GetPageSize() + usefulWidth := float64(width - left - right) + + textProp := contentProp.ToTextProp(align, 0, false, 0.0) + + for i, text := range textList { + gridSize := float64(contentProp.GridSizes[i]) + percentSize := gridSize / consts.MaxGridSum + colWidth := usefulWidth * percentSize + qtdLines := float64(s.text.GetLinesQuantity(text, textProp, colWidth)) if qtdLines > maxLines { maxLines = qtdLines } @@ -145,5 +140,5 @@ func (s *tableList) calcLinesHeight(textList []string, textProp props.Text, qtdC // Font size corrected by the scale factor from "mm" inside gofpdf f.k fontHeight := fontSize / s.font.GetScaleFactor() - return fontHeight*maxLines + 3.0 + return fontHeight * maxLines } diff --git a/internal/tablelist_test.go b/internal/tablelist_test.go index a2c91f89..6b3fbdd5 100644 --- a/internal/tablelist_test.go +++ b/internal/tablelist_test.go @@ -94,6 +94,8 @@ func TestTableList_Create_Happy(t *testing.T) { marotoGrid.On("Row", mock.Anything, mock.Anything).Return(nil) marotoGrid.On("Line", mock.Anything).Return(nil) marotoGrid.On("SetBackgroundColor", mock.Anything).Return(nil) + marotoGrid.On("GetPageMargins").Return(10.0, 10.0, 10.0, 10.0) + marotoGrid.On("GetPageSize").Return(200.0, 600.0) sut := internal.NewTableList(text, font) sut.BindGrid(marotoGrid) @@ -131,6 +133,8 @@ func TestTableList_Create_HappyWithBackgroundColor(t *testing.T) { marotoGrid.On("Row", mock.Anything, mock.Anything).Return(nil) marotoGrid.On("Line", mock.Anything).Return(nil) marotoGrid.On("SetBackgroundColor", mock.Anything).Return(nil) + marotoGrid.On("GetPageMargins").Return(10.0, 10.0, 10.0, 10.0) + marotoGrid.On("GetPageSize").Return(200.0, 600.0) sut := internal.NewTableList(text, font) sut.BindGrid(marotoGrid) @@ -175,6 +179,8 @@ func TestTableList_Create_Happy_Without_Line(t *testing.T) { marotoGrid := &mocks.Maroto{} marotoGrid.On("Row", mock.Anything, mock.Anything).Return(nil) marotoGrid.On("Line", mock.Anything).Return(nil) + marotoGrid.On("GetPageMargins").Return(10.0, 10.0, 10.0, 10.0) + marotoGrid.On("GetPageSize").Return(200.0, 600.0) sut := internal.NewTableList(text, font) sut.BindGrid(marotoGrid) diff --git a/internal/text.go b/internal/text.go index f0ff5560..0a671b9d 100644 --- a/internal/text.go +++ b/internal/text.go @@ -9,8 +9,8 @@ import ( // Text is the abstraction which deals of how to add text inside PDF type Text interface { - Add(text string, fontFamily props.Text, marginTop float64, actualCol float64, qtdCols float64) - GetLinesQuantity(text string, fontFamily props.Text, qtdCols float64) int + Add(text string, cell Cell, textProp props.Text) + GetLinesQuantity(text string, fontFamily props.Text, colWidth float64) int } type text struct { @@ -29,40 +29,42 @@ func NewText(pdf gofpdf.Pdf, math Math, font Font) *text { } // Add a text inside a cell. -func (s *text) Add(text string, textProp props.Text, marginTop float64, actualCol float64, qtdCols float64) { - actualWidthPerCol := s.math.GetWidthPerCol(qtdCols) - +func (s *text) Add(text string, cell Cell, textProp props.Text) { translator := s.pdf.UnicodeTranslatorFromDescriptor("") s.font.SetFont(textProp.Family, textProp.Style, textProp.Size) - // Apply Unicode - textTranslated := translator(text) + // duplicated + _, _, fontSize := s.font.GetFont() + fontHeight := fontSize / s.font.GetScaleFactor() - stringWidth := s.pdf.GetStringWidth(textTranslated) - words := strings.Split(textTranslated, " ") + cell.Y += fontHeight + + // Apply Unicode before calc spaces + unicodeText := translator(text) + + stringWidth := s.pdf.GetStringWidth(unicodeText) + words := strings.Split(unicodeText, " ") accumulateOffsetY := 0.0 // If should add one line - if stringWidth < actualWidthPerCol || textProp.Extrapolate || len(words) == 1 { - s.addLine(textProp, actualCol, actualWidthPerCol, marginTop, stringWidth, textTranslated) + if stringWidth < cell.Width || textProp.Extrapolate || len(words) == 1 { + s.addLine(textProp, cell.X, cell.Width, cell.Y, stringWidth, unicodeText) } else { - lines := s.getLines(words, actualWidthPerCol) + lines := s.getLines(words, cell.Width) for index, line := range lines { lineWidth := s.pdf.GetStringWidth(line) _, _, fontSize := s.font.GetFont() textHeight := fontSize / s.font.GetScaleFactor() - s.addLine(textProp, actualCol, actualWidthPerCol, marginTop+float64(index)*textHeight+accumulateOffsetY, lineWidth, line) + s.addLine(textProp, cell.X, cell.Width, cell.Y+float64(index)*textHeight+accumulateOffsetY, lineWidth, line) accumulateOffsetY += textProp.VerticalPadding } } } // GetLinesQuantity retrieve the quantity of lines which a text will occupy to avoid that text to extrapolate a cell -func (s *text) GetLinesQuantity(text string, textProp props.Text, qtdCols float64) int { - actualWidthPerCol := s.math.GetWidthPerCol(qtdCols) - +func (s *text) GetLinesQuantity(text string, textProp props.Text, colWidth float64) int { translator := s.pdf.UnicodeTranslatorFromDescriptor("") s.font.SetFont(textProp.Family, textProp.Style, textProp.Size) @@ -73,15 +75,15 @@ func (s *text) GetLinesQuantity(text string, textProp props.Text, qtdCols float6 words := strings.Split(textTranslated, " ") // If should add one line - if stringWidth < actualWidthPerCol || textProp.Extrapolate || len(words) == 1 { + if stringWidth < colWidth || textProp.Extrapolate || len(words) == 1 { return 1 } - lines := s.getLines(words, actualWidthPerCol) + lines := s.getLines(words, colWidth) return len(lines) } -func (s *text) getLines(words []string, actualWidthPerCol float64) []string { +func (s *text) getLines(words []string, colWidth float64) []string { currentlySize := 0.0 actualLine := 0 @@ -89,7 +91,7 @@ func (s *text) getLines(words []string, actualWidthPerCol float64) []string { lines = append(lines, "") for _, word := range words { - if s.pdf.GetStringWidth(word+" ")+currentlySize < actualWidthPerCol { + if s.pdf.GetStringWidth(word+" ")+currentlySize < colWidth { lines[actualLine] = lines[actualLine] + word + " " currentlySize += s.pdf.GetStringWidth(word + " ") } else { @@ -103,11 +105,11 @@ func (s *text) getLines(words []string, actualWidthPerCol float64) []string { return lines } -func (s *text) addLine(textProp props.Text, actualCol, actualWidthPerCol, marginTop, stringWidth float64, textTranslated string) { +func (s *text) addLine(textProp props.Text, xColOffset, colWidth, yColOffset, textWidth float64, text string) { left, top, _, _ := s.pdf.GetMargins() if textProp.Align == consts.Left { - s.pdf.Text(actualCol*actualWidthPerCol+left, marginTop+top, textTranslated) + s.pdf.Text(xColOffset+left, yColOffset+top, text) return } @@ -117,7 +119,7 @@ func (s *text) addLine(textProp props.Text, actualCol, actualWidthPerCol, margin modifier = 1 } - dx := (actualWidthPerCol - stringWidth) / modifier + dx := (colWidth - textWidth) / modifier - s.pdf.Text(dx+actualCol*actualWidthPerCol+left, marginTop+top, textTranslated) + s.pdf.Text(dx+xColOffset+left, yColOffset+top, text) } diff --git a/internal/text_test.go b/internal/text_test.go index d3a6b8fd..43ee4f6e 100644 --- a/internal/text_test.go +++ b/internal/text_test.go @@ -26,19 +26,16 @@ func TestText_GetLinesQuantity_WhenStringSmallerThanLimits(t *testing.T) { }) pdf.On("GetStringWidth", mock.Anything).Return(8.0) - math := &mocks.Math{} - math.On("GetWidthPerCol", mock.Anything).Return(10.0) - font := &mocks.Font{} font.On("SetFont", mock.Anything, mock.Anything, mock.Anything).Return(nil) - sut := internal.NewText(pdf, math, font) + sut := internal.NewText(pdf, nil, font) // Act lines := sut.GetLinesQuantity("AnyText With Spaces", props.Text{}, 2) // Assert - assert.Equal(t, lines, 1) + assert.Equal(t, lines, 4) } func TestText_GetLinesQuantity_WhenHasOneWord(t *testing.T) { @@ -49,13 +46,10 @@ func TestText_GetLinesQuantity_WhenHasOneWord(t *testing.T) { }) pdf.On("GetStringWidth", mock.Anything).Return(15.0) - math := &mocks.Math{} - math.On("GetWidthPerCol", mock.Anything).Return(10.0) - font := &mocks.Font{} font.On("SetFont", mock.Anything, mock.Anything, mock.Anything).Return(nil) - sut := internal.NewText(pdf, math, font) + sut := internal.NewText(pdf, nil, font) // Act lines := sut.GetLinesQuantity("OneWord", props.Text{}, 2) @@ -72,13 +66,10 @@ func TestText_GetLinesQuantity_WhenExtrapolate(t *testing.T) { }) pdf.On("GetStringWidth", mock.Anything).Return(15.0) - math := &mocks.Math{} - math.On("GetWidthPerCol", mock.Anything).Return(10.0) - font := &mocks.Font{} font.On("SetFont", mock.Anything, mock.Anything, mock.Anything).Return(nil) - sut := internal.NewText(pdf, math, font) + sut := internal.NewText(pdf, nil, font) // Act lines := sut.GetLinesQuantity("Many words", props.Text{Extrapolate: true}, 2) @@ -116,10 +107,8 @@ func TestText_Add(t *testing.T) { text string align consts.Align pdf func() *mocks.Pdf - math func() *mocks.Math font func() *mocks.Font assertPdf func(t *testing.T, pdf *mocks.Pdf) - assertMath func(t *testing.T, math *mocks.Math) assertFont func(t *testing.T, font *mocks.Font) }{ { @@ -134,11 +123,6 @@ func TestText_Add(t *testing.T) { _pdf.On("UnicodeTranslatorFromDescriptor", mock.Anything).Return(func(value string) string { return value }) return _pdf }, - func() *mocks.Math { - _math := &mocks.Math{} - _math.On("GetWidthPerCol", mock.Anything).Return(123.0) - return _math - }, func() *mocks.Font { _font := &mocks.Font{} _font.On("GetScaleFactor").Return(1.0) @@ -152,11 +136,7 @@ func TestText_Add(t *testing.T) { _pdf.AssertNumberOfCalls(t, "GetMargins", 1) _pdf.AssertNumberOfCalls(t, "Text", 1) - _pdf.AssertCalled(t, "Text", 133.0, 15.0, "TextHelper1") - }, - func(t *testing.T, _math *mocks.Math) { - _math.AssertNumberOfCalls(t, "GetWidthPerCol", 1) - _math.AssertCalled(t, "GetWidthPerCol", 15.0) + _pdf.AssertCalled(t, "Text", 11.0, 16.0, "TextHelper1") }, func(t *testing.T, _font *mocks.Font) { _font.AssertNumberOfCalls(t, "SetFont", 1) @@ -175,11 +155,6 @@ func TestText_Add(t *testing.T) { _pdf.On("UnicodeTranslatorFromDescriptor", mock.Anything).Return(func(value string) string { return value }) return _pdf }, - func() *mocks.Math { - _math := &mocks.Math{} - _math.On("GetWidthPerCol", mock.Anything).Return(123.0) - return _math - }, func() *mocks.Font { _font := &mocks.Font{} _font.On("GetScaleFactor").Return(1.0) @@ -194,11 +169,7 @@ func TestText_Add(t *testing.T) { _pdf.AssertNumberOfCalls(t, "GetMargins", 1) _pdf.AssertNumberOfCalls(t, "Text", 1) - _pdf.AssertCalled(t, "Text", 188.5, 15.0, "TextHelper2") - }, - func(t *testing.T, _math *mocks.Math) { - _math.AssertNumberOfCalls(t, "GetWidthPerCol", 1) - _math.AssertCalled(t, "GetWidthPerCol", 15.0) + _pdf.AssertCalled(t, "Text", 12.5, 16.0, "TextHelper2") }, func(t *testing.T, _font *mocks.Font) { _font.AssertNumberOfCalls(t, "SetFont", 1) @@ -217,11 +188,6 @@ func TestText_Add(t *testing.T) { _pdf.On("UnicodeTranslatorFromDescriptor", mock.Anything).Return(func(value string) string { return value }) return _pdf }, - func() *mocks.Math { - _math := &mocks.Math{} - _math.On("GetWidthPerCol", mock.Anything).Return(123.0) - return _math - }, func() *mocks.Font { _font := &mocks.Font{} _font.On("GetScaleFactor").Return(1.0) @@ -236,11 +202,7 @@ func TestText_Add(t *testing.T) { _pdf.AssertNumberOfCalls(t, "GetMargins", 1) _pdf.AssertNumberOfCalls(t, "Text", 1) - _pdf.AssertCalled(t, "Text", 244.0, 15.0, "TextHelper3") - }, - func(t *testing.T, _math *mocks.Math) { - _math.AssertNumberOfCalls(t, "GetWidthPerCol", 1) - _math.AssertCalled(t, "GetWidthPerCol", 15.0) + _pdf.AssertCalled(t, "Text", 14.0, 16.0, "TextHelper3") }, func(t *testing.T, _font *mocks.Font) { _font.AssertNumberOfCalls(t, "SetFont", 1) @@ -259,11 +221,6 @@ func TestText_Add(t *testing.T) { _pdf.On("UnicodeTranslatorFromDescriptor", mock.Anything).Return(func(value string) string { return value }) return _pdf }, - func() *mocks.Math { - _math := &mocks.Math{} - _math.On("GetWidthPerCol", mock.Anything).Return(123.0) - return _math - }, func() *mocks.Font { _font := &mocks.Font{} _font.On("GetScaleFactor").Return(1.0) @@ -278,11 +235,7 @@ func TestText_Add(t *testing.T) { _pdf.AssertNumberOfCalls(t, "GetMargins", 1) _pdf.AssertNumberOfCalls(t, "Text", 1) - _pdf.AssertCalled(t, "Text", 244.0, 15.0, "TextHelper4") - }, - func(t *testing.T, _math *mocks.Math) { - _math.AssertNumberOfCalls(t, "GetWidthPerCol", 1) - _math.AssertCalled(t, "GetWidthPerCol", 15.0) + _pdf.AssertCalled(t, "Text", 14.0, 16.0, "TextHelper4") }, func(t *testing.T, _font *mocks.Font) { _font.AssertNumberOfCalls(t, "SetFont", 1) @@ -302,11 +255,6 @@ func TestText_Add(t *testing.T) { _pdf.On("UnicodeTranslatorFromDescriptor", mock.Anything).Return(func(value string) string { return value }) return _pdf }, - func() *mocks.Math { - _math := &mocks.Math{} - _math.On("GetWidthPerCol", mock.Anything).Return(123.0) - return _math - }, func() *mocks.Font { _font := &mocks.Font{} _font.On("GetScaleFactor").Return(1.0) @@ -315,16 +263,12 @@ func TestText_Add(t *testing.T) { return _font }, func(t *testing.T, _pdf *mocks.Pdf) { - _pdf.AssertNumberOfCalls(t, "GetStringWidth", 199) + _pdf.AssertNumberOfCalls(t, "GetStringWidth", 275) _pdf.AssertCalled(t, "GetStringWidth", "Lorem Ipsum is simply dummy textá of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum.") - _pdf.AssertNumberOfCalls(t, "GetMargins", 16) + _pdf.AssertNumberOfCalls(t, "GetMargins", 92) - _pdf.AssertNumberOfCalls(t, "Text", 16) - }, - func(t *testing.T, _math *mocks.Math) { - _math.AssertNumberOfCalls(t, "GetWidthPerCol", 1) - _math.AssertCalled(t, "GetWidthPerCol", 15.0) + _pdf.AssertNumberOfCalls(t, "Text", 92) }, func(t *testing.T, _font *mocks.Font) { _font.AssertNumberOfCalls(t, "SetFont", 1) @@ -336,17 +280,21 @@ func TestText_Add(t *testing.T) { for _, c := range cases { // Arrange _pdf := c.pdf() - _math := c.math() _font := c.font() - text := internal.NewText(_pdf, _math, _font) + text := internal.NewText(_pdf, nil, _font) + + cell := internal.Cell{ + X: 1.0, + Y: 5.0, + Width: 15.0, + } // Act - text.Add(c.text, props.Text{Family: consts.Arial, Style: consts.BoldItalic, Size: 16.0, Align: c.align}, 5.0, 1, 15.0) + text.Add(c.text, cell, props.Text{Family: consts.Arial, Style: consts.BoldItalic, Size: 16.0, Align: c.align}) // Assert c.assertPdf(t, _pdf) - c.assertMath(t, _math) c.assertFont(t, _font) } } diff --git a/pkg/consts/consts.go b/pkg/consts/consts.go index ba44dcc5..14a12bff 100644 --- a/pkg/consts/consts.go +++ b/pkg/consts/consts.go @@ -1,5 +1,10 @@ package consts +const ( + // MaxGridSum represents the max value from a cell grid width + MaxGridSum float64 = 12.0 +) + // Family is a representation of a family Font type Family string diff --git a/pkg/pdf/example_test.go b/pkg/pdf/example_test.go index a67f7d17..d73d12b4 100644 --- a/pkg/pdf/example_test.go +++ b/pkg/pdf/example_test.go @@ -1,6 +1,7 @@ package pdf_test import ( + "encoding/base64" "fmt" "github.com/johnfercher/maroto/pkg/color" "github.com/johnfercher/maroto/pkg/consts" @@ -9,7 +10,7 @@ import ( "time" ) -// ExampleNewMaroto demonstratos how to create maroto +// ExampleNewMaroto demonstrates how to create maroto func ExampleNewMaroto() { m := pdf.NewMaroto(consts.Portrait, consts.A4) @@ -19,16 +20,6 @@ func ExampleNewMaroto() { // Do more things and save... } -// ExamplePdfMaroto_Line demonstrates how to draw a line -// separator. -func ExamplePdfMaroto_Line() { - m := pdf.NewMaroto(consts.Portrait, consts.A4) - - m.Line(1.0) - - // Do more things and save... -} - // ExamplePdfMaroto_Row demonstrates how to define a row. func ExamplePdfMaroto_Row() { m := pdf.NewMaroto(consts.Portrait, consts.A4) @@ -41,123 +32,188 @@ func ExamplePdfMaroto_Row() { // Do more things and save... } -// ExamplePdfMaroto_ColSpace demonstrates how to add -// an empty column inside a row. -func ExamplePdfMaroto_ColSpace() { +// ExamplePdfMaroto_Col demonstrates how to add +// an useful column +func ExamplePdfMaroto_Col() { m := pdf.NewMaroto(consts.Portrait, consts.A4) rowHeight := 5.0 + // Warning: The sum of all column gridSize cannot extrapolate 12 + m.Row(rowHeight, func() { + m.Col(12, func() { + // Add Image, Text, Signature, QrCode or Barcode... + }) + }) + + // Warning: The sum of all column gridSize cannot extrapolate 12 m.Row(rowHeight, func() { - m.ColSpace() + m.Col(6, func() { + // Add Image, Text, Signature, QrCode or Barcode... + }) + m.Col(6, func() { + // Add Image, Text, Signature, QrCode or Barcode... + }) }) // Do more things and save... } -// ExamplePdfMaroto_ColSpaces demonstrates how to add -// some empty columns inside a row. -func ExamplePdfMaroto_ColSpaces() { +// ExamplePdfMaroto_ColSpace demonstrates how to add +// an empty column inside a row. +func ExamplePdfMaroto_ColSpace() { m := pdf.NewMaroto(consts.Portrait, consts.A4) rowHeight := 5.0 + // Warning: The sum of all column gridSize cannot extrapolate 12 + m.Row(rowHeight, func() { + m.ColSpace(12) + }) + + // Warning: The sum of all column gridSize cannot extrapolate 12 m.Row(rowHeight, func() { - m.ColSpaces(2) + m.ColSpace(6) + m.ColSpace(6) }) // Do more things and save... } -// ExamplePdfMaroto_Col demonstrates how to add -// an useful column -func ExamplePdfMaroto_Col() { +// ExamplePdfMaroto_RegisterHeader demonstrates how to register header. +func ExamplePdfMaroto_RegisterHeader() { + // For register header in Maroto you need to call method RegisterHeader + // that receives a closure. + // In this closure you are free to set any components you want to compose + // your header. + // In this example there is a two texts with different props and one image. + // It is important to remember that it is recommended to create Row's and + // Col's if necessary. + // You have to register the header immediately after the Maroto + m := pdf.NewMaroto(consts.Portrait, consts.A4) - rowHeight := 5.0 - m.Row(rowHeight, func() { - m.Col(func() { - // Add Image, Text, Signature, QrCode or Barcode... + m.RegisterHeader(func() { + m.Row(10, func() { + m.Col(3, func() { + m.Text("lorem ipsum dolor", props.Text{Align: consts.Left}) + }) + m.Col(3, func() { + _ = m.FileImage("internal/assets/images/frontpage.png") + }) + m.Col(3, func() { + m.Text(time.Now().Format("02-January-2006"), + props.Text{Align: consts.Right}) + }) }) }) - // Do more things and save... + // Do more things or not and save... } -// ExamplePdfMaroto_SetBorder demonstrates how to -// enable the line drawing in every cell -func ExamplePdfMaroto_SetBorder() { - m := pdf.NewMaroto(consts.Portrait, consts.A4) - m.SetBorder(true) - - // Add some Rows, Cols, Lines and etc... - // Here will be drawn borders in every cell +// ExamplePdfMaroto_RegisterFooter demonstrates how to register footer. +func ExamplePdfMaroto_RegisterFooter() { + // For register footer you need to call method RegisterFooter + // that receives a closure. + // In this closure you are free to set any components you want to compose + // your footer. + // In this example there is a signature and a text with right align. + // It is important to remember that it is recommended to create Row's and + // Col's if necessary. + // You have to register the footer immediately after the Maroto - m.SetBorder(false) + m := pdf.NewMaroto(consts.Portrait, consts.A4) - // Add some Rows, Cols, Lines and etc... - // Here will not be drawn borders + m.RegisterFooter(func() { + m.Row(10, func() { + m.Col(6, func() { + m.Signature("lorem ipsum dolor") + }) + m.Col(6, func() { + m.Text(time.Now().Format("02-January-2006"), props.Text{Align: consts.Right}) + }) + }) + }) - // Do more things and save... + // Do more things or not and save... } -// ExamplePdfMaroto_SetBackgroundColor demonstrates how -// to use the SetBackgroundColor method. -func ExamplePdfMaroto_SetBackgroundColor() { - m := pdf.NewMaroto(consts.Portrait, consts.A4) - - m.SetBackgroundColor(color.Color{ - Red: 100, - Green: 20, - Blue: 30, - }) +// ExamplePdfMaroto_TableList demonstrates how to add a table +// with multiple rows and columns. +func ExamplePdfMaroto_TableList() { + // Not passing this table list prop will lead the method to use all the follow values. + // Not passing HeaderProp.Size, make the method use 10. + // Not passing HeaderProp.Family, make the method use arial. + // Not passing HeaderProp.Style, make the method use bold. + // Not passing HeaderProp.GridSizes, make the method use an array with same length + // of header array, the values will be perfectly divided to make all columns with the same size. + // Not passing Align, make the method to use left. + // Not passing ContentProp.Size, make the method use 10. + // Not passing ContentProp.Family, make the method use arial. + // Not passing ContentProp.Style, make the method use normal. + // Not passing Content.GridSizes, make the method use an array with same length + // of content array in the first line, the values will be perfectly divided to make all columns with the same size. + // Not passing HeaderContentSpace, will make the method use 4. - // This Row will be filled with the color - m.Row(20, func() { - m.Col(func() { - // Add components - }) - }) + m := pdf.NewMaroto(consts.Portrait, consts.A4) - m.SetBackgroundColor(color.NewWhite()) + headers := []string{"Header1", "Header2"} + contents := [][]string{ + {"Content1", "Content2"}, + {"Content3", "Content3"}, + } - // This Row will not be filled with the color - m.Row(20, func() { - m.Col(func() { - // Add components - }) + m.TableList(headers, contents, props.TableList{ + HeaderProp: props.TableListContent{ + Family: consts.Arial, + Style: consts.Bold, + Size: 11.0, + GridSizes: []uint{3, 9}, + }, + ContentProp: props.TableListContent{ + Family: consts.Courier, + Style: consts.Normal, + Size: 10.0, + GridSizes: []uint{3, 9}, + }, + Align: consts.Center, + AlternatedBackground: &color.Color{ + Red: 100, + Green: 20, + Blue: 255, + }, + HeaderContentSpace: 10.0, + Line: false, }) // Do more things and save... } -// ExamplePdfMaroto_GetBorder demonstrates how to -// obtain the actual borders status -func ExamplePdfMaroto_GetBorder() { +// ExamplePdfMaroto_Line demonstrates how to draw a line +// separator. +func ExamplePdfMaroto_Line() { m := pdf.NewMaroto(consts.Portrait, consts.A4) - // false - m.GetBorder() - - m.SetBorder(true) - - // true - m.GetBorder() + m.Line(1.0) // Do more things and save... } // ExamplePdfMaroto_Text demonstrates how to add -// a Text inside a col. Passing nil on fontProp makes the method -// use: arial Font, normal style, size 10.0 and align left. -// Not passing family, makes the method use arial. -// Not passing style, makes the method use normal. -// Not passing size, makes the method use 10.0. -// Not passing align, makes the method use left. +// a Text inside a col. func ExamplePdfMaroto_Text() { + // Not passing the text prop will lead to use all the follow default values. + // Not passing family, makes the method use arial. + // Not passing style, makes the method use normal. + // Not passing size, makes the method use 10.0. + // Not passing align, makes the method use left. + // Not passing extrapolate, makes the method use false. + // Top cannot be less than 0. + // VerticalPadding cannot be less than 0. + m := pdf.NewMaroto(consts.Portrait, consts.A4) rowHeight := 5.0 m.Row(rowHeight, func() { - m.Col(func() { + m.Col(12, func() { m.Text("TextContent", props.Text{ Size: 12.0, Style: consts.BoldItalic, @@ -173,22 +229,25 @@ func ExamplePdfMaroto_Text() { // Do more things and save... } -// ExamplePdfMaroto_Signature demonstrates how to add -// a Signature space inside a col. Passing nil on signatureProp make the method -// use: arial Font, normal style and size 10.0. -// Not passing family, make method use arial. -// Not passing style, make method use normal. -// Not passing size, make method use 10.0. -func ExamplePdfMaroto_Signature() { +// ExamplePdfMaroto_FileImage demonstrates how add an Image +// reading from disk. +func ExamplePdfMaroto_FileImage() { + // When props.Rect is nil, method make Image fulfill the context cell. + // When center is true, left and top has no effect. + // Percent represents the width/height of the Image inside the cell, + // Ex: 85, means that Image will have width of 85% of column width. + // When center is false, is possible to manually positioning the Image. + m := pdf.NewMaroto(consts.Portrait, consts.A4) rowHeight := 5.0 m.Row(rowHeight, func() { - m.Col(func() { - m.Signature("LabelForSignature", props.Font{ - Size: 12.0, - Style: consts.BoldItalic, - Family: consts.Courier, + m.Col(12, func() { + _ = m.FileImage("path/Image.jpg", props.Rect{ + Left: 5, + Top: 5, + Center: true, + Percent: 85, }) }) }) @@ -196,92 +255,107 @@ func ExamplePdfMaroto_Signature() { // Do more things and save... } -// ExamplePdfMaroto_TableList demonstrates how to add a table -// with multiple rows and columns -func ExamplePdfMaroto_TableList() { +// ExamplePdfMaroto_Base64Image demonstrates how to add an Image +// from a base64 string. +func ExamplePdfMaroto_Base64Image() { + // When props.Rect is nil, method make Image fulfill the context cell. + // When center is true, left and top has no effect. + // Percent represents the width/height of the Image inside the cell, + // Ex: 85, means that Image will have width of 85% of column width. + // When center is false, is possible to manually positioning the Image. + m := pdf.NewMaroto(consts.Portrait, consts.A4) + rowHeight := 5.0 - headers := []string{"Header1", "Header2"} - contents := [][]string{ - {"Content1", "Content2"}, - {"Content3", "Content3"}, - } + // Bytes of the image loaded + bytes := []byte{1, 2, 3} + base64String := base64.StdEncoding.EncodeToString(bytes) - // 1 Row of header - // 2 Rows of contents - // Each row have 2 columns - m.TableList(headers, contents, props.TableList{ - HeaderProp: props.Font{ - Family: consts.Arial, - Style: consts.Bold, - Size: 11.0, - }, - ContentProp: props.Font{ - Family: consts.Courier, - Style: consts.Normal, - Size: 10.0, - }, - Align: consts.Center, - AlternatedBackground: &color.Color{ - Red: 100, - Green: 20, - Blue: 255, - }, - HeaderContentSpace: 10.0, - Line: false, + m.Row(rowHeight, func() { + m.Col(12, func() { + _ = m.Base64Image(base64String, consts.Png, props.Rect{ + Left: 5, + Top: 5, + Center: true, + Percent: 85, + }) + }) }) // Do more things and save... } -// ExamplePdfMaroto_FileImage demonstrates how add an Image -// reading from disk. -// When props.Rect is nil, method make Image fulfill the context -// cell, based on width and cell from Image and cell. -// When center is true, left and top has no effect. -// Percent represents the width/height of the Image inside the cell: -// Ex: 85, means that Image will have width of 85% of column width. -// When center is false, is possible to manually positioning the Image -// with left and top.AddFromBase64(string, float64, float64, float64, float64, float64, consts.Extension) -func ExamplePdfMaroto_FileImage() { +// ExamplePdfMaroto_Barcode demonstrates how to place a barcode inside +// a Col. +func ExamplePdfMaroto_Barcode() { + // Passing nil on barcode props parameter implies the Barcode fills it's + // context cell depending on it's size. + // It's possible to define the barcode positioning through + // the top and left parameters unless center parameter is true. + // In brief, when center parameter equals true, left and top parameters has no effect. + // Percent parameter represents the Barcode's width/height inside the cell. + // i.e. Percent: 75 means that the Barcode will take up 75% of Col's width + // There is a constraint in the proportion defined, height cannot be greater than 20% of + // the width, and height cannot be smaller than 10% of the width. + + m := pdf.NewMaroto(consts.Portrait, consts.A4) + + // Do a lot of things on rows and columns... + + m.Col(12, func() { + _ = m.Barcode("https://github.com/johnfercher/maroto", props.Barcode{ + Percent: 75, + Proportion: props.Proportion{Width: 50, Height: 10}, + Center: true, + }) + }) + + // do more things... +} + +// ExamplePdfMaroto_QrCode demonstrates how to add +// a QR Code inside a Col. +func ExamplePdfMaroto_QrCode() { + // Passing nil on rectProps makes + // the QR Code fills the context cell. + // When center is true, left and top has no effect. + // Percent represents the width/height of the QR Code inside the cell. + // i.e. 80 means that the QR Code will take up 80% of Col's width + // When center is false, positioning of the QR Code can be done through + // left and top. + m := pdf.NewMaroto(consts.Portrait, consts.A4) rowHeight := 5.0 m.Row(rowHeight, func() { - m.Col(func() { - _ = m.FileImage("path/Image.jpg", props.Rect{ + m.Col(12, func() { + m.QrCode("https://godoc.org/github.com/johnfercher/maroto", props.Rect{ Left: 5, Top: 5, - Center: true, - Percent: 85, + Center: false, + Percent: 80, }) }) }) - - // Do more things and save... } -// ExamplePdfMaroto_Base64Image demonstrates how to add an Image -// from a base64 string. -// When rect properties is nil, the method makes the Image fulfill the context -// cell, based on width and height from Image and cell. -// When center is true, left and top has no effect. -// Percent represents the width/height of the Image inside the cell: -// Ex: 85, means that Image will have width of 85% of column width. -// When center is false, is possible to manually positioning the Image -// with left and top. -func ExamplePdfMaroto_Base64Image() { +// ExamplePdfMaroto_Signature demonstrates how to add +// a Signature space inside a col. +func ExamplePdfMaroto_Signature() { + // Not passing this font prop will lead the method to use all the follow values. + // Not passing family, make method use arial. + // Not passing style, make method use normal. + // Not passing size, make method use 10.0. + m := pdf.NewMaroto(consts.Portrait, consts.A4) rowHeight := 5.0 - base64String := "y7seWGHE923Sdgs..." m.Row(rowHeight, func() { - m.Col(func() { - _ = m.Base64Image(base64String, consts.Png, props.Rect{ - Left: 5, - Top: 5, - Center: true, - Percent: 85, + m.Col(12, func() { + m.Signature("LabelForSignature", props.Font{ + Size: 12.0, + Style: consts.BoldItalic, + Family: consts.Courier, }) }) }) @@ -315,121 +389,68 @@ func ExamplePdfMaroto_Output() { } } -// ExamplePdfMaroto_QrCode demonstrates how to add -// a QR Code inside a Col. Passing nil on rectProps makes -// the QR Code fills the context cell depending on width -// and height of the QR Code and the cell. -// When center is true, left and top has no effect. -// Percent represents the width/height of the QR Code inside the cell. -// i.e. 80 means that the QR Code will take up 80% of Col's width -// When center is false, positioning of the QR Code can be done through -// left and top. -func ExamplePdfMaroto_QrCode() { +// ExamplePdfMaroto_SetBorder demonstrates how to +// enable the line drawing in every cell +func ExamplePdfMaroto_SetBorder() { m := pdf.NewMaroto(consts.Portrait, consts.A4) - rowHeight := 5.0 - - m.Row(rowHeight, func() { - m.Col(func() { - m.QrCode("https://godoc.org/github.com/johnfercher/maroto", props.Rect{ - Left: 5, - Top: 5, - Center: false, - Percent: 80, - }) - }) - }) -} + m.SetBorder(true) -// ExamplePdfMaroto_Barcode demonstrates how to place a barcode inside -// a Col. -// Passing nil on barcode props parameter implies the Barcode fills it's -// context cell depending on it's size. -// It's possible to define the barcode positioning through -// the top and left parameters unless center parameter is true. -// In brief, when center parameter equals true, left and top parameters has no effect. -// Percent parameter represents the Barcode's width/height inside the cell. -// i.e. Percent: 75 means that the Barcode will take up 75% of Col's width -// There is a constraint in the proportion defined, height cannot be greater than 20% of -// the width, and height cannot be smaller than 10% of the width. -func ExamplePdfMaroto_Barcode() { - m := pdf.NewMaroto(consts.Portrait, consts.A4) + // Add some Rows, Cols, Lines and etc... + // Here will be drawn borders in every cell - // Do a lot of things on rows and columns... + m.SetBorder(false) - m.Col(func() { - _ = m.Barcode("https://github.com/johnfercher/maroto", props.Barcode{ - Percent: 75, - Proportion: props.Proportion{Width: 50, Height: 10}, - Center: true, - }) - }) + // Add some Rows, Cols, Lines and etc... + // Here will not be drawn borders - // do more things... + // Do more things and save... } -// ExamplePdfMaroto_RegisterFooter demonstrates how to register footer. -// For register footer in Maroto you need to call method RegisterFooter -// that receives a closure. -// In this closure you are free to set any components you want to compose -// your footer. -// In this example there is a signature and a text with right align. -// It is important to remember that it is recommended to create Row's and -// Col's if necessary. -func ExamplePdfMaroto_RegisterFooter() { +// ExamplePdfMaroto_SetBackgroundColor demonstrates how +// to use the SetBackgroundColor method. +func ExamplePdfMaroto_SetBackgroundColor() { m := pdf.NewMaroto(consts.Portrait, consts.A4) - m.RegisterFooter(func() { - m.Row(10, func() { - m.Col(func() { - m.Signature("lorem ipsum dolor") - }) - m.Col(func() { - m.Text(time.Now().Format("02-January-2006"), props.Text{Align: consts.Right}) - }) - }) + m.SetBackgroundColor(color.Color{ + Red: 100, + Green: 20, + Blue: 30, }) - // Do more things or not and save... -} + // This Row will be filled with the color + m.Row(20, func() { + m.Col(12, func() { + // Add components + }) + }) -// ExamplePdfMaroto_RegisterHeader demonstrates how to register header. -// For register header in Maroto you need to call method RegisterHeader -// that receives a closure. -// In this closure you are free to set any components you want to compose -// your header. -// In this example there is a two texts with different props and one image. -// It is important to remember that it is recommended to create Row's and -// Col's if necessary. -// A tip is to register the header immediately after the Maroto -// instantiation to make the code easier to read. -func ExamplePdfMaroto_RegisterHeader() { - m := pdf.NewMaroto(consts.Portrait, consts.A4) + m.SetBackgroundColor(color.NewWhite()) + // Note: The default value is White (255, 255, 255), if maroto see this value it will ignore not will the cell with any color - m.RegisterHeader(func() { - m.Row(10, func() { - m.Col(func() { - m.Text("lorem ipsum dolor", props.Text{Align: consts.Left}) - }) - m.Col(func() { - _ = m.FileImage("internal/assets/images/frontpage.png") - }) - m.Col(func() { - m.Text(time.Now().Format("02-January-2006"), - props.Text{Align: consts.Right}) - }) + // This Row will not be filled with the color + m.Row(20, func() { + m.Col(12, func() { + // Add components }) }) - // Do more things or not and save... + // Do more things and save... } -// ExamplePdfMaroto_SetPageMargins demonstrates how to set custom page margins. -func ExamplePdfMaroto_SetPageMargins() { +// ExamplePdfMaroto_GetBorder demonstrates how to +// obtain the actual borders status +func ExamplePdfMaroto_GetBorder() { m := pdf.NewMaroto(consts.Portrait, consts.A4) - m.SetPageMargins(10, 60, 10) + // false + _ = m.GetBorder() - // Do more things or not and save... + m.SetBorder(true) + + // true + _ = m.GetBorder() + + // Do more things and save... } // ExamplePdfMaroto_GetPageSize demonstrates how to obtain the current page size (width and height) @@ -480,6 +501,15 @@ func ExamplePdfMaroto_GetCurrentOffset() { // Do more things and save... } +// ExamplePdfMaroto_SetPageMargins demonstrates how to set custom page margins. +func ExamplePdfMaroto_SetPageMargins() { + m := pdf.NewMaroto(consts.Portrait, consts.A4) + + m.SetPageMargins(10, 60, 10) + + // Do more things or not and save... +} + // ExamplePdfMaroto_GetPageMargins demonstrates how to obtain the current page margins func ExamplePdfMaroto_GetPageMargins() { m := pdf.NewMaroto(consts.Portrait, consts.A4) diff --git a/pkg/pdf/pdf.go b/pkg/pdf/pdf.go index 48ba2d7a..84142f57 100644 --- a/pkg/pdf/pdf.go +++ b/pkg/pdf/pdf.go @@ -14,24 +14,13 @@ import ( type Maroto interface { // Grid System Row(height float64, closure func()) - Col(closure func()) - ColSpace() - ColSpaces(qtd int) + Col(width uint, closure func()) + ColSpace(gridSize uint) // Registers RegisterHeader(closure func()) RegisterFooter(closure func()) - // Helpers - SetBorder(on bool) - SetBackgroundColor(color color.Color) - GetBorder() bool - GetPageSize() (float64, float64) - GetCurrentPage() int - GetCurrentOffset() float64 - SetPageMargins(left, top, right float64) - GetPageMargins() (float64, float64, float64, float64) - // Outside Col/Row Components TableList(header []string, contents [][]string, prop ...props.TableList) Line(spaceHeight float64) @@ -47,6 +36,16 @@ type Maroto interface { // File System OutputFileAndClose(filePathName string) error Output() (bytes.Buffer, error) + + // Helpers + SetBorder(on bool) + SetBackgroundColor(color color.Color) + GetBorder() bool + GetPageSize() (width float64, height float64) + GetCurrentPage() int + GetCurrentOffset() float64 + SetPageMargins(left, top, right float64) + GetPageMargins() (left float64, top float64, right float64, bottom float64) } // PdfMaroto is the principal structure which implements Maroto abstraction @@ -63,9 +62,9 @@ type PdfMaroto struct { offsetY float64 marginTop float64 rowHeight float64 - rowColCount float64 + xColOffset float64 + colWidth float64 backgroundColor color.Color - colsClosures []func() headerClosure func() footerClosure func() footerHeight float64 @@ -127,42 +126,6 @@ func (s *PdfMaroto) RegisterHeader(closure func()) { s.headerClosure = closure } -func (s *PdfMaroto) footer() { - backgroundColor := s.backgroundColor - s.SetBackgroundColor(color.NewWhite()) - - _, pageHeight := s.Pdf.GetPageSize() - _, top, _, bottom := s.Pdf.GetMargins() - - totalOffsetY := int(s.offsetY + s.footerHeight) - maxOffsetPage := int(pageHeight - bottom - top) - - s.Row(float64(maxOffsetPage-totalOffsetY), func() { - s.ColSpace() - }) - - if s.footerClosure != nil { - s.footerClosure() - } - - s.SetBackgroundColor(backgroundColor) -} - -func (s *PdfMaroto) header() { - backgroundColor := s.backgroundColor - s.SetBackgroundColor(color.NewWhite()) - - s.Row(s.marginTop, func() { - s.ColSpace() - }) - - if s.headerClosure != nil { - s.headerClosure() - } - - s.SetBackgroundColor(backgroundColor) -} - // RegisterFooter define a sequence of Rows, Lines ou TableLists // which will be added in every new page func (s *PdfMaroto) RegisterFooter(closure func()) { @@ -190,18 +153,20 @@ func (s *PdfMaroto) GetCurrentOffset() float64 { // SetPageMargins overrides default margins (10,10,10) // the new page margin will affect all PDF pages func (s *PdfMaroto) SetPageMargins(left, top, right float64) { - if top <= 10 { - s.Pdf.SetMargins(left, top, right) - } else { + if top > 10 { s.marginTop = top - 10 - s.Pdf.SetMargins(left, 10, right) } + + s.Pdf.SetMargins(left, 10.0, right) } // GetPageMargins returns the set page margins. Comes in order of Left, Top, Right, Bottom // Default page margins is left: 10, top: 10, right: 10 func (s *PdfMaroto) GetPageMargins() (left float64, top float64, right float64, bottom float64) { - return s.Pdf.GetMargins() + left, top, right, bottom = s.Pdf.GetMargins() + top += s.marginTop + + return } // Signature add a space for a signature inside a cell, @@ -214,10 +179,14 @@ func (s *PdfMaroto) Signature(label string, prop ...props.Font) { signProp.MakeValid() - qtdCols := float64(len(s.colsClosures)) - sumOfYOffsets := s.offsetY + s.rowHeight + cell := internal.Cell{ + X: s.xColOffset, + Y: s.offsetY, + Width: s.colWidth, + Height: s.rowHeight, + } - s.SignHelper.AddSpaceFor(label, signProp.ToTextProp(consts.Center, 0.0, false, 0), qtdCols, sumOfYOffsets, s.rowColCount) + s.SignHelper.AddSpaceFor(label, cell, signProp.ToTextProp(consts.Center, 0.0, false, 0)) } // TableList create a table with multiple rows and columns. @@ -247,7 +216,7 @@ func (s *PdfMaroto) GetBorder() bool { } // GetPageSize return the actual page size -func (s *PdfMaroto) GetPageSize() (float64, float64) { +func (s *PdfMaroto) GetPageSize() (width float64, height float64) { return s.Pdf.GetPageSize() } @@ -255,7 +224,7 @@ func (s *PdfMaroto) GetPageSize() (float64, float64) { // in the currently row. func (s *PdfMaroto) Line(spaceHeight float64) { s.Row(spaceHeight, func() { - s.Col(func() { + s.Col(0, func() { width, _ := s.Pdf.GetPageSize() left, top, right, _ := s.Pdf.GetMargins() @@ -304,49 +273,40 @@ func (s *PdfMaroto) Row(height float64, closure func()) { } s.rowHeight = height - s.rowColCount = 0 + s.xColOffset = 0 - // This closure has only maroto.Cols, which are - // not executed firstly, they are added to colsClosures - // and this enable us to know how many cols will be added - // and calculate the width from the cells + // This closure has the Cols to be executed closure() - // Execute the codes inside the Cols - for _, colClosure := range s.colsClosures { - colClosure() - } - - s.colsClosures = nil s.offsetY += s.rowHeight s.Pdf.Ln(s.rowHeight) } // Col create a column inside a row and enable to add // components inside. -func (s *PdfMaroto) Col(closure func()) { - s.colsClosures = append(s.colsClosures, func() { - widthPerCol := s.Math.GetWidthPerCol(float64(len(s.colsClosures))) - s.createColSpace(widthPerCol) - closure() - s.rowColCount++ - }) -} +func (s *PdfMaroto) Col(width uint, closure func()) { + if width == 0 { + width = 12 + } -// ColSpace create an empty column inside a row. -func (s *PdfMaroto) ColSpace() { - s.colsClosures = append(s.colsClosures, func() { - widthPerCol := s.Math.GetWidthPerCol(float64(len(s.colsClosures))) - s.createColSpace(widthPerCol) - s.rowColCount++ - }) + percent := float64(width) / float64(12) + + pageWidth, _ := s.Pdf.GetPageSize() + left, _, right, _ := s.Pdf.GetMargins() + widthPerCol := (pageWidth - right - left) * percent + + s.colWidth = widthPerCol + s.createColSpace(widthPerCol) + + // This closure has the components to be executed + closure() + + s.xColOffset += s.colWidth } -// ColSpaces create some empty columns inside a row. -func (s *PdfMaroto) ColSpaces(qtd int) { - for i := 0; i < qtd; i++ { - s.ColSpace() - } +// ColSpace create an empty column inside a row. +func (s *PdfMaroto) ColSpace(gridSize uint) { + s.Col(gridSize, func() {}) } // Text create a text inside a cell. @@ -362,9 +322,14 @@ func (s *PdfMaroto) Text(text string, prop ...props.Text) { textProp.Top = s.rowHeight } - sumOfYOffsets := textProp.Top + s.offsetY + cell := internal.Cell{ + X: s.xColOffset, + Y: s.offsetY + textProp.Top, + Width: s.colWidth, + Height: 0, + } - s.TextHelper.Add(text, textProp, sumOfYOffsets, s.rowColCount, float64(len(s.colsClosures))) + s.TextHelper.Add(text, cell, textProp) } // FileImage add an Image reading from disk inside a cell. @@ -377,10 +342,14 @@ func (s *PdfMaroto) FileImage(filePathName string, prop ...props.Rect) error { rectProp.MakeValid() - qtdCols := float64(len(s.colsClosures)) - sumOfyOffsets := s.offsetY + rectProp.Top + cell := internal.Cell{ + X: s.xColOffset, + Y: s.offsetY + rectProp.Top, + Width: s.colWidth, + Height: s.rowHeight, + } - return s.Image.AddFromFile(filePathName, sumOfyOffsets, s.rowColCount, qtdCols, s.rowHeight, rectProp) + return s.Image.AddFromFile(filePathName, cell, rectProp) } // Base64Image add an Image reading byte slices inside a cell. @@ -393,26 +362,14 @@ func (s *PdfMaroto) Base64Image(base64 string, extension consts.Extension, prop rectProp.MakeValid() - qtdCols := float64(len(s.colsClosures)) - sumOfyOffsets := s.offsetY + rectProp.Top - - return s.Image.AddFromBase64(base64, sumOfyOffsets, s.rowColCount, qtdCols, s.rowHeight, rectProp, extension) -} - -// OutputFileAndClose save pdf in disk. -func (s *PdfMaroto) OutputFileAndClose(filePathName string) (err error) { - s.drawLastFooter() - err = s.Pdf.OutputFileAndClose(filePathName) - - return -} + cell := internal.Cell{ + X: s.xColOffset, + Y: s.offsetY + rectProp.Top, + Width: s.colWidth, + Height: s.rowHeight, + } -// Output extract PDF in byte slices -func (s *PdfMaroto) Output() (bytes.Buffer, error) { - s.drawLastFooter() - var buffer bytes.Buffer - err := s.Pdf.Output(&buffer) - return buffer, err + return s.Image.AddFromBase64(base64, cell, rectProp, extension) } // Barcode create an barcode inside a cell. @@ -424,10 +381,14 @@ func (s *PdfMaroto) Barcode(code string, prop ...props.Barcode) (err error) { barcodeProp.MakeValid() - qtdCols := float64(len(s.colsClosures)) - sumOfyOffsets := s.offsetY + barcodeProp.Top + cell := internal.Cell{ + X: s.xColOffset, + Y: s.offsetY + barcodeProp.Top, + Width: s.colWidth, + Height: s.rowHeight, + } - err = s.Code.AddBar(code, sumOfyOffsets, s.rowColCount, qtdCols, s.rowHeight, barcodeProp) + err = s.Code.AddBar(code, cell, barcodeProp) return } @@ -441,9 +402,30 @@ func (s *PdfMaroto) QrCode(code string, prop ...props.Rect) { rectProp.MakeValid() - qtdCols := float64(len(s.colsClosures)) - sumOfyOffsets := s.offsetY + rectProp.Top - s.Code.AddQr(code, sumOfyOffsets, s.rowColCount, qtdCols, s.rowHeight, rectProp) + cell := internal.Cell{ + X: s.xColOffset, + Y: s.offsetY + rectProp.Top, + Width: s.colWidth, + Height: s.rowHeight, + } + + s.Code.AddQr(code, cell, rectProp) +} + +// OutputFileAndClose save pdf in disk. +func (s *PdfMaroto) OutputFileAndClose(filePathName string) (err error) { + s.drawLastFooter() + err = s.Pdf.OutputFileAndClose(filePathName) + + return +} + +// Output extract PDF in byte slices +func (s *PdfMaroto) Output() (bytes.Buffer, error) { + s.drawLastFooter() + var buffer bytes.Buffer + err := s.Pdf.Output(&buffer) + return buffer, err } func (s *PdfMaroto) createColSpace(actualWidthPerCol float64) { @@ -468,3 +450,39 @@ func (s *PdfMaroto) drawLastFooter() { } } } + +func (s *PdfMaroto) footer() { + backgroundColor := s.backgroundColor + s.SetBackgroundColor(color.NewWhite()) + + _, pageHeight := s.Pdf.GetPageSize() + _, top, _, bottom := s.Pdf.GetMargins() + + totalOffsetY := int(s.offsetY + s.footerHeight) + maxOffsetPage := int(pageHeight - bottom - top) + + s.Row(float64(maxOffsetPage-totalOffsetY), func() { + s.ColSpace(12) + }) + + if s.footerClosure != nil { + s.footerClosure() + } + + s.SetBackgroundColor(backgroundColor) +} + +func (s *PdfMaroto) header() { + backgroundColor := s.backgroundColor + s.SetBackgroundColor(color.NewWhite()) + + s.Row(s.marginTop, func() { + s.ColSpace(12) + }) + + if s.headerClosure != nil { + s.headerClosure() + } + + s.SetBackgroundColor(backgroundColor) +} diff --git a/pkg/pdf/pdf_test.go b/pkg/pdf/pdf_test.go index f0f98b18..8fd0eaf1 100644 --- a/pkg/pdf/pdf_test.go +++ b/pkg/pdf/pdf_test.go @@ -3,6 +3,7 @@ package pdf_test import ( "bytes" "fmt" + "github.com/johnfercher/maroto/internal" "github.com/johnfercher/maroto/pkg/color" "testing" @@ -186,7 +187,7 @@ func TestPdfMaroto_Signature(t *testing.T) { func(m pdf.Maroto) { m.RegisterFooter(func() { m.Row(40, func() { - m.Col(func() { + m.Col(12, func() { m.Signature("Signature1") }) }) @@ -202,11 +203,11 @@ func TestPdfMaroto_Signature(t *testing.T) { }, func(t *testing.T, signature *mocks.Signature) { signature.AssertNumberOfCalls(t, "AddSpaceFor", 1) - signature.AssertCalled(t, "AddSpaceFor", "Signature1", props.Text{Family: consts.Arial, Style: consts.Bold, Size: 8.0, Align: consts.Center}, 1.0, 40.0, 0.0) + signature.AssertCalled(t, "AddSpaceFor", "Signature1", internal.Cell{X: 0.0, Y: 0.0, Width: 80.0, Height: 40.0}, props.Text{Family: consts.Arial, Style: consts.Bold, Size: 8.0, Align: consts.Center}) }, func(m pdf.Maroto) { m.Row(40, func() { - m.Col(func() { + m.Col(0, func() { m.Signature("Signature1") }) }) @@ -221,12 +222,12 @@ func TestPdfMaroto_Signature(t *testing.T) { }, func(t *testing.T, signature *mocks.Signature) { signature.AssertNumberOfCalls(t, "AddSpaceFor", 2) - signature.AssertCalled(t, "AddSpaceFor", "Signature2", props.Text{Family: consts.Arial, Style: consts.Bold, Size: 8.0, Align: consts.Center}, 1.0, 40.0, 0.0) - signature.AssertCalled(t, "AddSpaceFor", "Signature3", props.Text{Family: consts.Courier, Style: consts.BoldItalic, Size: 9.5, Align: consts.Center}, 1.0, 40.0, 0.0) + signature.AssertCalled(t, "AddSpaceFor", "Signature2", internal.Cell{X: 0.0, Y: 0.0, Width: 80.0, Height: 40.0}, props.Text{Family: consts.Arial, Style: consts.Bold, Size: 8.0, Align: consts.Center}) + signature.AssertCalled(t, "AddSpaceFor", "Signature3", internal.Cell{X: 0.0, Y: 0.0, Width: 80.0, Height: 40.0}, props.Text{Family: consts.Courier, Style: consts.BoldItalic, Size: 9.5, Align: consts.Center}) }, func(m pdf.Maroto) { m.Row(40, func() { - m.Col(func() { + m.Col(12, func() { m.Signature("Signature2") m.Signature("Signature3", props.Font{ Family: consts.Courier, @@ -246,15 +247,15 @@ func TestPdfMaroto_Signature(t *testing.T) { }, func(t *testing.T, signature *mocks.Signature) { signature.AssertNumberOfCalls(t, "AddSpaceFor", 2) - signature.AssertCalled(t, "AddSpaceFor", "Signature4", props.Text{Family: consts.Arial, Style: consts.Bold, Size: 8.0, Align: consts.Center}, 2.0, 40.0, 0.0) - signature.AssertCalled(t, "AddSpaceFor", "Signature5", props.Text{Family: consts.Courier, Style: consts.BoldItalic, Size: 9.5, Align: consts.Center}, 2.0, 40.0, 1.0) + signature.AssertCalled(t, "AddSpaceFor", "Signature4", internal.Cell{X: 0.0, Y: 0.0, Width: 40.0, Height: 40.0}, props.Text{Family: consts.Arial, Style: consts.Bold, Size: 8.0, Align: consts.Center}) + signature.AssertCalled(t, "AddSpaceFor", "Signature5", internal.Cell{X: 40.0, Y: 0.0, Width: 40.0, Height: 40.0}, props.Text{Family: consts.Courier, Style: consts.BoldItalic, Size: 9.5, Align: consts.Center}) }, func(m pdf.Maroto) { m.Row(40, func() { - m.Col(func() { + m.Col(6, func() { m.Signature("Signature4") }) - m.Col(func() { + m.Col(6, func() { m.Signature("Signature5", props.Font{ Family: consts.Courier, Style: consts.BoldItalic, @@ -273,17 +274,17 @@ func TestPdfMaroto_Signature(t *testing.T) { }, func(t *testing.T, signature *mocks.Signature) { signature.AssertNumberOfCalls(t, "AddSpaceFor", 2) - signature.AssertCalled(t, "AddSpaceFor", "Signature6", props.Text{Family: consts.Arial, Style: consts.Bold, Size: 8.0, Align: consts.Center}, 1.0, 40.0, 0.0) - signature.AssertCalled(t, "AddSpaceFor", "Signature7", props.Text{Family: consts.Courier, Style: consts.BoldItalic, Size: 9.5, Align: consts.Center}, 1.0, 80.0, 0.0) + signature.AssertCalled(t, "AddSpaceFor", "Signature6", internal.Cell{X: 0.0, Y: 0.0, Width: 40.0, Height: 40.0}, props.Text{Family: consts.Arial, Style: consts.Bold, Size: 8.0, Align: consts.Center}) + signature.AssertCalled(t, "AddSpaceFor", "Signature7", internal.Cell{X: 0.0, Y: 40.0, Width: 40.0, Height: 40.0}, props.Text{Family: consts.Courier, Style: consts.BoldItalic, Size: 9.5, Align: consts.Center}) }, func(m pdf.Maroto) { m.Row(40, func() { - m.Col(func() { + m.Col(6, func() { m.Signature("Signature6") }) }) m.Row(40, func() { - m.Col(func() { + m.Col(6, func() { m.Signature("Signature7", props.Font{ Family: consts.Courier, Style: consts.BoldItalic, @@ -321,11 +322,11 @@ func TestPdfMaroto_Text(t *testing.T) { "One text inside one column, inside a row, without props", func(t *testing.T, text *mocks.Text) { text.AssertNumberOfCalls(t, "Add", 1) - text.AssertCalled(t, "Add", "Text1", props.Text{Family: consts.Arial, Style: consts.Normal, Align: consts.Left, Top: 0.0, Extrapolate: false, Size: 10.0}, 0.0, 0.0, 1.0) + text.AssertCalled(t, "Add", "Text1", internal.Cell{X: 0.0, Y: 0.0, Width: 80.0, Height: 0.0}, props.Text{Family: consts.Arial, Style: consts.Normal, Align: consts.Left, Top: 0.0, Extrapolate: false, Size: 10.0}) }, func(m pdf.Maroto) { m.Row(40, func() { - m.Col(func() { + m.Col(12, func() { m.Text("Text1") }) }) @@ -335,12 +336,12 @@ func TestPdfMaroto_Text(t *testing.T) { "Two different text inside one colum, inside one row", func(t *testing.T, text *mocks.Text) { text.AssertNumberOfCalls(t, "Add", 2) - text.AssertCalled(t, "Add", "Text2", props.Text{Family: consts.Arial, Style: consts.Normal, Align: consts.Left, Top: 0.0, Extrapolate: false, Size: 10.0}, 0.0, 0.0, 1.0) - text.AssertCalled(t, "Add", "Text3", props.Text{Family: consts.Courier, Style: consts.BoldItalic, Align: consts.Center, Top: 5.0, Extrapolate: false, Size: 9.5}, 5.0, 0.0, 1.0) + text.AssertCalled(t, "Add", "Text2", internal.Cell{X: 0.0, Y: 0.0, Width: 80.0, Height: 0.0}, props.Text{Family: consts.Arial, Style: consts.Normal, Align: consts.Left, Top: 0.0, Extrapolate: false, Size: 10.0}) + text.AssertCalled(t, "Add", "Text3", internal.Cell{X: 0.0, Y: 5.0, Width: 80.0, Height: 0.0}, props.Text{Family: consts.Courier, Style: consts.BoldItalic, Align: consts.Center, Top: 5.0, Extrapolate: false, Size: 9.5}) }, func(m pdf.Maroto) { m.Row(40, func() { - m.Col(func() { + m.Col(12, func() { m.Text("Text2") m.Text("Text3", props.Text{ Family: consts.Courier, @@ -357,15 +358,15 @@ func TestPdfMaroto_Text(t *testing.T) { "Two different text with different columns, inside one row", func(t *testing.T, text *mocks.Text) { text.AssertNumberOfCalls(t, "Add", 2) - text.AssertCalled(t, "Add", "Text4", props.Text{Family: consts.Arial, Style: consts.Normal, Align: consts.Left, Top: 0.0, Extrapolate: false, Size: 10.0}, 0.0, 0.0, 2.0) - text.AssertCalled(t, "Add", "Text5", props.Text{Family: consts.Helvetica, Style: consts.Italic, Align: consts.Center, Top: 4.4, Extrapolate: false, Size: 8.5}, 4.4, 1.0, 2.0) + text.AssertCalled(t, "Add", "Text4", internal.Cell{X: 0.0, Y: 0.0, Width: 80.0, Height: 0.0}, props.Text{Family: consts.Arial, Style: consts.Normal, Align: consts.Left, Top: 0.0, Extrapolate: false, Size: 10.0}) + text.AssertCalled(t, "Add", "Text5", internal.Cell{X: 80.0, Y: 4.4, Width: 80.0, Height: 0.0}, props.Text{Family: consts.Helvetica, Style: consts.Italic, Align: consts.Center, Top: 4.4, Extrapolate: false, Size: 8.5}) }, func(m pdf.Maroto) { m.Row(40, func() { - m.Col(func() { + m.Col(12, func() { m.Text("Text4") }) - m.Col(func() { + m.Col(12, func() { m.Text("Text5", props.Text{ Family: consts.Helvetica, Style: consts.Italic, @@ -381,17 +382,17 @@ func TestPdfMaroto_Text(t *testing.T) { "Two different text with different columns, inside one row", func(t *testing.T, text *mocks.Text) { text.AssertNumberOfCalls(t, "Add", 2) - text.AssertCalled(t, "Add", "Text6", props.Text{Family: consts.Arial, Style: consts.Normal, Align: consts.Left, Top: 0.0, Extrapolate: false, Size: 10.0}, 0.0, 0.0, 1.0) - text.AssertCalled(t, "Add", "Text7", props.Text{Family: consts.Courier, Style: consts.BoldItalic, Align: consts.Left, Top: 0.0, Extrapolate: false, Size: 9.5}, 40.0, 0.0, 1.0) + text.AssertCalled(t, "Add", "Text6", internal.Cell{X: 0.0, Y: 0.0, Width: 80.0, Height: 0.0}, props.Text{Family: consts.Arial, Style: consts.Normal, Align: consts.Left, Top: 0.0, Extrapolate: false, Size: 10.0}) + text.AssertCalled(t, "Add", "Text7", internal.Cell{X: 0.0, Y: 40.0, Width: 80.0, Height: 0.0}, props.Text{Family: consts.Courier, Style: consts.BoldItalic, Align: consts.Left, Top: 0.0, Extrapolate: false, Size: 9.5}) }, func(m pdf.Maroto) { m.Row(40, func() { - m.Col(func() { + m.Col(12, func() { m.Text("Text6") }) }) m.Row(40, func() { - m.Col(func() { + m.Col(12, func() { m.Text("Text7", props.Text{ Family: consts.Courier, Style: consts.BoldItalic, @@ -405,11 +406,11 @@ func TestPdfMaroto_Text(t *testing.T) { "When top is greater than row height", func(t *testing.T, text *mocks.Text) { text.AssertNumberOfCalls(t, "Add", 1) - text.AssertCalled(t, "Add", "Text8", props.Text{Family: consts.Arial, Align: consts.Left, Top: 40.0, Extrapolate: false, Size: 10.0}, 40.0, 0.0, 1.0) + text.AssertCalled(t, "Add", "Text8", internal.Cell{X: 0.0, Y: 40.0, Width: 80.0, Height: 0.0}, props.Text{Family: consts.Arial, Align: consts.Left, Top: 40.0, Extrapolate: false, Size: 10.0}) }, func(m pdf.Maroto) { m.Row(40, func() { - m.Col(func() { + m.Col(12, func() { m.Text("Text8", props.Text{ Top: 50, }) @@ -451,7 +452,7 @@ func TestPdfMaroto_FileImage(t *testing.T) { }, func(t *testing.T, image *mocks.Image) { image.AssertNumberOfCalls(t, "AddFromFile", 1) - image.AssertCalled(t, "AddFromFile", "Image1", 0.0, 0.0, 1.0, 20.0, props.Rect{ + image.AssertCalled(t, "AddFromFile", "Image1", internal.Cell{X: 0.0, Y: 0.0, Width: 80.0, Height: 20.0}, props.Rect{ Left: 0, Top: 0, Percent: 100.0, @@ -460,7 +461,7 @@ func TestPdfMaroto_FileImage(t *testing.T) { }, func(m pdf.Maroto) { m.Row(20, func() { - m.Col(func() { + m.Col(12, func() { _ = m.FileImage("Image1") }) }) @@ -475,13 +476,13 @@ func TestPdfMaroto_FileImage(t *testing.T) { }, func(t *testing.T, image *mocks.Image) { image.AssertNumberOfCalls(t, "AddFromFile", 2) - image.AssertCalled(t, "AddFromFile", "Image2", 4.0, 0.0, 1.0, 20.0, props.Rect{ + image.AssertCalled(t, "AddFromFile", "Image2", internal.Cell{X: 0.0, Y: 4.0, Width: 80.0, Height: 20.0}, props.Rect{ Left: 2, Top: 4, Percent: 40, Center: false, }) - image.AssertCalled(t, "AddFromFile", "Image3", 0.0, 0.0, 1.0, 20.0, props.Rect{ + image.AssertCalled(t, "AddFromFile", "Image3", internal.Cell{X: 0.0, Y: 0.0, Width: 80.0, Height: 20.0}, props.Rect{ Left: 0, Top: 0, Percent: 40, @@ -490,7 +491,7 @@ func TestPdfMaroto_FileImage(t *testing.T) { }, func(m pdf.Maroto) { m.Row(20, func() { - m.Col(func() { + m.Col(0, func() { _ = m.FileImage("Image2", props.Rect{ Left: 2.0, Top: 4.0, @@ -513,13 +514,13 @@ func TestPdfMaroto_FileImage(t *testing.T) { }, func(t *testing.T, image *mocks.Image) { image.AssertNumberOfCalls(t, "AddFromFile", 2) - image.AssertCalled(t, "AddFromFile", "Image4", 4.5, 0.0, 2.0, 20.0, props.Rect{ + image.AssertCalled(t, "AddFromFile", "Image4", internal.Cell{X: 0.0, Y: 4.5, Width: 80.0, Height: 20.0}, props.Rect{ Left: 4, Top: 4.5, Percent: 55, Center: false, }) - image.AssertCalled(t, "AddFromFile", "Image5", 0.0, 1.0, 2.0, 20.0, props.Rect{ + image.AssertCalled(t, "AddFromFile", "Image5", internal.Cell{X: 80.0, Y: 0.0, Width: 80.0, Height: 20.0}, props.Rect{ Left: 0, Top: 0, Percent: 53, @@ -528,14 +529,14 @@ func TestPdfMaroto_FileImage(t *testing.T) { }, func(m pdf.Maroto) { m.Row(20, func() { - m.Col(func() { + m.Col(0, func() { _ = m.FileImage("Image4", props.Rect{ Left: 4.0, Top: 4.5, Percent: 55.0, }) }) - m.Col(func() { + m.Col(0, func() { _ = m.FileImage("Image5", props.Rect{ Percent: 53.0, Center: true, @@ -553,13 +554,13 @@ func TestPdfMaroto_FileImage(t *testing.T) { }, func(t *testing.T, image *mocks.Image) { image.AssertNumberOfCalls(t, "AddFromFile", 2) - image.AssertCalled(t, "AddFromFile", "Image6", 8.5, 0.0, 1.0, 20.0, props.Rect{ + image.AssertCalled(t, "AddFromFile", "Image6", internal.Cell{X: 0.0, Y: 8.5, Width: 80.0, Height: 20.0}, props.Rect{ Left: 7, Top: 8.5, Percent: 66, Center: false, }) - image.AssertCalled(t, "AddFromFile", "Image7", 20.0, 0.0, 1.0, 20.0, props.Rect{ + image.AssertCalled(t, "AddFromFile", "Image7", internal.Cell{X: 0.0, Y: 20.0, Width: 80.0, Height: 20.0}, props.Rect{ Left: 0, Top: 0, Percent: 98, @@ -568,7 +569,7 @@ func TestPdfMaroto_FileImage(t *testing.T) { }, func(m pdf.Maroto) { m.Row(20, func() { - m.Col(func() { + m.Col(12, func() { _ = m.FileImage("Image6", props.Rect{ Left: 7.0, Top: 8.5, @@ -577,7 +578,7 @@ func TestPdfMaroto_FileImage(t *testing.T) { }) }) m.Row(20, func() { - m.Col(func() { + m.Col(12, func() { _ = m.FileImage("Image7", props.Rect{ Percent: 98.0, Center: true, @@ -621,7 +622,7 @@ func TestPdfMaroto_Base64Image(t *testing.T) { }, func(t *testing.T, image *mocks.Image) { image.AssertNumberOfCalls(t, "AddFromBase64", 1) - image.AssertCalled(t, "AddFromBase64", "Image1", 0.0, 0.0, 1.0, 20.0, props.Rect{ + image.AssertCalled(t, "AddFromBase64", "Image1", internal.Cell{X: 0.0, Y: 0.0, Width: 80.0, Height: 20.0}, props.Rect{ Left: 0, Top: 0, Percent: 100, @@ -630,7 +631,7 @@ func TestPdfMaroto_Base64Image(t *testing.T) { }, func(m pdf.Maroto) { m.Row(20, func() { - m.Col(func() { + m.Col(12, func() { _ = m.Base64Image("Image1", consts.Jpg) }) }) @@ -645,13 +646,13 @@ func TestPdfMaroto_Base64Image(t *testing.T) { }, func(t *testing.T, image *mocks.Image) { image.AssertNumberOfCalls(t, "AddFromBase64", 2) - image.AssertCalled(t, "AddFromBase64", "Image2", 4.0, 0.0, 1.0, 20.0, props.Rect{ + image.AssertCalled(t, "AddFromBase64", "Image2", internal.Cell{X: 0.0, Y: 4.0, Width: 80.0, Height: 20.0}, props.Rect{ Left: 2, Top: 4, Percent: 40, Center: false, }, consts.Png) - image.AssertCalled(t, "AddFromBase64", "Image3", 0.0, 0.0, 1.0, 20.0, props.Rect{ + image.AssertCalled(t, "AddFromBase64", "Image3", internal.Cell{X: 0.0, Y: 0.0, Width: 80.0, Height: 20.0}, props.Rect{ Left: 0, Top: 0, Percent: 40, @@ -660,7 +661,7 @@ func TestPdfMaroto_Base64Image(t *testing.T) { }, func(m pdf.Maroto) { m.Row(20, func() { - m.Col(func() { + m.Col(0, func() { _ = m.Base64Image("Image2", consts.Png, props.Rect{ Left: 2.0, Top: 4.0, @@ -683,13 +684,13 @@ func TestPdfMaroto_Base64Image(t *testing.T) { }, func(t *testing.T, image *mocks.Image) { image.AssertNumberOfCalls(t, "AddFromBase64", 2) - image.AssertCalled(t, "AddFromBase64", "Image4", 4.5, 0.0, 2.0, 20.0, props.Rect{ + image.AssertCalled(t, "AddFromBase64", "Image4", internal.Cell{X: 0.0, Y: 4.5, Width: 40.0, Height: 20.0}, props.Rect{ Left: 4, Top: 4.5, Percent: 55, Center: false, }, consts.Png) - image.AssertCalled(t, "AddFromBase64", "Image5", 0.0, 1.0, 2.0, 20.0, props.Rect{ + image.AssertCalled(t, "AddFromBase64", "Image5", internal.Cell{X: 40.0, Y: 0.0, Width: 40.0, Height: 20.0}, props.Rect{ Left: 0, Top: 0, Percent: 53, @@ -698,14 +699,14 @@ func TestPdfMaroto_Base64Image(t *testing.T) { }, func(m pdf.Maroto) { m.Row(20, func() { - m.Col(func() { + m.Col(6, func() { _ = m.Base64Image("Image4", consts.Png, props.Rect{ Left: 4.0, Top: 4.5, Percent: 55.0, }) }) - m.Col(func() { + m.Col(6, func() { _ = m.Base64Image("Image5", consts.Jpg, props.Rect{ Percent: 53.0, Center: true, @@ -723,13 +724,13 @@ func TestPdfMaroto_Base64Image(t *testing.T) { }, func(t *testing.T, image *mocks.Image) { image.AssertNumberOfCalls(t, "AddFromBase64", 2) - image.AssertCalled(t, "AddFromBase64", "Image6", 8.5, 0.0, 1.0, 20.0, props.Rect{ + image.AssertCalled(t, "AddFromBase64", "Image6", internal.Cell{X: 0.0, Y: 8.5, Width: 80.0, Height: 20.0}, props.Rect{ Left: 7, Top: 8.5, Percent: 66, Center: false, }, consts.Png) - image.AssertCalled(t, "AddFromBase64", "Image7", 20.0, 0.0, 1.0, 20.0, props.Rect{ + image.AssertCalled(t, "AddFromBase64", "Image7", internal.Cell{X: 0.0, Y: 20.0, Width: 80.0, Height: 20.0}, props.Rect{ Left: 0, Top: 0, Percent: 98, @@ -738,7 +739,7 @@ func TestPdfMaroto_Base64Image(t *testing.T) { }, func(m pdf.Maroto) { m.Row(20, func() { - m.Col(func() { + m.Col(12, func() { _ = m.Base64Image("Image6", consts.Png, props.Rect{ Left: 7.0, Top: 8.5, @@ -747,7 +748,7 @@ func TestPdfMaroto_Base64Image(t *testing.T) { }) }) m.Row(20, func() { - m.Col(func() { + m.Col(12, func() { _ = m.Base64Image("Image7", consts.Jpg, props.Rect{ Percent: 98.0, Center: true, @@ -791,11 +792,11 @@ func TestPdfMaroto_QrCode(t *testing.T) { }, func(t *testing.T, code *mocks.Code) { code.AssertNumberOfCalls(t, "AddQr", 1) - code.AssertCalled(t, "AddQr", "Code1", 0.0, 0.0, 1.0, 20.0, props.Rect{Percent: 100, Center: false}) + code.AssertCalled(t, "AddQr", "Code1", internal.Cell{X: 0.0, Y: 0.0, Width: 80.0, Height: 20.0}, props.Rect{Percent: 100, Center: false}) }, func(m pdf.Maroto) { m.Row(20, func() { - m.Col(func() { + m.Col(0, func() { m.QrCode("Code1") }) }) @@ -810,19 +811,19 @@ func TestPdfMaroto_QrCode(t *testing.T) { }, func(t *testing.T, code *mocks.Code) { code.AssertNumberOfCalls(t, "AddQr", 2) - code.AssertCalled(t, "AddQr", "Code2", 4.0, 0.0, 1.0, 20.0, props.Rect{ + code.AssertCalled(t, "AddQr", "Code2", internal.Cell{X: 0.0, Y: 4.0, Width: 80.0, Height: 20.0}, props.Rect{ Left: 2.0, Top: 4.0, Percent: 40.0, }) - code.AssertCalled(t, "AddQr", "Code3", 0.0, 0.0, 1.0, 20.0, props.Rect{ + code.AssertCalled(t, "AddQr", "Code3", internal.Cell{X: 0.0, Y: 0.0, Width: 80.0, Height: 20.0}, props.Rect{ Percent: 40.0, Center: true, }) }, func(m pdf.Maroto) { m.Row(20, func() { - m.Col(func() { + m.Col(0, func() { m.QrCode("Code2", props.Rect{ Left: 2.0, Top: 4.0, @@ -845,26 +846,26 @@ func TestPdfMaroto_QrCode(t *testing.T) { }, func(t *testing.T, code *mocks.Code) { code.AssertNumberOfCalls(t, "AddQr", 2) - code.AssertCalled(t, "AddQr", "Code4", 4.5, 0.0, 2.0, 20.0, props.Rect{ + code.AssertCalled(t, "AddQr", "Code4", internal.Cell{X: 0.0, Y: 4.5, Width: 40.0, Height: 20.0}, props.Rect{ Left: 4.0, Top: 4.5, Percent: 55.0, }) - code.AssertCalled(t, "AddQr", "Code5", 0.0, 1.0, 2.0, 20.0, props.Rect{ + code.AssertCalled(t, "AddQr", "Code5", internal.Cell{X: 40.0, Y: 0.0, Width: 40.0, Height: 20.0}, props.Rect{ Percent: 53.0, Center: true, }) }, func(m pdf.Maroto) { m.Row(20, func() { - m.Col(func() { + m.Col(6, func() { m.QrCode("Code4", props.Rect{ Left: 4.0, Top: 4.5, Percent: 55.0, }) }) - m.Col(func() { + m.Col(6, func() { m.QrCode("Code5", props.Rect{ Percent: 53.0, Center: true, @@ -882,19 +883,19 @@ func TestPdfMaroto_QrCode(t *testing.T) { }, func(t *testing.T, code *mocks.Code) { code.AssertNumberOfCalls(t, "AddQr", 2) - code.AssertCalled(t, "AddQr", "Code6", 8.5, 0.0, 1.0, 20.0, props.Rect{ + code.AssertCalled(t, "AddQr", "Code6", internal.Cell{X: 0.0, Y: 8.5, Width: 80.0, Height: 20.0}, props.Rect{ Left: 7.0, Top: 8.5, Percent: 66.0, }) - code.AssertCalled(t, "AddQr", "Code7", 20.0, 0.0, 1.0, 20.0, props.Rect{ + code.AssertCalled(t, "AddQr", "Code7", internal.Cell{X: 0.0, Y: 20.0, Width: 80.0, Height: 20.0}, props.Rect{ Percent: 98.0, Center: true, }) }, func(m pdf.Maroto) { m.Row(20, func() { - m.Col(func() { + m.Col(0, func() { m.QrCode("Code6", props.Rect{ Left: 7.0, Top: 8.5, @@ -903,7 +904,7 @@ func TestPdfMaroto_QrCode(t *testing.T) { }) }) m.Row(20, func() { - m.Col(func() { + m.Col(12, func() { m.QrCode("Code7", props.Rect{ Percent: 98.0, Center: true, @@ -947,11 +948,11 @@ func TestPdfMaroto_Barcode(t *testing.T) { }, func(t *testing.T, code *mocks.Code) { code.AssertNumberOfCalls(t, "AddBar", 1) - code.AssertCalled(t, "AddBar", "Code1", 0.0, 0.0, 1.0, 20.0, props.Barcode{Percent: 100, Center: false, Proportion: props.Proportion{Width: 1, Height: 0.2}}) + code.AssertCalled(t, "AddBar", "Code1", internal.Cell{X: 0.0, Y: 0.0, Width: 80.0, Height: 20.0}, props.Barcode{Percent: 100, Center: false, Proportion: props.Proportion{Width: 1, Height: 0.2}}) }, func(m pdf.Maroto) { m.Row(20, func() { - m.Col(func() { + m.Col(12, func() { _ = m.Barcode("Code1", props.Barcode{Proportion: props.Proportion{Width: 1, Height: 0.2}}) }) }) @@ -966,13 +967,13 @@ func TestPdfMaroto_Barcode(t *testing.T) { }, func(t *testing.T, code *mocks.Code) { code.AssertNumberOfCalls(t, "AddBar", 2) - code.AssertCalled(t, "AddBar", "Code2", 4.0, 0.0, 1.0, 20.0, props.Barcode{ + code.AssertCalled(t, "AddBar", "Code2", internal.Cell{X: 0.0, Y: 4.0, Width: 80.0, Height: 20.0}, props.Barcode{ Left: 2.0, Top: 4.0, Percent: 40.0, Proportion: props.Proportion{Width: 1, Height: 0.2}, }) - code.AssertCalled(t, "AddBar", "Code3", 0.0, 0.0, 1.0, 20.0, props.Barcode{ + code.AssertCalled(t, "AddBar", "Code3", internal.Cell{X: 0.0, Y: 0.0, Width: 80.0, Height: 20.0}, props.Barcode{ Percent: 40.0, Center: true, Proportion: props.Proportion{Width: 1, Height: 0.2}, @@ -980,7 +981,7 @@ func TestPdfMaroto_Barcode(t *testing.T) { }, func(m pdf.Maroto) { m.Row(20, func() { - m.Col(func() { + m.Col(12, func() { _ = m.Barcode("Code2", props.Barcode{ Left: 2.0, Top: 4.0, @@ -1033,8 +1034,8 @@ func TestPdfMaroto_Row(t *testing.T) { assert.Equal(t, calledTimes, 1) }, func(t *testing.T, pdf *mocks.Pdf) { - pdf.AssertNumberOfCalls(t, "GetMargins", 2) - pdf.AssertNumberOfCalls(t, "GetPageSize", 2) + pdf.AssertNumberOfCalls(t, "GetMargins", 3) + pdf.AssertNumberOfCalls(t, "GetPageSize", 3) pdf.AssertNumberOfCalls(t, "Ln", 2) pdf.AssertCalled(t, "Ln", 30.0) }, @@ -1053,8 +1054,8 @@ func TestPdfMaroto_Row(t *testing.T) { assert.Equal(t, calledTimes, 2) }, func(t *testing.T, pdf *mocks.Pdf) { - pdf.AssertNumberOfCalls(t, "GetMargins", 3) - pdf.AssertNumberOfCalls(t, "GetPageSize", 3) + pdf.AssertNumberOfCalls(t, "GetMargins", 4) + pdf.AssertNumberOfCalls(t, "GetPageSize", 4) pdf.AssertNumberOfCalls(t, "Ln", 3) pdf.AssertCalled(t, "Ln", 30.0) @@ -1078,8 +1079,8 @@ func TestPdfMaroto_Row(t *testing.T) { assert.Equal(t, calledTimes, 3) }, func(t *testing.T, pdf *mocks.Pdf) { - pdf.AssertNumberOfCalls(t, "GetMargins", 4) - pdf.AssertNumberOfCalls(t, "GetPageSize", 4) + pdf.AssertNumberOfCalls(t, "GetMargins", 5) + pdf.AssertNumberOfCalls(t, "GetPageSize", 5) pdf.AssertNumberOfCalls(t, "Ln", 4) pdf.AssertCalled(t, "Ln", 30.0) @@ -1104,8 +1105,8 @@ func TestPdfMaroto_Row(t *testing.T) { assert.Equal(t, calledTimes, 3) }, func(t *testing.T, pdf *mocks.Pdf) { - pdf.AssertNumberOfCalls(t, "GetMargins", 7) - pdf.AssertNumberOfCalls(t, "GetPageSize", 7) + pdf.AssertNumberOfCalls(t, "GetMargins", 10) + pdf.AssertNumberOfCalls(t, "GetPageSize", 10) pdf.AssertNumberOfCalls(t, "Ln", 6) pdf.AssertCalled(t, "Ln", 50.0) @@ -1148,8 +1149,8 @@ func TestPdfMaroto_Line(t *testing.T) { return pdf }, func(t *testing.T, pdf *mocks.Pdf) { - pdf.AssertNumberOfCalls(t, "GetMargins", 3) - pdf.AssertNumberOfCalls(t, "GetPageSize", 3) + pdf.AssertNumberOfCalls(t, "GetMargins", 5) + pdf.AssertNumberOfCalls(t, "GetPageSize", 5) pdf.AssertNumberOfCalls(t, "Line", 1) pdf.AssertCalled(t, "Line", 10.0, 10.5, 90.0, 10.5) @@ -1166,8 +1167,8 @@ func TestPdfMaroto_Line(t *testing.T) { return pdf }, func(t *testing.T, pdf *mocks.Pdf) { - pdf.AssertNumberOfCalls(t, "GetMargins", 5) - pdf.AssertNumberOfCalls(t, "GetPageSize", 5) + pdf.AssertNumberOfCalls(t, "GetMargins", 8) + pdf.AssertNumberOfCalls(t, "GetPageSize", 8) pdf.AssertNumberOfCalls(t, "Line", 2) pdf.AssertCalled(t, "Line", 10.0, 11.0, 90.0, 11.0) @@ -1206,132 +1207,55 @@ func TestPdfMaroto_ColSpace(t *testing.T) { "One ColSpace inside one Row", func(m pdf.Maroto) { m.Row(40.0, func() { - m.ColSpace() + m.ColSpace(12) }) }, func(t *testing.T, pdf *mocks.Pdf) { pdf.AssertNumberOfCalls(t, "CellFormat", 2) - pdf.AssertCalled(t, "CellFormat", 20, 40, "", "", 0, "C", false, 0, "") + pdf.AssertCalled(t, "CellFormat", 80, 40, "", "", 0, "C", false, 0, "") }, }, { "Two ColSpace inside one Row", func(m pdf.Maroto) { m.Row(40.0, func() { - m.ColSpace() - m.ColSpace() + m.ColSpace(5) + m.ColSpace(7) }) }, func(t *testing.T, pdf *mocks.Pdf) { pdf.AssertNumberOfCalls(t, "CellFormat", 3) - pdf.AssertCalled(t, "CellFormat", 20, 40, "", "", 0, "C", false, 0, "") + pdf.AssertCalled(t, "CellFormat", 33, 40, "", "", 0, "C", false, 0, "") + pdf.AssertCalled(t, "CellFormat", 46, 40, "", "", 0, "C", false, 0, "") }, }, { "Two ColSpace inside two Rows", func(m pdf.Maroto) { - m.Row(40.0, func() { - m.ColSpace() + m.Row(33.0, func() { + m.ColSpace(12) }) m.Row(35.0, func() { - m.ColSpace() + m.ColSpace(12) }) }, func(t *testing.T, pdf *mocks.Pdf) { + pdf.AssertCalled(t, "CellFormat", 80, 33, "", "", 0, "C", false, 0, "") + pdf.AssertCalled(t, "CellFormat", 80, 35, "", "", 0, "C", false, 0, "") pdf.AssertNumberOfCalls(t, "CellFormat", 3) - pdf.AssertCalled(t, "CellFormat", 20, 40, "", "", 0, "C", false, 0, "") - pdf.AssertCalled(t, "CellFormat", 20, 35, "", "", 0, "C", false, 0, "") }, }, { - "ColSpace with Debug", + "ColSpace with Border", func(m pdf.Maroto) { m.SetBorder(true) - m.Row(40.0, func() { - m.ColSpace() + m.Row(23.0, func() { + m.ColSpace(12) }) }, func(t *testing.T, pdf *mocks.Pdf) { pdf.AssertNumberOfCalls(t, "CellFormat", 2) - pdf.AssertCalled(t, "CellFormat", 20, 40, "", "1", 0, "C", false, 0, "") - }, - }, - } - - for _, c := range cases { - // Arrange - pdf := basePdfTest(10, 10, 10) - math := baseMathTest() - tableList := baseTableList() - - m := newMarotoTest(pdf, math, nil, nil, nil, nil, nil, tableList) - - // Act - c.act(m) - - // Assert - c.assert(t, pdf) - } -} - -func TestPdfMaroto_ColSpaces(t *testing.T) { - cases := []struct { - name string - act func(m pdf.Maroto) - assert func(t *testing.T, pdf *mocks.Pdf) - }{ - { - "One ColSpaces inside one Row", - func(m pdf.Maroto) { - m.Row(40.0, func() { - m.ColSpaces(2) - }) - }, - func(t *testing.T, pdf *mocks.Pdf) { - pdf.AssertNumberOfCalls(t, "CellFormat", 3) - pdf.AssertCalled(t, "CellFormat", 20, 40, "", "", 0, "C", false, 0, "") - }, - }, - { - "Two ColSpaces inside one Row", - func(m pdf.Maroto) { - m.Row(40.0, func() { - m.ColSpaces(2) - m.ColSpaces(2) - }) - }, - func(t *testing.T, pdf *mocks.Pdf) { - pdf.AssertNumberOfCalls(t, "CellFormat", 5) - pdf.AssertCalled(t, "CellFormat", 20, 40, "", "", 0, "C", false, 0, "") - }, - }, - { - "Two ColSpaces inside two Rows", - func(m pdf.Maroto) { - m.Row(40.0, func() { - m.ColSpaces(2) - }) - m.Row(35.0, func() { - m.ColSpaces(2) - }) - }, - func(t *testing.T, pdf *mocks.Pdf) { - pdf.AssertNumberOfCalls(t, "CellFormat", 5) - pdf.AssertCalled(t, "CellFormat", 20, 40, "", "", 0, "C", false, 0, "") - pdf.AssertCalled(t, "CellFormat", 20, 35, "", "", 0, "C", false, 0, "") - }, - }, - { - "ColSpaces with Debug", - func(m pdf.Maroto) { - m.SetBorder(true) - m.Row(40.0, func() { - m.ColSpaces(2) - }) - }, - func(t *testing.T, pdf *mocks.Pdf) { - pdf.AssertNumberOfCalls(t, "CellFormat", 3) - pdf.AssertCalled(t, "CellFormat", 20, 40, "", "1", 0, "C", false, 0, "") + pdf.AssertCalled(t, "CellFormat", 80, 23, "", "1", 0, "C", false, 0, "") }, }, } @@ -1608,7 +1532,7 @@ func TestPdfMaroto_RegisterFooter(t *testing.T) { headers, contents := getContents() m.Row(20, func() { for _, header := range headers { - m.Col(func() { + m.Col(0, func() { m.Text(header) }) } @@ -1617,7 +1541,7 @@ func TestPdfMaroto_RegisterFooter(t *testing.T) { for _, content := range contents { m.Row(20, func() { for _, txt := range content { - m.Col(func() { + m.Col(12, func() { m.Text(txt) }) } @@ -1678,7 +1602,7 @@ func TestPdfMaroto_RegisterHeader(t *testing.T) { "Always execute header once when add something", func(m pdf.Maroto) { m.Row(20, func() { - m.ColSpace() + m.ColSpace(0) }) }, true, @@ -1692,7 +1616,7 @@ func TestPdfMaroto_RegisterHeader(t *testing.T) { headers, contents := getContents() m.Row(20, func() { for _, header := range headers { - m.Col(func() { + m.Col(12, func() { m.Text(header) }) } @@ -1701,7 +1625,7 @@ func TestPdfMaroto_RegisterHeader(t *testing.T) { for _, content := range contents { m.Row(20, func() { for _, txt := range content { - m.Col(func() { + m.Col(12, func() { m.Text(txt) }) } @@ -1772,7 +1696,7 @@ func TestPdfMaroto_GetCurrentPage(t *testing.T) { headers, contents := getContents() m.Row(20, func() { for _, header := range headers { - m.Col(func() { + m.Col(uint(12/len(headers)), func() { m.Text(header) }) } @@ -1781,7 +1705,7 @@ func TestPdfMaroto_GetCurrentPage(t *testing.T) { for _, content := range contents { m.Row(20, func() { for _, txt := range content { - m.Col(func() { + m.Col(uint(12/len(contents)), func() { m.Text(txt) }) } @@ -1841,7 +1765,7 @@ func TestPdfMaroto_GetCurrentPage_WhenIsNotZero(t *testing.T) { m := newMarotoTest(pdf, math, font, text, nil, nil, nil, tableList) m.Row(20, func() { - m.Col(func() { + m.Col(0, func() { m.Text("test") }) }) @@ -1857,17 +1781,24 @@ func TestPdfMaroto_SetPageMargins(t *testing.T) { cases := []struct { name string act func(m pdf.Maroto) - assert func(t *testing.T, left, top, right float64) + assert func(t *testing.T, m *mocks.Pdf) }{ { - "Set page margins should override default", + "Set page margins should override default, top greater than 10", func(m pdf.Maroto) { m.SetPageMargins(12.3, 19.3, 0.0) }, - func(t *testing.T, left, top, right float64) { - assert.Equal(t, 12.3, left) - assert.Equal(t, 19.3, top) - assert.Equal(t, 0.0, right) + func(t *testing.T, m *mocks.Pdf) { + m.AssertCalled(t, "SetMargins", 12.3, 10.0, 0.0) + }, + }, + { + "Set page margins should override default, top less than 10", + func(m pdf.Maroto) { + m.SetPageMargins(12.3, 9.0, 0.0) + }, + func(t *testing.T, m *mocks.Pdf) { + m.AssertCalled(t, "SetMargins", 12.3, 10.0, 0.0) }, }, } @@ -1882,8 +1813,7 @@ func TestPdfMaroto_SetPageMargins(t *testing.T) { c.act(m) // Assert - left, top, right, _ := m.GetPageMargins() - c.assert(t, left, top, right) + c.assert(t, pdf) } } @@ -1900,3 +1830,18 @@ func TestPdfMaroto_SetBackgroundColor(t *testing.T) { pdf.AssertCalled(t, "SetFillColor", white.Red, white.Green, white.Blue) pdf.AssertNumberOfCalls(t, "SetFillColor", 2) } + +func TestPdfMaroto_GetPageMargins(t *testing.T) { + // Arrange + pdf := basePdfTest(12.3, 19.3, 0) + m := newMarotoTest(pdf, nil, nil, nil, nil, nil, nil, nil) + + // Act + left, top, right, bottom := m.GetPageMargins() + + // Assert + assert.Equal(t, 12.3, left) + assert.Equal(t, 19.3, top) + assert.Equal(t, 0.0, right) + assert.Equal(t, 0.0, bottom) +} diff --git a/pkg/props/prop.go b/pkg/props/prop.go index 5d223d16..331c40a4 100644 --- a/pkg/props/prop.go +++ b/pkg/props/prop.go @@ -73,14 +73,29 @@ type Font struct { Size float64 } +// TableListContent represents properties from a line (header/content) from a TableList +type TableListContent struct { + // Family of the text, ex: consts.Arial, helvetica and etc + Family consts.Family + // Style of the text, ex: consts.Normal, bold and etc + Style consts.Style + // Size of the text + Size float64 + // GridSizes is the custom properties of the size of the grid + // the sum of the values cannot be greater than 12, if this + // value is not provided the width of all columns will be the + // same + GridSizes []uint +} + // TableList represents properties from a TableList type TableList struct { // HeaderProp is the custom properties of the text inside // the headers - HeaderProp Font + HeaderProp TableListContent // ContentProp is the custom properties of the text inside // the contents - ContentProp Font + ContentProp TableListContent // Align is the align of the text (header and content) inside the columns Align consts.Align // AlternatedBackground define the background color from even rows @@ -208,8 +223,25 @@ func (s *Font) ToTextProp(align consts.Align, top float64, extrapolate bool, ver return textProp } +// ToTextProp from Font return a TableListContent based on Font +func (s *TableListContent) ToTextProp(align consts.Align, top float64, extrapolate bool, verticalPadding float64) Text { + textProp := Text{ + Family: s.Family, + Style: s.Style, + Size: s.Size, + Align: align, + Top: top, + Extrapolate: extrapolate, + VerticalPadding: verticalPadding, + } + + textProp.MakeValid() + + return textProp +} + // MakeValid from TableList define default values for a TableList -func (s *TableList) MakeValid() { +func (s *TableList) MakeValid(header []string, contents [][]string) { if s.HeaderProp.Size == 0.0 { s.HeaderProp.Size = 10.0 } @@ -222,6 +254,15 @@ func (s *TableList) MakeValid() { s.HeaderProp.Style = consts.Bold } + if len(s.HeaderProp.GridSizes) == 0 { + gridSize := uint(12.0 / len(header)) + s.HeaderProp.GridSizes = []uint{} + + for range header { + s.HeaderProp.GridSizes = append(s.HeaderProp.GridSizes, gridSize) + } + } + if s.Align == "" { s.Align = consts.Left } @@ -238,6 +279,15 @@ func (s *TableList) MakeValid() { s.ContentProp.Style = consts.Normal } + if len(s.ContentProp.GridSizes) == 0 { + gridSize := uint(12.0 / len(header)) + s.ContentProp.GridSizes = []uint{} + + for range header { + s.ContentProp.GridSizes = append(s.ContentProp.GridSizes, gridSize) + } + } + if s.HeaderContentSpace == 0.0 { s.HeaderContentSpace = 4.0 } diff --git a/pkg/props/prop_test.go b/pkg/props/prop_test.go index c503987e..908a0df9 100644 --- a/pkg/props/prop_test.go +++ b/pkg/props/prop_test.go @@ -298,22 +298,24 @@ func TestTableListProp_MakeValid(t *testing.T) { { "When HeaderProp/ContentProp is not defined", &props.TableList{ - HeaderProp: props.Font{}, - ContentProp: props.Font{}, + HeaderProp: props.TableListContent{}, + ContentProp: props.TableListContent{}, }, func(t *testing.T, m *props.TableList) { assert.Equal(t, m.HeaderProp.Size, 10.0) assert.Equal(t, m.HeaderProp.Family, consts.Arial) assert.Equal(t, m.HeaderProp.Style, consts.Bold) + assert.Equal(t, len(m.HeaderProp.GridSizes), 3) assert.Equal(t, m.ContentProp.Size, 10.0) assert.Equal(t, m.ContentProp.Family, consts.Arial) assert.Equal(t, m.ContentProp.Style, consts.Normal) + assert.Equal(t, len(m.ContentProp.GridSizes), 3) }, }, { "When HeaderProp.Size is 0.0", &props.TableList{ - HeaderProp: props.Font{ + HeaderProp: props.TableListContent{ Size: 0.0, }, }, @@ -324,7 +326,7 @@ func TestTableListProp_MakeValid(t *testing.T) { { "When HeaderProp.Style is empty", &props.TableList{ - HeaderProp: props.Font{ + HeaderProp: props.TableListContent{ Style: "", }, }, @@ -335,7 +337,7 @@ func TestTableListProp_MakeValid(t *testing.T) { { "When HeaderProp.Family is empty", &props.TableList{ - HeaderProp: props.Font{ + HeaderProp: props.TableListContent{ Family: "", }, }, @@ -355,7 +357,7 @@ func TestTableListProp_MakeValid(t *testing.T) { { "When ContentProp.Size is 0.0", &props.TableList{ - ContentProp: props.Font{ + ContentProp: props.TableListContent{ Size: 0.0, }, }, @@ -366,7 +368,7 @@ func TestTableListProp_MakeValid(t *testing.T) { { "When ContentProp.Style is empty", &props.TableList{ - HeaderProp: props.Font{ + HeaderProp: props.TableListContent{ Style: "", }, }, @@ -377,7 +379,7 @@ func TestTableListProp_MakeValid(t *testing.T) { { "When ContentProp.Family is empty", &props.TableList{ - HeaderProp: props.Font{ + HeaderProp: props.TableListContent{ Family: "", }, }, @@ -398,7 +400,7 @@ func TestTableListProp_MakeValid(t *testing.T) { for _, c := range cases { // Act - c.tableListProp.MakeValid() + c.tableListProp.MakeValid([]string{"a", "b", "c"}, [][]string{{"a", "b", "c"}}) // Assert c.assert(t, c.tableListProp)