jung-kurt / gofpdf

A PDF document generator with high level support for text, drawing and images
http://godoc.org/github.com/jung-kurt/gofpdf
MIT License
4.31k stars 777 forks source link

Index out of range #273

Closed mthizozo closed 5 years ago

mthizozo commented 5 years ago

Hi Jung Kurt

Please assist as i am recieving an index out range error on fpdf.go link 2578 where the font is set after deploying to openshift, yet the code is working locally.

Please advise what if the library would behave differently on different environments

jung-kurt commented 5 years ago

Can you verify that the font used locally is exactly the same as the one used in openshift?

mthizozo commented 5 years ago

yes I used. pdf.setFont("arial" , "B", 13). This is the same code thats deployed yet locally it gives no error

jung-kurt commented 5 years ago

Can you verify that the JSON files in the font subdirectory are that same in both the remote and local locations? In your case, it looks like font/helveticab.json may be problematic.

mapreal19 commented 5 years ago

@jung-kurt we've experienced the same problem when deploying to heroku. Everything works in local too. Maybe getting the wrong path for the fonts? In heroku source code goes to the app directory

jung-kurt commented 5 years ago

Thanks, @mapreal19 -- your information will be helpful in correcting this problem.

mapreal19 commented 5 years ago

Found our problem. What happened is that we had an error when trying to render an image. That's why f.err wasn't nil here: https://github.com/jung-kurt/gofpdf/blob/master/fpdf.go#L1954-L1956 and made us think there was an issue in our fonts.

I think instead of returning we should panic here? SetFont should succeed otherwise we will have problems like this, assuming the font was set when actually it wasn't because of a previous error.

jung-kurt commented 5 years ago

Thank you for tracking this down, @mapreal19.

One can argue with the way gofpdf handles errors, but given the large number of steps that go into creating a document, embedding the error (one of the patterns shown in Errors are values) relieves the programmer of checking for an error condition at every turn. Rather than introduce panics into the codebase, I'd like to pinpoint where the embedded error scheme failed in this case. Somewhere I think the internal error value got overwritten or a method failed to execute a quick return on an error condition. In this case, the final error check when OutputFileAndClose() or friend is called should have clearly indicated that it was an image rendering error.

jung-kurt commented 5 years ago

@mapreal19, can you identify the minimal number of steps needed to reproduce the problem? Will an attempt to load a non-existent image followed by a call to SetFont() result in the crash? I will step through with Delve to see where the error handling is going astray.

mapreal19 commented 5 years ago

