日本語版: engine.ja.adoc
writer and calc are two applications over one engine.
The engine is the part that reads a document, understands it, and writes it back without damaging what it did not touch.
It has no window, so it runs anywhere: in the applications, in pip install officework, and — as of 2026-08-10 — inside a spreadsheet written by somebody else.
| crate | |
|---|---|
xlsx: reading, writing, formulas, cell formats |
|
docx: reading and writing |
|
line breaking, kinsoku, metrics, page coordinates |
|
putting a page onto paper |
|
None of these depend on GPUI. That is the point of the split, not a side-effect.
$ pip install officeworkSee the Python manual. Nothing on this page is needed for that; the sections below are about embedding the engine in another application.
genoffice is an Electron office suite whose spreadsheet talks to a Rust helper over stdin/stdout — one JSON line each way, twelve commands.
It reads the helper’s path from XLSX_SIDECAR_PATH.
That environment variable is the whole story. Point it at officework’s engine and genoffice’s spreadsheet runs on officework:
cargo build --release -p sidecar # in the officework checkout
XLSX_SIDECAR_PATH=/path/to/officework/target/release/xlsx-sidecar \
npm run dev -w @genoffice/sheetsgenoffice needs no patch. Not one line, not one file. Remove the variable and it is back to its own helper.
If you also want .xls import, keep a copy of genoffice’s helper somewhere and add GENOFFICE_SIDECAR=/that/copy; that one command is forwarded to it.
Point it at the engine itself and it will call itself forever.
Nothing else needs it.
The interesting part here is not our engine. It is that genoffice put a seam where a seam belonged, and the seam holds: a second implementation of a protocol, written by someone with no access to their build, drops in behind it.
Eleven of the twelve commands.
| Commands | Who does them |
|---|---|
|
officework — cells, formulas, formats, borders, merges, conditional formats, validation, defined names, freeze panes, tables, comments, pictures and shapes, and the arithmetic |
|
officework (session bookkeeping) |
|
officework — package-level only; nothing about xlsx is interpreted here |
|
not implemented. Converts |
So no copy of genoffice’s helper is needed. Set GENOFFICE_SIDECAR only if you want .xls import to keep working through their converter; without it, that one command says .xls is not readable and stops, rather than opening an empty workbook.
What still belongs to genoffice is the planning of a save. Its TypeScript decides which XML changes to make; the engine applies them to the package, copying every untouched entry through still compressed so its CRC32 and its compressed size both survive — genoffice checks both after every save, and re-deflating an entry fails that check. Charts are located but their series are not parsed, so chart objects are not reported; emitting a frame with no data would draw an empty box.
fd33934 (2026-08-10). The protocol is theirs and they can change it without telling anyone, because nobody here has asked them not to — this is a second implementation of someone else’s interface, not an arrangement with them.
So the commit is written down, and after pulling a newer genoffice the three tiers below get run again before trusting it.
Their Rust helper was untouched by the seven commits that landed on 2026-08-10, which is the usual case: the protocol moves far more slowly than the application around it.
-
Cross-check against genoffice’s own helper over 26 real workbooks (Bank of Japan flow-of-funds, Statistics Bureau household survey, and others): values, formulas, merges, extents and cell formats compared field by field.
-
genoffice’s own test suite, run against the engine with nothing forwarded: 16 of 21. Of the five, one is a chart fixture we do not parse; the rest are decisions — pivot-table output ranges we do not model, a style table numbered by the original
cellXfsindex where we renumber, and a test asserting thatCELL("filename")fails, which ours answers correctly. -
The application itself, opened and driven by hand: text, borders, merges, cell formats, recalculation down three levels of dependency, Save As, and a workbook carrying a logo, a stamp and a shape — all three drawn.
-
Byte fidelity on save, checked from outside: three real workbooks through genoffice’s save path, every untouched entry identical in CRC32 and compressed size, order preserved.
Each tier caught defects the one above it could not.
The cross-check cannot see a simplification taught to both sides; a test suite whose schema is passthrough() cannot see a field the running application rejects with strict().
Five defects surfaced only when the real application was launched.
officework is AGPL-3.0-or-later. genoffice is Apache-2.0, and nothing of genoffice is redistributed here — the recipe above runs a copy the user built themselves. Combining the two in a distributed work would make the combination AGPL, which is a decision for whoever distributes it, not something this page can grant.