From f68ccaf7a2275a48039e0ca2fc2031f9ae06a86d Mon Sep 17 00:00:00 2001 From: jfercher Date: Tue, 12 Nov 2019 21:01:10 -0300 Subject: [PATCH 1/2] Fix examples * Fix all examples; * Add an item to update examples in template; --- internal/examples/pdfs/certificate.pdf | Bin 17252 -> 17252 bytes internal/examples/pdfs/sample1.pdf | Bin 669316 -> 669319 bytes internal/examples/pdfs/zpl.pdf | Bin 209931 -> 209931 bytes internal/examples/sample1/main.go | 3 ++- internal/examples/zpl/main.go | 2 ++ pull_request_template.md | 1 + 6 files changed, 5 insertions(+), 1 deletion(-) diff --git a/internal/examples/pdfs/certificate.pdf b/internal/examples/pdfs/certificate.pdf index f5f36545b61b2bbd5d88b1a2ae850b6fbaedd5c4..05e3cb0e0787864baeb5666e8aec62ede2bb7f0c 100644 GIT binary patch delta 99 zcmaFT#`vU-af1!Z|8P7dW@4&f00Ihm3S3}@fuWI!;p7`mJpej79v}b! delta 99 zcmaFT#`vU-af1!ZWDZud$(1Zxlg(M2Cf@|oi&@Mz+p+dqPyXYe$!I!R&C!6>Ou;}Q xXz~dM9Y*uX;f}M|4U7zpj0{aD|8P7dW^AEg00Ihm3S3}@fuWI!+2k8eJpev}9x(s_ diff --git a/internal/examples/pdfs/sample1.pdf b/internal/examples/pdfs/sample1.pdf index 627ba8d2d8abbb64e8eb6b9283fded2f3ae2cb9b..c93e4f4491e049f9e5b09c28c52f1dedece41a45 100644 GIT binary patch delta 3812 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 JLf_H=^na@k=Xd}B delta 3785 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 B|3?4- diff --git a/internal/examples/pdfs/zpl.pdf b/internal/examples/pdfs/zpl.pdf index b7940f7b8449fd3151927b464320fca2d1124aa7..9c5de08e0b97aa758dc7a01a947c0c64581db3f0 100644 GIT binary patch delta 71 zcmeBv!PEVMr=f+hg=q`3mf`ehLuUQy8iveh&hbY(9p=p(7?dRa(lNCa|0s)D4i7T delta 71 zcmeBv!PEVMr=f+hg=q`3mf`ehLuUQy8iveB>=eEL diff --git a/internal/examples/sample1/main.go b/internal/examples/sample1/main.go index 6a672d95..ee6e84a9 100644 --- a/internal/examples/sample1/main.go +++ b/internal/examples/sample1/main.go @@ -46,6 +46,7 @@ func main() { m.Col(func() { id := "https://github.com/johnfercher/maroto" _ = m.Barcode(id, props.Barcode{ + Center: true, Proportion: props.Proportion{Width: 50, Height: 10}, Percent: 75, }) @@ -61,7 +62,7 @@ func main() { m.Row(12, func() { m.Col(func() { - _ = m.FileImage("internal/assets/images/goherbw.png", props.Rect{ + _ = m.FileImage("internal/assets/images/gopherbw.png", props.Rect{ Center: true, }) }) diff --git a/internal/examples/zpl/main.go b/internal/examples/zpl/main.go index 832e589a..b1051e1d 100644 --- a/internal/examples/zpl/main.go +++ b/internal/examples/zpl/main.go @@ -45,6 +45,7 @@ func main() { m.ColSpace() m.Col(func() { m.QrCode("https://github.com/johnfercher/maroto", props.Rect{ + Center: true, Percent: 75, }) }) @@ -55,6 +56,7 @@ func main() { 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{ diff --git a/pull_request_template.md b/pull_request_template.md index a3a4b1ae..90901a0d 100644 --- a/pull_request_template.md +++ b/pull_request_template.md @@ -21,5 +21,6 @@ - [ ] Updated docs/doc.go - [ ] 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 - [ ] Executed `go fmt github.com/johnfercher/maroto/...` to format all files \ No newline at end of file From 3741470112d06feabcbbafa322bd9b0ec8c18431 Mon Sep 17 00:00:00 2001 From: jfercher Date: Tue, 12 Nov 2019 22:03:31 -0300 Subject: [PATCH 2/2] Auto adjust of height * Create GetScalarFactor method in Text interface; * Create GetLinesQuantity method in Text interface; * Extract private method getLines to build lines from words, Text.Add and Text.GetLinesQuantity use this new method; * Remove HeaderHeight property from TableList prop; * Remove ContentHeight property from TableList prop; * Add VerticalPadding in Text prop; * Update mocks * Extract tablelist from pdf * Fix unit tests > Obtain the max number of lines from a column inside the row, multiply this value by the font size and sum the vertical padding; --- README.md | 175 +++++++--------------- go.mod | 1 + go.sum | 2 + internal/examples/certificate/main.go | 12 +- internal/examples/pdfs/certificate.pdf | Bin 17252 -> 17252 bytes internal/examples/pdfs/sample1.pdf | Bin 669319 -> 874372 bytes internal/examples/pdfs/zpl.pdf | Bin 209931 -> 209926 bytes internal/examples/sample1/main.go | 7 +- internal/examples/zpl/main.go | 5 + internal/font.go | 16 ++- internal/font_test.go | 13 ++ internal/mocks/font.go | 18 ++- internal/mocks/maroto.go | 71 +++++++-- internal/mocks/tablelist.go | 29 ++++ internal/mocks/text.go | 18 ++- internal/tablelist.go | 131 +++++++++++++++++ internal/tablelist_test.go | 126 ++++++++++++++++ internal/text.go | 72 +++++++--- internal/text_test.go | 102 +++++++++++++ pkg/pdf/pdf.go | 72 ++-------- pkg/pdf/pdf_test.go | 192 ++++++++++++++++++++----- pkg/props/prop.go | 158 ++++++++++---------- pkg/props/prop_test.go | 27 ++-- pull_request_template.md | 2 +- 24 files changed, 898 insertions(+), 351 deletions(-) create mode 100644 internal/mocks/tablelist.go create mode 100644 internal/tablelist.go create mode 100644 internal/tablelist_test.go diff --git a/README.md b/README.md index 16520c76..1c399a2a 100644 --- a/README.md +++ b/README.md @@ -69,161 +69,96 @@ with the code to generate the PDFs. package main import ( - "encoding/base64" "fmt" "github.com/johnfercher/maroto/pkg/consts" "github.com/johnfercher/maroto/pkg/pdf" "github.com/johnfercher/maroto/pkg/props" - "io/ioutil" + "os" ) func main() { - m := pdf.NewMaroto(consts.Portrait, consts.A4) - //m.SetBorder(true) + m := pdf.NewMaroto(consts.Portrait, consts.Letter) - byteSlices, _ := ioutil.ReadFile("internal/assets/images/biplane.jpg") - - base64 := base64.StdEncoding.EncodeToString(byteSlices) - - headerSmall, smallContent := getSmallContent() - headerMedium, mediumContent := getMediumContent() - - m.RegisterHeader(func() { - - m.Row(20, func() { - m.Col(func() { - m.Base64Image(base64, consts.Jpg, props.Rect{ - Percent: 70, - }) - }) - - m.ColSpaces(2) - - m.Col(func() { - m.QrCode("https://github.com/johnfercher/maroto", props.Rect{ - Percent: 75, - }) - }) - - m.Col(func() { - id := "https://github.com/johnfercher/maroto" - _ = m.Barcode(id, props.Barcode{ - Proportion: props.Proportion{50, 10}, - Percent: 75, - }) - m.Text(id, props.Text{ - Size: 7, - Align: consts.Center, - Top: 16, - }) + m.Row(40, func() { + m.Col(func() { + _ = m.FileImage("internal/assets/images/biplane.jpg", props.Rect{ + Center: true, + Percent: 80, }) }) - - m.Line(1.0) - - m.Row(12, func() { - m.Col(func() { - m.FileImage("internal/assets/images/gopherbw.png") + m.Col(func() { + m.Text("Gopher International Shipping, Inc.", props.Text{ + Top: 15, + Size: 20, + Extrapolate: true, }) - - m.ColSpace() - - m.Col(func() { - m.Text("Packages Report: Daily", props.Text{ - Top: 4, - }) - m.Text("Type: Small, Medium", props.Text{ - Top: 10, - }) + m.Text("1000 Shipping Gopher Golang TN 3691234 GopherLand (GL)", props.Text{ + Size: 12, + Top: 21, }) + }) + m.ColSpace() + }) - m.ColSpace() + m.Line(10) - m.Col(func() { - m.Text("20/07/1994", props.Text{ - Size: 10, - Style: consts.BoldItalic, - Top: 7.5, - Family: consts.Helvetica, - }) + 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.Line(1.0) - - m.Row(22, func() { - m.Col(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, - }) - m.Text("Brasil / São Paulo", props.Text{ - Size: 12, - Align: consts.Center, - Top: 17, - }) + m.ColSpace() + m.Col(func() { + m.QrCode("https://github.com/johnfercher/maroto", props.Rect{ + Center: true, + Percent: 75, }) }) - - m.Line(1.0) - }) - m.RegisterFooter(func() { - m.Row(40, func() { - m.Col(func() { - m.Signature("Signature 1", props.Font{ - Family: consts.Courier, - Style: consts.BoldItalic, - Size: 9, - }) - }) + m.Line(10) - m.Col(func() { - m.Signature("Signature 2") + m.Row(100, func() { + m.Col(func() { + _ = m.Barcode("https://github.com/johnfercher/maroto", props.Barcode{ + Center: true, + Percent: 70, }) - - m.Col(func() { - m.Signature("Signature 3") + m.Text("https://github.com/johnfercher/maroto", props.Text{ + Size: 20, + Align: consts.Center, + Top: 80, }) }) }) - m.Row(15, func() { + m.SetBorder(true) + + m.Row(40, func() { m.Col(func() { - m.Text("Small Packages / 39u.", props.Text{ - Top: 8, - Style: consts.Bold, + m.Text("CODE: 123412351645231245564 DATE: 20-07-1994 20:20:33", props.Text{ + Size: 15, + Top: 19, }) }) - }) - - m.TableList(headerSmall, smallContent) - - m.Row(15, func() { m.Col(func() { - m.Text("Medium Packages / 22u.", props.Text{ - Top: 8, - Style: consts.Bold, + m.Text("CA", props.Text{ + Top: 30, + Size: 85, + Align: consts.Center, }) }) }) - m.TableList(headerMedium, mediumContent, props.TableList{ - Align: consts.Center, - HeaderProp: props.Font{ - Family: consts.Courier, - Style: consts.BoldItalic, - }, - ContentProp: props.Font{ - Family: consts.Courier, - Style: consts.Italic, - }, - }) + m.SetBorder(false) - _ = m.OutputFileAndClose("internal/examples/pdfs/sample1.pdf") + err := m.OutputFileAndClose("internal/examples/pdfs/zpl.pdf") + if err != nil { + fmt.Println("Could not save PDF:", err) + os.Exit(1) + } } ``` diff --git a/go.mod b/go.mod index 8e4ea958..6a3a1f8a 100644 --- a/go.mod +++ b/go.mod @@ -4,6 +4,7 @@ go 1.13 require ( github.com/boombuler/barcode v1.0.0 + github.com/gojp/goreportcard v0.0.0-20191001233754-41818f5fd295 // indirect github.com/google/uuid v1.1.1 github.com/jung-kurt/gofpdf v1.4.2 github.com/pkg/errors v0.8.1 diff --git a/go.sum b/go.sum index 921e4daa..58edbb83 100644 --- a/go.sum +++ b/go.sum @@ -4,6 +4,8 @@ github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8 github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/gojp/goreportcard v0.0.0-20191001233754-41818f5fd295 h1:bxKAVMPMl+mdSoSbVPNExWq/ix0G2WX9h5QyAWtjAoY= +github.com/gojp/goreportcard v0.0.0-20191001233754-41818f5fd295/go.mod h1:/DA2Xpp+OaR3EHafQSnT9SKOfbG2NPQR/qp6Qr8AgIw= github.com/google/uuid v1.1.1 h1:Gkbcsh/GbpXz7lPftLA3P6TYMwjCLYm83jiFQZF/3gY= github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/jung-kurt/gofpdf v1.0.0/go.mod h1:7Id9E/uU8ce6rXgefFLlgrJj/GYY22cpxn+r32jIOes= diff --git a/internal/examples/certificate/main.go b/internal/examples/certificate/main.go index fd42705f..e887db50 100644 --- a/internal/examples/certificate/main.go +++ b/internal/examples/certificate/main.go @@ -6,9 +6,11 @@ import ( "github.com/johnfercher/maroto/pkg/pdf" "github.com/johnfercher/maroto/pkg/props" "os" + "time" ) func main() { + begin := time.Now() m := pdf.NewMaroto(consts.Landscape, consts.A4) //m.SetBorder(true) @@ -39,9 +41,10 @@ func main() { m.Col(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, + Size: 13, + Align: consts.Center, + Top: 60, + VerticalPadding: 2.0, }) }) }) @@ -63,4 +66,7 @@ func main() { fmt.Println("Could not save PDF:", err) os.Exit(1) } + + end := time.Now() + fmt.Println(end.Sub(begin)) } diff --git a/internal/examples/pdfs/certificate.pdf b/internal/examples/pdfs/certificate.pdf index 05e3cb0e0787864baeb5666e8aec62ede2bb7f0c..9f45eab08225aed51970f184777efdee69843739 100644 GIT binary patch delta 900 zcmV-~1AF}Bh5_V;0g#V>&yM6Y5XSHQ6yFV2T6z87$&rO+S+q!??H6$3O1GyIJH*a3 z{kXgdN@fOTR$O3?C3RQ*eqUA0@$-S>?!cLGxE|gg@$SdL1R2N-$~$4()|2jJXq0d* zSs8dscIdT`7Q!;laUAgBc;Jk4j*kborTbxHI6p| zMkr>rlpOtpcb~Y@%Bd!}mIUuREe0}nXu0WR-x}Xp*>c->j~_CAJM4<43(gp$gVNSW z;gk+-N8WbM%c1X7?*``^sUbPz91n``k5Edp4M0iON*n{e{g_9ePWYad8i&~XN^fwy z!-wPM;g}Ilx!uEm2*#zd64ol#ipDm(2X{s`|J#FmXB{^|G{kkQLy)@fj8kNk_uMzV z6P)_Cg-|a&1XBh|b0+Mr@i{Lv;nQ5!39+EWX&!F~>ona^>5uv+@_>3K%u7r)rW1S$ zsJEF4)xC}>tfekDhUdM`zMvyY*#7(UlWz)2^(puzJR5<3r{K1n8>Tdr6il}}AKlY} z5>romCt)}*Z*e_Sf=^h}CFN_v4o3@W&KG!`{5aBWC%?_K6PJGRQ#VpTtf(22zYzQ` zf-+MdCrapYzA${MJAGM~`*=^`U+}nO&BGO>T#?gw!w~NYJ*B!Xk;+?iYlR<6-l<3O zGh(V4G*VxGmz-k1k1h`xzQsb5k13`Ts1!;y`f>I>#PucKdL*+d?~ z6|LpvD@Z3H8_k5-GrY!n-Z>T;$rsv!`Al{3g(}LKs221&)klKoYn**4{~B%kGTWjtjB8Agu3=6;mLtAwxAY$#wC2S}B9vfS!EMjZ z4)`(8XIk)uQq0TKwJI){mX9MR7|oe>kL7C#H0Rq6zb42rJWV%NZR>sVZ@6TVKbEhZ zZmnh;Ts)ipC7u#~qJ`ezc!v+i!(Raa0RR6pJ;!g8qyw0f2n9Bic?2qxHv~eH*#s(+ zqXagyJ_U_Cll(v_0X35*K`;e2ATS_OlgL0U0XLIZL7E9NF*!0aGdMIgllno(7Be^? aFfcGMFd%Lq3NSD*FfcGNGBh@m*g}jvZLd)P delta 900 zcmV-~1AF}Bh5_V;0g#V>-;Ufg6vpr8DZV?XRJs1|vb(TbaRYmKq~v_R-|?|Iem-#A9XK-%*TefG-u*b3AOo2}c_(b!deWT?jS{XU zD+6!I4!suALRiK*jsrd%51et%@$tYJ=NzY3bow32GTSI?z`>z^Th=tKvk;oI#_>kL z2*s?HlB1vS?h{v9In@N$lHh%(#X!amEjOL)TjM(`TW%Zg@k7RMhh5Qh!5L$8P}&+P zoYJA~$lK0&IrN?C-Qav9H6&-8<3aKL5lU&c0Vv5@iDSUGAM@zb3E$IF;}Cma=?#u| z_;B1j95ccxw|f|W!MIdb!dk^z(b#79;LgbAe|vE6tm7t#hPZBZ2vYZ*af*!cp8KYE zf>YnN5bC9eV9G#g&V=1HKIerde45KTAr_Q4&EpMWou(Tq{Zao!9#GGOd5NjUbb?O- z^)^$Xy4NvC3v@$9oF@g2xq8V#iClBB$|&A>I>uN_AZ#mAB~D3O|;-Q;+0l z#8fkAq`oeHImLdDE)O$&i-jg1Q%omNDU@pT<9NfG`m;|b3Om%*7tDEHN590fi98Hf zw3e5zAf1G4G;7)&@*3-T=U8YYUuYZ5XR3=YR8h`EwV=pwfn{uWg&vn?9KxW)wOnr&kq$`N0-UHT6XTJz$P2qlkL7C#H0RqHeoc^LcuF@`F<~C#CNx|!$sfzt zrdz8U85hsezr<6*Pqfe*9PjYqc=#&-0RR6rFUN9|qyw0fp#(jXas(=q2n9Bi-UKR> zHv~JgJ_U_ClPy6h1vVfsAX1aaKr8__ll4F_0X36XL7E9MF)}hSFfcSYllno(7Bn>= aFfcGMFd%Lq3NSD*FfcGNGBh!h*g}l5Cb)+H diff --git a/internal/examples/pdfs/sample1.pdf b/internal/examples/pdfs/sample1.pdf index c93e4f4491e049f9e5b09c28c52f1dedece41a45..36d8989ba2262df1a9d471e8db06c28ed532c413 100644 GIT binary patch delta 5667 zcmZu!1z1#Fw^rH#lvWyPq$Xj8ZUh7bb?6vUx>I0i6yyVDC%^F zRy(<*U?kO-Xw2b*%DYJnt~VLvLstC|=8F*lJZ?g9vJpu&BINP&9lK|Avl%u8)C5q4eo} z{oBbVhAs9~LZuDDY{m~#nhV+v)xBNCt6qLbaq5qBHxZX3}@wLc&)Dj_!2($((q~#LlTjMd)+aW z5}_D(ZY?r#R&x(<*M$a!M)q3|YZ<&8b4;kY-{{5I@qE#OiPgn4kkgZo~j$*J7>>i(Iw~BxI^)UcqfV05AZ`+9J7JSl4UJ^bF?+G zsTAiN65e`JjBpmue5jYBuZdK0>}CcSO?OC}yT8e7XJ*KxauNSD=(eTx9qo_iic9c_;NPm#)$(dI2AC~Ms?G|IW( z^3XYuxwRUTYDj9#YLx&A`rHuwK8)|{MN?G5Rz*NZF*2MYLn=5hYs4XN8iTqEtBd5E z%gq^fS9849!SPA8Fk8Ompkv(xvzvXA_VM;3Bz-X3m__4q%d{=Casi^!R|Ma)eP-u( zQmY$JQ+KzT?VY_tHG|9uj3sFbz1h;57=n*e$p#);pYL@gOGI*x?_B0Ra~n`k>%3K| zdHSv(s;{#YcTym|@@5vR-w=W$H}iYFr*)VN

