如何在Google Apps Script中将外部CSS转为行内样式导出正确PDF
解决方案
这个需求完全可实现,你遇到的渲染异常属于Google Apps Script内置HTML转PDF接口的已知限制:该接口仅识别元素的行内style属性,不会解析头部的外部CSS引用和<style>标签内的样式,因此需要先把所有样式转换为行内样式再生成PDF。
以下是修改后的完整可运行代码:
function downloadFile() { const fileURL = "你的HTML页面地址"; const targetFolderId = "1gBA8YCs3PH7v7CNl3nlsjNqYzhOxhjYa"; let fileName = ""; let fileSize = ""; // 拉取原始HTML内容 const response = UrlFetchApp.fetch(fileURL, { headers: { Authorization: 'Bearer ' + ScriptApp.getOAuthToken() }, }); const htmlBody = response.getContentText(); const rc = response.getResponseCode(); if (rc == 200) { // 第一步:提取所有外部CSS链接并拉取样式内容 const cssLinks = htmlBody.match(/<link[^>]+rel="stylesheet"[^>]+href="([^"]+)"/gi) || []; let allCss = ""; cssLinks.forEach(linkTag => { const cssUrl = linkTag.match(/href="([^"]+)"/)[1]; // 补全相对路径的CSS地址(如果你的页面用了相对路径) const absoluteCssUrl = cssUrl.startsWith("http") ? cssUrl : new URL(cssUrl, fileURL).href; try { const cssRes = UrlFetchApp.fetch(absoluteCssUrl); allCss += cssRes.getContentText() + "\n"; } catch(e) { console.log("拉取CSS失败:" + absoluteCssUrl, e); } }); // 第二步:提取页面内部<style>标签的样式 const innerStyles = htmlBody.match(/<style[^>]*>([\s\S]*?)<\/style>/gi) || []; innerStyles.forEach(styleTag => { const styleContent = styleTag.replace(/<\/?style[^>]*>/gi, "").trim(); allCss += styleContent + "\n"; }); // 第三步:调用样式转行内工具处理HTML const processedHtml = convertCssToInline(htmlBody, allCss); // 第四步:用处理后的HTML生成PDF const blob = Utilities.newBlob(processedHtml, MimeType.HTML).getAs('application/pdf').setName('Nota.pdf'); const folder = DriveApp.getFolderById(targetFolderId); if(folder != null){ const file = folder.createFile(blob); fileName = file.getName(); fileSize = file.getSize(); } } const fileInfo = {'rc':rc,"filename": fileName, "fileSize":fileSize} Logger.log(fileInfo) } // 辅助函数:将CSS转换为对应元素的行内样式 function convertCssToInline(html, css) { // 移除原HTML中的link和style标签,避免重复加载 html = html.replace(/<link[^>]+rel="stylesheet"[^>]*>/gi, ""); html = html.replace(/<style[^>]*>[\s\S]*?<\/style>/gi, ""); // 基础CSS规则解析,复杂选择器可根据需求扩展 const cssRules = css.match(/[^}]+{[^}]+}/g) || []; cssRules.forEach(rule => { const parts = rule.split("{"); const selector = parts[0].trim(); const styles = parts[1].replace("}", "").trim(); if (!selector || !styles) return; // 匹配所有符合选择器的元素,添加行内样式 const regex = new RegExp(`(<${selector}[^>]*)>`, "gi"); html = html.replace(regex, (match, prefix) => { // 如果已有style属性则追加样式,无则新增 if (prefix.includes("style=")) { return prefix.replace(/style="([^"]*)"/, `style="$1;${styles}"`) + ">"; } else { return `${prefix} style="${styles}">`; } }); }); return html; }
注意事项
- 上述辅助函数的CSS选择器解析逻辑默认支持基础的元素选择器,如需支持类选择器、ID选择器,可自行扩展
convertCssToInline函数的匹配规则即可。 - 如果页面样式依赖JavaScript动态渲染,需要先处理动态内容再转换样式,可通过调整拉取页面的逻辑实现。
内容的提问来源于stack exchange,提问作者user3284182
相关产品推荐
相关产品推荐

