【问题标题】:Google Apps Script problems formatting PDFGoogle Apps 脚本在格式化 PDF 时出现问题
【发布时间】:2021-04-21 06:05:04
【问题描述】:

我正在使用 GAS 在源电子表格中生成给定工作表的 PDF。 我的方法是首先将给定的工作表复制到一个新的电子表格文件中,我称之为“中间”,设置样式,然后使用fileToPDF.getAs("application/pdf"); 将其 PDF 化。

文件被创建,它拥有我想要的一切,除了两个问题:

  • 我使用sheet.getRange(borderRange).setBorder(true, true, true, true, true, true); 为某些范围应用了一些边框,它们在“中间”文件中显示没有问题。但是,它们在 PDF 中消失了。
  • 我已经合并了几组范围,那里没有问题,但是有一个很大的例外,工作表中最后一个合并的范围在 PDF 中没有显示为一个合并的单元格。

我使用的代码如下:

function crearPdf() {
  const ssID = SpreadsheetApp.getActiveSpreadsheet().getId();
  const destFolder = DriveApp.getFileById(ssID).getParents().next();
  const ss = SpreadsheetApp.getActiveSpreadsheet();
  const ws = ss.getActiveSheet();

  const sourceRange = ws.getRange(1, 1, ws.getMaxRows(), ws.getMaxColumns());
  const numFac = ws.getRange("D20");
  const sourceValues = sourceRange.getDisplayValues();
  const dest = SpreadsheetApp.create("temp " + Date());
  const destSheet = dest.getSheets()[0];
  const destRange = destSheet.getRange(1, 1, destSheet.getMaxRows(), destSheet.getMaxColumns());

  destRange.setValues(sourceValues);
  styleSheet(destSheet);

  const destId = dest.getId();
  const fileToPDF = DriveApp.getFileById(destId);
  const fileAsPDF = fileToPDF.getAs("application/pdf");
  const newFile = DriveApp.createFile(fileAsPDF).setName("Factura " + numFac.getValue() + ' - ' + " '21");
  // const fileUrl = newFile.getDownloadUrl();

  newFile.moveTo(destFolder);
  // fileToPDF.setTrashed(true);
}

function styleSheet(sheet) {
  // Declaring our constants
  const arrToMerge = ["B24:G24", "B25:G25", "B26:G26", "B40:H40", "B20:C20", "B21:C21", "B22:C22"];
  const arrToBorder = ["B20:D22", "B24:H26", "G27:H28"];
  const arrToBold = ["B11", "B20", "B21", "B22", "B24", "B31", "B33", "B34", "B35", "B36", "G28", "H3", "H24", "H28"];

  // Direct tweaks
  sheet.setHiddenGridlines(true);
  sheet.setColumnWidth(1, 15);
  sheet.getDataRange().setFontFamily("Open Sans");
  sheet.getRange("B40").setWrap(true);
  sheet.getRange("B40:B47").setFontSize(9);
  sheet.autoResizeRows(40, 1);
  sheet.setColumnWidth(3, 54);
  // sheet.setColumnWidth(2, 79);
  sheet.setRowHeights(37, 3, 7);
  // sheet.setRowHeights(40, 7, 16);
  sheet.getRange("h24").setHorizontalAlignment("right");
  sheet.getRange("B24:H24").setBackground("#666666").setFontColor("white");

  // Loops for multiple ranges tweaks
  for (let range of arrToMerge) {
    sheet.getRange(range).merge();
  }
  for (let cell of arrToBold) {
    sheet.getRange(cell).setFontWeight("bold");
  }
  for (let range of arrToBorder) {
    sheet.getRange(range).setBorder(true, true, true, true, true, true);
  }
}

我还从原始文件中删除了一些单元格以使其更易于理解(并没有真正删除代码中的所有引用,所以如果您看到对看起来为空的单元格的引用不要感到惊讶)

而下图分别对应:

原始电子表格

中间文件(生成此文件是因为原始电子表格中有很多工作表)

最终的 PDF

最后,link to a public spreadsheet with dummy data