mce&zl!lzv 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 g8W~ucm|LK$votk@+BtG^o;0DJz83jhEB delta 907 zcmZpB!PEVMXF>y`>BL5v`ag>8OY;2INby|~Yc(^EU!kgbazlA;YIog(x`|2UqN=X7 z84qNhfB3f7{ByKYO@xAt&IJqpdfj`b8~!Zab!O)dt&@|F&FH#Y#OGuu_OxeXMy~Eg zrj?E@%MR^|T;S-kBNj;9*!$31-{A?5lUg zo^J8AYuom8m8k`1?$;-`x;%IO%1vyUE%z_NN7S(L>kFgSCbL^}Zf=WyzSP2#GdROG zF*BLPt(l|vXgRMz=SDu8$%>6?hmYp|-=puo<8@onn@a^Rc7NfR{rBO|pBLWNwe4PK zI{kvpfnAbcPyc`K^JkUuE&X#X9qH=z7EgD)l@H$D{LFmEpI230YY$34(%hP%B32z# z>l54dyZd(k_vz`9?8zq{S{Ronb^ zyIEQ|_AhfT_cLBsH%()fgxbk8i)ls&(k$e5#oA6eZgXu~htK*KDpL04|F6ilO-eLg z^040cPJGR+nEK$~tWCxmfq#zNtK48FAs)eO@l3e?EzA7%(O*@Mdab+_R3ehCn{shi zOn~9lH^S#WoK?-);`ZfrnE&S;SH&j?^r%ahay{7laauBql2O)8!-Ae;tj8_bH<)EE zn82NXvn#Co-s+@vTD!g`P3&aa&SL&I`lWieo6QE{`sC-a@7f>!{G7G(j%?fC`O%lZ z`JZmDyMMJP=ljzgHD1T-UhJP>F@5=p$Er7e3$D;sG8B3kk-+?T=KkmWmA#sqa+>Bd zRQ~^cwu)8x`H@E+Ym*hXom=@=U*Sl~6t%l&OE@^tlw2B)&h*R->=Vonh=Hc~JE0fjsT zE)Z>GU} 0 { + 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) + + // Draw header + s.pdf.Row(headerHeight, func() { + headerMarginTop := 2.0 + + for i, h := range header { + hs := h + is := i + + s.pdf.Col(func() { + if headerMarginTop > headerHeight { + headerMarginTop = headerHeight + } + + reason := hs + + sumOyYOffesets := headerMarginTop + s.pdf.GetCurrentOffset() + 2.5 + + s.text.Add(reason, headerTextProp, sumOyYOffesets, float64(is), qtdCols) + }) + } + }) + + // Define space between header and contents + s.pdf.Row(tableProp.HeaderContentSpace, func() { + s.pdf.ColSpace() + }) + + contentMarginTop := 2.0 + + // Draw contents + for _, content := range contents { + contentTextProp := tableProp.ContentProp.ToTextProp(tableProp.Align, 0.0, false, 1.0) + contentHeight := s.calcLinesHeight(content, contentTextProp, qtdCols) + + s.pdf.Row(contentHeight, func() { + for j, 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) + }) + } + }) + } +} + +func (s *tableList) calcLinesHeight(textList []string, textProp props.Text, qtdCols float64) float64 { + maxLines := 1.0 + + for _, text := range textList { + qtdLines := float64(s.text.GetLinesQuantity(text, textProp, qtdCols)) + if qtdLines > maxLines { + maxLines = qtdLines + } + } + + _, _, fontSize := s.font.GetFont() + + // Font size corrected by the scale factor from "mm" inside gofpdf f.k + fontHeight := fontSize / s.font.GetScaleFactor() + + return fontHeight*maxLines + 3.0 +} diff --git a/internal/tablelist_test.go b/internal/tablelist_test.go new file mode 100644 index 00000000..751496c3 --- /dev/null +++ b/internal/tablelist_test.go @@ -0,0 +1,126 @@ +package internal_test + +import ( + "fmt" + "github.com/johnfercher/maroto/internal" + "github.com/johnfercher/maroto/internal/mocks" + "github.com/johnfercher/maroto/pkg/consts" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/mock" + "testing" +) + +func TestNewTableList(t *testing.T) { + // Act + tableList := internal.NewTableList(nil, nil) + + // Assert + assert.NotNil(t, tableList) + assert.Equal(t, fmt.Sprintf("%T", tableList), "*internal.tableList") +} + +func TestTableList_Create_WhenHeaderIsNil(t *testing.T) { + // Arrange + text := &mocks.Text{} + text.On("GetLinesQuantity", mock.Anything, mock.Anything, mock.Anything).Return(1) + sut := internal.NewTableList(text, nil) + + _, contents := getContents() + + // Act + sut.Create(nil, contents) + + // Assert + text.AssertNotCalled(t, "GetLinesQuantity") +} + +func TestTableList_Create_WhenHeaderIsEmpty(t *testing.T) { + // Arrange + text := &mocks.Text{} + text.On("GetLinesQuantity", mock.Anything, mock.Anything, mock.Anything).Return(1) + sut := internal.NewTableList(text, nil) + + _, contents := getContents() + + // Act + sut.Create([]string{}, contents) + + // Assert + text.AssertNotCalled(t, "GetLinesQuantity") +} + +func TestTableList_Create_WhenContentIsNil(t *testing.T) { + // Arrange + text := &mocks.Text{} + text.On("GetLinesQuantity", mock.Anything, mock.Anything, mock.Anything).Return(1) + sut := internal.NewTableList(text, nil) + + headers, _ := getContents() + + // Act + sut.Create(headers, nil) + + // Assert + text.AssertNotCalled(t, "GetLinesQuantity") +} + +func TestTableList_Create_WhenContentIsEmpty(t *testing.T) { + // Arrange + text := &mocks.Text{} + text.On("GetLinesQuantity", mock.Anything, mock.Anything, mock.Anything).Return(1) + sut := internal.NewTableList(text, nil) + + headers, _ := getContents() + + // Act + sut.Create(headers, [][]string{}) + + // Assert + text.AssertNotCalled(t, "GetLinesQuantity") +} + +func TestTableList_Create_Happy(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) + + sut := internal.NewTableList(text, font) + sut.BindGrid(marotoGrid) + + headers, contents := getContents() + + // Act + sut.Create(headers, contents) + + // 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) +} + +func getContents() ([]string, [][]string) { + header := []string{"j = 0", "j = 1", "j = 2", "j = 4"} + + contents := [][]string{} + for i := 0; i < 20; i++ { + content := []string{} + for j := 0; j < 4; j++ { + content = append(content, fmt.Sprintf("i = %d, j = %d", i, j)) + } + contents = append(contents, content) + } + + return header, contents +} diff --git a/internal/text.go b/internal/text.go index 73ad97d5..f0ff5560 100644 --- a/internal/text.go +++ b/internal/text.go @@ -10,6 +10,7 @@ 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 } type text struct { @@ -34,37 +35,74 @@ func (s *text) Add(text string, textProp props.Text, marginTop float64, actualCo translator := s.pdf.UnicodeTranslatorFromDescriptor("") s.font.SetFont(textProp.Family, textProp.Style, textProp.Size) + // Apply Unicode textTranslated := translator(text) + stringWidth := s.pdf.GetStringWidth(textTranslated) words := strings.Split(textTranslated, " ") + accumulateOffsetY := 0.0 + // If should add one line if stringWidth < actualWidthPerCol || textProp.Extrapolate || len(words) == 1 { s.addLine(textProp, actualCol, actualWidthPerCol, marginTop, stringWidth, textTranslated) } else { - currentlySize := 0.0 - actualLine := 0 - lines := []string{} - lines = append(lines, "") - - for _, word := range words { - if s.pdf.GetStringWidth(word+" ")+currentlySize < actualWidthPerCol { - lines[actualLine] = lines[actualLine] + word + " " - currentlySize += s.pdf.GetStringWidth(word + " ") - } else { - lines = append(lines, "") - actualLine++ - lines[actualLine] = lines[actualLine] + word + " " - currentlySize = s.pdf.GetStringWidth(word + " ") - } - } + lines := s.getLines(words, actualWidthPerCol) for index, line := range lines { lineWidth := s.pdf.GetStringWidth(line) - s.addLine(textProp, actualCol, actualWidthPerCol, marginTop+float64(index)*textProp.Size/2.0, lineWidth, line) + _, _, fontSize := s.font.GetFont() + textHeight := fontSize / s.font.GetScaleFactor() + + s.addLine(textProp, actualCol, actualWidthPerCol, marginTop+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) + + translator := s.pdf.UnicodeTranslatorFromDescriptor("") + s.font.SetFont(textProp.Family, textProp.Style, textProp.Size) + + // Apply Unicode + textTranslated := translator(text) + + stringWidth := s.pdf.GetStringWidth(textTranslated) + words := strings.Split(textTranslated, " ") + + // If should add one line + if stringWidth < actualWidthPerCol || textProp.Extrapolate || len(words) == 1 { + return 1 + } + + lines := s.getLines(words, actualWidthPerCol) + return len(lines) +} + +func (s *text) getLines(words []string, actualWidthPerCol float64) []string { + currentlySize := 0.0 + actualLine := 0 + + lines := []string{} + lines = append(lines, "") + + for _, word := range words { + if s.pdf.GetStringWidth(word+" ")+currentlySize < actualWidthPerCol { + lines[actualLine] = lines[actualLine] + word + " " + currentlySize += s.pdf.GetStringWidth(word + " ") + } else { + lines = append(lines, "") + actualLine++ + lines[actualLine] = lines[actualLine] + word + " " + currentlySize = s.pdf.GetStringWidth(word + " ") + } + } + + return lines +} + func (s *text) addLine(textProp props.Text, actualCol, actualWidthPerCol, marginTop, stringWidth float64, textTranslated string) { left, top, _, _ := s.pdf.GetMargins() diff --git a/internal/text_test.go b/internal/text_test.go index 137e3453..d3a6b8fd 100644 --- a/internal/text_test.go +++ b/internal/text_test.go @@ -18,6 +18,98 @@ func TestNewText(t *testing.T) { assert.Equal(t, fmt.Sprintf("%T", text), "*internal.text") } +func TestText_GetLinesQuantity_WhenStringSmallerThanLimits(t *testing.T) { + // Arrange + pdf := &mocks.Pdf{} + pdf.On("UnicodeTranslatorFromDescriptor", mock.Anything).Return(func(text string) string { + return text + }) + 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) + + // Act + lines := sut.GetLinesQuantity("AnyText With Spaces", props.Text{}, 2) + + // Assert + assert.Equal(t, lines, 1) +} + +func TestText_GetLinesQuantity_WhenHasOneWord(t *testing.T) { + // Arrange + pdf := &mocks.Pdf{} + pdf.On("UnicodeTranslatorFromDescriptor", mock.Anything).Return(func(text string) string { + return text + }) + 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) + + // Act + lines := sut.GetLinesQuantity("OneWord", props.Text{}, 2) + + // Assert + assert.Equal(t, lines, 1) +} + +func TestText_GetLinesQuantity_WhenExtrapolate(t *testing.T) { + // Arrange + pdf := &mocks.Pdf{} + pdf.On("UnicodeTranslatorFromDescriptor", mock.Anything).Return(func(text string) string { + return text + }) + 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) + + // Act + lines := sut.GetLinesQuantity("Many words", props.Text{Extrapolate: true}, 2) + + // Assert + assert.Equal(t, lines, 1) +} + +func TestText_GetLinesQuantity_WhenHasToBreakLines(t *testing.T) { + // Arrange + pdf := &mocks.Pdf{} + pdf.On("UnicodeTranslatorFromDescriptor", mock.Anything).Return(func(text string) string { + return text + }) + 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) + + // Act + lines := sut.GetLinesQuantity("Many words", props.Text{}, 2) + + // Assert + assert.Equal(t, lines, 3) +} + func TestText_Add(t *testing.T) { cases := []struct { name string @@ -49,6 +141,8 @@ func TestText_Add(t *testing.T) { }, func() *mocks.Font { _font := &mocks.Font{} + _font.On("GetScaleFactor").Return(1.0) + _font.On("GetFont").Return(consts.Arial, consts.Bold, 1.0) _font.On("SetFont", mock.Anything, mock.Anything, mock.Anything) return _font }, @@ -88,6 +182,8 @@ func TestText_Add(t *testing.T) { }, func() *mocks.Font { _font := &mocks.Font{} + _font.On("GetScaleFactor").Return(1.0) + _font.On("GetFont").Return(consts.Arial, consts.Bold, 1.0) _font.On("SetFont", mock.Anything, mock.Anything, mock.Anything) return _font }, @@ -128,6 +224,8 @@ func TestText_Add(t *testing.T) { }, func() *mocks.Font { _font := &mocks.Font{} + _font.On("GetScaleFactor").Return(1.0) + _font.On("GetFont").Return(consts.Arial, consts.Bold, 1.0) _font.On("SetFont", mock.Anything, mock.Anything, mock.Anything) return _font }, @@ -168,6 +266,8 @@ func TestText_Add(t *testing.T) { }, func() *mocks.Font { _font := &mocks.Font{} + _font.On("GetScaleFactor").Return(1.0) + _font.On("GetFont").Return(consts.Arial, consts.Bold, 1.0) _font.On("SetFont", mock.Anything, mock.Anything, mock.Anything) return _font }, @@ -209,6 +309,8 @@ func TestText_Add(t *testing.T) { }, func() *mocks.Font { _font := &mocks.Font{} + _font.On("GetScaleFactor").Return(1.0) + _font.On("GetFont").Return(consts.Arial, consts.Bold, 1.0) _font.On("SetFont", mock.Anything, mock.Anything, mock.Anything) return _font }, diff --git a/pkg/pdf/pdf.go b/pkg/pdf/pdf.go index 3b0bece2..71c6b95b 100644 --- a/pkg/pdf/pdf.go +++ b/pkg/pdf/pdf.go @@ -26,6 +26,7 @@ type Maroto interface { GetBorder() bool GetPageSize() (float64, float64) GetCurrentPage() int + GetCurrentOffset() float64 // Outside Col/Row Components TableList(header []string, contents [][]string, prop ...props.TableList) @@ -53,6 +54,7 @@ type PdfMaroto struct { SignHelper internal.Signature Image internal.Image Code internal.Code + TableListHelper internal.TableList pageIndex int offsetY float64 rowHeight float64 @@ -84,6 +86,8 @@ func NewMaroto(orientation consts.Orientation, pageSize consts.PageSize) Maroto code := internal.NewCode(fpdf, math) + tableList := internal.NewTableList(text, font) + maroto := &PdfMaroto{ Pdf: fpdf, Math: math, @@ -92,11 +96,14 @@ func NewMaroto(orientation consts.Orientation, pageSize consts.PageSize) Maroto SignHelper: signature, Image: image, Code: code, + TableListHelper: tableList, pageSize: pageSize, orientation: orientation, calculationMode: false, } + maroto.TableListHelper.BindGrid(maroto) + maroto.Font.SetFamily(consts.Arial) maroto.Font.SetStyle(consts.Bold) maroto.Font.SetSize(16) @@ -132,6 +139,11 @@ func (s *PdfMaroto) GetCurrentPage() int { return s.pageIndex } +// GetCurrentOffset obtain the current offset in y axis +func (s *PdfMaroto) GetCurrentOffset() float64 { + return s.offsetY +} + // Signature add a space for a signature inside a cell, // the space will have a line and a text below func (s *PdfMaroto) Signature(label string, prop ...props.Font) { @@ -145,7 +157,7 @@ func (s *PdfMaroto) Signature(label string, prop ...props.Font) { qtdCols := float64(len(s.colsClosures)) sumOfYOffsets := s.offsetY + s.rowHeight - s.SignHelper.AddSpaceFor(label, signProp.ToTextProp(consts.Center, 0.0), qtdCols, sumOfYOffsets, s.rowColCount) + s.SignHelper.AddSpaceFor(label, signProp.ToTextProp(consts.Center, 0.0, false, 0), qtdCols, sumOfYOffsets, s.rowColCount) } // TableList create a table with multiple rows and columns. @@ -153,63 +165,7 @@ func (s *PdfMaroto) Signature(label string, prop ...props.Font) { // Headers have bold style, and localized at the top of table. // Contents are array of arrays. Each array is one line. func (s *PdfMaroto) TableList(header []string, contents [][]string, prop ...props.TableList) { - if len(header) == 0 { - return - } - - if len(contents) == 0 { - return - } - - tableProp := props.TableList{} - if len(prop) > 0 { - tableProp = prop[0] - } - - tableProp.MakeValid() - - s.Row(tableProp.HeaderHeight, func() { - headerMarginTop := 2.0 - qtdCols := float64(len(header)) - - for i, h := range header { - hs := h - is := i - - s.Col(func() { - if headerMarginTop > s.rowHeight { - headerMarginTop = s.rowHeight - } - - reason := hs - - sumOyYOffesets := headerMarginTop + s.offsetY + 2.5 - - s.TextHelper.Add(reason, tableProp.HeaderProp.ToTextProp(tableProp.Align, 0.0), sumOyYOffesets, float64(is), qtdCols) - }) - } - }) - - s.Row(tableProp.HeaderContentSpace, func() { - s.ColSpace() - }) - - contentMarginTop := 2.0 - - for _, content := range contents { - s.Row(tableProp.ContentHeight, func() { - for j, c := range content { - cs := c - js := j - hs := float64(len(header)) - sumOyYOffesets := contentMarginTop + s.offsetY + 2.0 - - s.Col(func() { - s.TextHelper.Add(cs, tableProp.ContentProp.ToTextProp(tableProp.Align, 0.0), sumOyYOffesets, float64(js), hs) - }) - } - }) - } + s.TableListHelper.Create(header, contents, prop...) } // SetBorder enable the draw of lines in every cell. diff --git a/pkg/pdf/pdf_test.go b/pkg/pdf/pdf_test.go index 3e87d895..806c1cbd 100644 --- a/pkg/pdf/pdf_test.go +++ b/pkg/pdf/pdf_test.go @@ -299,7 +299,8 @@ func TestPdfMaroto_Signature(t *testing.T) { signature := c.signature() pdf := basePdfTest() math := baseMathTest() - m := newMarotoTest(pdf, math, nil, nil, signature, nil, nil) + tableList := baseTableList() + m := newMarotoTest(pdf, math, nil, nil, signature, nil, nil, tableList) // Act c.act(m) @@ -422,7 +423,8 @@ func TestPdfMaroto_Text(t *testing.T) { text := baseTextTest() pdf := basePdfTest() math := baseMathTest() - m := newMarotoTest(pdf, math, nil, text, nil, nil, nil) + tableList := baseTableList() + m := newMarotoTest(pdf, math, nil, text, nil, nil, nil, tableList) // Act c.act(m) @@ -590,8 +592,9 @@ func TestPdfMaroto_FileImage(t *testing.T) { pdf := basePdfTest() math := baseMathTest() image := c.image() + tableList := baseTableList() - m := newMarotoTest(pdf, math, nil, nil, nil, image, nil) + m := newMarotoTest(pdf, math, nil, nil, nil, image, nil, tableList) // Act c.act(m) @@ -759,8 +762,9 @@ func TestPdfMaroto_Base64Image(t *testing.T) { pdf := basePdfTest() math := baseMathTest() image := c.image() + tableList := baseTableList() - m := newMarotoTest(pdf, math, nil, nil, nil, image, nil) + m := newMarotoTest(pdf, math, nil, nil, nil, image, nil, tableList) // Act c.act(m) @@ -914,8 +918,9 @@ func TestPdfMaroto_QrCode(t *testing.T) { pdf := basePdfTest() math := baseMathTest() code := c.code() + tableList := baseTableList() - m := newMarotoTest(pdf, math, nil, nil, nil, nil, code) + m := newMarotoTest(pdf, math, nil, nil, nil, nil, code, tableList) // Act c.act(m) @@ -997,8 +1002,9 @@ func TestPdfMaroto_Barcode(t *testing.T) { pdf := basePdfTest() math := baseMathTest() code := c.code() + tableList := baseTableList() - m := newMarotoTest(pdf, math, nil, nil, nil, nil, code) + m := newMarotoTest(pdf, math, nil, nil, nil, nil, code, tableList) // Act c.act(m) @@ -1112,8 +1118,9 @@ func TestPdfMaroto_Row(t *testing.T) { // Arrange pdf := basePdfTest() math := baseMathTest() + tableList := baseTableList() - m := newMarotoTest(pdf, math, nil, nil, nil, nil, nil) + m := newMarotoTest(pdf, math, nil, nil, nil, nil, nil, tableList) calledTimes := 0 // Act @@ -1176,8 +1183,9 @@ func TestPdfMaroto_Line(t *testing.T) { // Arrange pdf := c.pdf() math := baseMathTest() + tableList := baseTableList() - m := newMarotoTest(pdf, math, nil, nil, nil, nil, nil) + m := newMarotoTest(pdf, math, nil, nil, nil, nil, nil, tableList) // Act c.act(m) @@ -1253,8 +1261,9 @@ func TestPdfMaroto_ColSpace(t *testing.T) { // Arrange pdf := basePdfTest() math := baseMathTest() + tableList := baseTableList() - m := newMarotoTest(pdf, math, nil, nil, nil, nil, nil) + m := newMarotoTest(pdf, math, nil, nil, nil, nil, nil, tableList) // Act c.act(m) @@ -1330,8 +1339,9 @@ func TestPdfMaroto_ColSpaces(t *testing.T) { // Arrange pdf := basePdfTest() math := baseMathTest() + tableList := baseTableList() - m := newMarotoTest(pdf, math, nil, nil, nil, nil, nil) + m := newMarotoTest(pdf, math, nil, nil, nil, nil, nil, tableList) // Act c.act(m) @@ -1420,7 +1430,9 @@ func TestPdfMaroto_Output(t *testing.T) { // Arrange pdf := c.pdf() math := baseMathTest() - m := newMarotoTest(pdf, math, nil, nil, nil, nil, nil) + tableList := baseTableList() + + m := newMarotoTest(pdf, math, nil, nil, nil, nil, nil, tableList) footerCalls := 0 // Act @@ -1490,8 +1502,9 @@ func TestPdfMaroto_OutputFileAndClose(t *testing.T) { // Arrange pdf := c.pdf() math := baseMathTest() + tableList := baseTableList() - m := newMarotoTest(pdf, math, nil, nil, nil, nil, nil) + m := newMarotoTest(pdf, math, nil, nil, nil, nil, nil, tableList) // Act err := m.OutputFileAndClose("AnyName") @@ -1502,15 +1515,17 @@ func TestPdfMaroto_OutputFileAndClose(t *testing.T) { } } -func newMarotoTest(fpdf *mocks.Pdf, math *mocks.Math, font *mocks.Font, text *mocks.Text, signature *mocks.Signature, image *mocks.Image, code *mocks.Code) pdf.Maroto { +func newMarotoTest(fpdf *mocks.Pdf, math *mocks.Math, font *mocks.Font, text *mocks.Text, + signature *mocks.Signature, image *mocks.Image, code *mocks.Code, tableList *mocks.TableList) pdf.Maroto { m := &pdf.PdfMaroto{ - Pdf: fpdf, - Math: math, - Font: font, - TextHelper: text, - SignHelper: signature, - Image: image, - Code: code, + Pdf: fpdf, + Math: math, + Font: font, + TextHelper: text, + SignHelper: signature, + Image: image, + Code: code, + TableListHelper: tableList, } return m @@ -1522,6 +1537,7 @@ func basePdfTest() *mocks.Pdf { pdf.On("GetMargins").Return(10.0, 10.0, 10.0, 10.0) pdf.On("CellFormat", mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything) pdf.On("Ln", mock.Anything) + pdf.On("GetFontSize").Return(1.0, 1.0) return pdf } @@ -1534,9 +1550,22 @@ func baseMathTest() *mocks.Math { func baseTextTest() *mocks.Text { text := &mocks.Text{} text.On("Add", mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything) + text.On("GetLinesQuantity", mock.Anything, mock.Anything, mock.Anything).Return(1) return text } +func baseFontTest() *mocks.Font { + font := &mocks.Font{} + font.On("GetFontSize").Return(1.0, 1.0) + return font +} + +func baseTableList() *mocks.TableList { + tableList := &mocks.TableList{} + tableList.On("Create", mock.Anything, mock.Anything, mock.Anything).Return(nil) + return tableList +} + func getContents() ([]string, [][]string) { header := []string{"j = 0", "j = 1", "j = 2", "j = 4"} @@ -1569,14 +1598,30 @@ func TestPdfMaroto_RegisterFooter(t *testing.T) { }, }, { - "Execute twice when create a second page", + "Execute 6 times when create a 6 pages", func(m pdf.Maroto) { - header, contents := getContents() - m.TableList(header, contents) + headers, contents := getContents() + m.Row(20, func() { + for _, header := range headers { + m.Col(func() { + m.Text(header) + }) + } + }) + + for _, content := range contents { + m.Row(20, func() { + for _, txt := range content { + m.Col(func() { + m.Text(txt) + }) + } + }) + } }, true, func(t *testing.T, footerCalls int) { - assert.Equal(t, footerCalls, 2) + assert.Equal(t, footerCalls, 6) }, }, { @@ -1597,9 +1642,11 @@ func TestPdfMaroto_RegisterFooter(t *testing.T) { pdf := basePdfTest() math := baseMathTest() text := baseTextTest() + font := baseFontTest() + tableList := baseTableList() footerCalls := 0 - m := newMarotoTest(pdf, math, nil, text, nil, nil, nil) + m := newMarotoTest(pdf, math, font, text, nil, nil, nil, tableList) if c.hasFooter { m.RegisterFooter(func() { @@ -1635,14 +1682,30 @@ func TestPdfMaroto_RegisterHeader(t *testing.T) { }, }, { - "Execute twice when create a second page", + "Execute 6 times when create a 6 pages", func(m pdf.Maroto) { - header, contents := getContents() - m.TableList(header, contents) + headers, contents := getContents() + m.Row(20, func() { + for _, header := range headers { + m.Col(func() { + m.Text(header) + }) + } + }) + + for _, content := range contents { + m.Row(20, func() { + for _, txt := range content { + m.Col(func() { + m.Text(txt) + }) + } + }) + } }, true, func(t *testing.T, headerCalls int) { - assert.Equal(t, headerCalls, 2) + assert.Equal(t, headerCalls, 6) }, }, { @@ -1663,9 +1726,12 @@ func TestPdfMaroto_RegisterHeader(t *testing.T) { pdf := basePdfTest() math := baseMathTest() text := baseTextTest() + font := baseFontTest() + tableList := baseTableList() + headerCalls := 0 - m := newMarotoTest(pdf, math, nil, text, nil, nil, nil) + m := newMarotoTest(pdf, math, font, text, nil, nil, nil, tableList) if c.hasClosure { m.RegisterHeader(func() { @@ -1698,11 +1764,27 @@ func TestPdfMaroto_GetCurrentPage(t *testing.T) { { "When has a secund page, page index should be 2", func(m pdf.Maroto) { - header, contents := getContents() - m.TableList(header, contents) + headers, contents := getContents() + m.Row(20, func() { + for _, header := range headers { + m.Col(func() { + m.Text(header) + }) + } + }) + + for _, content := range contents { + m.Row(20, func() { + for _, txt := range content { + m.Col(func() { + m.Text(txt) + }) + } + }) + } }, func(t *testing.T, pageIndex int) { - assert.Equal(t, pageIndex, 1) + assert.Equal(t, pageIndex, 5) }, }, } @@ -1712,8 +1794,10 @@ func TestPdfMaroto_GetCurrentPage(t *testing.T) { pdf := basePdfTest() math := baseMathTest() text := baseTextTest() + font := baseFontTest() + tableList := baseTableList() - m := newMarotoTest(pdf, math, nil, text, nil, nil, nil) + m := newMarotoTest(pdf, math, font, text, nil, nil, nil, tableList) // Act c.act(m) @@ -1723,3 +1807,43 @@ func TestPdfMaroto_GetCurrentPage(t *testing.T) { c.assert(t, pageIndex) } } + +func TestPdfMaroto_GetCurrentPage_WhenCreateOffsetIsZero(t *testing.T) { + // Arrange + pdf := basePdfTest() + math := baseMathTest() + text := baseTextTest() + font := baseFontTest() + tableList := baseTableList() + + m := newMarotoTest(pdf, math, font, text, nil, nil, nil, tableList) + + // Act + offset := m.GetCurrentOffset() + + // Assert + assert.Zero(t, offset) +} + +func TestPdfMaroto_GetCurrentPage_WhenIsNotZero(t *testing.T) { + // Arrange + pdf := basePdfTest() + math := baseMathTest() + text := baseTextTest() + font := baseFontTest() + tableList := baseTableList() + + m := newMarotoTest(pdf, math, font, text, nil, nil, nil, tableList) + + m.Row(20, func() { + m.Col(func() { + m.Text("test") + }) + }) + + // Act + offset := m.GetCurrentOffset() + + // Assert + assert.Equal(t, offset, float64(20)) +} diff --git a/pkg/props/prop.go b/pkg/props/prop.go index e9af8509..4b019d1a 100644 --- a/pkg/props/prop.go +++ b/pkg/props/prop.go @@ -56,6 +56,8 @@ type Text struct { // Extrapolate define if the text will automatically add a new line when // text reach the right cell boundary Extrapolate bool + // VerticalPadding define an additional space between lines + VerticalPadding float64 } // Font represents properties from a text @@ -70,13 +72,9 @@ type Font struct { // TableList represents properties from a TableList type TableList struct { - // HeaderHeight is the height of the cell with headers - HeaderHeight float64 // HeaderProp is the custom properties of the text inside // the headers HeaderProp Font - // ContentHeight is the height of the cells with contents - ContentHeight float64 // ContentProp is the custom properties of the text inside // the contents ContentProp Font @@ -88,106 +86,112 @@ type TableList struct { // MakeValid from Rect means will make the properties from a rectangle reliable to fit inside a cell // and define default values for a rectangle -func (r *Rect) MakeValid() { - if r.Percent <= 0.0 || r.Percent > 100.0 { - r.Percent = 100.0 +func (s *Rect) MakeValid() { + if s.Percent <= 0.0 || s.Percent > 100.0 { + s.Percent = 100.0 } - if r.Center { - r.Left = 0 - r.Top = 0 + if s.Center { + s.Left = 0 + s.Top = 0 } - if r.Left < 0.0 { - r.Left = 0.0 + if s.Left < 0.0 { + s.Left = 0.0 } - if r.Top < 0.0 { - r.Top = 0 + if s.Top < 0.0 { + s.Top = 0 } } // MakeValid from Barcode means will make the properties from a barcode reliable to fit inside a cell // and define default values for a barcode -func (r *Barcode) MakeValid() { - if r.Percent <= 0.0 || r.Percent > 100.0 { - r.Percent = 100.0 +func (s *Barcode) MakeValid() { + if s.Percent <= 0.0 || s.Percent > 100.0 { + s.Percent = 100.0 } - if r.Center { - r.Left = 0 - r.Top = 0 + if s.Center { + s.Left = 0 + s.Top = 0 } - if r.Left < 0.0 { - r.Left = 0.0 + if s.Left < 0.0 { + s.Left = 0.0 } - if r.Top < 0.0 { - r.Top = 0 + if s.Top < 0.0 { + s.Top = 0 } - if r.Proportion.Width <= 0 { - r.Proportion.Width = 1 + if s.Proportion.Width <= 0 { + s.Proportion.Width = 1 } - if r.Proportion.Height <= 0 { - r.Proportion.Height = 1 + if s.Proportion.Height <= 0 { + s.Proportion.Height = 1 } - if r.Proportion.Height > r.Proportion.Width*0.20 { - r.Proportion.Height = r.Proportion.Width * 0.20 - } else if r.Proportion.Height < r.Proportion.Width*0.10 { - r.Proportion.Height = r.Proportion.Width * 0.10 + if s.Proportion.Height > s.Proportion.Width*0.20 { + s.Proportion.Height = s.Proportion.Width * 0.20 + } else if s.Proportion.Height < s.Proportion.Width*0.10 { + s.Proportion.Height = s.Proportion.Width * 0.10 } } // MakeValid from Text define default values for a Text -func (f *Text) MakeValid() { - if f.Family == "" { - f.Family = consts.Arial +func (s *Text) MakeValid() { + if s.Family == "" { + s.Family = consts.Arial } - if f.Style == "" { - f.Style = consts.Normal + if s.Style == "" { + s.Style = consts.Normal } - if f.Align == "" { - f.Align = consts.Left + if s.Align == "" { + s.Align = consts.Left } - if f.Size == 0.0 { - f.Size = 10.0 + if s.Size == 0.0 { + s.Size = 10.0 } - if f.Top < 0.0 { - f.Top = 0.0 + if s.Top < 0.0 { + s.Top = 0.0 + } + + if s.VerticalPadding < 0 { + s.VerticalPadding = 0 } } // MakeValid from Font define default values for a Signature -func (f *Font) MakeValid() { - if f.Family == "" { - f.Family = consts.Arial +func (s *Font) MakeValid() { + if s.Family == "" { + s.Family = consts.Arial } - if f.Style == "" { - f.Style = consts.Bold + if s.Style == "" { + s.Style = consts.Bold } - if f.Size == 0.0 { - f.Size = 8.0 + if s.Size == 0.0 { + s.Size = 8.0 } } // ToTextProp from Font return a Text based on Font -func (f *Font) ToTextProp(align consts.Align, top float64) Text { +func (s *Font) ToTextProp(align consts.Align, top float64, extrapolate bool, verticalPadding float64) Text { textProp := Text{ - Family: f.Family, - Style: f.Style, - Size: f.Size, - Align: align, - Top: top, + Family: s.Family, + Style: s.Style, + Size: s.Size, + Align: align, + Top: top, + Extrapolate: extrapolate, + VerticalPadding: verticalPadding, } textProp.MakeValid() @@ -196,44 +200,36 @@ func (f *Font) ToTextProp(align consts.Align, top float64) Text { } // MakeValid from TableList define default values for a TableList -func (t *TableList) MakeValid() { - if t.HeaderProp.Size == 0.0 { - t.HeaderProp.Size = 10.0 - } - - if t.HeaderProp.Family == "" { - t.HeaderProp.Family = consts.Arial - } - - if t.HeaderProp.Style == "" { - t.HeaderProp.Style = consts.Bold +func (s *TableList) MakeValid() { + if s.HeaderProp.Size == 0.0 { + s.HeaderProp.Size = 10.0 } - if t.HeaderHeight == 0.0 { - t.HeaderHeight = 7.0 + if s.HeaderProp.Family == "" { + s.HeaderProp.Family = consts.Arial } - if t.Align == "" { - t.Align = consts.Left + if s.HeaderProp.Style == "" { + s.HeaderProp.Style = consts.Bold } - if t.ContentProp.Size == 0.0 { - t.ContentProp.Size = 10.0 + if s.Align == "" { + s.Align = consts.Left } - if t.ContentProp.Family == "" { - t.ContentProp.Family = consts.Arial + if s.ContentProp.Size == 0.0 { + s.ContentProp.Size = 10.0 } - if t.ContentProp.Style == "" { - t.ContentProp.Style = consts.Normal + if s.ContentProp.Family == "" { + s.ContentProp.Family = consts.Arial } - if t.ContentHeight == 0.0 { - t.ContentHeight = 5.0 + if s.ContentProp.Style == "" { + s.ContentProp.Style = consts.Normal } - if t.HeaderContentSpace == 0.0 { - t.HeaderContentSpace = 4.0 + if s.HeaderContentSpace == 0.0 { + s.HeaderContentSpace = 4.0 } } diff --git a/pkg/props/prop_test.go b/pkg/props/prop_test.go index 5229c0b6..c503987e 100644 --- a/pkg/props/prop_test.go +++ b/pkg/props/prop_test.go @@ -228,6 +228,15 @@ func TestText_MakeValid(t *testing.T) { assert.Equal(t, prop.Top, 0.0) }, }, + { + "When vertical padding is less than 0", + &props.Text{ + VerticalPadding: -5.0, + }, + func(t *testing.T, prop *props.Text) { + assert.Equal(t, prop.VerticalPadding, 0.0) + }, + }, } for _, c := range cases { @@ -334,15 +343,6 @@ func TestTableListProp_MakeValid(t *testing.T) { assert.Equal(t, m.HeaderProp.Family, consts.Arial) }, }, - { - "When HeaderHeight is 0.0", - &props.TableList{ - HeaderHeight: 0.0, - }, - func(t *testing.T, m *props.TableList) { - assert.Equal(t, m.HeaderHeight, 7.0) - }, - }, { "When Align is empty", &props.TableList{ @@ -385,15 +385,6 @@ func TestTableListProp_MakeValid(t *testing.T) { assert.Equal(t, m.ContentProp.Family, consts.Arial) }, }, - { - "When ContentHeight is 0.0", - &props.TableList{ - ContentHeight: 0.0, - }, - func(t *testing.T, m *props.TableList) { - assert.Equal(t, m.ContentHeight, 5.0) - }, - }, { "When HeaderContentSpace is 0.0", &props.TableList{ diff --git a/pull_request_template.md b/pull_request_template.md index 90901a0d..6ea055f5 100644 --- a/pull_request_template.md +++ b/pull_request_template.md @@ -21,6 +21,6 @@ - [ ] Updated docs/doc.go - [ ] Updated pkg/pdf/example_test.go - [ ] Updated README.md -- [ ] Updated all examples inside internal/examples +- [ ] Updated all examples inside internal/examples - [ ] New public methods has comments upside them explaining what it does - [ ] Executed `go fmt github.com/johnfercher/maroto/...` to format all files \ No newline at end of file