Recipe: Office Files to Page Images¶
Goal¶
Let a model see Word, Excel and PowerPoint files as they look on the page: charts, pictures, diagrams and layout that the text readers can only count. Show the pages to the model, or save them as image files for a report or another step.
Prerequisites¶
- The
docsextra (pymupdf, Pillow and the Office readers). -
LibreOffice where the tools run. It turns each file into a PDF, which pymupdf then renders. On Debian or Ubuntu:
apt-get install libreoffice-core libreoffice-writer libreoffice-calc libreoffice-impressThe Deployment page has a Dockerfile with it. Install fonts for the documents you expect, such as
fonts-noto-cjkfor Chinese, Japanese or Korean.
PDFs render without LibreOffice, with pymupdf alone.
Steps¶
Tool calls are written as Python calls; the model sends the same arguments as JSON. The results are from a run in a Linux container with LibreOffice.
1. Read the text first, and look for media¶
The text readers are fast and cost no image tokens. They also say when a file holds something they cannot read:
read_powerpoint(path="decks/q3_review.pptx")
{
"slides": [
{"slide": 1, "text": ["Q3 review", "Revenue and margin"], "image_count": 0, "chart_count": 0, "has_media": false},
{"slide": 2, "text": ["Revenue by region"], "image_count": 0, "chart_count": 1, "has_media": true},
{"slide": 3, "text": ["Next steps", "Hire two analysts"], "image_count": 0, "chart_count": 0, "has_media": false}
],
"slide_count": 3,
"has_media": true
}
Slide 2 is a chart with a title: its numbers are not in the text. read_excel reports has_charts,
has_images and has_media the same way. read_word ends with a line such as:
[media_summary] embedded_images=1 — this document contains images; if text looks incomplete, render it with read_document_pages_as_images.
2. Show the model the pages that matter¶
read_document_pages_as_images(path="decks/q3_review.pptx", pages="2")
This returns one image per page, for the model to look at. In the run above, a standard 4:3 slide came back as a 1440 × 1080 PNG, and the LibreOffice step took about 1.5 seconds per file.
| Tool | Use it for |
|---|---|
read_document_pages_as_images(path, pages, scale=2.0, max_pages=20) |
The pages you name: "2", "1-3", "1-3,7" |
render_document_thumbnail(path, max_pages=5) |
A quick look at the first pages, at 72 dpi (a Letter page is 612 × 792) |
render_document_page(path, page=1, scale=2.0) |
One page |
Every image is brought within DPTTOOLS_IMAGE_MAX_EDGE_PX (1568 px) and DPTTOOLS_IMAGE_MAX_MB
(3 MB) before the model sees it. A tall spreadsheet page rendered at 1109 × 1568 for that reason.
A higher scale doesn't add detail past that limit. To read small print, crop the page with
Image Tools and read_image the crop.
Ask for page ranges rather than "all": each page costs image tokens, and pages past max_pages
are dropped without an error.
3. Save page images as files¶
When the images are an output rather than something for the model to look at, write them to disk
with convert_document:
convert_document(path="decks/q3_review.pptx", to="png", page="1-2", outdir="exports")
{
"output_files": ["/workspace/exports/q3_review-p1.png", "/workspace/exports/q3_review-p2.png"],
"pages": 2,
"engine": "libreoffice+pymupdf"
}
- Pages are rendered at 144 dpi and named
<name>-p<N>.png;to="jpg"writes JPEGs. outdirmust already exist. Without it, the files go next to the source.-
An existing file is never replaced unless you pass
overwrite=True:Error: '/workspace/exports/q3_review-p1.png' (and 1 more) already exists. Pass overwrite=True to replace it, or choose another outdir. -
convert_document(path="sales.xlsx", to="pdf")makes a PDF instead, withpagesset to its page count.
To check a saved page, read_image(path="exports/q3_review-p2.png") shows it to the model.
4. Give an agent the tools¶
from doc_processing_toolkit import Agent
from doc_processing_std_tools import document_processing_server
agent = Agent(
model="claude-sonnet-5",
instructions=(
"Read Office files with read_word, read_excel or read_powerpoint first. When a result reports "
"charts, images or media, look at those pages with read_document_pages_as_images, a few "
"pages at a time."
),
tools=[{"type": "fastmcp", "server": document_processing_server,
"allowed_tools": ["get_file_overview", "read_word", "read_excel", "read_powerpoint",
"read_document_pages_as_images", "convert_document", "read_image"]}],
)
result = agent.run_sync("What does the revenue chart in decks/q3_review.pptx show?")
Image results reach the model in-process only with toolkit 0.5.1 or later (Compatibility); out of process they work with any version. A LibreOffice run takes a second or more, so a busy agent is better served out of process, where the composed launcher runs it on a worker thread.
What to validate¶
- The text readers report media:
chart_countandhas_media(PowerPoint),has_charts(Excel), the[media_summary]line (Word). read_document_pages_as_imagesreturns one image per requested page, each within the image limits.convert_document(..., to="png", page="1-2")writes exactly those pages and lists them inoutput_files.- Running the same conversion again fails, naming the existing file, until
overwrite=True. - Without LibreOffice, rendering an Office file returns an
Error:with the install command, and PDFs still render.
Test sources¶
tests/integration/test_libreoffice_live.py: real conversions (DOCX to PNG and PDF, XLSX to CSV, two at once,render_document_pageon a real DOCX). Skipped without LibreOffice; run it in the Linux image withtests/integration/run_in_docker.py.tests/unit/test_soffice.py: finding LibreOffice, per-run profiles, timeouts, and the no-overwrite rule.tests/unit/test_document_processing.py:TestReadPowerPoint,TestReadExcel,TestReadWord(media reporting), and the three render tools.
Related pages¶
- Documents server: every target
convert_documentsupports. - Images: how images are fitted to the model limits.