https://txtfetch.com/tools/odp-to-text/
Your .odp deck, as text. No code required.
Drop a real OpenDocument presentation below, zipped .odp or flat .fodp. See every slide's text, and its speaker notes, come out right here, in your browser. Nothing is uploaded. It's the same read txtfetch's API gives you, just local.
Drop a Word, Excel, PowerPoint, or OpenDocument (.odt/.ods/.odp, zipped or flat) file below to see the actual text it extracts to. It runs entirely in your browser, and nothing is uploaded.
Up to 25 MB, read fully in-browser — larger files still work through the API.
What's not in this text:
curl -X POST https://api.txtfetch.com/v1/extract \
-H "Authorization: Bearer $TXTFETCH_KEY" \
-F file=@__FILENAME__import os
import requests
with open("__FILENAME__", "rb") as f:
r = requests.post(
"https://api.txtfetch.com/v1/extract",
headers={"Authorization": f"Bearer {os.environ['TXTFETCH_KEY']}"},
files={"file": f},
)
print(r.json()["extracted_text"])import { readFile } from "node:fs/promises";
const file = new Blob([await readFile("__FILENAME__")]);
const form = new FormData();
form.append("file", file, "__FILENAME__");
const res = await fetch("https://api.txtfetch.com/v1/extract", {
method: "POST",
headers: { Authorization: `Bearer ${process.env.TXTFETCH_KEY}` },
body: form,
});
const { extracted_text } = await res.json();
console.log(extracted_text);package main
import (
"bytes"
"encoding/json"
"fmt"
"io"
"mime/multipart"
"net/http"
"os"
)
type extractResponse struct {
Status string `json:"status"`
ExtractedText string `json:"extracted_text"`
}
func main() {
f, err := os.Open("__FILENAME__")
if err != nil {
panic(err)
}
defer f.Close()
var body bytes.Buffer
writer := multipart.NewWriter(&body)
part, err := writer.CreateFormFile("file", "__FILENAME__")
if err != nil {
panic(err)
}
if _, err := io.Copy(part, f); err != nil {
panic(err)
}
writer.Close()
req, err := http.NewRequest("POST", "https://api.txtfetch.com/v1/extract", &body)
if err != nil {
panic(err)
}
req.Header.Set("Authorization", "Bearer "+os.Getenv("TXTFETCH_KEY"))
req.Header.Set("Content-Type", writer.FormDataContentType())
resp, err := http.DefaultClient.Do(req)
if err != nil {
panic(err)
}
defer resp.Body.Close()
var result extractResponse
if err := json.NewDecoder(resp.Body).Decode(&result); err != nil {
panic(err)
}
fmt.Println(result.ExtractedText)
}whats-hard-about-odp
A slide's speaker notes aren't stored in a separate part of the file the way PowerPoint's are. In ODF, <presentation:notes> is a CHILD element sitting inside the very same <draw:page> as the slide's own shapes. It sits one level down, rather than off in its own file. Collect everything inside a slide's <draw:page> without watching for that element, and the notes text runs straight into the slide body. Then there's nothing to tell the two apart. This tool watches for it specifically, and pulls its text into its own labelled section instead.
Not everything visible on a rendered slide even lives in content.xml. A slide's own title and body text do. A master slide's boilerplate lives in styles.xml instead: page numbers, a standing footer, whatever a template put in the corners. styles.xml is a separate part this extraction doesn't read. That's deliberate. Reading the master would repeat the same footer text on every single slide's extraction, which is noise nobody asked for in a text dump.
A slide's shapes are read in the order they sit in the file's own shape tree. That covers its title, body text box, and any extra text box someone dragged onto it. It's the same order LibreOffice lists them in when you tab through a slide's objects. It's not always top-to-bottom reading order on screen, though. A slide that's been rearranged by dragging boxes around can have a shape-tree order that no longer matches the visual layout. A deck built with a lot of manual repositioning is exactly where this extraction's line order is most likely to look surprising.
what-to-do-next
Got the text out and want the API call for it directly? The panel above already has it, with your file's real name. Extracting many decks, or files bigger than this browser tool's 25 MB cap? See the full OpenDocument extraction guide (.odt, .ods, .odp) →
faq
- Does this tool upload my deck anywhere?
- No. Everything happens in your browser. A zipped .odp is read with the Web Platform's own DecompressionStream API, and a flat .fodp is read directly with no unzip step at all. Only the finished text ever leaves your machine, and only if you choose to copy or download it.
- Does it work on the flat .fodp variant, not just a zipped .odp?
- Yes. This tool recognizes a flat file's own shape from its bytes: an XML document rooted at <office:document>, not a zip. It reads that the same way it reads a zipped file's content.xml.
- Are speaker notes included?
- Yes, and clearly labelled. A slide's <presentation:notes> element sits nested inside that same slide's markup, rather than in a separate part of the file. This tool reads it out separately, and marks the notes text "Speaker notes:" so it's never confused with what's actually on the slide.
- Does it include a master slide's footer or page numbers?
- No, deliberately. Master-slide boilerplate lives in styles.xml, not in a slide's own content. Including it would repeat the same footer or page number on every single slide's extraction. Anything genuinely typed onto a specific slide is still read normally.
- Does it read diagrams or embedded chart data?
- No. Those live in their own drawing and chart structures this reader doesn't walk. Plain text boxes and table cells on a slide are covered.
- Does the text come out in the same order the slide looks on screen?
- Usually, but not always. Shapes are read in the order they sit in the file's shape tree, which normally matches visual layout. The exception is a slide that's been rearranged by dragging boxes around after the fact. There, the tree order can drift from what you'd read looking at the slide.
- Is there a file size limit?
- This tool reads up to 25 MB entirely in your browser. Larger decks, or a batch you want to automate, go through the same extraction via the API, which has no such limit.
That was one file. The API does the queue.
This page read your ODP on your own machine. The API reads a folder of them.
Read the quickstart →