在Go语言中从HTML创建PDF

40
如何在Google Go中从HTML输入创建PDF文件?如果还不可能,是否有任何旨在解决此问题的项目?
我正在寻找类似于PHP中的TCPDF的解决方案。

1
请查看此链接:https://github.com/SebastiaanKlippert/go-wkhtmltopdf - muthukumar selvaraj
在大多数语言的PDF生成器中,你只能得到基本的HTML支持。没有适当的字体或最新的CSS。到目前为止,我在使用许多编程语言后找到的最佳解决方案是使用Chrome Puppeteer作为生成PDF的微服务。它运行得非常完美。否则,你就必须使用付费的库。 - Krishnadas PC
8个回答

17

安装

go get -u github.com/SebastiaanKlippert/go-wkhtmltopdf

go version go1.9.2 linux/amd64

代码

   import (
        "fmt"
        "strings"
        wkhtml "github.com/SebastiaanKlippert/go-wkhtmltopdf"
    )  
    
      func main(){
                 pdfg, err :=  wkhtml.NewPDFGenerator()
               if err != nil{
                  return
              }
              htmlStr := `<html><body><h1 style="color:red;">This is an html
 from pdf to test color<h1><img src="http://api.qrserver.com/v1/create-qr-
code/?data=HelloWorld" alt="img" height="42" width="42"></img></body></html>`
            
              pdfg.AddPage(wkhtml.NewPageReader(strings.NewReader(htmlStr)))
            
   
              // Create PDF document in internal buffer
              err = pdfg.Create()
              if err != nil {
                  log.Fatal(err)
              }
            
               //Your Pdf Name
               err = pdfg.WriteFile("./Your_pdfname.pdf")
              if err != nil {
                  log.Fatal(err)
              }
            
              fmt.Println("Done")
        }

以上代码适用于使用嵌入式CSS样式标签并带有正确背景图像的HTML转PDF的golang程序。

查看仓库

查看拉取请求改进文档

