gochromedp

Go ChromeDP ignores any external or internal css during printing to pdf and uses only those, that in html file ONLY


Go ChromeDP not using any css either internal or external styles (only those, that was written in html, not other file). I using method

page.SetDocumentContent(frameTree.Frame.ID, string(buf.Bytes())).Do(ctx)

to add html file to chromedp, and

buf, _, err: = page.PrintToPDF().Do(ctx)
if err != nil {
    return err
}
_, err = outputBuf.Write(buf)
if err != nil {
    return err
}

to print to pdf, but pdf in result not styled (even with external css fileserver). I tried to add it with page.GetResourceTree().Do(ctx) + css.CreateStyleSheet(resourceTree.Frame.ID).Do(ctx) +

css.SetStyleSheetText(stylesheet, `.c {
     color: red;
     font-size: 30px;
     background-color: aqua;
}
`).Do(ctx)

and it worked, but it sad to use it every time I want generate pdf, especially in my case, because I am using html from html/template. Maybe there are easy way to add external css into single html file? What do you think?

Thanks for any answer

Bohdan

I want to convert go template with external css, images and font into pdf using chromedp, but it ignored anything beyond main html file.


Solution

  • Please note that external resources take time to load. You should wait for them to be loaded. When the page is ready, the Page.loadEventFired event is raised. So we can wait for this event and print the page afterward. See the demo below:

    package main
    
    import (
        "context"
        "fmt"
        "log"
        "net/http"
        "net/http/httptest"
        "os"
        "sync"
        "time"
    
        "github.com/chromedp/cdproto/page"
        "github.com/chromedp/chromedp"
    )
    
    func main() {
        ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
            // Simulate a network latency.
            time.Sleep(2 * time.Second)
            w.Header().Set("Content-Type", "text/css")
            fmt.Fprint(w, `h1 {font-size: 100pt; color: red;}`)
        }))
        defer ts.Close()
    
        ctx, cancel := chromedp.NewContext(context.Background())
        defer cancel()
    
        // construct your html
        html := `<html>
        <head>
            <link rel="stylesheet" href="%s/style.css">
        </head>
        <body>
            <h1> Hello World! </h1>
        </body>
    </html>
    `
    
        var wg sync.WaitGroup
        wg.Add(1)
        if err := chromedp.Run(ctx,
            chromedp.Navigate("about:blank"),
            // setup the listener to listen for the page.EventLoadEventFired
            chromedp.ActionFunc(func(ctx context.Context) error {
                lctx, cancel := context.WithCancel(ctx)
                chromedp.ListenTarget(lctx, func(ev interface{}) {
                    if _, ok := ev.(*page.EventLoadEventFired); ok {
                        wg.Done()
                        // remove the event listener
                        cancel()
                    }
                })
                return nil
            }),
            chromedp.ActionFunc(func(ctx context.Context) error {
                frameTree, err := page.GetFrameTree().Do(ctx)
                if err != nil {
                    return err
                }
                return page.SetDocumentContent(frameTree.Frame.ID, fmt.Sprintf(html, ts.URL)).Do(ctx)
            }),
            // wait for page.EventLoadEventFired
            chromedp.ActionFunc(func(ctx context.Context) error {
                wg.Wait()
                return nil
            }),
            chromedp.ActionFunc(func(ctx context.Context) error {
                buf, _, err := page.PrintToPDF().Do(ctx)
                if err != nil {
                    return err
                }
                return os.WriteFile("sample.pdf", buf, 0644)
            }),
        ); err != nil {
            log.Fatal(err)
        }
    
        log.Println("done!")
    }
    

    Reference: https://github.com/chromedp/chromedp/issues/1152.