From 30db3448f752261d3753db27edebf3364f3623dc Mon Sep 17 00:00:00 2001 From: Adrian-George Bostan Date: Wed, 29 Apr 2020 22:22:00 +0300 Subject: [PATCH 1/2] Add support for multi-block styled paragraphs (#331) * Add support for multi-block styled paragraphs * Fix context space when drawing division inside tables * Update context height when drawing tables * Update advanced invoice test case * Add basic multi-block styled paragraph test case --- creator/division.go | 2 +- creator/invoice_test.go | 9 ++- creator/styled_paragraph.go | 105 ++++++++++++++++++------------- creator/styled_paragraph_test.go | 12 ++++ creator/table.go | 19 +++--- 5 files changed, 89 insertions(+), 58 deletions(-) diff --git a/creator/division.go b/creator/division.go index 217be597..bee6a8db 100644 --- a/creator/division.go +++ b/creator/division.go @@ -112,7 +112,7 @@ func (div *Division) GeneratePageBlocks(ctx DrawContext) ([]*Block, DrawContext, ctx.X += div.margins.left ctx.Y += div.margins.top ctx.Width -= div.margins.left + div.margins.right - ctx.Height -= div.margins.top + ctx.Height -= div.margins.top + div.margins.bottom } // Set the inline mode of the division to the context. diff --git a/creator/invoice_test.go b/creator/invoice_test.go index add25fc7..1b51c083 100644 --- a/creator/invoice_test.go +++ b/creator/invoice_test.go @@ -198,9 +198,12 @@ func TestInvoiceAdvanced(t *testing.T) { invoice.SetTotal("$85.00") // Set invoice content sections. - invoice.SetNotes("Notes", "Thank you for your business.") - invoice.SetTerms("Terms and conditions", "Full refund for 60 days after purchase.") - invoice.AddSection("Custom section", "This is a custom section.") + invoice.SetNotes("NOTES", "Thank you for your business.") + invoice.SetTerms("I. TERMS OF PAYMENT", "Net 30 days on all invoices. In addition, Buyer shall pay all sales, use, customs, excise or other taxes presently or hereafter payable in regards to this transaction, and Buyer shall reimburse Seller for any such taxes or charges paid by the Seller.\nSeller shall have the continuing right to approve Buyer’s credit. Seller may at any time demand advance payment, additional security or guarantee of prompt payment. If Buyer refuses to give the payment, security or guarantee demanded, Seller may terminate the Agreement, refuse to deliver any undelivered goods and Buyer shall immediately become liable to Seller for the unpaid price of all goods delivered & for damages as provided in Paragraph V below. Buyer agrees to pay Seller cost of collection of overdue invoices, including reasonable attorney’s fees incurred by Seller in collecting said sums. F.O.B. point shall be point of SHIP TO on face hereof.") + invoice.AddSection("II. DELIVERY, TOLERANCES, WEIGHT", "Upon due tender of goods for delivery at the F.O.B. point all risk of loss or damage and other incident of ownership pass to Buyer, but Seller retains a security interest in the goods until purchase price is paid. All deliveries are subject to weight at shipping point which shall govern.") + invoice.AddSection("III. WARRANTIES", "Seller warrants that goods sold hereunder are merchantable UNLESS manufactured in conformance with Buyer’s particular specification, and that Seller conveys good title thereto. IN NO EVENT WILL SELLER BE LIABLE FOR CONSEQUENTIAL DAMAGES EVEN IF CUSTOMER HAS NOT BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. EXCEPT FOR THE EXPRESS WARRANTY STATED IN THIS PARAGRAPH IV, SELLER GRANTS NO WARRANTIES, EITHER EXPRESS OR IMPLIED HEREIN, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, AND THIS STATED EXPRESS WARRANTY IS IN LIEU OF ALL LIABILITIES OR OBLIGATIONS OF SELLER FOR DAMAGES INCLUDING BUT NOT LIMITED TO, CONSEQUENTIAL DAMAGES OCCURRING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF ANY GOODS SOLD HEREUNDER. Seller specifically does not warrant the accuracy of sufficiency of any advice or recommendations given to Buyer in connection with the sale of goods hereunder.") + invoice.AddSection("IV. FORCE MAJEURE", "Seller shall not be liable for any damages resulting from: any delay or failure of performance arising from any cause not reasonably within Seller’s control; accidents to, breakdowns or mechanical failure of machinery or equipment, however caused; strikes or other labor troubles, shortage of labor, transportation, raw materials, energy sources, or failure of ususal means of supply; fire; flood; war, declared or undeclared; insurrection; riots; acts of God or the public enemy; or priorities, allocations or limitations or other acts required or requested by Federal, State or local governments or any of their sub-divisions, bureaus or agencies. Seller may, at its option, cancel this Agreement or delay performance hereunder for any period reasonably necessary due to any of the foregoing, during which time this Agreement shall remain in full force and effect. Seller shall have the further right to then allocate its available goods between its own uses and its customers in such manner as Seller may consider equitable.") + invoice.AddSection("V. PATENT INDEMNITY", "Seller shall defend and hold Buyer harmless for any action against Seller based in a claim that Buyer’s sale or use of goods normally offered for sale by Seller, supplied by Seller hereunder, and while in the form, state or conditions supplies constitutes infringement of any United States letters patent; provided Seller shall receive prompt written notice of the claim or action, and Buyer shall give Seller authority, information and assistance at Seller’s expense. Buyer shall defend and hold Seller harmless for any action against Seller or its suppliers based in a claim that the manufacture or sale of goods hereunder constitutes infringement of any United States letters patent, if such goods were manufactured pursuant to Buyer’s designs, specifications and /or formulae, and were not normally offered for sale by Seller; provided Buyer shall receive prompt written notice of the claim or action and Seller shall give Buyer authority, information and assistance at Buyer’s expense. Buyer and Seller agree that the foregoing constitutes the parties’ entire liability for claims or actions based on patent infringement.") // Customize note styles. noteStyle := invoice.NoteStyle() diff --git a/creator/styled_paragraph.go b/creator/styled_paragraph.go index f01680db..05a4a0b7 100644 --- a/creator/styled_paragraph.go +++ b/creator/styled_paragraph.go @@ -507,7 +507,7 @@ func (p *StyledParagraph) wrapText() error { return nil } -// GeneratePageBlocks generates the page blocks. Multiple blocks are generated +// GeneratePageBlocks generates the page blocks. Multiple blocks are generated // if the contents wrap over multiple pages. Implements the Drawable interface. func (p *StyledParagraph) GeneratePageBlocks(ctx DrawContext) ([]*Block, DrawContext, error) { origContext := ctx @@ -523,29 +523,9 @@ func (p *StyledParagraph) GeneratePageBlocks(ctx DrawContext) ([]*Block, DrawCon // Use available space. p.SetWidth(ctx.Width) - - if p.Height() > ctx.Height { - // Goes out of the bounds. Write on a new template instead and create a new context at upper - // left corner. - // TODO: Handle case when Paragraph is larger than the Page... - // Should be fine if we just break on the paragraph, i.e. splitting it up over 2+ pages - - blocks = append(blocks, blk) - blk = NewBlock(ctx.PageWidth, ctx.PageHeight) - - // New Page. - ctx.Page++ - newContext := ctx - newContext.Y = ctx.Margins.top // + p.Margins.top - newContext.X = ctx.Margins.left + p.margins.left - newContext.Height = ctx.PageHeight - ctx.Margins.top - ctx.Margins.bottom - p.margins.bottom - newContext.Width = ctx.PageWidth - ctx.Margins.left - ctx.Margins.right - p.margins.left - p.margins.right - ctx = newContext - } } else { - // Absolute. + // Absolute. Use necessary space. if int(p.wrapWidth) <= 0 { - // Use necessary space. p.SetWidth(p.getTextWidth()) } ctx.X = p.xPos @@ -556,14 +536,40 @@ func (p *StyledParagraph) GeneratePageBlocks(ctx DrawContext) ([]*Block, DrawCon p.beforeRender(p, ctx) } - // Place the Paragraph on the template at position (x,y) based on the ctx. - ctx, err := drawStyledParagraphOnBlock(blk, p, ctx) - if err != nil { - common.Log.Debug("ERROR: %v", err) + // Wrap paragraph chunks into lines, based on the available context width. + if err := p.wrapText(); err != nil { return nil, ctx, err } - blocks = append(blocks, blk) + // Draw paragraph blocks. + lines := p.lines + for { + // Draw paragraph on block. + newCtx, remaining, err := drawStyledParagraphOnBlock(blk, p, lines, ctx) + if err != nil { + common.Log.Debug("ERROR: %v", err) + return nil, ctx, err + } + ctx = newCtx + blocks = append(blocks, blk) + + // If the current block height was not sufficient for all the paragraph + // lines, create a new block to draw the remaining lines on. + if lines = remaining; len(remaining) == 0 { + break + } + + // Create new page block. + blk = NewBlock(ctx.PageWidth, ctx.PageHeight) + ctx.Page++ + newCtx = ctx + newCtx.Y = ctx.Margins.top + newCtx.X = ctx.Margins.left + p.margins.left + newCtx.Height = ctx.PageHeight - ctx.Margins.top - ctx.Margins.bottom - p.margins.bottom + newCtx.Width = ctx.PageWidth - ctx.Margins.left - ctx.Margins.right - p.margins.left - p.margins.right + ctx = newCtx + } + if p.positioning.isRelative() { ctx.X -= p.margins.left // Move back. ctx.Width = origContext.Width @@ -574,7 +580,7 @@ func (p *StyledParagraph) GeneratePageBlocks(ctx DrawContext) ([]*Block, DrawCon } // Draw block on specified location on Page, adding to the content stream. -func drawStyledParagraphOnBlock(blk *Block, p *StyledParagraph, ctx DrawContext) (DrawContext, error) { +func drawStyledParagraphOnBlock(blk *Block, p *StyledParagraph, lines [][]*TextChunk, ctx DrawContext) (DrawContext, [][]*TextChunk, error) { // Find first free index for the font resources of the paragraph. num := 1 fontName := core.PdfObjectName(fmt.Sprintf("Font%d", num)) @@ -586,40 +592,51 @@ func drawStyledParagraphOnBlock(blk *Block, p *StyledParagraph, ctx DrawContext) // Add default font to the page resources. err := blk.resources.SetFontByName(fontName, p.defaultStyle.Font.ToPdfObject()) if err != nil { - return ctx, err + return ctx, nil, err } num++ defaultFontName := fontName defaultFontSize := p.defaultStyle.FontSize - // Wrap the text into lines. - p.wrapText() - // Add the fonts of all chunks to the page resources. + relativePos := p.positioning.isRelative() var fonts [][]core.PdfObjectName - var yOffset float64 - for i, line := range p.lines { + var nextBlockLines [][]*TextChunk + var totalHeight float64 + for i, line := range lines { var fontLine []core.PdfObjectName - + var height float64 for _, chunk := range line { style := chunk.Style if i == 0 && style.FontSize > yOffset { yOffset = style.FontSize } + if style.FontSize > height { + height = style.FontSize + } fontName = core.PdfObjectName(fmt.Sprintf("Font%d", num)) err := blk.resources.SetFontByName(fontName, style.Font.ToPdfObject()) if err != nil { - return ctx, err + return ctx, nil, err } fontLine = append(fontLine, fontName) num++ } + // Check if line fits on the current block. + height *= p.lineHeight + if relativePos && totalHeight+height > ctx.Height { + nextBlockLines = lines[i:] + lines = lines[:i] + break + } + + totalHeight += height fonts = append(fonts, fontLine) } @@ -637,7 +654,7 @@ func drawStyledParagraphOnBlock(blk *Block, p *StyledParagraph, ctx DrawContext) cc.Add_BT() currY := yPos - for idx, line := range p.lines { + for idx, line := range lines { currX := ctx.X if idx != 0 { @@ -645,7 +662,7 @@ func drawStyledParagraphOnBlock(blk *Block, p *StyledParagraph, ctx DrawContext) cc.Add_Tstar() } - isLastLine := idx == len(p.lines)-1 + isLastLine := idx == len(lines)-1 // Get width of the line (excluding spaces). var ( @@ -665,7 +682,7 @@ func drawStyledParagraphOnBlock(blk *Block, p *StyledParagraph, ctx DrawContext) spaceMetrics, found := style.Font.GetRuneMetrics(' ') if !found { - return ctx, errors.New("the font does not have a space glyph") + return ctx, nil, errors.New("the font does not have a space glyph") } var chunkSpaces uint @@ -683,7 +700,7 @@ func drawStyledParagraphOnBlock(blk *Block, p *StyledParagraph, ctx DrawContext) metrics, found := style.Font.GetRuneMetrics(r) if !found { common.Log.Debug("Unsupported rune %v in font\n", r) - return ctx, errors.New("unsupported text glyph") + return ctx, nil, errors.New("unsupported text glyph") } chunkWidth += style.FontSize * metrics.Wx @@ -750,7 +767,7 @@ func drawStyledParagraphOnBlock(blk *Block, p *StyledParagraph, ctx DrawContext) if p.alignment != TextAlignmentJustify || isLastLine { spaceMetrics, found := style.Font.GetRuneMetrics(' ') if !found { - return ctx, errors.New("the font does not have a space glyph") + return ctx, nil, errors.New("the font does not have a space glyph") } fontName = fonts[idx][k] @@ -870,8 +887,8 @@ func drawStyledParagraphOnBlock(blk *Block, p *StyledParagraph, ctx DrawContext) blk.addContents(ops) - if p.positioning.isRelative() { - pHeight := p.Height() + p.margins.bottom + if relativePos { + pHeight := totalHeight + p.margins.bottom ctx.Y += pHeight ctx.Height -= pHeight @@ -881,5 +898,5 @@ func drawStyledParagraphOnBlock(blk *Block, p *StyledParagraph, ctx DrawContext) } } - return ctx, nil + return ctx, nextBlockLines, nil } diff --git a/creator/styled_paragraph_test.go b/creator/styled_paragraph_test.go index 3e8af20e..f3f5afc4 100644 --- a/creator/styled_paragraph_test.go +++ b/creator/styled_paragraph_test.go @@ -885,3 +885,15 @@ func TestStyledParagraphCharacterSpaceWrapping(t *testing.T) { // Write output file. testWriteAndRender(t, c, "styled_paragraph_character_space_wrapping.pdf") } + +func TestStyledParagraphMultiBlock(t *testing.T) { + c := New() + + // Draw paragraph. + p := c.NewStyledParagraph() + p.Append("Spicy jalapeno bacon ipsum dolor amet chislic short ribs shankle pork loin, turkey swine meatloaf jowl short loin venison sausage flank andouille picanha fatback. Tenderloin swine alcatra porchetta biltong, fatback drumstick tail picanha beef ribs t-bone andouille kevin. Pork loin beef ribs jerky pastrami porchetta tri-tip ham biltong cupim sirloin venison shank frankfurter chislic. Drumstick ham hock bacon, t-bone beef ribs tail hamburger. Ground round pancetta brisket meatloaf, sausage tenderloin leberkas pastrami bacon jerky sirloin frankfurter beef ribs chislic. Pork ribeye tail cow, pork belly pork loin bresaola tenderloin bacon tri-tip. Shankle corned beef short ribs prosciutto, short loin kevin bacon biltong shoulder tri-tip chuck fatback t-bone.Bacon shoulder kevin landjaeger, ribeye tri-tip short loin boudin pork belly pastrami filet mignon corned beef. Jerky turkey pastrami landjaeger, meatball venison pig shoulder biltong sausage jowl flank kielbasa pork loin spare ribs. Flank pastrami jerky tail shank ribeye pancetta hamburger burgdoggen pork belly meatball kevin doner beef ribs. Corned beef tongue sausage burgdoggen chislic beef ribs ham t-bone ham hock kielbasa kevin. Shank ground round meatloaf meatball sirloin. Alcatra ground round cupim meatball, tongue tenderloin pastrami ham beef. Burgdoggen sausage beef ribs pork chop pork loin, hamburger meatball ribeye capicola landjaeger alcatra turducken.Ball tip sirloin pork loin venison rump. Tri-tip biltong short loin brisket ribeye. T-bone chislic jerky tenderloin kevin salami beef swine chuck bresaola filet mignon kielbasa ground round pastrami. Jerky bresaola brisket, chislic spare ribs short ribs turkey buffalo kevin pork belly ham shoulder. Pork loin shankle hamburger short ribs pork chop buffalo. Landjaeger corned beef bacon flank ground round leberkas.Cow swine shankle turkey, chislic chuck drumstick brisket meatloaf buffalo beef ribs pastrami biltong. Brisket strip steak kevin venison. T-bone kielbasa prosciutto, ham ham hock pancetta swine bresaola. Pastrami pork belly short ribs jerky sirloin venison strip steak meatball tenderloin shoulder fatback meatloaf beef ribs cupim. Landjaeger short ribs andouille chislic, pancetta strip steak salami. Leberkas flank meatloaf tail burgdoggen.Pork loin ham hock biltong meatloaf landjaeger porchetta. Venison turkey biltong shank pork, capicola filet mignon strip steak ribeye rump meatloaf frankfurter jowl short loin chislic. Bresaola sirloin ground round boudin, porchetta rump burgdoggen beef ribs ham hock landjaeger leberkas tail pork brisket sausage. Brisket porchetta buffalo jerky capicola short ribs spare ribs ham hock corned beef. Doner kevin venison cupim, turducken ham tongue porchetta swine buffalo prosciutto salami.Spicy jalapeno bacon ipsum dolor amet chislic short ribs shankle pork loin, turkey swine meatloaf jowl short loin venison sausage flank andouille picanha fatback. Tenderloin swine alcatra porchetta biltong, fatback drumstick tail picanha beef ribs t-bone andouille kevin. Pork loin beef ribs jerky pastrami porchetta tri-tip ham biltong cupim sirloin venison shank frankfurter chislic. Drumstick ham hock bacon, t-bone beef ribs tail hamburger. Ground round pancetta brisket meatloaf, sausage tenderloin leberkas pastrami bacon jerky sirloin frankfurter beef ribs chislic. Pork ribeye tail cow, pork belly pork loin bresaola tenderloin bacon tri-tip. Shankle corned beef short ribs prosciutto, short loin kevin bacon biltong shoulder tri-tip chuck fatback t-bone.Bacon shoulder kevin landjaeger, ribeye tri-tip short loin boudin pork belly pastrami filet mignon corned beef. Jerky turkey pastrami landjaeger, meatball venison pig shoulder biltong sausage jowl flank kielbasa pork loin spare ribs. Flank pastrami jerky tail shank ribeye pancetta hamburger burgdoggen pork belly meatball kevin doner beef ribs. Corned beef tongue sausage burgdoggen chislic beef ribs ham t-bone ham hock kielbasa kevin. Shank ground round meatloaf meatball sirloin. Alcatra ground round cupim meatball, tongue tenderloin pastrami ham beef. Burgdoggen sausage beef ribs pork chop pork loin, hamburger meatball ribeye capicola landjaeger alcatra turducken.Ball tip sirloin pork loin venison rump. Tri-tip biltong short loin brisket ribeye. T-bone chislic jerky tenderloin kevin salami beef swine chuck bresaola filet mignon kielbasa ground round pastrami. Jerky bresaola brisket, chislic spare ribs short ribs turkey buffalo kevin pork belly ham shoulder. Pork loin shankle hamburger short ribs pork chop buffalo. Landjaeger corned beef bacon flank ground round leberkas.Cow swine shankle turkey, chislic chuck drumstick brisket meatloaf buffalo beef ribs pastrami biltong. Brisket strip steak kevin venison. T-bone kielbasa prosciutto, ham ham hock pancetta swine bresaola. Pastrami pork belly short ribs jerky sirloin venison strip steak meatball tenderloin shoulder fatback meatloaf beef ribs cupim. Landjaeger short ribs andouille chislic, pancetta strip steak salami. Leberkas flank meatloaf tail burgdoggen.Pork loin ham hock biltong meatloaf landjaeger porchetta. Venison turkey biltong shank pork, capicola filet mignon strip steak ribeye rump meatloaf frankfurter jowl short loin chislic. Bresaola sirloin ground round boudin, porchetta rump burgdoggen beef ribs ham hock landjaeger leberkas tail pork brisket sausage. Brisket porchetta buffalo jerky capicola short ribs spare ribs ham hock corned beef. Doner kevin venison cupim, turducken ham tongue porchetta swine buffalo prosciutto salami. Spicy jalapeno bacon ipsum dolor amet chislic short ribs shankle pork loin, turkey swine meatloaf jowl short loin venison sausage flank andouille picanha fatback. Tenderloin swine alcatra porchetta biltong, fatback drumstick tail picanha beef ribs t-bone andouille kevin. Pork loin beef ribs jerky pastrami porchetta tri-tip ham biltong cupim sirloin venison shank frankfurter chislic. Drumstick ham hock bacon, t-bone beef ribs tail hamburger. Ground round pancetta brisket meatloaf, sausage tenderloin leberkas pastrami bacon jerky sirloin frankfurter beef ribs chislic. Pork ribeye tail cow, pork belly pork loin bresaola tenderloin bacon tri-tip. Shankle corned beef short ribs prosciutto, short loin kevin bacon biltong shoulder tri-tip chuck fatback t-bone.Bacon shoulder kevin landjaeger, ribeye tri-tip short loin boudin pork belly pastrami filet mignon corned beef. Jerky turkey pastrami landjaeger, meatball venison pig shoulder biltong sausage jowl flank kielbasa pork loin spare ribs. Flank pastrami jerky tail shank ribeye pancetta hamburger burgdoggen pork belly meatball kevin doner beef ribs. Corned beef tongue sausage burgdoggen chislic beef ribs ham t-bone ham hock kielbasa kevin. Shank ground round meatloaf meatball sirloin. Alcatra ground round cupim meatball, tongue tenderloin pastrami ham beef. Burgdoggen sausage beef ribs pork chop pork loin, hamburger meatball ribeye capicola landjaeger alcatra turducken.Ball tip sirloin pork loin venison rump. Tri-tip biltong short loin brisket ribeye. T-bone chislic jerky tenderloin kevin salami beef swine chuck bresaola filet mignon kielbasa ground round pastrami. Jerky bresaola brisket, chislic spare ribs short ribs turkey buffalo kevin pork belly ham shoulder. Pork loin shankle hamburger short ribs pork chop buffalo. Landjaeger corned beef bacon flank ground round leberkas.Cow swine shankle turkey, chislic chuck drumstick brisket meatloaf buffalo beef ribs pastrami biltong. Brisket strip steak kevin venison. T-bone kielbasa prosciutto, ham ham hock pancetta swine bresaola. Pastrami pork belly short ribs jerky sirloin venison strip steak meatball tenderloin shoulder fatback meatloaf beef ribs cupim. Landjaeger short ribs andouille chislic, pancetta strip steak salami. Leberkas flank meatloaf tail burgdoggen.Pork loin ham hock biltong meatloaf landjaeger porchetta. Venison turkey biltong shank pork, capicola filet mignon strip steak ribeye rump meatloaf frankfurter jowl short loin chislic. Bresaola sirloin ground round boudin, porchetta rump burgdoggen beef ribs ham hock landjaeger leberkas tail pork brisket sausage. Brisket porchetta buffalo jerky capicola short ribs spare ribs ham hock corned beef. Doner kevin venison cupim, turducken ham tongue porchetta swine buffalo prosciutto salami.Spicy jalapeno bacon ipsum dolor amet chislic short ribs shankle pork loin, turkey swine meatloaf jowl short loin venison sausage flank andouille picanha fatback. Tenderloin swine alcatra porchetta biltong, fatback drumstick tail picanha beef ribs t-bone andouille kevin. Pork loin beef ribs jerky pastrami porchetta tri-tip ham biltong cupim sirloin venison shank frankfurter chislic. Drumstick ham hock bacon, t-bone beef ribs tail hamburger. Ground round pancetta brisket meatloaf, sausage tenderloin leberkas pastrami bacon jerky sirloin frankfurter beef ribs chislic. Pork ribeye tail cow, pork belly pork loin bresaola tenderloin bacon tri-tip. Shankle corned beef short ribs prosciutto, short loin kevin bacon biltong shoulder tri-tip chuck fatback t-bone.Bacon shoulder kevin landjaeger, ribeye tri-tip short loin boudin pork belly pastrami filet mignon corned beef. Jerky turkey pastrami landjaeger, meatball venison pig shoulder biltong sausage jowl flank kielbasa pork loin spare ribs. Flank pastrami jerky tail shank ribeye pancetta hamburger burgdoggen pork belly meatball kevin doner beef ribs. Corned beef tongue sausage burgdoggen chislic beef ribs ham t-bone ham hock kielbasa kevin. Shank ground round meatloaf meatball sirloin. Alcatra ground round cupim meatball, tongue tenderloin pastrami ham beef. Burgdoggen sausage beef ribs pork chop pork loin, hamburger meatball ribeye capicola landjaeger alcatra turducken.Ball tip sirloin pork loin venison rump. Tri-tip biltong short loin brisket ribeye. T-bone chislic jerky tenderloin kevin salami beef swine chuck bresaola filet mignon kielbasa ground round pastrami. Jerky bresaola brisket, chislic spare ribs short ribs turkey buffalo kevin pork belly ham shoulder. Pork loin shankle hamburger short ribs pork chop buffalo. Landjaeger corned beef bacon flank ground round leberkas.Cow swine shankle turkey, chislic chuck drumstick brisket meatloaf buffalo beef ribs pastrami biltong. Brisket strip steak kevin venison. T-bone kielbasa prosciutto, ham ham hock pancetta swine bresaola. Pastrami pork belly short ribs jerky sirloin venison strip steak meatball tenderloin shoulder fatback meatloaf beef ribs cupim. Landjaeger short ribs andouille chislic, pancetta strip steak salami. Leberkas flank meatloaf tail burgdoggen.Pork loin ham hock biltong meatloaf landjaeger porchetta. Venison turkey biltong shank pork, capicola filet mignon strip steak ribeye rump meatloaf frankfurter jowl short loin chislic. Bresaola sirloin ground round boudin, porchetta rump burgdoggen beef ribs ham hock landjaeger leberkas tail pork brisket sausage. Brisket porchetta buffalo jerky capicola short ribs spare ribs ham hock corned beef. Doner kevin venison cupim, turducken ham tongue porchetta swine buffalo prosciutto salami.Spicy jalapeno bacon ipsum dolor amet chislic short ribs shankle pork loin, turkey swine meatloaf jowl short loin venison sausage flank andouille picanha fatback. Tenderloin swine alcatra porchetta biltong, fatback drumstick tail picanha beef ribs t-bone andouille kevin. Pork loin beef ribs jerky pastrami porchetta tri-tip ham biltong cupim sirloin venison shank frankfurter chislic. Drumstick ham hock bacon, t-bone beef ribs tail hamburger. Ground round pancetta brisket meatloaf, sausage tenderloin leberkas pastrami bacon jerky sirloin frankfurter beef ribs chislic. Pork ribeye tail cow, pork belly pork loin bresaola tenderloin bacon tri-tip. Shankle corned beef short ribs prosciutto, short loin kevin bacon biltong shoulder tri-tip chuck fatback t-bone.Bacon shoulder kevin landjaeger, ribeye tri-tip short loin boudin pork belly pastrami filet mignon corned beef. Jerky turkey pastrami landjaeger, meatball venison pig shoulder biltong sausage jowl flank kielbasa pork loin spare ribs. Flank pastrami jerky tail shank ribeye pancetta hamburger burgdoggen pork belly meatball kevin doner beef ribs. Corned beef tongue sausage burgdoggen chislic beef ribs ham t-bone ham hock kielbasa kevin. Shank ground round meatloaf meatball sirloin. Alcatra ground round cupim meatball, tongue tenderloin pastrami ham beef. Burgdoggen sausage beef ribs pork chop pork loin, hamburger meatball ribeye capicola landjaeger alcatra turducken.Ball tip sirloin pork loin venison rump. Tri-tip biltong short loin brisket ribeye. T-bone chislic jerky tenderloin kevin salami beef swine chuck bresaola filet mignon kielbasa ground round pastrami. Jerky bresaola brisket, chislic spare ribs short ribs turkey buffalo kevin pork belly ham shoulder. Pork loin shankle hamburger short ribs pork chop buffalo. Landjaeger corned beef bacon flank ground round leberkas.Cow swine shankle turkey, chislic chuck drumstick brisket meatloaf buffalo beef ribs pastrami biltong. Brisket strip steak kevin venison. T-bone kielbasa prosciutto, ham ham hock pancetta swine bresaola. Pastrami pork belly short ribs jerky sirloin venison strip steak meatball tenderloin shoulder fatback meatloaf beef ribs cupim. Landjaeger short ribs andouille chislic, pancetta strip steak salami. Leberkas flank meatloaf tail burgdoggen.Pork loin ham hock biltong meatloaf landjaeger porchetta. Venison turkey biltong shank pork, capicola filet mignon strip steak ribeye rump meatloaf frankfurter jowl short loin chislic. Bresaola sirloin ground round boudin, porchetta rump burgdoggen beef ribs ham hock landjaeger leberkas tail pork brisket sausage. Brisket porchetta buffalo jerky capicola short ribs spare ribs ham hock corned beef. Doner kevin venison cupim, turducken ham tongue porchetta swine buffalo prosciutto salami.Spicy jalapeno bacon ipsum dolor amet chislic short ribs shankle pork loin, turkey swine meatloaf jowl short loin venison sausage flank andouille picanha fatback. Tenderloin swine alcatra porchetta biltong, fatback drumstick tail picanha beef ribs t-bone andouille kevin. Pork loin beef ribs jerky pastrami porchetta tri-tip ham biltong cupim sirloin venison shank frankfurter chislic. Drumstick ham hock bacon, t-bone beef ribs tail hamburger. Ground round pancetta brisket meatloaf, sausage tenderloin leberkas pastrami bacon jerky sirloin frankfurter beef ribs chislic. Pork ribeye tail cow, pork belly pork loin bresaola tenderloin bacon tri-tip. Shankle corned beef short ribs prosciutto, short loin kevin bacon biltong shoulder tri-tip chuck fatback t-bone.Bacon shoulder kevin landjaeger, ribeye tri-tip short loin boudin pork belly pastrami filet mignon corned beef. Jerky turkey pastrami landjaeger, meatball venison pig shoulder biltong sausage jowl flank kielbasa pork loin spare ribs. Flank pastrami jerky tail shank ribeye pancetta hamburger burgdoggen pork belly meatball kevin doner beef ribs. Corned beef tongue sausage burgdoggen chislic beef ribs ham t-bone ham hock kielbasa kevin. Shank ground round meatloaf meatball sirloin. Alcatra ground round cupim meatball, tongue tenderloin pastrami ham beef. Burgdoggen sausage beef ribs pork chop pork loin, hamburger meatball ribeye capicola landjaeger alcatra turducken.Ball tip sirloin pork loin venison rump. Tri-tip biltong short loin brisket ribeye. T-bone chislic jerky tenderloin kevin salami beef swine chuck bresaola filet mignon kielbasa ground round pastrami. Jerky bresaola brisket, chislic spare ribs short ribs turkey buffalo kevin pork belly ham shoulder. Pork loin shankle hamburger short ribs pork chop buffalo. Landjaeger corned beef bacon flank ground round leberkas.Cow swine shankle turkey, chislic chuck drumstick brisket meatloaf buffalo beef ribs pastrami biltong. Brisket strip steak kevin venison. T-bone kielbasa prosciutto, ham ham hock pancetta swine bresaola. Pastrami pork belly short ribs jerky sirloin venison strip steak meatball tenderloin shoulder fatback meatloaf beef ribs cupim. Landjaeger short ribs andouille chislic, pancetta strip steak salami. Leberkas flank meatloaf tail burgdoggen.Pork loin ham hock biltong meatloaf landjaeger porchetta. Venison turkey biltong shank pork, capicola filet mignon strip steak ribeye rump meatloaf frankfurter jowl short loin chislic. Bresaola sirloin ground round boudin, porchetta rump burgdoggen beef ribs ham hock landjaeger leberkas tail pork brisket sausage. Brisket porchetta buffalo jerky capicola short ribs spare ribs ham hock corned beef. Doner kevin venison cupim, turducken ham tongue porchetta swine buffalo prosciutto salami. Spicy jalapeno bacon ipsum dolor amet chislic short ribs shankle pork loin, turkey swine meatloaf jowl short loin venison sausage flank andouille picanha fatback. Tenderloin swine alcatra porchetta biltong, fatback drumstick tail picanha beef ribs t-bone andouille kevin. Pork loin beef ribs jerky pastrami porchetta tri-tip ham biltong cupim sirloin venison shank frankfurter chislic. Drumstick ham hock bacon, t-bone beef ribs tail hamburger. Ground round pancetta brisket meatloaf, sausage tenderloin leberkas pastrami bacon jerky sirloin frankfurter beef ribs chislic. Pork ribeye tail cow, pork belly pork loin bresaola tenderloin bacon tri-tip. Shankle corned beef short ribs prosciutto, short loin kevin bacon biltong shoulder tri-tip chuck fatback t-bone.Bacon shoulder kevin landjaeger, ribeye tri-tip short loin boudin pork belly pastrami filet mignon corned beef. Jerky turkey pastrami landjaeger, meatball venison pig shoulder biltong sausage jowl flank kielbasa pork loin spare ribs. Flank pastrami jerky tail shank ribeye pancetta hamburger burgdoggen pork belly meatball kevin doner beef ribs. Corned beef tongue sausage burgdoggen chislic beef ribs ham t-bone ham hock kielbasa kevin. Shank ground round meatloaf meatball sirloin. Alcatra ground round cupim meatball, tongue tenderloin pastrami ham beef. Burgdoggen sausage beef ribs pork chop pork loin, hamburger meatball ribeye capicola landjaeger alcatra turducken.Ball tip sirloin pork loin venison rump. Tri-tip biltong short loin brisket ribeye. T-bone chislic jerky tenderloin kevin salami beef swine chuck bresaola filet mignon kielbasa ground round pastrami. Jerky bresaola brisket, chislic spare ribs short ribs turkey buffalo kevin pork belly ham shoulder. Pork loin shankle hamburger short ribs pork chop buffalo. Landjaeger corned beef bacon flank ground round leberkas.Cow swine shankle turkey, chislic chuck drumstick brisket meatloaf buffalo beef ribs pastrami biltong. Brisket strip steak kevin venison. T-bone kielbasa prosciutto, ham ham hock pancetta swine bresaola. Pastrami pork belly short ribs jerky sirloin venison strip steak meatball tenderloin shoulder fatback meatloaf beef ribs cupim. Landjaeger short ribs andouille chislic, pancetta strip steak salami. Leberkas flank meatloaf tail burgdoggen.Pork loin ham hock biltong meatloaf landjaeger porchetta. Venison turkey biltong shank pork, capicola filet mignon strip steak ribeye rump meatloaf frankfurter jowl short loin chislic. Bresaola sirloin ground round boudin, porchetta rump burgdoggen beef ribs ham hock landjaeger leberkas tail pork brisket sausage. Brisket porchetta buffalo jerky capicola short ribs spare ribs ham hock corned beef. Doner kevin venison cupim, turducken ham tongue porchetta swine buffalo prosciutto salami.Spicy jalapeno bacon ipsum dolor amet chislic short ribs shankle pork loin, turkey swine meatloaf jowl short loin venison sausage flank andouille picanha fatback. Tenderloin swine alcatra porchetta biltong, fatback drumstick tail picanha beef ribs t-bone andouille kevin. Pork loin beef ribs jerky pastrami porchetta tri-tip ham biltong cupim sirloin venison shank frankfurter chislic. Drumstick ham hock bacon, t-bone beef ribs tail hamburger. Ground round pancetta brisket meatloaf, sausage tenderloin leberkas pastrami bacon jerky sirloin frankfurter beef ribs chislic. Pork ribeye tail cow, pork belly pork loin bresaola tenderloin bacon tri-tip. Shankle corned beef short ribs prosciutto, short loin kevin bacon biltong shoulder tri-tip chuck fatback t-bone.Bacon shoulder kevin landjaeger, ribeye tri-tip short loin boudin pork belly pastrami filet mignon corned beef. Jerky turkey pastrami landjaeger, meatball venison pig shoulder biltong sausage jowl flank kielbasa pork loin spare ribs. Flank pastrami jerky tail shank ribeye pancetta hamburger burgdoggen pork belly meatball kevin doner beef ribs. Corned beef tongue sausage burgdoggen chislic beef ribs ham t-bone ham hock kielbasa kevin. Shank ground round meatloaf meatball sirloin. Alcatra ground round cupim meatball, tongue tenderloin pastrami ham beef. Burgdoggen sausage beef ribs pork chop pork loin, hamburger meatball ribeye capicola landjaeger alcatra turducken.Ball tip sirloin pork loin venison rump. Tri-tip biltong short loin brisket ribeye. T-bone chislic jerky tenderloin kevin salami beef swine chuck bresaola filet mignon kielbasa ground round pastrami. Jerky bresaola brisket, chislic spare ribs short ribs turkey buffalo kevin pork belly ham shoulder. Pork loin shankle hamburger short ribs pork chop buffalo. Landjaeger corned beef bacon flank ground round leberkas.Cow swine shankle turkey, chislic chuck drumstick brisket meatloaf buffalo beef ribs pastrami biltong. Brisket strip steak kevin venison. T-bone kielbasa prosciutto, ham ham hock pancetta swine bresaola. Pastrami pork belly short ribs jerky sirloin venison strip steak meatball tenderloin shoulder fatback meatloaf beef ribs cupim. Landjaeger short ribs andouille chislic, pancetta strip steak salami. Leberkas flank meatloaf tail burgdoggen.Pork loin ham hock biltong meatloaf landjaeger porchetta. Venison turkey biltong shank pork, capicola filet mignon strip steak ribeye rump meatloaf frankfurter jowl short loin chislic. Bresaola sirloin ground round boudin, porchetta rump burgdoggen beef ribs ham hock landjaeger leberkas tail pork brisket sausage. Brisket porchetta buffalo jerky capicola short ribs spare ribs ham hock corned beef. Doner kevin venison cupim, turducken ham tongue porchetta swine buffalo prosciutto salami.") + require.NoError(t, c.Draw(p)) + + // Write output file. + testWriteAndRender(t, c, "styled_paragraph_multiblock.pdf") +} diff --git a/creator/table.go b/creator/table.go index 601e3904..0ab2d15a 100644 --- a/creator/table.go +++ b/creator/table.go @@ -373,21 +373,20 @@ func (table *Table) GeneratePageBlocks(ctx DrawContext) ([]*Block, DrawContext, case *Division: div := t - ctx := DrawContext{ - X: xrel, - Y: yrel, - Width: w, - } + c := ctx + c.X = xrel + c.Y = yrel + c.Width = w // Mock call to generate page blocks. - divBlocks, updCtx, err := div.GeneratePageBlocks(ctx) + divBlocks, _, err := div.GeneratePageBlocks(c) if err != nil { return nil, ctx, err } if len(divBlocks) > 1 { // Wraps across page, make cell reach all the way to bottom of current page. - newh := ctx.Height - h + newh := c.Height - h if newh > h { diffh := newh - h // Add diff to last row. @@ -395,10 +394,8 @@ func (table *Table) GeneratePageBlocks(ctx DrawContext) ([]*Block, DrawContext, } } - newh := div.Height() + div.margins.top + div.margins.bottom - _ = updCtx - // Get available width and height. + newh := div.Height() + div.margins.top + div.margins.bottom if newh > h { diffh := newh - h // Add diff to last row. @@ -604,6 +601,7 @@ func (table *Table) GeneratePageBlocks(ctx DrawContext) ([]*Block, DrawContext, } ctx.Y += h + ctx.Height -= h // Resume previous state after headers have been rendered. if drawingHeaders && cellIdx+1 > endHeaderCell { @@ -629,6 +627,7 @@ func (table *Table) GeneratePageBlocks(ctx DrawContext) ([]*Block, DrawContext, ctx.Width = origCtx.Width // Add the bottom margin. ctx.Y += table.margins.bottom + ctx.Height -= table.margins.bottom return blocks, ctx, nil } From c17719d232eff9e28b58ee49dd3e6bec0c020e01 Mon Sep 17 00:00:00 2001 From: Adrian-George Bostan Date: Fri, 1 May 2020 15:56:43 +0300 Subject: [PATCH 2/2] Invoice component improvements (#334) * Add invoice address heading field * Update invoice test cases * Add default value to buyer address heading * Add Street2 and State address fields * Add configurable address separator field * Improve invoice test cases --- creator/invoice.go | 71 +++++++++++++++++++++++++++++------------ creator/invoice_test.go | 23 +++++++------ 2 files changed, 65 insertions(+), 29 deletions(-) diff --git a/creator/invoice.go b/creator/invoice.go index bb9321fd..007b792c 100644 --- a/creator/invoice.go +++ b/creator/invoice.go @@ -5,19 +5,29 @@ package creator -import "fmt" +import ( + "fmt" +) // InvoiceAddress contains contact information that can be displayed // in an invoice. It is used for the seller and buyer information in the // invoice template. type InvoiceAddress struct { + Heading string Name string Street string + Street2 string Zip string City string + State string Country string Phone string Email string + + // Separator defines the separator between different address components, + // such as the city, state and zip code. It defaults to ", " when the + // field is an empty string. + Separator string } // InvoiceCellProps holds all style properties for an invoice cell. @@ -52,6 +62,7 @@ type Invoice struct { // Invoice addresses. buyerAddress *InvoiceAddress sellerAddress *InvoiceAddress + addrSeparator string // Invoice information. number [2]*InvoiceCell @@ -101,14 +112,22 @@ func newInvoice(defaultStyle, headingStyle TextStyle) *Invoice { title: "INVOICE", // Addresses. - sellerAddress: &InvoiceAddress{}, - buyerAddress: &InvoiceAddress{}, + addrSeparator: ", ", // Styles. defaultStyle: defaultStyle, headingStyle: headingStyle, } + // Addresses. + i.sellerAddress = &InvoiceAddress{ + Separator: i.addrSeparator, + } + i.buyerAddress = &InvoiceAddress{ + Heading: "Bill to", + Separator: i.addrSeparator, + } + // Default colors. lightGrey := ColorRGBFrom8bit(245, 245, 245) mediumGrey := ColorRGBFrom8bit(155, 155, 155) @@ -158,7 +177,7 @@ func newInvoice(defaultStyle, headingStyle TextStyle) *Invoice { } i.dueDate = [2]*InvoiceCell{ - i.newCell("Date", i.infoProps), + i.newCell("Due Date", i.infoProps), i.newCell("", i.infoProps), } @@ -536,14 +555,14 @@ func (i *Invoice) setCellBorder(cell *TableCell, invoiceCell *InvoiceCell) { cell.SetBorderColor(invoiceCell.BorderColor) } -func (i *Invoice) drawAddress(title, name string, addr *InvoiceAddress) []*StyledParagraph { +func (i *Invoice) drawAddress(addr *InvoiceAddress) []*StyledParagraph { var paragraphs []*StyledParagraph // Address title. - if title != "" { + if addr.Heading != "" { titleParagraph := newStyledParagraph(i.addressHeadingStyle) titleParagraph.SetMargins(0, 0, 0, 7) - titleParagraph.Append(title) + titleParagraph.Append(addr.Heading) paragraphs = append(paragraphs, titleParagraph) } @@ -552,23 +571,36 @@ func (i *Invoice) drawAddress(title, name string, addr *InvoiceAddress) []*Style addressParagraph := newStyledParagraph(i.addressStyle) addressParagraph.SetLineHeight(1.2) - city := addr.City - if addr.Zip != "" { - if city != "" { - city += ", " - } - - city += addr.Zip + separator := addr.Separator + if separator == "" { + separator = i.addrSeparator } - if name != "" { + locality := addr.City + if addr.State != "" { + if locality != "" { + locality += separator + } + locality += addr.State + } + if addr.Zip != "" { + if locality != "" { + locality += separator + } + locality += addr.Zip + } + + if addr.Name != "" { addressParagraph.Append(addr.Name + "\n") } if addr.Street != "" { addressParagraph.Append(addr.Street + "\n") } - if city != "" { - addressParagraph.Append(city + "\n") + if addr.Street2 != "" { + addressParagraph.Append(addr.Street2 + "\n") + } + if locality != "" { + addressParagraph.Append(locality + "\n") } if addr.Country != "" { addressParagraph.Append(addr.Country + "\n") @@ -727,10 +759,9 @@ func (i *Invoice) generateInformationBlocks(ctx DrawContext) ([]*Block, DrawCont separatorParagraph := newStyledParagraph(i.defaultStyle) separatorParagraph.SetMargins(0, 0, 0, 20) - addrParagraphs := i.drawAddress(i.sellerAddress.Name, "", i.sellerAddress) + addrParagraphs := i.drawAddress(i.sellerAddress) addrParagraphs = append(addrParagraphs, separatorParagraph) - addrParagraphs = append(addrParagraphs, - i.drawAddress("Bill to", i.buyerAddress.Name, i.buyerAddress)...) + addrParagraphs = append(addrParagraphs, i.drawAddress(i.buyerAddress)...) addrDivision := newDivision() for _, addrParagraph := range addrParagraphs { diff --git a/creator/invoice_test.go b/creator/invoice_test.go index 1b51c083..11499641 100644 --- a/creator/invoice_test.go +++ b/creator/invoice_test.go @@ -30,7 +30,7 @@ func TestInvoiceSimple(t *testing.T) { // Set invoice addresses. invoice.SetSellerAddress(&InvoiceAddress{ - Name: "John Doe", + Heading: "John Doe", Street: "8 Elm Street", City: "Cambridge", Zip: "CB14DH", @@ -40,6 +40,7 @@ func TestInvoiceSimple(t *testing.T) { }) invoice.SetBuyerAddress(&InvoiceAddress{ + Heading: "Bill to", Name: "Jane Doe", Street: "9 Elm Street", City: "London", @@ -127,7 +128,7 @@ func TestInvoiceAdvanced(t *testing.T) { // Set invoice addresses. invoice.SetSellerAddress(&InvoiceAddress{ - Name: "John Doe", + Heading: "JOHN DOE", Street: "8 Elm Street", City: "Cambridge", Zip: "CB14DH", @@ -137,13 +138,17 @@ func TestInvoiceAdvanced(t *testing.T) { }) invoice.SetBuyerAddress(&InvoiceAddress{ - Name: "Jane Doe", - Street: "9 Elm Street", - City: "London", - Zip: "LB15FH", - Country: "United Kingdom", - Phone: "xxx-xxx-xxxx", - Email: "janedoe@email.com", + Heading: "JANE DOE", + Name: "Jane Doe and Associates", + Street: "Suite #134569", + Street2: "1960 W CHELSEA AVE STE 2006R", + City: "ALLENTOWN", + State: "PA", + Zip: "18104", + Country: "United States", + Phone: "xxx-xxx-xxxx", + Email: "janedoe@email.com", + Separator: " ", }) // Customize address styles.