You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.26 06:55:34