chromedp is a faster and simpler Go library that supports the Chrome DevTools Protocol, it is one of the most popular headless browser libraries, you can use it to do a lot of tasks that can only be performed through the browser, such as web screenshots, web rendering tests, downloading videos, simulating login, etc. Today Today I’m going to introduce a useful and simple feature: generating a pdf screenshot of a web page, for more examples you can see the official example chromedp/examples.

First of all, you need to have chrome installed so that the chromedp library can call chrome to perform tasks (actions) via the cdp protocol.

Generate pdf for web pages

First, you need to introduce the chromedp library.

1
go get -u github.com/chromedp/chromedp

Then, you can perform a series of actions through `chromedp.Run, such as our example is to first navigate to a page, and then generate the page as a pdf:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
// Generate task list
func printToPDF(urlstr string, res *[]byte) chromedp.Tasks {
	return chromedp.Tasks{
		chromedp.Navigate(urlstr), // Browse the specified page
		chromedp.ActionFunc(func(ctx context.Context) error {
			buf, _, err := page.PrintToPDF().WithPrintBackground(true).Do(ctx) // PrintToPDF through cdp implementation
			if err != nil {
				return err
			}
			*res = buf
			return nil
		}),
	}
}

ActionFunc is a convenience method to execute a function as an Action, just like the standard library http.Handler and `http. Because here we want to implement the logic is relatively simple, so through a function to achieve it.

page.PrintToPDF () is to define the implementation of the output pdf of some parameters, you can set some additional parameters, these parameters include.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
type PrintToPDFParams struct {
	Landscape               bool                   `json:"landscape,omitempty"`               // Print in landscape orientation. Default is false.
	DisplayHeaderFooter     bool                   `json:"displayHeaderFooter,omitempty"`     // Print header and footer. default false.
	PrintBackground         bool                   `json:"printBackground,omitempty"`         // Print the background image.  Default is false.
	Scale                   float64                `json:"scale,omitempty"`                   // Scaling factor. Default is 1.
	PaperWidth              float64                `json:"paperWidth,omitempty"`              // Page width (inches). Default 8.5 inches (US Letter standard size, not much different from A4 paper).
	PaperHeight             float64                `json:"paperHeight,omitempty"`             // Page height (inches). Default 11 inches (Letter standard size).
	MarginTop               float64                `json:"marginTop"`                         // Top margin (inch). Default 1cm (about 0.4 inches).

	MarginBottom            float64                `json:"marginBottom"`                      // Bottom Margin (inch). Default 1cm (about 0.4 inches).
	MarginLeft              float64                `json:"marginLeft"`                        // Left margin (inch). Default 1cm (about 0.4 inches).
	MarginRight             float64                `json:"marginRight"`                       // Right margin (inch). Default 1cm (about 0.4 inches).
	PageRanges              string                 `json:"pageRanges,omitempty"`              // The page number to print, for example, '1-5, 8, 11-13'. Default is empty, print all.
	IgnoreInvalidPageRanges bool                   `json:"ignoreInvalidPageRanges,omitempty"` // Whether to ignore illegal page ranges. Default is false.
	HeaderTemplate          string                 `json:"headerTemplate,omitempty"`          // HTML template head. 
	FooterTemplate          string                 `json:"footerTemplate,omitempty"`          // HTML template footer.
	PreferCSSPageSize       bool                   `json:"preferCSSPageSize,omitempty"`       // Is the css-defined page size preferred? Default false, will automatically adapt.
	TransferMode            PrintToPDFTransferMode `json:"transferMode,omitempty"`            // Return to stream
}

Here our example does not do additional settings, only adjust the print background image parameters, of course, you in order to print a beautiful pdf words, you can adjust the parameters here, more suitable for reading and printing.

Do through the cdp protocol to perform printing and return the results.

The main logic is complete, the next step is to perform these tasks.

First to create a chromedp of the Context:

1
ctx, cancel := chromedp.NewContext(context.Background())

Then just call chromedp.Run to execute the task:

1
2
3
if err := chromedp.Run(ctx, printToPDF(`https://colobu.com/`, &buf)); err != nil {
	log.Fatal(err)
}

