diff --git a/internal/examples/billing/main.go b/internal/examples/billing/main.go new file mode 100644 index 00000000..da4324af --- /dev/null +++ b/internal/examples/billing/main.go @@ -0,0 +1,204 @@ +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() { + begin := time.Now() + + darkGrayColor := color.Color{ + Red: 144, + Green: 144, + Blue: 144, + } + + grayColor := color.Color{ + Red: 200, + Green: 200, + Blue: 200, + } + + whiteColor := color.NewWhite() + + m := pdf.NewMaroto(consts.Portrait, consts.A4) + m.SetPageMargins(10, 15, 10) + //m.SetBorder(true) + + m.RegisterHeader(func() { + m.Row(20, func() { + m.Col(func() { + _ = m.FileImage("internal/assets/images/biplane.jpg", props.Rect{ + Center: true, + Percent: 80, + }) + }) + m.ColSpaces(2) + m.Col(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, + Style: consts.BoldItalic, + Size: 8, + Align: consts.Right, + }) + m.Text("www.mycompany.com", props.Text{ + Top: 19, + 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.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.Row(10, func() { + m.Col(func() { + m.Text("Invoice ABC123456789", props.Text{ + Top: 6, + Style: consts.Bold, + Align: consts.Center, + }) + }) + }) + + m.SetBackgroundColor(darkGrayColor) + m.Row(7, func() { + m.Col(func() { + m.Text("Transactions", props.Text{ + Top: 4.5, + Size: 9, + Style: consts.Bold, + Align: consts.Center, + }) + }) + m.ColSpaces(3) + }) + 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.TableList(header, contents, props.TableList{ + HeaderProp: props.Font{ + Size: 9, + }, + ContentProp: props.Font{ + Size: 8, + }, + Align: consts.Center, + AlternatedBackground: &grayColor, + HeaderContentSpace: 1, + Line: false, + }) + + m.Row(20, func() { + m.ColSpaces(2) + m.Col(func() { + m.Text("Total:", props.Text{ + Top: 5, + Style: consts.Bold, + Size: 8, + Align: consts.Right, + }) + }) + m.Col(func() { + m.Text("R$ 2.567,00", props.Text{ + Top: 5, + Style: consts.Bold, + Size: 8, + Align: consts.Center, + }) + }) + }) + + m.Row(15, func() { + m.Col(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() + }) + + 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/internal/examples/pdfs/billing.pdf b/internal/examples/pdfs/billing.pdf new file mode 100644 index 00000000..c591e5ee Binary files /dev/null and b/internal/examples/pdfs/billing.pdf differ diff --git a/internal/examples/pdfs/certificate.pdf b/internal/examples/pdfs/certificate.pdf index e174b054..9eb716ce 100644 Binary files a/internal/examples/pdfs/certificate.pdf and b/internal/examples/pdfs/certificate.pdf differ diff --git a/internal/examples/pdfs/sample1.pdf b/internal/examples/pdfs/sample1.pdf index c6dbf550..a48d4497 100644 Binary files a/internal/examples/pdfs/sample1.pdf and b/internal/examples/pdfs/sample1.pdf differ diff --git a/internal/examples/pdfs/zpl.pdf b/internal/examples/pdfs/zpl.pdf index 551db543..523b94ea 100644 Binary files a/internal/examples/pdfs/zpl.pdf and b/internal/examples/pdfs/zpl.pdf differ diff --git a/internal/examples/sample1/main.go b/internal/examples/sample1/main.go index 53b89641..31ab5ee7 100644 --- a/internal/examples/sample1/main.go +++ b/internal/examples/sample1/main.go @@ -15,6 +15,7 @@ import ( func main() { begin := time.Now() m := pdf.NewMaroto(consts.Portrait, consts.A4) + m.SetPageMargins(10, 15, 10) //m.SetBorder(true) byteSlices, err := ioutil.ReadFile("internal/assets/images/biplane.jpg") @@ -204,9 +205,6 @@ func getSmallContent() ([]string, [][]string) { 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{"Osasco", "São Paulo", "", "R$ 7,00"}) - contents = append(contents, []string{"Congonhas", "Fortaleza", "", "R$ 113,00"}) - contents = append(contents, []string{"Natal", "Santo André", "", "R$ 198,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"}) diff --git a/pkg/pdf/pdf.go b/pkg/pdf/pdf.go index a7b03383..48ba2d7a 100644 --- a/pkg/pdf/pdf.go +++ b/pkg/pdf/pdf.go @@ -61,6 +61,7 @@ type PdfMaroto struct { TableListHelper internal.TableList pageIndex int offsetY float64 + marginTop float64 rowHeight float64 rowColCount float64 backgroundColor color.Color @@ -126,6 +127,42 @@ 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()) { @@ -153,7 +190,12 @@ 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) { - s.Pdf.SetMargins(left, top, right) + if top <= 10 { + s.Pdf.SetMargins(left, top, right) + } else { + s.marginTop = top - 10 + s.Pdf.SetMargins(left, 10, right) + } } // GetPageMargins returns the set page margins. Comes in order of Left, Top, Right, Bottom @@ -244,21 +286,19 @@ func (s *PdfMaroto) Row(height float64, closure func()) { // height of the footer, add the footer if totalOffsetY > maxOffsetPage { if !s.headerFooterContextActive { - if s.footerClosure != nil { - s.headerFooterContextActive = true - s.footerClosure() - s.headerFooterContextActive = false - } + s.headerFooterContextActive = true + s.footer() + s.headerFooterContextActive = false s.offsetY = 0 s.pageIndex++ } } // If is a new page, add the header - if !s.headerFooterContextActive && s.headerClosure != nil { + if !s.headerFooterContextActive { if s.offsetY == 0 { s.headerFooterContextActive = true - s.headerClosure() + s.header() s.headerFooterContextActive = false } } diff --git a/pkg/pdf/pdf_test.go b/pkg/pdf/pdf_test.go index b9e0d0c5..f0f98b18 100644 --- a/pkg/pdf/pdf_test.go +++ b/pkg/pdf/pdf_test.go @@ -1033,9 +1033,9 @@ func TestPdfMaroto_Row(t *testing.T) { assert.Equal(t, calledTimes, 1) }, func(t *testing.T, pdf *mocks.Pdf) { - pdf.AssertNumberOfCalls(t, "GetMargins", 1) - pdf.AssertNumberOfCalls(t, "GetPageSize", 1) - pdf.AssertNumberOfCalls(t, "Ln", 1) + pdf.AssertNumberOfCalls(t, "GetMargins", 2) + pdf.AssertNumberOfCalls(t, "GetPageSize", 2) + pdf.AssertNumberOfCalls(t, "Ln", 2) pdf.AssertCalled(t, "Ln", 30.0) }, }, @@ -1053,9 +1053,9 @@ func TestPdfMaroto_Row(t *testing.T) { assert.Equal(t, calledTimes, 2) }, func(t *testing.T, pdf *mocks.Pdf) { - pdf.AssertNumberOfCalls(t, "GetMargins", 2) - pdf.AssertNumberOfCalls(t, "GetPageSize", 2) - pdf.AssertNumberOfCalls(t, "Ln", 2) + pdf.AssertNumberOfCalls(t, "GetMargins", 3) + pdf.AssertNumberOfCalls(t, "GetPageSize", 3) + pdf.AssertNumberOfCalls(t, "Ln", 3) pdf.AssertCalled(t, "Ln", 30.0) pdf.AssertCalled(t, "Ln", 40.0) @@ -1078,9 +1078,9 @@ func TestPdfMaroto_Row(t *testing.T) { assert.Equal(t, calledTimes, 3) }, func(t *testing.T, pdf *mocks.Pdf) { - pdf.AssertNumberOfCalls(t, "GetMargins", 3) - pdf.AssertNumberOfCalls(t, "GetPageSize", 3) - pdf.AssertNumberOfCalls(t, "Ln", 3) + pdf.AssertNumberOfCalls(t, "GetMargins", 4) + pdf.AssertNumberOfCalls(t, "GetPageSize", 4) + pdf.AssertNumberOfCalls(t, "Ln", 4) pdf.AssertCalled(t, "Ln", 30.0) pdf.AssertCalled(t, "Ln", 40.0) @@ -1104,9 +1104,9 @@ func TestPdfMaroto_Row(t *testing.T) { assert.Equal(t, calledTimes, 3) }, func(t *testing.T, pdf *mocks.Pdf) { - pdf.AssertNumberOfCalls(t, "GetMargins", 3) - pdf.AssertNumberOfCalls(t, "GetPageSize", 3) - pdf.AssertNumberOfCalls(t, "Ln", 3) + pdf.AssertNumberOfCalls(t, "GetMargins", 7) + pdf.AssertNumberOfCalls(t, "GetPageSize", 7) + pdf.AssertNumberOfCalls(t, "Ln", 6) pdf.AssertCalled(t, "Ln", 50.0) pdf.AssertCalled(t, "Ln", 40.0) @@ -1148,8 +1148,8 @@ func TestPdfMaroto_Line(t *testing.T) { return pdf }, 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, "Line", 1) pdf.AssertCalled(t, "Line", 10.0, 10.5, 90.0, 10.5) @@ -1166,8 +1166,8 @@ func TestPdfMaroto_Line(t *testing.T) { return pdf }, 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, "Line", 2) pdf.AssertCalled(t, "Line", 10.0, 11.0, 90.0, 11.0) @@ -1210,7 +1210,7 @@ func TestPdfMaroto_ColSpace(t *testing.T) { }) }, func(t *testing.T, pdf *mocks.Pdf) { - pdf.AssertNumberOfCalls(t, "CellFormat", 1) + pdf.AssertNumberOfCalls(t, "CellFormat", 2) pdf.AssertCalled(t, "CellFormat", 20, 40, "", "", 0, "C", false, 0, "") }, }, @@ -1223,7 +1223,7 @@ func TestPdfMaroto_ColSpace(t *testing.T) { }) }, func(t *testing.T, pdf *mocks.Pdf) { - pdf.AssertNumberOfCalls(t, "CellFormat", 2) + pdf.AssertNumberOfCalls(t, "CellFormat", 3) pdf.AssertCalled(t, "CellFormat", 20, 40, "", "", 0, "C", false, 0, "") }, }, @@ -1238,7 +1238,7 @@ func TestPdfMaroto_ColSpace(t *testing.T) { }) }, func(t *testing.T, pdf *mocks.Pdf) { - pdf.AssertNumberOfCalls(t, "CellFormat", 2) + 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, "") }, @@ -1252,7 +1252,7 @@ func TestPdfMaroto_ColSpace(t *testing.T) { }) }, func(t *testing.T, pdf *mocks.Pdf) { - pdf.AssertNumberOfCalls(t, "CellFormat", 1) + pdf.AssertNumberOfCalls(t, "CellFormat", 2) pdf.AssertCalled(t, "CellFormat", 20, 40, "", "1", 0, "C", false, 0, "") }, }, @@ -1288,7 +1288,7 @@ func TestPdfMaroto_ColSpaces(t *testing.T) { }) }, func(t *testing.T, pdf *mocks.Pdf) { - pdf.AssertNumberOfCalls(t, "CellFormat", 2) + pdf.AssertNumberOfCalls(t, "CellFormat", 3) pdf.AssertCalled(t, "CellFormat", 20, 40, "", "", 0, "C", false, 0, "") }, }, @@ -1301,7 +1301,7 @@ func TestPdfMaroto_ColSpaces(t *testing.T) { }) }, func(t *testing.T, pdf *mocks.Pdf) { - pdf.AssertNumberOfCalls(t, "CellFormat", 4) + pdf.AssertNumberOfCalls(t, "CellFormat", 5) pdf.AssertCalled(t, "CellFormat", 20, 40, "", "", 0, "C", false, 0, "") }, }, @@ -1316,7 +1316,7 @@ func TestPdfMaroto_ColSpaces(t *testing.T) { }) }, func(t *testing.T, pdf *mocks.Pdf) { - pdf.AssertNumberOfCalls(t, "CellFormat", 4) + 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, "") }, @@ -1330,7 +1330,7 @@ func TestPdfMaroto_ColSpaces(t *testing.T) { }) }, func(t *testing.T, pdf *mocks.Pdf) { - pdf.AssertNumberOfCalls(t, "CellFormat", 2) + pdf.AssertNumberOfCalls(t, "CellFormat", 3) pdf.AssertCalled(t, "CellFormat", 20, 40, "", "1", 0, "C", false, 0, "") }, }, diff --git a/pull_request_template.md b/pull_request_template.md index d8870403..8d94a106 100644 --- a/pull_request_template.md +++ b/pull_request_template.md @@ -1,5 +1,3 @@ - -