From 2186fffb6ea4a9c7ee46bfbc315ce36a28005952 Mon Sep 17 00:00:00 2001 From: jfercher Date: Wed, 8 Jan 2020 20:40:35 -0300 Subject: [PATCH] Enable to set background color --- internal/examples/pdfs/certificate.pdf | Bin 17252 -> 17252 bytes internal/examples/pdfs/sample1.pdf | Bin 874738 -> 669686 bytes internal/examples/pdfs/zpl.pdf | Bin 209926 -> 209926 bytes internal/examples/sample1/main.go | 25 ++--- internal/mocks/maroto.go | 46 +++++++++ internal/tablelist.go | 13 ++- internal/tablelist_test.go | 48 +++++++++ pkg/color/color.go | 28 +++++ pkg/color/color_test.go | 25 +++++ pkg/pdf/example_test.go | 138 +++++++++++++++++++++++-- pkg/pdf/pdf.go | 23 +++-- pkg/pdf/pdf_test.go | 18 ++++ pkg/props/prop.go | 13 ++- pull_request_template.md | 2 +- 14 files changed, 344 insertions(+), 35 deletions(-) create mode 100644 pkg/color/color.go create mode 100644 pkg/color/color_test.go diff --git a/internal/examples/pdfs/certificate.pdf b/internal/examples/pdfs/certificate.pdf index 9f45eab08225aed51970f184777efdee69843739..e174b0541f327fb50bef5348c512407350cf510f 100644 GIT binary patch delta 39 vcmaFT#`vU-al>hc$-0jElTSJ%O^$J##A9S&U}#`rWMFJ&V6yp_V-_O-J?IWW delta 39 vcmaFT#`vU-al>hc$$uOYCZBXLoE+mgiO0~=(8$oz$jI2j#B}p7$1FwwR{jqp diff --git a/internal/examples/pdfs/sample1.pdf b/internal/examples/pdfs/sample1.pdf index 8a2a309ec67a44fedbc8f810da7930c042b340e8..c6dbf550fe99983ffc9a0681282c015c1057b4cf 100644 GIT binary patch delta 4776 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 diff --git a/internal/examples/pdfs/zpl.pdf b/internal/examples/pdfs/zpl.pdf index b0e2828e709a15107a8752d240c28de896257ed6..551db543fec864d85578d596402b9c8ef9cb07c8 100644 GIT binary patch delta 64 zcmZpB!PEAFr=f+hg=q`3s^Ro7LuUQy%7)B_(_0Oh1*iW2ai 0 { tableProp = prop[0] } @@ -96,10 +99,14 @@ func (s *tableList) Create(header []string, contents [][]string, prop ...props.T contentMarginTop := 2.0 // Draw contents - for _, content := range contents { + for index, content := range contents { contentTextProp := tableProp.ContentProp.ToTextProp(tableProp.Align, 0.0, false, 1.0) contentHeight := s.calcLinesHeight(content, contentTextProp, qtdCols) + if tableProp.AlternatedBackground != nil && index%2 == 0 { + s.pdf.SetBackgroundColor(*tableProp.AlternatedBackground) + } + s.pdf.Row(contentHeight, func() { for j, c := range content { cs := c @@ -113,6 +120,10 @@ func (s *tableList) Create(header []string, contents [][]string, prop ...props.T } }) + if tableProp.AlternatedBackground != nil && index%2 == 0 { + s.pdf.SetBackgroundColor(color.NewWhite()) + } + if tableProp.Line { s.pdf.Line(1.0) } diff --git a/internal/tablelist_test.go b/internal/tablelist_test.go index cd49ffc6..a2c91f89 100644 --- a/internal/tablelist_test.go +++ b/internal/tablelist_test.go @@ -4,6 +4,7 @@ import ( "fmt" "github.com/johnfercher/maroto/internal" "github.com/johnfercher/maroto/internal/mocks" + "github.com/johnfercher/maroto/pkg/color" "github.com/johnfercher/maroto/pkg/consts" "github.com/johnfercher/maroto/pkg/props" "github.com/stretchr/testify/assert" @@ -92,6 +93,7 @@ func TestTableList_Create_Happy(t *testing.T) { marotoGrid := &mocks.Maroto{} marotoGrid.On("Row", mock.Anything, mock.Anything).Return(nil) marotoGrid.On("Line", mock.Anything).Return(nil) + marotoGrid.On("SetBackgroundColor", mock.Anything).Return(nil) sut := internal.NewTableList(text, font) sut.BindGrid(marotoGrid) @@ -113,6 +115,52 @@ func TestTableList_Create_Happy(t *testing.T) { marotoGrid.AssertCalled(t, "Row", mock.Anything, mock.Anything) marotoGrid.AssertNumberOfCalls(t, "Row", 22) marotoGrid.AssertNumberOfCalls(t, "Line", 20) + marotoGrid.AssertNotCalled(t, "SetBackgroundColor") +} + +func TestTableList_Create_HappyWithBackgroundColor(t *testing.T) { + // Arrange + text := &mocks.Text{} + text.On("GetLinesQuantity", mock.Anything, mock.Anything, mock.Anything).Return(1) + + font := &mocks.Font{} + font.On("GetFont").Return(consts.Arial, consts.Bold, 1.0) + font.On("GetScaleFactor").Return(1.5) + + marotoGrid := &mocks.Maroto{} + marotoGrid.On("Row", mock.Anything, mock.Anything).Return(nil) + marotoGrid.On("Line", mock.Anything).Return(nil) + marotoGrid.On("SetBackgroundColor", mock.Anything).Return(nil) + + sut := internal.NewTableList(text, font) + sut.BindGrid(marotoGrid) + + headers, contents := getContents() + color := color.Color{ + Red: 200, + Green: 200, + Blue: 200, + } + + // Act + sut.Create(headers, contents, props.TableList{ + AlternatedBackground: &color, + }) + + // Assert + text.AssertNotCalled(t, "GetLinesQuantity") + text.AssertNumberOfCalls(t, "GetLinesQuantity", 84) + + font.AssertCalled(t, "GetFont") + font.AssertNumberOfCalls(t, "GetFont", 21) + + marotoGrid.AssertCalled(t, "Row", mock.Anything, mock.Anything) + marotoGrid.AssertNumberOfCalls(t, "Row", 22) + + marotoGrid.AssertNotCalled(t, "Line") + + marotoGrid.AssertCalled(t, "SetBackgroundColor", color) + marotoGrid.AssertNumberOfCalls(t, "SetBackgroundColor", 20) } func TestTableList_Create_Happy_Without_Line(t *testing.T) { diff --git a/pkg/color/color.go b/pkg/color/color.go new file mode 100644 index 00000000..28bb9703 --- /dev/null +++ b/pkg/color/color.go @@ -0,0 +1,28 @@ +package color + +// Color represents a color in the RGB (Red, Green, Blue) space, +// is possible mix values, when all values are 0 the result color is black +// when all values are 255 the result color is white +type Color struct { + // Red is the amount of red + Red int + // Green is the amount of red + Green int + // Blue is the amount of red + Blue int +} + +// IsWhite from Color will return true if all components of color +// are in the maximum value +func (s *Color) IsWhite() bool { + return s.Red == 255 && s.Green == 255 && s.Blue == 255 +} + +// NewWhite return a Color with all components (red, green and blue) as 255 +func NewWhite() Color { + return Color{ + Red: 255, + Green: 255, + Blue: 255, + } +} diff --git a/pkg/color/color_test.go b/pkg/color/color_test.go new file mode 100644 index 00000000..61c50319 --- /dev/null +++ b/pkg/color/color_test.go @@ -0,0 +1,25 @@ +package color_test + +import ( + "github.com/johnfercher/maroto/pkg/color" + "github.com/stretchr/testify/assert" + "testing" +) + +func TestNewWhite(t *testing.T) { + // Act + white := color.NewWhite() + + // Assert + assert.Equal(t, 255, white.Red) + assert.Equal(t, 255, white.Green) + assert.Equal(t, 255, white.Blue) +} + +func TestColor_IsWhite(t *testing.T) { + // Act + white := color.NewWhite() + + // Assert + assert.True(t, white.IsWhite()) +} diff --git a/pkg/pdf/example_test.go b/pkg/pdf/example_test.go index 372fdb04..a67f7d17 100644 --- a/pkg/pdf/example_test.go +++ b/pkg/pdf/example_test.go @@ -1,12 +1,24 @@ package pdf_test 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" "time" ) +// ExampleNewMaroto demonstratos how to create maroto +func ExampleNewMaroto() { + m := pdf.NewMaroto(consts.Portrait, consts.A4) + + // Do things + m.GetPageMargins() + + // Do more things and save... +} + // ExamplePdfMaroto_Line demonstrates how to draw a line // separator. func ExamplePdfMaroto_Line() { @@ -87,6 +99,36 @@ func ExamplePdfMaroto_SetBorder() { // Do more things 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, + }) + + // This Row will be filled with the color + m.Row(20, func() { + m.Col(func() { + // Add components + }) + }) + + m.SetBackgroundColor(color.NewWhite()) + + // This Row will not be filled with the color + m.Row(20, func() { + m.Col(func() { + // Add components + }) + }) + + // Do more things and save... +} + // ExamplePdfMaroto_GetBorder demonstrates how to // obtain the actual borders status func ExamplePdfMaroto_GetBorder() { @@ -117,11 +159,13 @@ func ExamplePdfMaroto_Text() { m.Row(rowHeight, func() { m.Col(func() { m.Text("TextContent", props.Text{ - Size: 12.0, - Style: consts.BoldItalic, - Family: consts.Courier, - Align: consts.Center, - Top: 1.0, + Size: 12.0, + Style: consts.BoldItalic, + Family: consts.Courier, + Align: consts.Center, + Top: 1.0, + Extrapolate: false, + VerticalPadding: 1.0, }) }) }) @@ -167,7 +211,24 @@ func ExamplePdfMaroto_TableList() { // 2 Rows of contents // Each row have 2 columns m.TableList(headers, contents, props.TableList{ - Line: false, + 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, }) // Do more things and save... @@ -365,5 +426,70 @@ func ExamplePdfMaroto_RegisterHeader() { // 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_GetPageSize demonstrates how to obtain the current page size (width and height) +func ExamplePdfMaroto_GetPageSize() { + m := pdf.NewMaroto(consts.Portrait, consts.A4) + + // Get + width, height := m.GetPageSize() + fmt.Println(width) + fmt.Println(height) + + // Do more things and save... +} + +// ExamplePdfMaroto_GetCurrentPage demonstrates how to obtain the current page index +func ExamplePdfMaroto_GetCurrentPage() { + m := pdf.NewMaroto(consts.Portrait, consts.A4) + + // Index here will be 0 + _ = m.GetCurrentPage() + + // Add Rows, Cols and Components + + // Index here will not be 0 + _ = m.GetCurrentPage() + + // Do more things and save... +} + +// ExamplePdfMaroto_GetCurrentOffset demonstrates how to obtain the current write offset +// i.e the height of cursor adding content in the pdf +func ExamplePdfMaroto_GetCurrentOffset() { + m := pdf.NewMaroto(consts.Portrait, consts.A4) + + // Offset here will be 0 + _ = m.GetCurrentOffset() + + // Add Rows, Cols and Components until maroto add a new page + + // Offset here will not be 0 + _ = m.GetCurrentOffset() + + // Add Rows, Cols and Components to maroto add a new page + + // Offset here will be 0 + _ = m.GetCurrentOffset() + + // Do more things and save... +} + +// ExamplePdfMaroto_GetPageMargins demonstrates how to obtain the current page margins +func ExamplePdfMaroto_GetPageMargins() { + m := pdf.NewMaroto(consts.Portrait, consts.A4) + + // Get + left, top, right, bottom := m.GetPageMargins() + fmt.Println(left) + fmt.Println(top) + fmt.Println(right) + fmt.Println(bottom) + + // Do more things and save... } diff --git a/pkg/pdf/pdf.go b/pkg/pdf/pdf.go index b8fadc3a..a7b03383 100644 --- a/pkg/pdf/pdf.go +++ b/pkg/pdf/pdf.go @@ -2,6 +2,7 @@ package pdf import ( "bytes" + "github.com/johnfercher/maroto/pkg/color" "github.com/johnfercher/maroto/internal" "github.com/johnfercher/maroto/pkg/consts" @@ -23,6 +24,7 @@ type Maroto interface { // Helpers SetBorder(on bool) + SetBackgroundColor(color color.Color) GetBorder() bool GetPageSize() (float64, float64) GetCurrentPage() int @@ -61,13 +63,14 @@ type PdfMaroto struct { offsetY float64 rowHeight float64 rowColCount float64 + backgroundColor color.Color colsClosures []func() headerClosure func() footerClosure func() footerHeight float64 headerFooterContextActive bool calculationMode bool - DebugMode bool + debugMode bool orientation consts.Orientation pageSize consts.PageSize } @@ -102,6 +105,7 @@ func NewMaroto(orientation consts.Orientation, pageSize consts.PageSize) Maroto pageSize: pageSize, orientation: orientation, calculationMode: false, + backgroundColor: color.NewWhite(), } maroto.TableListHelper.BindGrid(maroto) @@ -109,7 +113,7 @@ func NewMaroto(orientation consts.Orientation, pageSize consts.PageSize) Maroto maroto.Font.SetFamily(consts.Arial) maroto.Font.SetStyle(consts.Bold) maroto.Font.SetSize(16) - maroto.DebugMode = false + maroto.debugMode = false maroto.Pdf.AddPage() @@ -185,12 +189,19 @@ func (s *PdfMaroto) TableList(header []string, contents [][]string, prop ...prop // SetBorder enable the draw of lines in every cell. // Draw borders in all columns created. func (s *PdfMaroto) SetBorder(on bool) { - s.DebugMode = on + s.debugMode = on +} + +// SetBackgroundColor define the background color of the PDF. +// This method can be used to toggle background from rows +func (s *PdfMaroto) SetBackgroundColor(color color.Color) { + s.backgroundColor = color + s.Pdf.SetFillColor(s.backgroundColor.Red, s.backgroundColor.Green, s.backgroundColor.Blue) } // GetBorder return the actual border value. func (s *PdfMaroto) GetBorder() bool { - return s.DebugMode + return s.debugMode } // GetPageSize return the actual page size @@ -398,11 +409,11 @@ func (s *PdfMaroto) QrCode(code string, prop ...props.Rect) { func (s *PdfMaroto) createColSpace(actualWidthPerCol float64) { border := "" - if s.DebugMode { + if s.debugMode { border = "1" } - s.Pdf.CellFormat(actualWidthPerCol, s.rowHeight, "", border, 0.0, "C", false, 0.0, "") + s.Pdf.CellFormat(actualWidthPerCol, s.rowHeight, "", border, 0.0, "C", !s.backgroundColor.IsWhite(), 0.0, "") } func (s *PdfMaroto) drawLastFooter() { diff --git a/pkg/pdf/pdf_test.go b/pkg/pdf/pdf_test.go index 3d12034b..b9e0d0c5 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/pkg/color" "testing" "github.com/johnfercher/maroto/internal/mocks" @@ -1528,6 +1529,8 @@ func newMarotoTest(fpdf *mocks.Pdf, math *mocks.Math, font *mocks.Font, text *mo TableListHelper: tableList, } + m.SetBackgroundColor(color.NewWhite()) + return m } @@ -1539,6 +1542,7 @@ func basePdfTest(left, top, right float64) *mocks.Pdf { pdf.On("Ln", mock.Anything) pdf.On("GetFontSize").Return(1.0, 1.0) pdf.On("SetMargins", mock.AnythingOfType("float64"), mock.AnythingOfType("float64"), mock.AnythingOfType("float64")) + pdf.On("SetFillColor", mock.Anything, mock.Anything, mock.Anything) return pdf } @@ -1882,3 +1886,17 @@ func TestPdfMaroto_SetPageMargins(t *testing.T) { c.assert(t, left, top, right) } } + +func TestPdfMaroto_SetBackgroundColor(t *testing.T) { + // Arrange + pdf := basePdfTest(12.3, 19.3, 0) + m := newMarotoTest(pdf, nil, nil, nil, nil, nil, nil, nil) + white := color.NewWhite() + + // Act + m.SetBackgroundColor(white) + + // Assert + pdf.AssertCalled(t, "SetFillColor", white.Red, white.Green, white.Blue) + pdf.AssertNumberOfCalls(t, "SetFillColor", 2) +} diff --git a/pkg/props/prop.go b/pkg/props/prop.go index 97d4645a..5d223d16 100644 --- a/pkg/props/prop.go +++ b/pkg/props/prop.go @@ -1,6 +1,9 @@ package props -import "github.com/johnfercher/maroto/pkg/consts" +import ( + "github.com/johnfercher/maroto/pkg/color" + "github.com/johnfercher/maroto/pkg/consts" +) // Proportion represents a proportion from a rectangle, example: 16x9, 4x3... type Proportion struct { @@ -80,13 +83,17 @@ type TableList struct { ContentProp Font // Align is the align of the text (header and content) inside the columns Align consts.Align + // AlternatedBackground define the background color from even rows + // i.e rows with index (0, 2, 4, ..., N) will have background colorized, + // rows with index (1, 3, 5, ..., N) will not + AlternatedBackground *color.Color // HeaderContentSpace is the space between the header and the contents HeaderContentSpace float64 // Line adds a line after every content-row to separate rows. The line's spaceHeight is set to 1.0 Line bool } -// MakeValid from Rect means will make the properties from a rectangle reliable to fit inside a cell +// MakeValid from Rect will make the properties from a rectangle reliable to fit inside a cell // and define default values for a rectangle func (s *Rect) MakeValid() { if s.Percent <= 0.0 || s.Percent > 100.0 { @@ -107,7 +114,7 @@ func (s *Rect) MakeValid() { } } -// MakeValid from Barcode means will make the properties from a barcode reliable to fit inside a cell +// MakeValid from Barcode will make the properties from a barcode reliable to fit inside a cell // and define default values for a barcode func (s *Barcode) MakeValid() { if s.Percent <= 0.0 || s.Percent > 100.0 { diff --git a/pull_request_template.md b/pull_request_template.md index 6ea055f5..d8870403 100644 --- a/pull_request_template.md +++ b/pull_request_template.md @@ -22,5 +22,5 @@ - [ ] Updated pkg/pdf/example_test.go - [ ] Updated README.md - [ ] Updated all examples inside internal/examples -- [ ] New public methods has comments upside them explaining what it does +- [ ] New public methods/structs/interfaces has comments upside them explaining they responsibilities - [ ] Executed `go fmt github.com/johnfercher/maroto/...` to format all files \ No newline at end of file