`getAs("application/pdf") 方法是否存在一些已知的奇怪行为?有没有办法让它正常工作?

提前感谢您的帮助!

【问题讨论】:

    标签: google-apps-script google-sheets pdf-generation


    【解决方案1】:

    好吧,事实证明,您使用 Apps 脚本格式化电子表格的顺序很重要,这就是我丢失了一些格式的原因。

    我设计我的格式的方式包括对多个单元格数组进行某些调整,例如合并单元格、设置边框、设置粗体等,以及使用 for-of 循​​环遍历每个单元格范围。碰巧的是,如果您还有其他调整要做,这些 for-of 循​​环似乎必须是您要做的第一件事,否则它们似乎不起作用。

    感谢大家的帮助!

    【讨论】:

      【解决方案2】:

      而不是使用getAs() 将工作表导出为pdf

      你可以这样做

      • 创建导出链接"https://docs.google.com/spreadsheets/d/"+destId+"/export?"
      • 使用UrlFetchApp.fetch()
      • 从响应的blob 创建一个新文件
      • 使用 SpreadsheetApp.flush() 确保在导出为 pdf 之前完成电子表格的格式化。

      示例:

      function crearPdf() {
      
        const ssID = SpreadsheetApp.getActiveSpreadsheet().getId();
        const destFolder = DriveApp.getFileById(ssID).getParents().next();
        const ss = SpreadsheetApp.getActiveSpreadsheet();
        const ws = ss.getActiveSheet();
      
        const sourceRange = ws.getRange(1, 1, ws.getMaxRows(), ws.getMaxColumns());
        const numFac = ws.getRange("D20");
        const sourceValues = sourceRange.getDisplayValues();
        const dest = SpreadsheetApp.create("temp " + Date());
        const destSheet = dest.getSheets()[0];
        const destRange = destSheet.getRange(1, 1, destSheet.getMaxRows(), destSheet.getMaxColumns());
      
        destRange.setValues(sourceValues);
        SpreadsheetApp.flush();
        styleSheet(destSheet);
        SpreadsheetApp.flush();
      
        const destId = dest.getId();
        
        
       //  THIS IS NEW
        var token = ScriptApp.getOAuthToken();
        var url = "https://docs.google.com/spreadsheets/d/"+destId+"/export?";
      
        var url_ext = 'exportFormat=pdf&format=pdf'        // export as pdf / csv / xls / xlsx
        
        // adjust the following parametrers as desired
        
        
        + '&size=letter'                                   // paper size legal / letter / A4
        + '&portrait=true'                                // orientation, false for landscape
        +'&top_margin=0.50'
        +'&bottom_margin=0.50' 
        +'&left_margin=0.50' 
        +'&right_margin=0.50'
        + '&gridlines=false' 
        // other parameters if you need
        /*
        + '&fitw=true&source=labnol'                       // fit to page width, false for actual size
        + '&sheetnames=false&printtitle=false'             // hide optional headers and footers
        + '&pagenumbers=false'                               // hide page numbers 
        + '&fzr=false'                                     // do not repeat row headers (frozen rows) on each page
        + '&gid=';                                         // the sheet's Id
        */
      
        var response = UrlFetchApp.fetch(url + url_ext, 
         {
          headers: {
           'Authorization': 'Bearer ' +  token
          },
          muteHttpExceptions:true
        });
        
        const newFile =  DriveApp.createFile(response.getBlob().setName("Factura " + numFac.getValue() + ' - ' + " '21"));
        // const fileUrl = newFile.getDownloadUrl();
      
        newFile.moveTo(destFolder);
        // fileToPDF.setTrashed(true);
      }
      

      【讨论】:

      • 感谢@ziganotschka 的回答。尽管它提供了一种很好且优雅的方式来创建 PDF 并更好地控制输出文件,但使用此方法没有解决我的任何问题。
      • 我在我的环境中测试了这个解决方案,它可以正常工作。我想知道在您的情况下,由于 Apps 脚本的异步性,pdf 是在电子表格的格式化完成之前创建的(由于函数 styleSheet() 执行的请求数量)。在这种情况下,您可以使用 SpreadsheetApp.flush() 强制脚本等待 SpreadsheetApp 完成 - 请参阅我更新的答案。
      • 感谢您的加入!它似乎非常有用。但是,在我的情况下,在我移动我的 for 循环之后它没有任何区别(请参阅我自己的答案)所以有一些东西,因为现在所有样式都应用和不使用 SpreadsheetApp.flush()。毫无疑问,我将来会使用它,但在这种情况下,我最初的方式和你的方式都一样有效。再次感谢!
      猜你喜欢
      • 1970-01-01
      • 2015-05-11
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-04-08
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多