Skip to content

Latest commit

 

History

History
99 lines (69 loc) · 5.66 KB

File metadata and controls

99 lines (69 loc) · 5.66 KB

The officework engine

日本語版: 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

sheet

docx: reading and writing

ooxml

line breaking, kinsoku, metrics, page coordinates

engine (kumihan)

putting a page onto paper

paper

None of these depend on GPUI. That is the point of the split, not a side-effect.

Using it from Python

$ pip install officework

See the Python manual. Nothing on this page is needed for that; the sections below are about embedding the engine in another application.

Running the engine inside genoffice

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/sheets

genoffice 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.

What is actually replaced

Eleven of the twelve commands.

Commands Who does them

open read_range read_formula_cells read_media recalc_cells

officework — cells, formulas, formats, borders, merges, conditional formats, validation, defined names, freeze panes, tables, comments, pictures and shapes, and the arithmetic

close cancel

officework (session bookkeeping)

archive_manifest read_entries scan_entries save_archive

officework — package-level only; nothing about xlsx is interpreted here

convert_workbook

not implemented. Converts .xls, which is BIFF8, a different format

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.

Which genoffice this was checked against

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.

How far it has been checked

  • 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 cellXfs index where we renumber, and a test asserting that CELL("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.

Licence

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.