Create, parse, and edit ODT (OpenDocument Text) files locally using Python and odfdo. Use when the user asks to create, edit, read, update, append to, inspec...
Local ODT document creation and editing powered by odfdo.
Library: odfdo v3.20+ — actively maintained, single dep (lxml), full ODF support.
pip install odfdo
This skill is purely local — it has no network access and knows nothing about NextCloud.
For NextCloud workflows, pair with nextcloud-aio-oc:
| Step | Skill |
|---|---|
| Find file on NextCloud | nextcloud-aio-oc → node scripts/nextcloud.js files search |
| Download binary ODT | nextcloud-aio-oc → python3 scripts/files_binary.py download |
| Edit / create ODT | odt-filemgr-oc → python3 scripts/odt_tool.py ... |
| Upload binary ODT | nextcloud-aio-oc → python3 scripts/files_binary.py upload |
Scripts live at: ~/.openclaw/skills/odt-filemgr-oc/scripts/
NC=~/.openclaw/skills/nextcloud-aio-oc/scripts
ODT=~/.openclaw/skills/odt-filemgr-oc/scripts
# 1. Download
python3 $NC/files_binary.py download "/Documents/MyDoc.odt" /tmp/MyDoc.odt
# 2. Inspect
python3 $ODT/odt_tool.py inspect /tmp/MyDoc.odt
# 3. Edit
python3 $ODT/odt_tool.py append /tmp/MyDoc.odt --text "New Section" --heading 2
python3 $ODT/odt_tool.py append /tmp/MyDoc.odt --text "Section content here."
# 4. Upload back
python3 $NC/files_binary.py upload /tmp/MyDoc.odt "/Documents/MyDoc.odt"
# 1. Create locally
python3 $ODT/odt_tool.py create /tmp/NewDoc.odt --title "Q1 Report"
# 2. Add content
python3 $ODT/odt_tool.py append /tmp/NewDoc.odt --text "Summary" --heading 2
python3 $ODT/odt_tool.py append /tmp/NewDoc.odt --text "Results were positive across all metrics."
# 3. Push to NextCloud
python3 $NC/files_binary.py upload /tmp/NewDoc.odt "/Documents/Q1 Report.odt"
python3 $ODT/odt_tool.py inspect myfile.odt
python3 $ODT/odt_tool.py to-text myfile.odt
python3 $ODT/odt_tool.py append myfile.odt --text "Added paragraph."
python3 $ODT/odt_tool.py replace myfile.odt --find "draft" --sub "final"
python3 $ODT/odt_tool.py set-meta myfile.odt --title "Final Report"
| Command | Description |
|---|---|
inspect <file> | Show metadata + paragraph/heading/table structure |
to-text <file> | Extract all text content in document order |
create <file> [--title T] | Create blank ODT (optional H1 title) |
append <file> --text T [--heading N] [--style S] | Append heading (N=1–6) or paragraph |
replace <file> --find F --sub S [--regex] | Find and replace text |
set-meta <file> [--title T] [--subject S] [--description D] | Update metadata |
set-font <file> --font F [--size N] [--no-preserve-mono] | Set font across all style layers (default + all named/inline overrides) |
set-outline <file> --numbered | --plain | Toggle heading numbering (1.1.1 or none) |
merge-styles <file> --template T | Apply all styles from a template ODT |
For table edits, style changes, or multi-step operations, write inline Python:
from odfdo import Document, Header, Paragraph
doc = Document("/tmp/file.odt")
body = doc.body
# Read all content in order
for child in body.children:
if isinstance(child, Header):
print(f"H{child.level}: {child.inner_text}")
elif isinstance(child, Paragraph) and child.inner_text.strip():
print(f"[{child.style}]: {child.inner_text}")
# Add content
body.append(Header(2, "New Section"))
body.append(Paragraph("Content here."))
# Table edit
table = body.get_table_by_name("MyTable")
table.set_value(0, 0, "Updated cell")
doc.save("/tmp/file.odt", pretty=True)
For the full odfdo API (tables, lists, spans, styles, metadata), see REFERENCE.md.
/tmp/ for working files; clean up after upload.merge-styles with a template rather than generating styles from scratch. set-font and set-outline work safely on any document without a template.| Error | Fix |
|---|---|
ModuleNotFoundError: odfdo | pip install odfdo |
zipfile.BadZipFile | File corrupted or not a real ODT — re-download |
| Heading doesn't appear as H1 | Use Header(1, "text"), not Paragraph("text", style="Heading 1") |
| Font still shows as Liberation in Collabora | ODT has 6 font attributes per style rule. Old approach only set 3 (style:font-name-*). Collabora renders from fo:font-family — now all 6 are set. Re-run set-font. |
| Font name must match exactly | LibreOffice/Collabora font names are case-sensitive: "Noto Sans" not "noto sans" |
set-outline changes not visible | Some documents override numbering via paragraph styles — use merge-styles with a clean template instead |
ZIP package — ready to use