Finally, write the pdf to the file, complete.

1
2
3
if err := ioutil.WriteFile("colobu.pdf", buf, 0644); err != nil {
	log.Fatal(err)
}

The effect of the generated pdf is as follows:

pdf image

The complete code is as follows:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
package main
import (
	"context"
	"io/ioutil"
	"log"
	"github.com/chromedp/cdproto/page"
	"github.com/chromedp/chromedp"
)
func main() {
	// Create context
	ctx, cancel := chromedp.NewContext(context.Background())
	defer cancel()
	// Generate pdf
	var buf []byte
	if err := chromedp.Run(ctx, printToPDF(`https://colobu.com/`, &buf)); err != nil {
		log.Fatal(err)
	}
	if err := ioutil.WriteFile("colobu.pdf", buf, 0644); err != nil {
		log.Fatal(err)
	}
}
// Generate task list
func printToPDF(urlstr string, res *[]byte) chromedp.Tasks {
	return chromedp.Tasks{
		chromedp.Navigate(urlstr), // Browse the specified page
		chromedp.ActionFunc(func(ctx context.Context) error {
			buf, _, err := page.PrintToPDF().WithPrintBackground(false).Do(ctx) // PrintToPDF through cdp implementation
			if err != nil {
				return err
			}
			*res = buf
			return nil
		}),
	}
}

Generate beautiful charts

echarts is a very famous charting library contributed by baidu, which can generate huge beautiful charts for web pages via js to display data. go language has some “toy” charting libraries, but there is no real icon library, so some people use echarts to generate a web page to display data, this library is go-echarts.

But, after all, this is the convoluted way, and the final data generated is a web page.

Since we just through chromedp can generate pdf, then is it also possible to screenshot, the go-echarts generated charts screenshot into a Go Image object? Let’s give it a try.

First, we first use go-echarts to generate a chart, and save it as a html page:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
func generateEcharts() {
	bar := charts.NewBar()
	// set some global options like Title/Legend/ToolTip or anything else
	bar.SetGlobalOptions(charts.WithTitleOpts(opts.Title{
		Title:    "生成一个漂亮的bar图表",
		Subtitle: "我要得到它的灵魂",
	}))
	// Put data into instance
	bar.SetXAxis([]string{"Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"}).
		AddSeries("Category A", generateBarItems()).
		AddSeries("Category B", generateBarItems())
	// Where the magic happens
	f, _ := os.Create("bar.html")
	bar.Render(f)
}
func generateBarItems() []opts.BarData {
	items := make([]opts.BarData, 0)
	for i := 0; i < 7; i++ {
		items = append(items, opts.BarData{Value: rand.Intn(300)})
	}
	return items
}

The next step is chromedp’s work, browsing this local page and taking screenshots:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
// Generate echarts pages
generateEcharts()
// create chromedp context
ctx, cancel := chromedp.NewContext(
	context.Background(),
	chromedp.WithDebugf(log.Printf),
)
defer cancel()
// Define tasks
elementScreenshot := func(urlstr, sel string, res *[]byte) chromedp.Tasks {
	return chromedp.Tasks{
		chromedp.Navigate(urlstr),
		chromedp.Screenshot(sel, res, chromedp.NodeVisible),
	}
}
// Generate screenshots
var buf []byte
barFile, _ := filepath.Abs("./bar.html")
if err := chromedp.Run(ctx, elementScreenshot(`file://`+barFile, `canvas`, &buf)); err != nil {
	log.Fatal(err)
}
// Writing screenshots to a file
if err := ioutil.WriteFile("bar.png", buf, 0o644); err != nil {
	log.Fatal(err)
}

Eventually, a screenshot is generated. You can generate this screenshot as an Image object or save it to a file. Here we don’t do any additional processing, so we save it to a file. The generated file is as follows:

echarts screenshot

Of course, there are many other things you can do with chromedp’s print and screenshot functions, such as converting epub eBooks to pdf format, grafana screenshot alarms, etc.


Reference https://colobu.com/2021/05/05/generate-pdf-for-a-web-page-by-using-chromedp/