Skip to main content

officework

xlsx and docx engines built not to destroy your forms — and that print. Read a spreadsheet or a document, change it, write back only what you changed, and turn it into a PDF. No office suite, no headless browser, no print driver.

Written in Rust (about 88,000 lines across the crates in this wheel, 1,158 tests), exposed to Python through PyO3.

日本語の説明は GitHub にあります (Japanese documentation on GitHub): Python の手引き

Install

$ pip install officework

0.5.0 is out, and plenty in it is still broken. The release notes list the gaps we know about. Please report what breaks at the issues.

Wheels are abi3 (CPython 3.10+), so one wheel per platform covers every version; Linux, macOS and Windows are published. The wheel is just the engines — no GUI, and nothing to install alongside: the equation typesetter is inside it, so there is no TeX to set up. That makes it a 30 MB download, 78 MB installed. pandas is imported only if you ask for it (pip install officework[pandas]).

Spreadsheets

from officework import sheet
b = sheet.Book.open("form7.xlsx")
s = b["quote"]
s.insert_row(30)                         # make room; the formulas below follow the move
s["A30"] = "Sample Trading Co., Ltd."    # only the value is rewritten
s["C30"] = "=B30*100"                    # a formula; recalculated on the spot
print(s["C30"].value)                    # the computed value, as in openpyxl
b.save("out.xlsx")                       # the xlsx to send on
b.save("quote.pdf")                      # the same sheet, straight to paper

Indexing follows openpyxl exactly: ws["A1"] is a cell, ws["A1:C3"] a tuple of rows, ws[1] a row, ws["A"] a column. Reading an untouched address gives you a cell whose .value is None, not None itself — so writing into a merged region's top-left works the way the articles show.

Documents

from officework import doc
d = doc.Doc.open("report.docx")
d.replace("Old Name Ltd.", "New Name Ltd.")   # replaces the text, run by run
d.fill("customer", "Sample Trading K.K.")     # fill a named form field
d.save("out.docx")                       # the docx to send on
d.save("report.pdf")                     # the same document, typeset for paper

PDF and PNG — the part the others cannot do

save() looks at the extension. The same book or document you just edited becomes a PDF, laid out by the same typesetting engine that drives the desktop app, so the paper matches the screen:

b.save("quote.pdf")                      # the sheet, paginated, repeating header rows
d.save("report.pdf")                     # the document, typeset

.png gives you the same page as an image — for a thumbnail, a preview in a web page, or a picture to drop into a chat message:

b.save("quote.png")                      # 150 dpi by default; A4 comes out 1240x1754
d.save("report.png", dpi=300)            # print resolution

One file per page. The first page keeps the name you gave it, and later pages get -2, -3 appended, so a three-page document writes report.png, report-2.png and report-3.png. Both formats come off the same laid-out page, so the image and the PDF agree.

This runs on a server with nothing else installed. There is no LibreOffice to launch, no Chromium to drive, no wkhtmltopdf, no temporary HTML. It is one library call, and it is fast enough to sit inside a request handler.

Fonts are subsetted, so only the glyphs you used are embedded. A Japanese page comes out under 30 KB — measured here at 8 KB for plain text and 25 KB for a page with a table, colour and shading — where embedding a whole CJK font costs 20 MB. Line breaking follows JIS X 4051, so Japanese text does not break before a closing bracket or after an opening one.

Neither openpyxl nor python-docx can produce a PDF at all. The commercial libraries that can are priced accordingly.

Shapes in a document

python-docx can only place pictures. This engine can place real shapes — rectangles, rounded rectangles, ellipses, arrows, diamonds and lines — pinned to the page, with fill, outline, text inside, rotation, opacity and a drop shadow:

d.add_shape("roundRect", 25, 80, 40, 25, fill="DDE7F0", line="2E5A87",
            text="Approved", shadow=True)

Coordinates and sizes are millimetres from the top-left of the page. They are written as DrawingML, so Word and LibreOffice open them as shapes you can select and edit — not as a flattened picture. They also come out in the PDF, they are read back when you reopen the file, and page= puts a shape on any page, not just the first.

Charts

Charts are drawn by this library, as shapes, rather than written as an instruction for Excel to render later. So they appear in the PDF, not only after someone opens the file in Excel:

ws.add_chart("bar", data="B3:C8", categories="A4:A8", at="A10",
             title="Target and actual")

All eleven chart types openpyxl can write are built in: bar, line, area, pie, doughnut, projected pie, radar, scatter, bubble, stock (high-low-close) and surface. For finer control there is a small chart layer whose shape is borrowed from d3 — build a scale, then place marks through it:

from officework import chart
c = chart.Chart(340, 180, title="Attainment")
x = c.band(branches)
y = c.linear([0, 150])
c.axis_left(y, fmt=lambda v: f"{int(v)}%")
c.bars(x, y, rates, color="70AD47", labels=True)
c.place(ws, "A20")

What you give up is a live Excel chart: ours is fixed at the data it was drawn from. Redraw it to update it.

Equations

officework.tex takes LaTeX and returns SVG or PNG. With TeX installed it typesets there (matrix columns align); without it, matplotlib's mathtext does the job; with neither, it refuses with the reason — never a silent empty picture.

