Skip to content
// Docs · Drive · Apps Script

Convert a Google Doc to PDF in Google Docs.

Export any Google Doc as a PDF file to Drive using Apps Script — including the saveAndClose flush you need so in-script edits actually land in the export.

I want to programmatically export a Google Doc as a PDF and save it to Drive, ideally triggered by a script that also modifies the document first.

The script

copy · paste · trigger
exportDocAsPdf.gs
Apps Script
// Export the active Google Doc as a PDF into a target Drive folder.
// Call saveAndClose() before getAs() — otherwise in-script edits are dropped.
function exportDocAsPdf() {
  var doc = DocumentApp.getActiveDocument();
  var targetFolderId = 'YOUR_FOLDER_ID';

  // Write any pending changes and close the Document service connection.
  doc.saveAndClose();

  var docFile = DriveApp.getFileById(doc.getId());
  var pdfBlob = docFile.getAs('application/pdf');
  pdfBlob.setName(doc.getName() + '.pdf');

  var folder = DriveApp.getFolderById(targetFolderId);
  var saved = folder.createFile(pdfBlob);

  Logger.log('PDF saved: ' + saved.getUrl());
}

Need a variant? Gnaw writes a custom version from one sentence — fields, triggers, edge cases handled.

Walkthrough

Why saveAndClose comes first

The Document service and the Drive service are two separate layers. When your script calls DocumentApp methods — appending a paragraph, filling a table cell, whatever — those changes sit in an in-memory representation that the Document service manages. DriveApp.getFileById().getAs() reads the stored file on Drive, not that in-memory state.

If you call getAs() before saveAndClose(), the PDF reflects whatever was on disk when the script started. Any edits your script made are silently absent. The first time I hit this, the PDF looked correct in the editor but arrived blank of the values the script had just written — twenty minutes of confused staring before I found the flush requirement in the API docs.

saveAndClose() does two things: it flushes the Document service buffer to disk, and it closes the service handle so the document is no longer locked by DocumentApp. After that call, DriveApp reads a file that actually contains your changes.

Getting the folder ID right

The targetFolderId string comes from the URL of the destination folder in Drive: drive.google.com/drive/folders/FOLDER_ID_HERE. Copy that segment — it's the only part you need.

If you want the PDF to land in the same folder as the source document, replace the hardcoded ID with a lookup: DriveApp.getFileById(doc.getId()).getParents().next().getId(). That works for most cases; if the file has multiple parent folders (shared drives allow this), next() returns whichever parent comes first in the iterator, which is usually the one the user sees.

Permissions cascade from the folder. If the script runs as a service account or a different user than the folder owner, createFile() will throw a 403 unless that identity has at least Editor access on the folder.

Running this on a schedule or from a trigger

Bound scripts (attached to the Doc via Extensions > Apps Script) can use DocumentApp.getActiveDocument() because they run in the context of the open file. Standalone scripts cannot — there is no active document. In a standalone script, replace that call with DocumentApp.openById('YOUR_DOC_ID').

For scheduled exports, set a time-driven trigger on exportDocAsPdf in the Apps Script triggers panel. The script runs as the account that owns the trigger, so that account needs both read access on the source Doc and write access on the target folder. A common failure mode is setting the trigger under a service account that was never granted access to the folder.

The PDF export goes through Google's internal conversion pipeline, so the output matches what you would get from File > Download > PDF in the Docs UI — page breaks, margins, header/footer all included.

Want a custom version?

Describe your sheet and the rule you want. Gnaw writes the Apps Script — fields, triggers, edge cases — in one shot.

FAQ

4 questions
Why is the exported PDF missing content I added in the script?
You called getAs() before saveAndClose(). The Drive API reads the file from disk; the Document service buffers edits in memory until you explicitly flush them. Add doc.saveAndClose() immediately before the DriveApp call.
Can I export a Doc I don't have open — just its file ID?
Yes. Use DocumentApp.openById('FILE_ID') to get the document object, call saveAndClose() on it, then pass that same FILE_ID to DriveApp.getFileById(). The active-document pattern only works in a script bound to an open file.
How do I avoid creating duplicate PDFs every time the script runs?
Search the target folder for an existing file with the same name before creating: folder.getFilesByName(pdfBlob.getName()). If the iterator hasNext(), call next().setContent(pdfBlob) to overwrite rather than calling createFile() again. Drive does not deduplicate by name on its own.
The script runs fine manually but fails on a time-driven trigger with a permissions error — why?
The trigger runs as the account that created it. That account needs explicit Editor access on both the source Doc and the destination folder. Sharing the folder with 'anyone with the link' does not count — service-level access requires a direct share or a domain-wide delegation grant.
// one good script a week

Get a working Apps Script snippet in your inbox, weekly.