Marketplace libreoffice-writer
Use when creating, editing, formatting, exporting, or extracting LibreOffice Writer (.odt) documents via UNO, including session-based edits, structured text targets, tables, images, lists, patch workflows, and snapshots.
install
source · Clone the upstream repo
git clone https://github.com/aiskillstore/marketplace
Claude Code · Install into ~/.claude/skills/
T=$(mktemp -d) && git clone --depth=1 https://github.com/aiskillstore/marketplace "$T" && mkdir -p ~/.claude/skills && cp -r "$T/skills/dfk1352/libreoffice-writer" ~/.claude/skills/aiskillstore-marketplace-libreoffice-writer && rm -rf "$T"
manifest:
skills/dfk1352/libreoffice-writer/SKILL.mdsource content
LibreOffice Writer
Use the bundled
writer modules for UNO-backed Writer document work.
All paths must be absolute. Bundled modules live under scripts/ in this
skill directory, so set PYTHONPATH=<skill_base_dir>/scripts.
If setup or runtime issues appear, check references/troubleshooting.md.
API Surface
# Non-session utilities create_document(path) export_document(path, output_path, format) # formats: "pdf", "docx" snapshot_page(doc_path, output_path, page=1, dpi=150) # Session (primary editing API) open_writer_session(path) -> WriterSession WriterSession methods: read_text(target: WriterTarget | None = None) -> str insert_text(text, target: WriterTarget | None = None) replace_text(target: WriterTarget, new_text) delete_text(target: WriterTarget) format_text(target: WriterTarget, formatting: TextFormatting) insert_table(rows, cols, data=None, name=None, target: WriterTarget | None = None) update_table(target: WriterTarget, data) delete_table(target: WriterTarget) insert_image(image_path, width=None, height=None, name=None, target: WriterTarget | None = None) update_image(target: WriterTarget, image_path=None, width=None, height=None) delete_image(target: WriterTarget) insert_list(items: list[ListItem], ordered: bool, target: WriterTarget | None = None) replace_list(target: WriterTarget, items: list[ListItem], ordered: bool | None = None) delete_list(target: WriterTarget) patch(patch_text, mode="atomic") -> PatchApplyResult export(output_path, format) reset() close(save=True) # Standalone patch utility patch(path, patch_text, mode="atomic") -> PatchApplyResult
Structured Targets: WriterTarget
WriterTargetfrom writer import WriterTarget WriterTarget( kind="text" | "insertion" | "table" | "image" | "list", text=None, after=None, before=None, occurrence=None, name=None, index=None, )
Target kinds
| Kind | Supported fields | Use |
|---|---|---|
| , , , | Read, replace, delete, or format matched text |
| , , , | Insert at a resolved boundary or after a matched span |
| or | Update/delete a table |
| or | Update/delete an image |
| , , , | Replace/delete one logical list block |
Resolution rules
- Omit
to read the full document or append inserted content at the end.target - Use
andafter
to constrain a search window.before - Use
when repeated text is expected; otherwise matching must be unique.occurrence - Prefer full sentences or distinctive paragraph-sized phrases for
,text
, andafter
anchors; single-word anchors are often too brittle for realistic prose edits.before - For table/image targets, prefer
; usename
only when order is stable.index - For insertion after inline text, Writer inserts at the boundary after the matched span; paragraph breaks must come from the inserted text or the session helper.
Formatting Payload
from writer import TextFormatting TextFormatting( bold=None, italic=None, underline=None, font_name=None, font_size=None, color=None, # named color or integer align=None, # "left" | "center" | "right" | "justify" line_spacing=None, spacing_before=None, spacing_after=None, )
Notes:
- Character and paragraph formatting can be combined in one call.
- Paragraph properties such as
apply to the full paragraph containing the match, not just the exact matched span.align - At least one formatting field must be set.
List Items
from writer import ListItem ListItem(text="Confirm scope", level=0)
is zero-based nesting.level- Nesting cannot skip levels.
uses a numbering style;ordered=True
uses bullets.ordered=False
Patch DSL
Use
patch() or session.patch() to apply ordered operations.
[operation] type = format_text target.kind = text target.text = Quarterly revenue grew 18%. target.after = Financial Summary target.before = Action Items format.bold = true format.align = center [operation] type = insert_list target.kind = insertion target.after = Action Items list.ordered = false items <<JSON [ {"text": "Confirm scope", "level": 0}, {"text": "Review output", "level": 0}, {"text": "Update packaging", "level": 1} ] JSON
Supported operation types
insert_textreplace_textdelete_textformat_textinsert_tableupdate_tabledelete_tableinsert_imageupdate_imagedelete_imageinsert_listreplace_listdelete_list
Patch value rules
- Use
fields for target definition.target.* - Use
fields for formatting payloads.format.* - Use
plus JSONlist.ordered
for list operations.items
anditems
must be valid JSON.data- Heredoc blocks are supported with
for multiline text or JSON.<<TAG ... TAG
Modes
stops on first failure, resets the session, and persists nothing.atomic
keeps successful earlier operations and records failures.best_effort
PatchApplyResult fields:
mode
=overall_status"ok" | "partial" | "failed"
= list ofoperationsPatchOperationResultdocument_persisted
For standalone
patch(path, ...), document_persisted means the changes were
saved to disk. For session.patch(...), it means the patch produced successful
mutations in the current open session state.
Example: Edit a Report in Session
from pathlib import Path from writer import ListItem, TextFormatting, WriterTarget, open_writer_session from writer.core import create_document output = str(Path("test-output/report.odt").resolve()) create_document(output) with open_writer_session(output) as session: session.insert_text( "Executive Summary\n\n" "Financial Summary\n\n" "Quarterly revenue grew 18%.\n\n" "Action Items" ) session.format_text( WriterTarget( kind="text", text="Quarterly revenue grew 18%.", after="Financial Summary", before="Action Items", ), TextFormatting(bold=True, align="center"), ) session.insert_list( [ ListItem(text="Confirm scope", level=0), ListItem(text="Review output", level=0), ListItem(text="Update packaging", level=1), ], ordered=False, target=WriterTarget(kind="insertion", after="Action Items"), )
Example: Patch an Existing Document
from writer import patch result = patch( "/abs/path/report.odt", """ [operation] type = replace_text target.kind = text target.text = Draft new_text = Final [operation] type = update_table target.kind = table target.name = Summary data = [["Metric", "Value"], ["Revenue", "$2M"]] [operation] type = replace_list target.kind = list target.text = Confirm scope items = [{"text": "Approve release", "level": 0}, {"text": "Notify team", "level": 1}] list.ordered = true """, mode="best_effort", ) print(result.overall_status)
Snapshots
from pathlib import Path from writer import snapshot_page result = snapshot_page(doc_path, "/tmp/page1.png", page=1, dpi=150) print(result.file_path, result.width, result.height) Path(result.file_path).unlink(missing_ok=True)
Use snapshots to verify layout after formatting, list edits, image placement, or table changes.
Common Mistakes
- Passing a relative path; UNO-facing Writer APIs expect absolute file paths.
- Omitting
for repeated text and then getting an ambiguity error.occurrence - Using anchors that are too short or too common; prefer full-sentence or paragraph-level anchor text plus
/after
bounds when possible.before - Expecting
to apply only to a phrase; Writer applies paragraph alignment to the containing paragraph.align - Supplying malformed JSON in
oritems
patch fields.data - Calling
or other methods aftersession.export()
.session.close()