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?
- Prepare HTML data.
- This has already been prepared in your script.
- Convert HTML data to Google Document.
- In this case, Drive API is used.
- Convert Google Document to PDF data.
- 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.