3

Using Google Apps Script, I have an html template that I fill and then send out (via fax and/or email) as a pdf. The template includes a two-column table with questions/answers.

If there are enough rows, the table breaks across pages in the pdf, and the page breaks usually happen in the middle of cells; I'd like to avoid this.

I've tried using break-inside: avoid; in both the <tr> and <td> elements, with no effect.

Here's the template:

<table style="border-collapse: collapse; border: 1px solid black; table-layout: fixed; width: 100%;">
  <tbody>
    <tr>
      <th style="border: 1px solid black; width: 30%; padding: 5px;">Question</th>
      <th style="border: 1px solid black; width: 70%; padding: 5px;">Response</th>
    </tr>
  <? rows.forEach(function(row){ ?>
    <tr style="break-inside: avoid;">
      <td style="break-inside: avoid; border: 1px solid black; padding: 5px; line-height: 1.5rem;"> <?= row.question ?> </td>
      <td style="break-inside: avoid; border: 1px solid black; padding: 5px; line-height: 1.5rem;"> <?!= row.answer ?> </td>
    </tr>
  <? }) ?>
  </tbody>
</table>

and here's the Apps Script code that converts it to pdf:

var html = '<h3>' + subject + '</h3>'
var tableTemplate = HtmlService.createTemplateFromFile('response-table-template')
tableTemplate.rows = rows;
html += tableTemplate.evaluate().getContent();

var blob = Utilities.newBlob(html, MimeType.HTML, subject).getAs(MimeType.PDF); 

and then the blob is attached to an email/fax. All of that works fine except for my question: Is there a way to avoid breaking table rows over the pages? Possibly another way to convert the html to pdf that respects the break-inside property? Or an html/css solution that will be respected by Apps Script when converting the html blob to pdf?

Aaron Dunigan AtLee
  • 1,860
  • 7
  • 18
  • Although I'm not sure whether I could correctly understand about your goal, I tried to understand about it and proposed a modified script as an answer. Could you please confirm it? If that was not the direction you expect, I apologize. – Tanaike Oct 07 '20 at 00:48
  • Related [Can I force a page break in HTML printing?](https://stackoverflow.com/q/1664049/1595451) – Rubén Oct 07 '20 at 01:27

2 Answers2

3

Unfortunately, in the current stage, it seems that break-inside is not reflected to Utilities.newBlob(html, MimeType.HTML, subject).getAs(MimeType.PDF). But I'm not sure whether this is the current specification. So as a workaround, in your case, how about the following flow?

  1. Prepare HTML data.
    • This has already been prepared in your script.
  2. Convert HTML data to Google Document.
    • In this case, Drive API is used.
  3. Convert Google Document to PDF data.
  4. Create blob as a file.

When your script is modified, it becomes as follows.

Modified script:

Before you use this script, please enable Drive API at Advanced Google services.

From:
var blob = Utilities.newBlob(html, MimeType.HTML, subject).getAs(MimeType.PDF); 
To:
// 1. Prepare HTML data.
var blob = Utilities.newBlob(html, MimeType.HTML, subject);

// 2. Convert HTML data to Google Doc
var id = Drive.Files.insert({title: subject, mimeType: MimeType.GOOGLE_DOCS}, blob).id;

// 3. Convert Google Document to PDF data.
var url = "https://docs.google.com/feeds/download/documents/export/Export?exportFormat=pdf&id=" + id;
var pdf = UrlFetchApp.fetch(url, {headers: {authorization: "Bearer " + ScriptApp.getOAuthToken()}}).getBlob().setName(subject);
DriveApp.getFileById(id).setTrashed(true);

// 4. Create blob as a file.
DriveApp.createFile(pdf);

Reference:

Tanaike
  • 181,128
  • 11
  • 97
  • 165
  • Thanks, that's a good solution and addresses the problem well. For privacy reasons my client hopes to avoid placing the data in an external document, even one that's immediately deleted, but if nothing else presents itself, I'll use this method. – Aaron Dunigan AtLee Oct 07 '20 at 02:31
  • Actually, I just tested this and even the Google Doc breaks the table cell across pages, though at least it does it more aesthetically (with a border on both pages). So the question still stands. – Aaron Dunigan AtLee Oct 07 '20 at 03:15
  • 1
    @Aaron Dunigan AtLee Thank you for replying. At first, I apologize that my proposed workaround was not useful for your situation. But, unfortunately, I cannot understand about `it more aesthetically (with a border on both pages)`. This is due to my poor English skill. I deeply apologize for this. Can I ask you about the detail of it? – Tanaike Oct 07 '20 at 05:15
3

By experimentation I found that Utilities.newBlob(html, MimeType.HTML, subject).getAs(MimeType.PDF) will respect page-break-inside: avoid; (rather than break-inside: avoid;) but only if it's applied to an entire table. So I had to treat each table row as a separate table. This version of the html template solved the problem:

<table style="border-collapse: collapse; border: 1px solid black; table-layout: fixed; width: 100%;">
  <tbody>
    <tr>
      <th style="border: 1px solid black; width: 30%; padding: 5px;">Question</th>
      <th style="border: 1px solid black; width: 70%; padding: 5px;">Response</th>
    </tr>
  </tbody>
</table>
<? rows.forEach(function(row){ ?>
  <table style="page-break-inside: avoid; border-collapse: collapse; border: 1px solid black; table-layout: fixed; width: 100%;">
    <tbody>
      <tr>
        <td style="width: 30%; border: 1px solid black; padding: 5px; line-height: 1.5rem;"> <?= row.question ?> </td>
        <td style="width: 70%; avoid; border: 1px solid black; padding: 5px; line-height: 1.5rem;"> <?!= row.answer ?> </td>
      </tr>
    </tbody>  
  </table>
<? }) ?>

For the record, converting to a Google Doc and then to pdf did not solve the problem; it appears that Google Doc also allows table cells to break across pages. However, writing the data to a Google Sheet and then downloading as pdf does prevent breaking cells across pages; Sheets seems to take care of this automatically, so that's an alternate solution.

Aaron Dunigan AtLee
  • 1,860
  • 7
  • 18