How to Convert HTML to PDF File with Google Script

With Google Apps Script, you can easily convert any HTML content into a PDF file. The converted PDF file can be either saved to a folder in your Google Drive, you can email the file as an attachment or the use the UrlFetchApp service of Apps Script to post the PDF file to an external service like Amazon S3 or Dropbox.

/* This function will convert HTML content to a PDF file, 
   and also send it as an email attachment */

const convertHTMLtoPDF = () => {
  const htmlContent = `
   <p>All standard HTML5 tags are supported during conversion
   including <b>bold</b>, <i>italic</i>, <u>underline</u>, tables,
   and <a href='https://digitalinspiration.com/'>inline URLs</a></p>`;

  const blob = Utilities.newBlob(htmlContent, MimeType.HTML);
  blob.setName("file.pdf");

  const recipientEmail = "amit@labnol.org";
  const emailSubject = "The PDF file is attached";

  MailApp.sendEmail({
    to: recipientEmail,
    subject: emailSubject,
    htmlBody: htmlContent,
    attachments: [blob.getAs(MimeType.PDF)],
  });
};

This approach is recommended since it doesn’t require access to any sensitive OAuth scopes and uses the Utilities services of Apps Script to create a Blob object from an HTML string.

Create PDF files with Google Drive

You can also use the Advanced Drive Service of Apps script to convert HTML content into PDF using a Google Document at an intermediate step.

The idea is that you create a Google Document in Drive with your HTML content and then export that document as a PDF file and trash the temporary document. Or you can override the content of the HTML document with the PDF blob.

To get started, go to your Apps Script editor, open the appsscript.json manifest file and update scope as shown below:

{
  "dependencies": {
    "enabledAdvancedServices": [
      {
        "userSymbol": "Drive",
        "serviceId": "drive",
        "version": "v2"
      }
    ]
  },
  "oauthScopes": ["https://www.googleapis.com/auth/drive.file"],
  "runtimeVersion": "V8",
  "timeZone": "Asia/Kolkata",
  "exceptionLogging": "STACKDRIVER"
}

Next, inside the main code editor, paste the following snippet. It takes a three step approach:

  1. Convert the HTML string to a blob
  2. Convert the Blob into a Google Document
  3. Export the Google Document as a PDF file and trash the file created in step 2.
const convertHTMLtoPDF = () => {
  const htmlContent = `
   <p>All standard HTML5 tags are supported during conversion
   including <b>bold</b>, <i>italic</i>, <u>underline</u>, tables,
   and <a href="https://digitalinspiration.com/">inline URLs</a></p>`;

  const { id, exportLinks } = Drive.Files.insert(
    { mimeType: MimeType.GOOGLE_DOCS },
    htmlBlob: Utilities.newBlob(htmlContent, MimeType.HTML)
  );

  const pdfExportLink = exportLinks[MimeType.PDF];

  const blob = UrlFetchApp.fetch(pdfExportLink, {
    headers: { Authorization: `Bearer ${ScriptApp.getOAuthToken()}` },
  }).getBlob();

  Drive.Files.trash(id);

  const { alternateLink } = Drive.Files.insert({ title: "file.pdf" }, blob);

  Logger.log("View file %s", alternateLink);
};

Tip: We are using the drive.file reduced scope in the manifest file but if you wish to save files in specific folders of your Google Drive, or Shared Team Drives, use the broader googleapis.com/auth/drive scope.

Convert HTML to PDF with Chrome Puppeteer

If you wish to build a standalone HTML to PDF conversion engine that doesn’t use any of the Google Drive services, Chrome Puppeteer with Node JS can be a good option. You can host the service on AWS Lambda or Google Cloud functions and invoke the service with an HTTP call.

const express = require("express");
const chromium = require("chrome-aws-lambda");

const app = express();

app.use(express.json());
app.use(express.urlencoded({ extended: false }));

const html2pdf = async (html) => {
  const browser = await chromium.puppeteer.launch({
    args: chromium.args,
    executablePath: await chromium.executablePath,
    headless: true,
    ignoreHTTPSErrors: true,
  });

  const page = await browser.newPage();

  await page.setContent(html, {
    waitUntil: ["networkidle0", "load", "domcontentloaded"],
    timeout: 30000,
  });

  const pdf = await page.pdf({
    format: "A4",
    printBackground: true,
  });

  await browser.close();

  return pdf;
};

app.post("/pdf", async (request, response) => {
  try {
    const { content } = request.body;
    const pdf = await html2pdf(content);
    response.contentType("application/pdf");
    response.status(200).send(pdf);
  } catch (f) {
    response.status(500).send(f.message);
  }
});

const PORT = process.env.PORT || 8080;

app.listen(PORT, async () => {
  console.log(`App listening on port ${PORT}`);
});