Just calling this I think would do (let me know if you're able to reproduce):

var pdf = gofpdf.New("P", "pt", "A4", "")
pdf.AddPage()

// set imgPath to a non-existing path
pdf.ImageOptions(imgPath, margin, margin, imgSz, imgSz, false, opt, 0, "")
pdf.SetFont(titleFont, "B", titleSz)

I suspect the error is being set here: https://github.com/jung-kurt/gofpdf/blob/master/fpdf.go#L3090

And yeah thanks for the explanation, panic there wasn't a great idea given the way you handle errors. It was meant for a quick patch. Hope you guys can find where that overwrite is being done.

jung-kurt commented 5 years ago

Thanks, @mapreal19 -- this is helpful.

jung-kurt commented 5 years ago

I am not able to reproduce the problem with the following snippet. However, I am not able to test it on openshift or heroku where the problems have been reported.

package main

import (
  "fmt"
  "os"

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

func main() {
  var pdf = gofpdf.New("P", "pt", "A4", "")
  pdf.AddPage()
  pdf.ImageOptions("foo.jpg", 10, 10, 100, 100, false, gofpdf.ImageOptions{}, 0, "")
  pdf.SetFont("helvetica", "", 14)
  err := pdf.OutputFileAndClose("foo.pdf")
  if err != nil {
    fmt.Fprintf(os.Stderr, "%s\n", err)
  }
}

Locally, this results in the expected error open foo.jpg: no such file or directory. I do recall some error handling changes were made to some font logic. Can anyone experiencing this problem verify that an up-to-date version of the package is being used?

mapreal19 commented 5 years ago

Hmm I think I missed this line after pdf.SetFont:

pdf.MultiCell(0, 10, "some-text", "", "", false)

This will have the error here: https://github.com/jung-kurt/gofpdf/blob/f92bdc0bcf650f019ee76c1298a4d04c8237cba0/fpdf.go#L2578

mthizozo commented 5 years ago

@jung-kurt the helveiticab.json files are the same locally and remotely.

mthizozo commented 5 years ago

Hi @jung-kurt , the code is failing when pdf.multicell is called,

mthizozo commented 5 years ago

@mapreal19 we are experiencing the exact same issue when using multicell, and its failing on fpdf.go at that same line 2578. what is most strange is that its working fine locally, what would be different after deploying the code?

mapreal19 commented 5 years ago

@mthizozo maybe there is a hidden error like in my case? are you using images?

This commit may be helpful for you: https://github.com/mapreal19/gofpdf/commit/83db124beba3a79b5812070468cce87bfc81c05d

What I did is forking the project and added a bunch of logs until I discovered what was going on

jung-kurt commented 5 years ago

3aaf0b1a66c0ab02d925917376898a5856061d59 (and f58a9cb00082f4f0ee8b2e5c64a6455de46bd659 for version 2) fixes the particular problem with MultiCell() but I think there may be a larger problem here. I'd like to identify why cloud-based builds fail when local ones do not.

mapreal19 commented 5 years ago

In my case it was because the image path was hardcoded in local, hence it didn't work in heroku so not a problem with gofpdf. Not sure about the Openshift case

jung-kurt commented 5 years ago

Let's reopen this if it is still a problem with some environments.

devplayg commented 4 years ago

On windows 10 pro, it works fine; howerver on linux, it does not.

Error

panic: runtime error: index out of range

goroutine 1 [running]:
github.com/jung-kurt/gofpdf.(*Fpdf).SplitLines(0xc00020c000, 0xc0001dd7d0, 0x10, 0x20, 0x4055800000000000, 0x20, 0x40, 0x0)
        /gohome/pkg/mod/github.com/jung-kurt/gofpdf@v1.12.6/fpdf.go:2519 +0x4d4
mysite.com/my_project/smartfactory-server/report.DrawColumnTable(0xc00020c000, 0xc000192960, 0x3, 0x3)
        /gohome/src/mysite.com/my_project/smartfactory-server/report/table.go:133 +0x64a
mysite.com/my_project/smartfactory-server/report.(*Report).writeReportCover(0xc00020a000, 0x0, 0x0)
        /gohome/src/mysite.com/my_project/smartfactory-server/report/report.go:252 +0xca2
mysite.com/my_project/smartfactory-server/report.(*Report).Start(0xc00020a000, 0x7ffd9b71a779, 0x8)
        /gohome/src/mysite.com/my_project/smartfactory-server/report/report.go:188 +0x482
mysite.com/my_project/smartfactory-server/report.(*Server).Start(0xc000173260, 0x9a3822, 0x6)
        /gohome/src/mysite.com/my_project/smartfactory-server/report/server.go:116 +0x107
github.com/devplayg/hippo.(*Engine).Start(0xc000192690, 0x0, 0x0)
        /gohome/pkg/mod/github.com/devplayg/hippo@v0.0.0-20190916124707-bb4b6afa7a8d/engine.go:39 +0x40
main.main()
        /gohome/src/mysite.com/my_project/smartfactory-server/cmd/report/report.go:46 +0x1ea

Code: "DrawColumnTable" function

func DrawTable(pdf *gofpdf.Fpdf, data [][]string) {
    resetTable(pdf)

    colCount := len(data[0])
    colWidth := float64(PDF_WIDTH / colCount)
    const (
        marginLeft = 15.0
        lineHeight = 4.5
        cellGap    = 2.0
    )
    type cellType struct {
        str  string
        list [][]byte
        ht   float64
    }
    var (
        cell cellType
    )

    pdf.Ln(-1)

    startRowNum := 0
    header := data[0]
    // Headers
    pdf.SetX(marginLeft)
    pdf.SetTextColor(24, 24, 24)
    pdf.SetFillColor(230, 230, 230)
    for colJ := 0; colJ < colCount; colJ++ {
        pdf.CellFormat(colWidth, 8, header[colJ], "1", 0, "CM", true, 0, "")
    }
    pdf.Ln(-1)
    pdf.SetTextColor(24, 24, 24)
    pdf.SetFillColor(255, 255, 255)

    startRowNum = 1

    // Rows
    y := pdf.GetY()
    for row := startRowNum; row < len(data); row++ {
        maxHt := lineHeight
        // Cell height calculation loop
        cellList := make([]cellType, 0)
        for col := 0; col < colCount; col++ {

            cell.str = data[row][col]
            cell.list = pdf.SplitLines([]byte(cell.str), colWidth-cellGap-cellGap)
            cell.ht = float64(len(cell.list)) * lineHeight
            if cell.ht > maxHt {
                maxHt = cell.ht
            }
            cellList = append(cellList, cell)
        }
        // Cell render loop
        x := marginLeft
        for colJ := 0; colJ < colCount; colJ++ {
            pdf.Rect(x, y, colWidth, maxHt+cellGap+cellGap, "D")
            cell = cellList[colJ]
            cellY := y + cellGap + (maxHt-cell.ht)/2
            for splitJ := 0; splitJ < len(cell.list); splitJ++ {
                pdf.SetXY(x+cellGap, cellY)
                pdf.CellFormat(colWidth-cellGap-cellGap, lineHeight, string(cell.list[splitJ]), "", 0, "L", false, 0, "")
                cellY += lineHeight
            }
            x += colWidth
        }
        y += maxHt + cellGap + cellGap
    }

    pdf.Ln(-1)
}

Input data of [][]string

([][]string) (len=3 cap=3) {
 ([]string) (len=2 cap=2) {
  (string) (len=16) "보고서 이름",
  (string) (len=16) "기간 보고서"
 },
 ([]string) (len=2 cap=2) {
  (string) (len=13) "대상 기간",
  (string) (len=23) "2019-10-04 ~ 2019-10-04"
 },
 ([]string) (len=2 cap=2) {
  (string) (len=13) "작성 시간",
  (string) (len=25) "2019-10-07T10:15:40+09:00"
 }
}

PDF

func CreatePdf(fontDir string) *gofpdf.Fpdf {
    var fontMap = map[string]string{
        HangulFont:     "NanumGothic.ttf",
        HangulBoldFont: "NanumGothicBold.ttf",
    }

    pdf := gofpdf.New("P", "mm", "A4", fontDir)

    pdf.AddUTF8Font(HangulFont, "", fontMap[HangulFont])
    pdf.AddUTF8Font(HangulBoldFont, "B", fontMap[HangulBoldFont])
    pdf.AddUTF8Font(HangulFont, "I", fontMap[HangulFont])
    pdf.AddUTF8Font(HangulBoldFont, "BI", fontMap[HangulBoldFont])

    pdf.SetTopMargin(30)
    pdf.SetHeaderFuncMode(func() {
        pdf.Image("img/logo_dsme.png", 10, 6, 30, 0, false, "", 0, "")
        pdf.SetY(5)
        //pdf.SetFont(HangulFont, "", 15)
        pdf.SetFont(HangulFont, "", 10)
        pdf.Cell(80, 0, "")
        pdf.CellFormat(30, 9, "SMART FACTORY REPORT", "0", 0, "C", false, 0, "")
        pdf.Ln(20)
    }, true)
    pdf.SetFooterFunc(func() {
        pdf.SetY(-15)
        pdf.SetFont(HangulFont, "I", 8)
        pdf.CellFormat(0, 10, fmt.Sprintf("Page %d/{nb}", pdf.PageNo()),
            "", 0, "C", false, 0, "")
    })
    pdf.AliasNbPages("")

    pdf.SetFont(HangulFont, "", 16)
    return pdf
}
kent-h commented 4 years ago

Just hit this in my environment:

2019/10/09 03:15:33 http: panic serving 127.0.0.1:42980: runtime error: index out of range
goroutine 720 [running]:
net/http.(*conn).serve.func1(0xc0003426e0)
    C:/Go/src/net/http/server.go:1746 +0xd7
panic(0x861700, 0xc4f5c0)
    C:/Go/src/runtime/panic.go:513 +0x1c7
github.com/kent-h/nextslope-backend/vendor/github.com/jung-kurt/gofpdf.(*utf8FontFile).getSymbols(0xc0003c02a0, 0xadc2, 0xc00038ca10, 0xc0012f8c90, 0xc0012f87e0, 0xc000487400, 0x3b, 0x80, 0xc00047a3c0, 0x0, ...)
    E:/Kent/git/go/src/github.com/kent-h/nextslope-backend/vendor/github.com/jung-kurt/gofpdf/utf8fontfile.go:793 +0x535
github.com/kent-h/nextslope-backend/vendor/github.com/jung-kurt/gofpdf.(*utf8FontFile).getSymbols(0xc0003c02a0, 0x13, 0xc00038ca10, 0xc0012f8c90, 0xc0012f87e0, 0xc00047a3c0, 0x3a, 0x3a, 0xc00038ca10, 0xc0012f8c90, ...)
    E:/Kent/git/go/src/github.com/kent-h/nextslope-backend/vendor/github.com/jung-kurt/gofpdf/utf8fontfile.go:814 +0x2b2
github.com/kent-h/nextslope-backend/vendor/github.com/jung-kurt/gofpdf.(*utf8FontFile).parseSymbols(0xc0003c02a0, 0xc0012f8d20, 0x37, 0xc0012f8780, 0x3ff0000000000000, 0x0, 0x44, 0x4)
    E:/Kent/git/go/src/github.com/kent-h/nextslope-backend/vendor/github.com/jung-kurt/gofpdf/utf8fontfile.go:563 +0x56b
github.com/kent-h/nextslope-backend/vendor/github.com/jung-kurt/gofpdf.(*utf8FontFile).GenerateСutFont(0xc0003c02a0, 0xc0012f8d20, 0x0, 0xc00041c620, 0xe)
    E:/Kent/git/go/src/github.com/kent-h/nextslope-backend/vendor/github.com/jung-kurt/gofpdf/utf8fontfile.go:672 +0x432
github.com/kent-h/nextslope-backend/vendor/github.com/jung-kurt/gofpdf.(*Fpdf).putfonts(0xc00050e000)
    E:/Kent/git/go/src/github.com/kent-h/nextslope-backend/vendor/github.com/jung-kurt/gofpdf/fpdf.go:4031 +0xb55
github.com/kent-h/nextslope-backend/vendor/github.com/jung-kurt/gofpdf.(*Fpdf).putresources(0xc00050e000)
    E:/Kent/git/go/src/github.com/kent-h/nextslope-backend/vendor/github.com/jung-kurt/gofpdf/fpdf.go:4506 +0x99
github.com/kent-h/nextslope-backend/vendor/github.com/jung-kurt/gofpdf.(*Fpdf).enddoc(0xc00050e000)
    E:/Kent/git/go/src/github.com/kent-h/nextslope-backend/vendor/github.com/jung-kurt/gofpdf/fpdf.go:4693 +0x77
github.com/kent-h/nextslope-backend/vendor/github.com/jung-kurt/gofpdf.(*Fpdf).Close(0xc00050e000)
    E:/Kent/git/go/src/github.com/kent-h/nextslope-backend/vendor/github.com/jung-kurt/gofpdf/fpdf.go:688 +0x9b
github.com/kent-h/nextslope-backend/vendor/github.com/jung-kurt/gofpdf.(*Fpdf).Output(0xc00050e000, 0x98ce00, 0xc000164770, 0x0, 0xc00050e800)
    E:/Kent/git/go/src/github.com/kent-h/nextslope-backend/vendor/github.com/jung-kurt/gofpdf/fpdf.go:3414 +0xde
github.com/kent-h/nextslope-backend/report/pdf.(*reportGenerator).Complete(0xc0003041e0, 0xc00041c2e0, 0xc, 0xc00041c2f0)
    E:/Kent/git/go/src/github.com/kent-h/nextslope-backend/report/pdf/generate.go:145 +0x64
github.com/kent-h/nextslope-backend/report.Generate(0xc000306b80, 0x6, 0x66, 0x8, 0x0, 0x0, 0x1, 0x1, 0x8, 0x0, ...)
    E:/Kent/git/go/src/github.com/kent-h/nextslope-backend/report/report.go:131 +0x11d9
github.com/kent-h/nextslope-backend/server.postMaintenanceSignOff(0x991280, 0xc0001760e0, 0xc0003b1000)
    E:/Kent/git/go/src/github.com/kent-h/nextslope-backend/server/maintenance_record.go:257 +0x464
net/http.HandlerFunc.ServeHTTP(0x90aee0, 0x991280, 0xc0001760e0, 0xc0003b1000)
    C:/Go/src/net/http/server.go:1964 +0x4b
github.com/kent-h/nextslope-backend/vendor/github.com/gorilla/mux.(*Router).ServeHTTP(0xc0000f8cc0, 0x991280, 0xc0001760e0, 0xc0003b1000)
    E:/Kent/git/go/src/github.com/kent-h/nextslope-backend/vendor/github.com/gorilla/mux/mux.go:133 +0xf8
net/http.StripPrefix.func1(0x991280, 0xc0001760e0, 0xc0003b0d00)
    C:/Go/src/net/http/server.go:2003 +0x192
net/http.HandlerFunc.ServeHTTP(0xc00036bbf0, 0x991280, 0xc0001760e0, 0xc0003b0d00)
    C:/Go/src/net/http/server.go:1964 +0x4b
github.com/kent-h/nextslope-backend/vendor/github.com/gorilla/mux.(*Router).ServeHTTP(0xc00037b020, 0x991280, 0xc0001760e0, 0xc0003b0d00)
    E:/Kent/git/go/src/github.com/kent-h/nextslope-backend/vendor/github.com/gorilla/mux/mux.go:133 +0xf8
net/http.serverHandler.ServeHTTP(0xc000196a90, 0x991280, 0xc0001760e0, 0xc0003b0b00)
    C:/Go/src/net/http/server.go:2741 +0xb2
net/http.(*conn).serve(0xc0003426e0, 0x991540, 0xc000069d80)
    C:/Go/src/net/http/server.go:1847 +0x64d
created by net/http.(*Server).Serve
    C:/Go/src/net/http/server.go:2851 +0x2fc

The strange part is that it seems to run perfectly fine (generates the pdf without errors) exactly once when the process is started, but fails on every subsequent attempt.

I'm using in-memory fonts (AddUTF8FontFromBytes), so I currently suspect something is being overwritten where it shouldn't be...

jung-kurt commented 4 years ago

@Kent-H -- This seems to be related to #315. A hacky workaround has been merged that makes a copy of in-memory fonts. I don't see any version information in the panic report but if your version predates eeeab0b32e45c4544d2fe95ad76c07a1bf695aa2 from four days ago you may want to update the gofpdf package(go get -u github.com/jung-kurt/gofpdf). Can you do this and report back?

kent-h commented 4 years ago

Updating appears to have fixed my issue, thank you!

jung-kurt commented 4 years ago

Thanks for the report, @Kent-H.