建议(来自https://wkhtmltopdf.org/status.html):

不要使用wkhtmltopdf处理任何不受信任的HTML——请务必对任何用户提供的HTML / JS进行消毒处理,否则可能会导致服务器完全被接管! 请考虑使用强制访问控制系统,例如AppArmor或SELinux,请参见建议的AppArmor策略。

如果您将其用于报告生成(即使用您控制的HTML),还可以考虑使用WeasyPrint或商业工具Prince——注意,我与任一项目均无关联,请勤勉尽责。

如果您将其用于转换使用动态JS的站点,请考虑使用puppeteer或其众多包装器之一。


4
go-wkhtmltopdf 依赖于 wkhtmltopdf 可执行文件。在使用前必须先将其安装到系统中。而 wkhtmltopdf 又依赖于来自 xserver 的约50到60个包。因此它并不适合作为后端解决方案。 - Igor
需要注意的是,wkhtmltopdf已被存档,不再得到积极维护。 - David

17

29
这两个库都没有解决这个问题。用户正在寻找将HTML转换为PDF的方法,而这些库只是PDF生成器。尽管它们可能是好的PDF生成器,但是它们都不能将HTML文档转换为PDF。我需要在另一个主题中再次提出这个问题。 - taystack
以上两个依赖项都无法将<html>转换为.pdf - muthukumar selvaraj
这个答案似乎是正确的: https://dev59.com/JmUp5IYBdhLWcg3w8bJB#48568415 - Hamed Lohi

7

page.PrintToPDF()函数非常好用。

下面是一个使用chromedp(go get -u github.com/chromedp/chromedp)的示例:

import (
    "context"
    "fmt"
    "io/ioutil"
    "log"
    "net/http"
    "os"
    "time"

    "github.com/chromedp/cdproto/emulation"
    "github.com/chromedp/cdproto/page"
    "github.com/chromedp/chromedp"
)

func main() {
        taskCtx, cancel := chromedp.NewContext(
            context.Background(),
            chromedp.WithLogf(log.Printf),
        )
        defer cancel()
        var pdfBuffer []byte
        if err := chromedp.Run(taskCtx, pdfGrabber("https://www.wikipedia.org", "body", &pdfBuffer)); err != nil {
            log.Fatal(err)
        }
        if err := ioutil.WriteFile("coolsite.pdf", pdfBuffer, 0644); err != nil {
            log.Fatal(err)
        }
}

func pdfGrabber(url string, sel string, res *[]byte) chromedp.Tasks {

    start := time.Now()
    return chromedp.Tasks{
        emulation.SetUserAgentOverride("WebScraper 1.0"),
        chromedp.Navigate(url),
        // wait for footer element is visible (ie, page is loaded)
        // chromedp.ScrollIntoView(`footer`),
        chromedp.WaitVisible(`body`, chromedp.ByQuery),
        // chromedp.Text(`h1`, &res, chromedp.NodeVisible, chromedp.ByQuery),
        chromedp.ActionFunc(func(ctx context.Context) error {
            buf, _, err := page.PrintToPDF().WithPrintBackground(true).Do(ctx)
            if err != nil {
                return err
            }
            *res = buf
            //fmt.Printf("h1 contains: '%s'\n", res)
            fmt.Printf("\nTook: %f secs\n", time.Since(start).Seconds())
            return nil
        }),
    }
}

以上代码将在 Chrome 无头模式下加载 wikipedia.org 并等待 body 元素出现,然后将其保存为 PDF。
终端中的结果:
$ go run main.go
https://www.wikipedia.org
Scraping url now...

Took: 2.772797 secs

7

还有这个包wkhtmltopdf-go,它使用libwkhtmltox库。不过我不确定它的稳定性如何。


它非常糟糕,一直占用我的CPU资源,并产生僵尸进程。 - George Thomas

4
我不认为我理解你的需求。由于HTML是一种标记语言,它需要上下文来呈现(CSS和屏幕尺寸)。我看到的现有实现通常在无头浏览器中打开页面并创建PDF文件。
个人建议只需使用现有的软件包并从Go外部调用。这个软件包看起来不错;甚至在此答案中也推荐使用。
如果您真的决心要在Go中实现,可以查看这个WebKit封装。我不确定您将用什么来生成PDF,但至少它是一个开始。

我没有太多特殊的要求。我需要创建PDF文件,但最好不是从go代码创建,而是从一个在灵活性和易学性之间取得良好平衡的源中创建。在php中,有多个库可用于将HTML文档转换为PDF,因为HTML易于学习且相当灵活。我很好奇是否已经有人编写了这样的库。谢谢您的回答。 - mimrock

3

我正在创建一个替代库,以更简单的方式创建PDF(https://github.com/johnfercher/maroto)。它使用 gofpdf,具有网格系统和一些像 Bootstrap 这样的组件。


1

另一个选择是Athena。它有一个用Go编写的微服务或者可以用作CLI。


-4
另一个选择是UniHTML(基于容器的API),它与UniPDF互操作,可用于基于HTML模板创建PDF报告等。
它在容器中使用无头Chrome引擎,因此呈现完美并具有所有HTML功能。与UniPDF的组合提供了额外的优势,例如自动生成目录、大纲等。以及添加密码保护、添加PDF表单、数字签名等的能力。
要为磁盘上的HTML模板创建PDF,可以执行以下操作:
package main

import (
    "fmt"
    "os"

    "github.com/unidoc/unihtml"
    "github.com/unidoc/unipdf/v3/common/license"
    "github.com/unidoc/unipdf/v3/creator"
)

func main() {
    // Set the UniDoc license.
    if err := license.SetMeteredKey("my api key goes here"); err != nil {
        fmt.Printf("Err: setting metered key failed: %v\n", err)
        os.Exit(1)
    }

    // Establish connection with the UniHTML Server.
    if err := unihtml.Connect(":8080"); err != nil {
        fmt.Printf("Err:  Connect failed: %v\n", err)
        os.Exit(1)
    }

    // Get new PDF Creator.
    c := creator.New()

    // AddTOC enables Table of Contents generation.
    c.AddTOC = true

    chapter := c.NewChapter("Points")

    // Read the content of the sample.html file and load it to the conversion.
    htmlDocument, err := unihtml.NewDocument("sample.html")
    if err != nil {
        fmt.Printf("Err: NewDocument failed: %v\n", err)
        os.Exit(1)
    }

    // Draw the html document file in the context of the creator.
    if err = chapter.Add(htmlDocument); err != nil {
        fmt.Printf("Err: Draw failed: %v\n", err)
        os.Exit(1)
    }

    if err = c.Draw(chapter); err != nil {
        fmt.Printf("Err: Draw failed: %v\n", err)
        os.Exit(1)
    }


    // Write the result file to PDF.
    if err = c.WriteToFile("sample.pdf"); err != nil {
        fmt.Printf("Err: %v\n", err)
        os.Exit(1)
    }
}

我已经写了一篇关于UniHTML的介绍文章,如果需要更多信息可以参考这里(https://www.unidoc.io/post/html-for-pdf-reports-in-go)。

声明:我是UniPDF的原始开发者。


8
建议您警告人们,使用您的解决方案至少需要支付1500美元。来源:https://www.unidoc.io/pricing - ewen-lbh

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接