Extract all deck text into a sheet
Pull text out of every slide for review, translation, or copy-editing.
Published Jan 4, 2026
Northwind decks tend to grow in two directions at once — new slides at the back, tweaks throughout the middle — and the copy inside them quietly drifts away from the brand voice document. Editing in the deck is fiddly: shapes overlap, fonts shift, and reviewing 40 slides means clicking into every text box. What the copy editor actually wants is the words in a single column they can read top to bottom, mark up in place, and hand back.
This script flattens a deck into a sheet. One row per text box, with the slide number and the shape’s object ID alongside the text, so once edits come back you know exactly which shape on which slide to update. It is the first half of a round-trip translation or copy-edit workflow — pair it with a sister script that writes the edited column back into the deck.
What you’ll need
- A Google Slides deck whose text you want to extract.
- A Google Sheet to receive the rows — the script writes to the first tab.
- Nothing else — both files just need to be readable by you.
The script
// The columns the script writes. Keep these names if you plan to round-trip
// edits back into the deck — a sister script can look them up by header.
const HEADERS = ['Slide', 'ShapeId', 'Text'];
/**
* Walks every shape in every slide of a deck and writes the text into the
* first tab of the given spreadsheet. One row per non-empty text box.
*
* @param {string} deckId The Slides file ID.
* @param {string} sheetId The Spreadsheet to write rows into.
*/
function extractDeckText(deckId, sheetId) {
if (!deckId || !sheetId) {
throw new Error('extractDeckText needs both a deckId and a sheetId.');
}
const deck = SlidesApp.openById(deckId);
const rows = [];
// 1. Walk every slide, every shape, and collect non-empty text.
deck.getSlides().forEach((slide, slideIndex) => {
for (const shape of slide.getShapes()) {
// Not every shape is a text container — images, lines, and tables
// don't have a `.getText()` method, so skip those defensively.
if (typeof shape.getText !== 'function') continue;
const text = shape.getText().asString().trim();
if (!text) continue;
// Slide number is 1-based for humans; object ID is the stable
// identifier the round-trip script will use to find the shape again.
rows.push([slideIndex + 1, shape.getObjectId(), text]);
}
});
// 2. Reset the destination sheet — easier to reason about than diffing.
const sheet = SpreadsheetApp.openById(sheetId).getSheets()[0];
sheet.clear();
sheet.getRange(1, 1, 1, HEADERS.length).setValues([HEADERS]);
if (!rows.length) {
Logger.log('Deck has no text shapes — nothing to write.');
return;
}
// 3. Single batch write — much faster than one row at a time.
sheet.getRange(2, 1, rows.length, HEADERS.length).setValues(rows);
sheet.setFrozenRows(1);
Logger.log(`Extracted ${rows.length} text shapes from deck ${deckId}.`);
}
/**
* Convenience wrapper so you can click Run in the editor without writing a
* caller. Replace the IDs with your own.
*/
function extractNorthwindDeck() {
extractDeckText('1abcDeckId', '1abcSheetId');
}
How it works
extractDeckTextfirst guards against missing IDs — running the script from the editor with no arguments would otherwise throw a less helpful error deep insideSlidesApp.- It opens the deck and iterates every slide. For each slide it walks every
shape, checking that the shape supports
getText— images, lines, and tables don’t, and calling it on them would crash the loop. - For each text shape it reads the string, trims it, and skips empty boxes. Empty placeholders are common in templates and would just create blank rows in the sheet.
- It records the 1-based slide number, the shape’s
objectId, and the trimmed text. The object ID is the stable handle a round-trip editor would use to write changes back to the right shape. - It clears the destination sheet, writes the header row, and writes all
the collected rows in one batched
setValuescall — far quicker than appending one row at a time. - It freezes the header row so the columns stay labelled when the editor scrolls through a long deck’s worth of text.
Example run
Say 1abcDeckId is a five-slide Northwind pitch. After one run, the sheet
looks like this:
| Slide | ShapeId | Text |
|---|---|---|
| 1 | g123abc | Northwind Studio |
| 1 | g123def | Bespoke creative for ambitious brands |
| 2 | g456abc | What we do |
| 2 | g456def | Strategy, design, production — all under one roof. |
| 3 | g789abc | Selected work |
| 4 | g321abc | Our process |
| 5 | g654abc | Let’s talk |
The copy editor can now read the deck end-to-end, mark up the Text column in place, and hand it back. A round-trip script uses the ShapeId to find each shape again and overwrite its text.
Run it
This is an on-demand job — you run it whenever a deck is ready for review:
- In the Apps Script editor, select
extractNorthwindDeckand click Run. - Approve the authorisation prompt the first time.
- Open the destination sheet to read or edit the extracted text.
If you regularly extract from a folder full of decks, wrap the call in a loop
over DriveApp.getFolderById(...).getFiles() and write each deck to its own
tab — but for one-off review work, the editor button is enough.
Watch out for
- Grouped shapes are tricky. A group is itself a shape but its children carry
the text — if a slide uses groups heavily, walk
shape.getChildren()when the shape type isGROUPinstead of skipping it. - Tables are skipped by the
typeof getTextcheck. If your decks use tables for content, add a branch that iteratesshape.asTable().getCell(...)and records each cell separately. - Speaker notes are not extracted. They live on
slide.getNotesPage(), not on the slide’s shapes — if your reviewer needs the script too, fetch them in a second pass and add aNotesrow per slide. - The sheet is cleared on every run. If a reviewer has already marked it up, copy the marked-up text out before extracting again or you’ll lose their edits.
Related
Generate sales-enablement decks per segment
Tailor Northwind's messaging slides by audience segment — fintech, healthcare, retail.
Updated Dec 28, 2025
Insert chapter divider slides from an outline
Add section-break slides between chapters in a Northwind deck.
Updated Dec 21, 2025
Build a deck accessibility checker
Flag missing alt text, low contrast, and tiny fonts across a Northwind deck.
Updated Dec 14, 2025
Drive menu and price-list signage from a Sheet
Generate display slides for a Northwind venue — menus or price lists driven by a Sheet.
Updated Dec 7, 2025
Build a Slides-based countdown timer
Embed a live countdown timer in a Northwind deck — refreshed via a scheduled script.
Updated Nov 30, 2025