gofpdf icon indicating copy to clipboard operation
gofpdf copied to clipboard

Question: How to generate a table with cell word wrap?

Open vegh1010 opened this issue 7 years ago • 20 comments

Hi, I'm trying to create a table where each cell can be word wrap. How can I do that? I tried using MultiCell but it goes to the next line for each cell and CellFormat does not seem to support MultiCell.

Something like this.

Name Description
John John has a cat.
John has a dog.
Jane Jane has a cat.

vegh1010 avatar Aug 23 '17 03:08 vegh1010

This can be done with SplitLines(). The limitation is that only one font and attribute per cell are supported.

For each row, you will need to loop through each column twice. The first time, call SplitLines() to calculate each cell's height. The second time, use the maximum cell height from the previous loop as the row height. Apply padding as needed to vertically center the text of cells that have a height less than the row height.

jung-kurt avatar Aug 23 '17 11:08 jung-kurt

I've tried your suggestion and manage to get the max height but i couldn't word wrap using CellFormat like MultiCell

vegh1010 avatar Aug 23 '17 23:08 vegh1010

The example accompanying the documentation for SplitLines() shows how each of the substrings (of type []byte) returned by SplitLines() has to be passed to CellFormat() individually. It's really SplitLines() that determines where the wrap breaks are; after that the substrings are manually rendered.

I'll try to find some time tomorrow to augment the table example to demonstrate this.

jung-kurt avatar Aug 24 '17 01:08 jung-kurt

Cool thanks

vegh1010 avatar Aug 25 '17 03:08 vegh1010

Demonstrated in 4491d0fe5997270270f8e4023786afd99251f604

jung-kurt avatar Aug 25 '17 20:08 jung-kurt

Awesome. I'll have a go on your example. Thanks

vegh1010 avatar Aug 27 '17 22:08 vegh1010

Your example works. Thanks

vegh1010 avatar Aug 28 '17 01:08 vegh1010

Hi @jung-kurt, thank you for this awesome lib!

I think it would be awesome if the lib had that abstracted in a function or struct. It's a pretty common use case.

Thanks again! 😃

andreynering avatar Aug 28 '17 18:08 andreynering

Thanks, @andreynering. I agree, an abstraction of word-wrapped table cells make sense. If it is done at the table level, then it could facilitate alternate row shading and page breaks with header rows. I'll reflect on various approaches and put something together in the days ahead.

jung-kurt avatar Aug 28 '17 19:08 jung-kurt

I tried the approach it works fine with only one page. If the table is longer than one page and auto break is enabled the layout gets messed up. I'm working on a solution.

jacobfederer avatar Nov 23 '18 10:11 jacobfederer

Thanks, @jacobfederer -- I look forward to seeing what you come up with.

jung-kurt avatar Nov 23 '18 12:11 jung-kurt

hi @jung-kurt Can merge the cell's in the table? How would you do?

dineshkumars98 avatar Mar 23 '19 07:03 dineshkumars98

Can merge the cell's in the table? How would you do?

Tables in gofpdf are built up of individual cells. You can merge them manually with something like this:

package main

import (
  "fmt"

  "github.com/jung-kurt/gofpdf/v2"
)

func main() {
  const (
    colCount = 4
    rowCount = 3
    margin   = 32.0
    fontHt   = 14.0 // point
  )
  pdf := gofpdf.New("L", "mm", "A4", "")
  cellWd := (297 - margin - margin) / colCount
  cellHt := pdf.PointToUnitConvert(fontHt) + 10.0
  pdf.SetFont("Arial", "", fontHt)
  pdf.AddPage()
  pdf.SetXY(margin, margin)
  for rowJ := 0; rowJ < rowCount; rowJ++ {
    for colJ := 0; colJ < colCount; colJ++ {
      pdf.SetXY(margin+float64(colJ)*cellWd, margin+float64(rowJ)*cellHt)
      if rowJ == 1 && (colJ == 1 || colJ == 2) {
        if colJ == 1 { // Merged cells
          pdf.CellFormat(cellWd*2.0, cellHt, fmt.Sprintf("Merged cells"), "1", 0, "CM", false, 0, "")
        }
      } else {
        pdf.CellFormat(cellWd, cellHt, fmt.Sprintf("Row %d, Col %d", rowJ, colJ), "1", 0, "CM", false, 0, "")
      }
    }
  }
  err := pdf.OutputFileAndClose("colmerge.pdf")
  if err != nil {
    fmt.Printf("%s\n", err)
  }
}