Your old vocabulary still works

Code written for openpyxl or python-docx largely runs as-is:

ws = wb.active                          # openpyxl: cell(), append, iter_rows,
ws.cell(2, 3).value                     #   dimensions, create_sheet,
ws.append(["Aug", "pens", 5000])        #   copy_worksheet, freeze_panes …
d.tables[0].cell(0, 1).text             # python-docx: row_cells, columns,
d[3].runs[0].font.name                  #   runs, clear …

What is the same as Word and Excel, and what is deliberately not, is set out in How this differs from docx and xlsx. Interop is proven with the originals' own eyes: openpyxl reads what this engine writes, including the computed values it cannot produce itself. See the Python manual for the details and the deliberate differences.

Why the engines exist

openpyxl and python-docx rewrite the parts of the file they do not understand. For a document used as a printed form — the way most Japanese offices use one — that means the borders, merged cells, column widths, shapes, styles and headers you spent an afternoon on come back wrong.

When these engines open an xlsx or docx and write it back, they rewrite only what changed. b.unsupported / d.unsupported list what they recognised as unreadable, as far as they can tell.

The docx side is checked against an independent reader (genoffice's TypeScript docx engine) over 51 real documents, 43 of which this project did not write: 46 survive an open-and-save untouched, and no document loses a single part of its zip. The rest — footnote marks, second and later section breaks, equations — are listed in d.unsupported rather than dropped quietly.

Measured on one machine, 1096 rows × 20 columns (21,920 cells):

DataFrame → sheet 44 ms
sheet → DataFrame 65 ms

License

AGPL-3.0-or-later.

Using it inside your company — building forms, running ledgers, writing scripts — carries no obligations at all. Obligations appear only if you ship something built on it to third parties, or offer a modified version as a network service.

Metadata

Release files for officework 0.8.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for officework 0.8.0
File Size Uploaded Provenance Actions
officework-0.8.0.tar.gz 2.6 MB With provenance Details for officework-0.8.0.tar.gz

Built distributions (wheels)

Table of built distributions (wheels) for officework 0.8.0
File Interpreter ABI Platform Actions
officework-0.8.0-cp310-abi3-win_amd64.whl CPython 3.10 abi3 Windows x86-64 Details for officework-0.8.0-cp310-abi3-win_amd64.whl
officework-0.8.0-cp310-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl CPython 3.10 abi3 Linux glibc 2.17+ x86-64 Details for officework-0.8.0-cp310-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
officework-0.8.0-cp310-abi3-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl CPython 3.10 abi3 macOS 10.12+ x86-64, macOS 10.12+ universal2 (ARM64, x86-64), macOS 11.0+ ARM64 Details for officework-0.8.0-cp310-abi3-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl

Total release size: 132.2 MB

Release files / officework-0.8.0.tar.gz

Download URL officework-0.8.0.tar.gz
Size 2.6 MB
Tags Source
SHA-256 checksum
How to use checksums
259a84c492c8807dc75405ef3d7f61edf8f9ea881d6fb4977d1f64295944c939
BLAKE2b-256 checksum
How to use checksums
b92936678ba90b0332bad8ecaca250f5091fe88ce0b7f6837b13153ea1a21782
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 27, 2026.

Transparency log

Release files / officework-0.8.0-cp310-abi3-win_amd64.whl

Download URL officework-0.8.0-cp310-abi3-win_amd64.whl
Size 35.2 MB
Tags CPython 3.10 Windows x86-64 abi3
SHA-256 checksum
How to use checksums
2afadcfc157c1eed4f75a105cdd8d46e70d8ecc1d78e04ad41f0b90b15ffc752
BLAKE2b-256 checksum
How to use checksums
f25616f10f8f44b0e75c419900d71ce94d25956625a36bc429519a5f42729879
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 27, 2026.

Transparency log

Release files / officework-0.8.0-cp310-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl

Download URL officework-0.8.0-cp310-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Size 32.9 MB
Tags CPython 3.10 Linux glibc 2.17+ x86-64 abi3
SHA-256 checksum
How to use checksums
91a664e85bfe7f9ac43f9b399d8e7e5e5f55f1aa48c07d21b3d8e9011347f708
BLAKE2b-256 checksum
How to use checksums
8647b3d2c34b79e62af93f709139d26cfa0b1d70dfd19e904887e5b5e6847088
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 27, 2026.

Transparency log

Release files / officework-0.8.0-cp310-abi3-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl

Download URL officework-0.8.0-cp310-abi3-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl
Size 61.4 MB
Tags CPython 3.10 abi3 macOS 10.12+ universal2 (ARM64, x86-64) macOS 10.12+ x86-64 macOS 11.0+ ARM64
SHA-256 checksum
How to use checksums
8527d47721e122c43c8dc840003ae25a3031e155fd0543c2bbe815d39b6fc282
BLAKE2b-256 checksum
How to use checksums
4713c8de7763238a602e6f149223d89480f43402cdcc6422978770f966cb7c08
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 27, 2026.

Transparency log

Security for officework 0.8.0

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

Full provenance

All 4 files in this release have been uploaded with provenance metadata.

Select from 4 files

Attestation for officework-0.8.0.tar.gz

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 27, 2026.

Transparency log

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page