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

如何提升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参数也无法满足高清需求。以下是针对性优化方案:

关键优化点

  1. 提升PDF导出分辨率:在Spreadsheet导出PDF的URL中添加scale=4参数(4倍缩放,可根据需求调整为2-6),让PDF本身拥有更高的像素密度,为后续转PNG提供充足细节。
  2. 替换缩略图为直接导出:不再使用Drive的缩略图链接,改用Drive API直接将单页PDF导出为PNG,获取原生高清转换结果。
  3. 修复语法错误:邮件主题末尾多余的逗号会导致代码报错,需删除。

修改后的完整代码

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高级服务:

  1. 点击编辑器左侧的「服务」按钮(加号图标)
  2. 在弹出的服务列表中找到「Drive API」,点击「添加」

内容的提问来源于stack exchange,提问作者Timonek

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.23 01:52:04