jung-kurt avatar Mar 23 '19 14:03 jung-kurt

very very Thankful

[image: Mailtrack] https://mailtrack.io?utm_source=gmail&utm_medium=signature&utm_campaign=signaturevirality5& Sender notified by Mailtrack https://mailtrack.io?utm_source=gmail&utm_medium=signature&utm_campaign=signaturevirality5& 27/03/19, 7:06:46 pm

On Sat, Mar 23, 2019 at 7:45 PM Kurt Jung [email protected] wrote:

Can merge the cell's in the table? How would you do?

Tables in gofpdf are built up of individual cells. You can merge them manually with something like this:

package main import ( "fmt"

"github.com/jung-kurt/gofpdf/v2" ) func main() { const ( colCount = 4 rowCount = 3 margin = 32.0 fontHt = 14.0 // point ) pdf := gofpdf.New("L", "mm", "A4", "") cellWd := (297 - margin - margin) / colCount cellHt := pdf.PointToUnitConvert(fontHt) + 10.0 pdf.SetFont("Arial", "", fontHt) pdf.AddPage() pdf.SetXY(margin, margin) for rowJ := 0; rowJ < rowCount; rowJ++ { for colJ := 0; colJ < colCount; colJ++ { pdf.SetXY(margin+float64(colJ)*cellWd, margin+float64(rowJ)cellHt) if rowJ == 1 && (colJ == 1 || colJ == 2) { if colJ == 1 { // Merged cells pdf.CellFormat(cellWd2.0, cellHt, fmt.Sprintf("Merged cells"), "1", 0, "CM", false, 0, "") } } else { pdf.CellFormat(cellWd, cellHt, fmt.Sprintf("Row %d, Col %d", rowJ, colJ), "1", 0, "CM", false, 0, "") } } } err := pdf.OutputFileAndClose("colmerge.pdf") if err != nil { fmt.Printf("%s\n", err) } }

— You are receiving this because you commented. Reply to this email directly, view it on GitHub https://github.com/jung-kurt/gofpdf/issues/129#issuecomment-475873331, or mute the thread https://github.com/notifications/unsubscribe-auth/Ad0bauryaJCz2ZehXL_Z4xYxAGEqGa_3ks5vZjcHgaJpZM4O_c_v .

dineshkumars98 avatar Mar 27 '19 13:03 dineshkumars98

But here we can't be merge corner cell. Like:- Merged cell Name: Class: Sub Mark1 Mark2 Mark3 SUB1 1 2 3 SUB2 4 5 6 SUB3 7 8 9

[image: Mailtrack] https://mailtrack.io?utm_source=gmail&utm_medium=signature&utm_campaign=signaturevirality5& Sender notified by Mailtrack https://mailtrack.io?utm_source=gmail&utm_medium=signature&utm_campaign=signaturevirality5& 27/03/19, 7:25:34 pm

On Wed, Mar 27, 2019 at 7:07 PM Dinesh kumar S [email protected] wrote:

very very Thankful

[image: Mailtrack] https://mailtrack.io?utm_source=gmail&utm_medium=signature&utm_campaign=signaturevirality5& Sender notified by Mailtrack https://mailtrack.io?utm_source=gmail&utm_medium=signature&utm_campaign=signaturevirality5& 27/03/19, 7:06:46 pm

On Sat, Mar 23, 2019 at 7:45 PM Kurt Jung [email protected] wrote:

Can merge the cell's in the table? How would you do?

Tables in gofpdf are built up of individual cells. You can merge them manually with something like this:

package main import ( "fmt"

"github.com/jung-kurt/gofpdf/v2" ) func main() { const ( colCount = 4 rowCount = 3 margin = 32.0 fontHt = 14.0 // point ) pdf := gofpdf.New("L", "mm", "A4", "") cellWd := (297 - margin - margin) / colCount cellHt := pdf.PointToUnitConvert(fontHt) + 10.0 pdf.SetFont("Arial", "", fontHt) pdf.AddPage() pdf.SetXY(margin, margin) for rowJ := 0; rowJ < rowCount; rowJ++ { for colJ := 0; colJ < colCount; colJ++ { pdf.SetXY(margin+float64(colJ)*cellWd, margin+float64(rowJ)cellHt) if rowJ == 1 && (colJ == 1 || colJ == 2) { if colJ == 1 { // Merged cells pdf.CellFormat(cellWd2.0, cellHt, fmt.Sprintf("Merged cells"), "1", 0, "CM", false, 0, "") } } else { pdf.CellFormat(cellWd, cellHt, fmt.Sprintf("Row %d, Col %d", rowJ, colJ), "1", 0, "CM", false, 0, "") } } } err := pdf.OutputFileAndClose("colmerge.pdf") if err != nil { fmt.Printf("%s\n", err) } }

— You are receiving this because you commented. Reply to this email directly, view it on GitHub https://github.com/jung-kurt/gofpdf/issues/129#issuecomment-475873331, or mute the thread https://github.com/notifications/unsubscribe-auth/Ad0bauryaJCz2ZehXL_Z4xYxAGEqGa_3ks5vZjcHgaJpZM4O_c_v .

dineshkumars98 avatar Mar 27 '19 13:03 dineshkumars98

Hi, Thank you... I got it!.... by the way package main

import ( "fmt"

"github.com/jung-kurt/gofpdf"

)

func main() { const ( colCount = 4 rowCount = 3 margin = 32.0 fontHt = 14.0 // point ) pdf := gofpdf.New("L", "mm", "A4", "") cellWd := (297 - margin - margin) / colCount cellHt := pdf.PointToUnitConvert(fontHt) + 10.0 pdf.SetFont("Arial", "", fontHt) pdf.AddPage() pdf.SetXY(margin, margin) for rowJ := 0; rowJ < rowCount; rowJ++ { for colJ := 0; colJ < colCount; colJ++ { pdf.SetXY(margin+float64(colJ)cellWd, margin+float64 (rowJ)cellHt) if (rowJ == 0 || rowJ == 1) && (colJ == 0 || colJ == 1) { if rowJ == 0 && colJ == 0 { pdf.CellFormat(cellWd2.0, cellHt2.0, fmt.Sprintf("Merged cells"), "1", 0, "CM", false, 0, "") } } else if (rowJ == 0 || rowJ == 1) && (colJ == 2 || colJ == 3) { if (rowJ == 0 || rowJ == 1) && colJ == 2 { pdf.CellFormat(cellWd*2.0, cellHt, fmt.Sprintf("Merged cells"), "1", 0, "CM", false, 0, "") } } else { pdf.CellFormat(cellWd, cellHt, fmt.Sprintf("Row %d, Col %d", rowJ, colJ), "1", 0, "CM", false, 0, "") } } } err := pdf.OutputFileAndClose("colmerge.pdf") if err != nil { fmt.Printf("%s\n", err) } }

Result:-

[image: Mailtrack] https://mailtrack.io?utm_source=gmail&utm_medium=signature&utm_campaign=signaturevirality5& Sender notified by Mailtrack https://mailtrack.io?utm_source=gmail&utm_medium=signature&utm_campaign=signaturevirality5& 27/03/19, 8:10:59 pm

On Wed, Mar 27, 2019 at 7:26 PM Dinesh kumar S [email protected] wrote:

But here we can't be merge corner cell. Like:- Merged cell Name: Class: Sub Mark1 Mark2 Mark3 SUB1 1 2 3 SUB2 4 5 6 SUB3 7 8 9

[image: Mailtrack] https://mailtrack.io?utm_source=gmail&utm_medium=signature&utm_campaign=signaturevirality5& Sender notified by Mailtrack https://mailtrack.io?utm_source=gmail&utm_medium=signature&utm_campaign=signaturevirality5& 27/03/19, 7:25:34 pm

On Wed, Mar 27, 2019 at 7:07 PM Dinesh kumar S [email protected] wrote:

very very Thankful

[image: Mailtrack] https://mailtrack.io?utm_source=gmail&utm_medium=signature&utm_campaign=signaturevirality5& Sender notified by Mailtrack https://mailtrack.io?utm_source=gmail&utm_medium=signature&utm_campaign=signaturevirality5& 27/03/19, 7:06:46 pm

On Sat, Mar 23, 2019 at 7:45 PM Kurt Jung [email protected] wrote:

Can merge the cell's in the table? How would you do?

Tables in gofpdf are built up of individual cells. You can merge them manually with something like this:

package main import ( "fmt"

"github.com/jung-kurt/gofpdf/v2" ) func main() { const ( colCount = 4 rowCount = 3 margin = 32.0 fontHt = 14.0 // point ) pdf := gofpdf.New("L", "mm", "A4", "") cellWd := (297 - margin - margin) / colCount cellHt := pdf.PointToUnitConvert(fontHt) + 10.0 pdf.SetFont("Arial", "", fontHt) pdf.AddPage() pdf.SetXY(margin, margin) for rowJ := 0; rowJ < rowCount; rowJ++ { for colJ := 0; colJ < colCount; colJ++ { pdf.SetXY(margin+float64(colJ)*cellWd, margin+float64(rowJ)cellHt) if rowJ == 1 && (colJ == 1 || colJ == 2) { if colJ == 1 { // Merged cells pdf.CellFormat(cellWd2.0, cellHt, fmt.Sprintf("Merged cells"), "1", 0, "CM", false, 0, "") } } else { pdf.CellFormat(cellWd, cellHt, fmt.Sprintf("Row %d, Col %d", rowJ, colJ), "1", 0, "CM", false, 0, "") } } } err := pdf.OutputFileAndClose("colmerge.pdf") if err != nil { fmt.Printf("%s\n", err) } }

— You are receiving this because you commented. Reply to this email directly, view it on GitHub https://github.com/jung-kurt/gofpdf/issues/129#issuecomment-475873331, or mute the thread https://github.com/notifications/unsubscribe-auth/Ad0bauryaJCz2ZehXL_Z4xYxAGEqGa_3ks5vZjcHgaJpZM4O_c_v .

dineshkumars98 avatar Mar 27 '19 14:03 dineshkumars98

Thank you... I got it!

Great!

jung-kurt avatar Mar 27 '19 15:03 jung-kurt

hi @jung-kurt One more question..! If the table exists the 1-page alignment is missed... This is a bug or else need to configure anything? like: Screenshot from 2019-04-01 15-47-20

dineshkumars98 avatar Apr 01 '19 10:04 dineshkumars98

Sorry to bump this issue, but I ran into this too! The above was a complete dealbreaker, so I actually solved it a rather different way - by creating a writeRow() function. Do you think, if I flesh it out and fix it a little, this might be merged into the library?

type rowContents struct {
	Contents            string
	ColumnWidthFraction float64
}
...
    usablePageWidth = pageWidth - 2*margin
...

func writeRow(pdf *gofpdf.Fpdf, lineHeight int, contents ...rowContents) {
	var maxHeight float64

	for _, cellContent := range contents {
		columnWidth := (cellContent.ColumnWidthFraction * usablePageWidth) - 2 // -2 for table margins

		splitStrings := pdf.SplitText(cellContent.Contents, columnWidth)
		if newHeight := float64(len(splitStrings)) * float64(lineHeight); newHeight > maxHeight {
			maxHeight = newHeight
		}
	}

	for _, cellContent := range contents {
		pdf.CellFormat(cellContent.ColumnWidthFraction*usablePageWidth, maxHeight,
			cellContent.Contents, "1", 0, "L", false, 0, "")
	}
	pdf.Ln(-1)
}

sk0g avatar Jul 02 '19 05:07 sk0g

Do you think, if I flesh it out and fix it a little, this might be merged into the library?

Definitely! Thanks, @sk0g.

Of course, an example demonstrating it is always helpful.

jung-kurt avatar Jul 02 '19 11:07 jung-kurt