Prevent table cells from breaking across page when converting html to pdf

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?


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:

  • Files: insert

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.