Go ChromeDP转PDF样式失效求助:如何整合外部CSS到HTML模板
解决ChromeDP加载HTML时外部资源失效的问题
当使用page.SetDocumentContent直接注入HTML字符串时,ChromeDP不会自动触发外部资源(CSS、图片、字体)的加载请求,导致生成的PDF丢失样式和媒体资源。以下是几种简便的解决方案:
1. 预渲染时内联CSS到HTML
在渲染html/template模板阶段,直接读取外部CSS文件内容,插入到HTML的<style>标签中:
模板示例(template.html)
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>PDF Content</title> <style>{{.CSSContent}}</style> </head> <body> <!-- 页面内容 --> <div class="c">带样式的内容</div> </body> </html>
Go代码实现
// 读取外部CSS文件 cssBytes, err := os.ReadFile("./styles/main.css") if err != nil { return err } // 准备模板数据 templateData := map[string]interface{}{ "CSSContent": string(cssBytes), // 其他模板变量 } // 渲染模板到缓冲区 var buf bytes.Buffer tmpl, err := template.ParseFiles("./template.html") if err != nil { return err } if err := tmpl.Execute(&buf, templateData); err != nil { return err } // 注入渲染后的HTML到ChromeDP page.SetDocumentContent(frameTree.Frame.ID, buf.String()).Do(ctx)
2. 图片与字体转Data URI内联
将本地图片、字体文件转为base64编码的Data URI,直接嵌入HTML或CSS中,避免外部资源请求:
处理图片的示例代码
// 读取图片文件 imgBytes, err := os.ReadFile("./images/logo.png") if err != nil { return err } // 生成Data URI imgDataURI := fmt.Sprintf("data:image/png;base64,%s", base64.StdEncoding.EncodeToString(imgBytes)) // 模板中使用该Data URI // <img src="{{.LogoDataURI}}" alt="Logo">
处理字体的示例代码
// 读取字体文件 fontBytes, err := os.ReadFile("./fonts/custom.ttf") if err != nil { return err } fontDataURI := fmt.Sprintf("data:font/truetype;base64,%s", base64.StdEncoding.EncodeToString(fontBytes)) // 模板中CSS部分使用 // @font-face { // font-family: 'CustomFont'; // src: url('{{.FontDataURI}}') format('truetype'); // }
3. 自动内联资源的批量处理
如果资源较多,可使用goquery库遍历DOM,自动替换外部资源路径为Data URI:
import "github.com/PuerkitoBio/goquery" // 渲染模板到缓冲区后,解析HTML doc, err := goquery.NewDocumentFromReader(&buf) if err != nil { return err } // 替换所有img标签的src为Data URI doc.Find("img").Each(func(i int, s *goquery.Selection) { src, exists := s.Attr("src") if !exists { return } // 读取本地图片文件(确保src是相对/绝对路径) imgBytes, err := os.ReadFile(src) if err != nil { return } // 获取MIME类型 ext := filepath.Ext(src) mimeType := mime.TypeByExtension(ext) if mimeType == "" { mimeType = "application/octet-stream" } // 生成Data URI并替换 dataURI := fmt.Sprintf("data:%s;base64,%s", mimeType, base64.StdEncoding.EncodeToString(imgBytes)) s.SetAttr("src", dataURI) }) // 将修改后的HTML转为字符串 htmlStr, err := doc.Html() if err != nil { return err } // 注入到ChromeDP page.SetDocumentContent(frameTree.Frame.ID, htmlStr).Do(ctx)
4. 替代方案:通过Navigate加载本地文件/临时服务器
如果不想内联资源,可将生成的HTML保存为临时文件,或启动临时HTTP服务器,用page.Navigate加载,让Chrome自动处理外部资源:
临时文件方案
// 渲染模板到临时文件 tempFile, err := os.CreateTemp("", "pdf-*.html") if err != nil { return err } defer os.Remove(tempFile.Name()) // 清理临时文件 if err := tmpl.Execute(tempFile, templateData); err != nil { return err } tempFile.Close() // 获取文件绝对路径 absPath, err := filepath.Abs(tempFile.Name()) if err != nil { return err } // 导航到本地文件(需用file://协议) page.Navigate("file://" + absPath).Do(ctx) // 等待页面加载完成 page.WaitForLoadEvent().Do(ctx) // 生成PDF buf, _, err := page.PrintToPDF().Do(ctx) if err != nil { return err }
内容的提问来源于stack exchange,提问作者Bohdan Myronchuk
相关产品推荐
相关产品推荐

