如何提升PDF转PNG的画质?Google Apps Script代码优化咨询
问题描述
我用Google Apps Script编写了一段代码,可将电子表格指定区域导出为PDF后转为PNG图片,嵌入邮件作为报告发送。功能运行正常,但邮件内嵌的PNG画质偏低;导出的PDF本身画质良好,仅转后的PNG画质不佳。目前已尝试将表格字体放大至36号,画质有所改善但仍未达预期,希望通过代码优化解决此问题,原代码如下:
async function convertPDFToPNG_(blob) { // Convert PDF to PNG images. const cdnjs = "https://cdn.jsdelivr.net/npm/pdf-lib/dist/pdf-lib.min.js"; eval(UrlFetchApp.fetch(cdnjs).getContentText()); // Load pdf-lib const setTimeout = function (f, t) { // Overwrite setTimeout with Google Apps Script. Utilities.sleep(t); return f(); } const data = new Uint8Array(blob.getBytes()); const pdfData = await PDFLib.PDFDocument.load(data); const pageLength = pdfData.getPageCount(); console.log(`Total pages: ${pageLength}`); const obj = { imageBlobs: [], fileIds: [] }; for (let i = 0; i < pageLength; i++) { console.log(`Processing page: ${i + 1}`); const pdfDoc = await PDFLib.PDFDocument.create(); const [page] = await pdfDoc.copyPages(pdfData, [i]); pdfDoc.addPage(page); const bytes = await pdfDoc.save(); const blob = Utilities.newBlob([...new Int8Array(bytes)], MimeType.PDF, `sample${i + 1}.pdf`); const id = DriveApp.createFile(blob).getId(); Utilities.sleep(4000); // This is used for preparing the thumbnail of the created file. const link = Drive.Files.get(id, { fields: "thumbnailLink" }).thumbnailLink; if (!link) { throw new Error("In this case, please increase the value of 3000 in Utilities.sleep(3000), and test it again."); } const imageBlob = UrlFetchApp.fetch(link.replace(/\=s\d*/, "=s1000")).getBlob().setName(`page${i + 1}.png`); obj.imageBlobs.push(imageBlob); obj.fileIds.push(id); } obj.fileIds.forEach(id => DriveApp.getFileById(id).setTrashed(true)); return obj.imageBlobs; } // Please run this function. async function sendReport() { // Retrieve Spreadsheet and Sheet objects. const ss = SpreadsheetApp.openById("IDIDIDIDIDID"); const sheet = ss.getSheetByName("SheetName"); // Retrieve PDF blob. const url = `https://docs.google.com/spreadsheets/d/${ss.getId()}/export?format=pdf&range=B2:S52&gid=${sheet.getSheetId()}&size=6&portrait=false&`; const pdfBlob = UrlFetchApp.fetch(url, { headers: { authorization: "Bearer " + ScriptApp.getOAuthToken() } }).getBlob(); const SOURCE_TEMPLATE = pdfBlob; const TARGET_FOLDER = "FOLDER-IDIDIDIDID"; // Use a method for converting all pages in a PDF file to PNG images. const blob = pdfBlob const imageBlobs = await convertPDFToPNG_(blob); // As a sample, create PNG images as PNG files. const folder = DriveApp.getFolderById(TARGET_FOLDER); //imageBlobs.forEach(b => folder.createFile(b)); const reportDate = sheet.getRange(84, 6).getValue(); const month = reportDate.getMonth() + 1; const day = reportDate.getDate(); const sender = sheet.getRange(84, 7).getValue(); // I modified the below script. const htmlBody = ` <html> <body> <p>TEST</p> <p>${month}${day}REPORT</p> <img src="cid:inlineImage" /> <p>${sender}</p> </body> </html> `; const inlineImage = imageBlobs[0].setName("inlineImage"); const rangeTo = sheet.getRange(84, 4, ss.getLastRow(),1).getValues(); const emailTo = rangeTo.reduce(function(prev, next) { return prev.concat(next); }); const rangeCC = sheet.getRange(84, 5, ss.getLastRow(),1).getValues(); const emailCC = rangeCC.reduce(function(prev, next) { return prev.concat(next); }); MailApp.sendEmail({ to: emailTo.join(","), cc: emailCC.join(",") || null, bcc: "" || null, subject: "REPORT "+month+"/"+day+, //body: "TEST", htmlBody: htmlBody, inlineImages: { inlineImage: inlineImage }, attachments: [pdfBlob] }); }
解决方案
问题核心在于你依赖Drive生成的缩略图转换PDF为PNG——缩略图是为文件预览设计的,分辨率和画质存在上限,即便修改s1000参数也无法满足高清需求。以下是针对性优化方案:
关键优化点
- 提升PDF导出分辨率:在Spreadsheet导出PDF的URL中添加
scale=4参数(4倍缩放,可根据需求调整为2-6),让PDF本身拥有更高的像素密度,为后续转PNG提供充足细节。 - 替换缩略图为直接导出:不再使用Drive的缩略图链接,改用Drive API直接将单页PDF导出为PNG,获取原生高清转换结果。
- 修复语法错误:邮件主题末尾多余的逗号会导致代码报错,需删除。
修改后的完整代码
async function convertPDFToPNG_(blob) { // Convert PDF to PNG images. const cdnjs = "https://cdn.jsdelivr.net/npm/pdf-lib/dist/pdf-lib.min.js"; eval(UrlFetchApp.fetch(cdnjs).getContentText()); // Load pdf-lib const setTimeout = function (f, t) { // Overwrite setTimeout with Google Apps Script. Utilities.sleep(t); return f(); } const data = new Uint8Array(blob.getBytes()); const pdfData = await PDFLib.PDFDocument.load(data); const pageLength = pdfData.getPageCount(); console.log(`Total pages: ${pageLength}`); const obj = { imageBlobs: [], fileIds: [] }; for (let i = 0; i < pageLength; i++) { console.log(`Processing page: ${i + 1}`); const pdfDoc = await PDFLib.PDFDocument.create(); const [page] = await pdfDoc.copyPages(pdfData, [i]); pdfDoc.addPage(page); const bytes = await pdfDoc.save(); const blob = Utilities.newBlob([...new Int8Array(bytes)], MimeType.PDF, `sample${i + 1}.pdf`); const id = DriveApp.createFile(blob).getId(); Utilities.sleep(2000); // 等待文件完成处理,可根据实际情况调整时长 // 直接导出PDF为PNG,替代缩略图获取方式 const pngBlob = Drive.Files.export(id, MimeType.PNG).getBlob().setName(`page${i + 1}.png`); obj.imageBlobs.push(pngBlob); obj.fileIds.push(id); } obj.fileIds.forEach(id => DriveApp.getFileById(id).setTrashed(true)); return obj.imageBlobs; } // 执行主函数 async function sendReport() { // 获取表格对象 const ss = SpreadsheetApp.openById("IDIDIDIDIDID"); const sheet = ss.getSheetByName("SheetName"); // 导出高分辨率PDF(添加scale=4参数) const url = `https://docs.google.com/spreadsheets/d/${ss.getId()}/export?format=pdf&range=B2:S52&gid=${sheet.getSheetId()}&size=6&portrait=false&scale=4`; const pdfBlob = UrlFetchApp.fetch(url, { headers: { authorization: "Bearer " + ScriptApp.getOAuthToken() } }).getBlob(); const TARGET_FOLDER = "FOLDER-IDIDIDIDID"; const imageBlobs = await convertPDFToPNG_(pdfBlob); // 生成邮件内容 const reportDate = sheet.getRange(84, 6).getValue(); const month = reportDate.getMonth() + 1; const day = reportDate.getDate(); const sender = sheet.getRange(84, 7).getValue(); const htmlBody = ` <html> <body> <p>TEST</p> <p>${month}${day}REPORT</p> <img src="cid:inlineImage" style="max-width:100%;" /> <p>${sender}</p> </body> </html> `; const inlineImage = imageBlobs[0].setName("inlineImage"); // 处理收件人列表 const rangeTo = sheet.getRange(84, 4, ss.getLastRow(),1).getValues(); const emailTo = rangeTo.reduce((prev, next) => prev.concat(next), []); const rangeCC = sheet.getRange(84, 5, ss.getLastRow(),1).getValues(); const emailCC = rangeCC.reduce((prev, next) => prev.concat(next), []); // 发送邮件(修复主题语法错误) MailApp.sendEmail({ to: emailTo.join(","), cc: emailCC.join(",") || null, bcc: "", subject: `REPORT ${month}/${day}`, htmlBody: htmlBody, inlineImages: { inlineImage: inlineImage }, attachments: [pdfBlob] }); }
额外配置
在运行代码前,需在Google Apps Script编辑器中启用Drive API高级服务:
- 点击编辑器左侧的「服务」按钮(加号图标)
- 在弹出的服务列表中找到「Drive API」,点击「添加」
内容的提问来源于stack exchange,提问作者Timonek
相关产品推荐
相关产品推荐

