# Go 利用 chromedp 生成 pdf ## chromedp Package [chromedp](https://github.com/chromedp/chromedp) is a faster, simpler way to drive browsers supporting the Chrome DevTools Protocol in Go without external dependencies. 可以查看官方的[示例](https://github.com/chromedp/examples)。 以下示例用的版本为 `github.com/chromedp/chromedp v0.8.4`。 ## 示例 ### 打印在线页面 :point_down: 有时需要打印一个在线页面成 pdf,比如把`https://www.baidu.com/`这个页面打印成 pdf,如下 ```go package main import ( "bufio" "bytes" "context" "fmt" "net/http" "github.com/chromedp/cdproto/page" "github.com/chromedp/chromedp" "github.com/gin-gonic/gin" "github.com/pkg/errors" ) func dp(c *gin.Context) { var ( err error buf = make([]byte, 0) ) ctx, cancel := chromedp.NewContext(context.Background()) defer cancel() err = chromedp.Run(ctx, chromedp.Tasks{ chromedp.Navigate("https://www.baidu.com/"), chromedp.ActionFunc(func(ctx context.Context) error { buf, _, err = page.PrintToPDF(). WithPrintBackground(true). Do(ctx) return err }), }) if err != nil { err = errors.Wrapf(err, "chromedp Run failed") c.JSON(http.StatusInternalServerError, gin.H{ "msg": err.Error(), }) return } buffer := &bytes.Buffer{} buffer.WriteString("\xEF\xBB\xBF") // 防止中文乱码 writer := bufio.NewWriter(buffer) _, err = writer.Write(buf) if err != nil { err = errors.Wrapf(err, "bufio Write err") c.JSON(http.StatusInternalServerError, gin.H{ "msg": err.Error(), }) return } _ = writer.Flush() fileName := fmt.Sprintf("1111.pdf") c.Header("Content-Type", "text/pdf") c.Header("Content-Disposition", "attachment;filename="+fileName) _, _ = c.Writer.Write(buffer.Bytes()) return } func main() { route := gin.New() route.GET("/dp", dp) route.Run(":8080") } ``` ### 打印本地文件 有时需要将一个本地的 html 文件渲染后,提供下载链接,下载成一个 pdf 格式的文件。 ```go package main import ( "bufio" "bytes" "context" "fmt" "html/template" "net/http" "net/http/httptest" "os" "github.com/chromedp/cdproto/page" "github.com/chromedp/chromedp" "github.com/gin-gonic/gin" "github.com/pkg/errors" ) type Content struct { ChannelLogo template.URL ProductLogo template.URL Title string Content string UserName string OrgName string ChannelName string Number string Date string BackgroundImage template.URL } func writeHTML(content Content) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "text/html") var ( wd string err error tmpl *template.Template ) wd, _ = os.Getwd() tmpl, err = template.ParseFiles(wd + "/tmp.html") if err != nil { err = errors.Wrapf(err, "template.ParseFiles err") _, _ = w.Write([]byte(err.Error())) return } buffer := &bytes.Buffer{} err = tmpl.Execute(buffer, content) if err != nil { err = errors.Wrapf(err, "tmpl.Execute err") _, _ = w.Write([]byte(err.Error())) return } _, _ = w.Write(buffer.Bytes()) }) } func dp(c *gin.Context) { var ( err error buf = make([]byte, 0) ) ctx, cancel := chromedp.NewContext(context.Background()) defer cancel() mux := http.NewServeMux() mux.Handle("/pre", writeHTML(Content{ ChannelLogo: "", ProductLogo: "", Title: "我是title", Content: "我是内容", UserName: "1111", OrgName: "2222", ChannelName: "3333", Number: "4444", Date: "2006-01-02 15:04:05", BackgroundImage: "", })) ts := httptest.NewServer(mux) defer ts.Close() url := fmt.Sprintf("%s/pre", ts.URL) err = chromedp.Run(ctx, chromedp.Tasks{ chromedp.Navigate(url), chromedp.WaitReady("body"), chromedp.ActionFunc(func(ctx context.Context) error { var err error buf, _, err = page.PrintToPDF(). WithPrintBackground(true). WithPageRanges("1"). Do(ctx) return err }), }) if err != nil { err = errors.Wrapf(err, "chromedp Run failed") c.JSON(http.StatusInternalServerError, gin.H{ "msg": err.Error(), }) return } buffer := &bytes.Buffer{} buffer.WriteString("\xEF\xBB\xBF") // 防止中文乱码 writer := bufio.NewWriter(buffer) _, err = writer.Write(buf) if err != nil { err = errors.Wrapf(err, "bufio Write err") c.JSON(http.StatusInternalServerError, gin.H{ "msg": err.Error(), }) return } _ = writer.Flush() fileName := fmt.Sprintf("1111.pdf") c.Header("Content-Type", "text/pdf") c.Header("Content-Disposition", "attachment;filename="+fileName) _, _ = c.Writer.Write(buffer.Bytes()) return } func main() { route := gin.New() route.GET("/dp", dp) route.Run(":8080") } ``` 本地文件 tmp.html: ```html
{{.ChannelName}}
{{.Date}}
{{.OrgName}}
{{.Date}}