dorfteich/docs/architecture/adr/0009-import-export-converters.md
Claude Fable 5 0629411966 Add architecture documentation, ADRs, and operations concept
Initial deliverable of the architecture phase: 16 ADRs (stack, CRDT
collaboration, plugin sandbox, import/export, backups, CI/CD), data
model, permission model, real-time collaboration and plugin concepts,
deployment/operations/security documentation, and the milestone roadmap
that the implementation issues are derived from.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-04 14:36:16 +02:00

2.7 KiB

ADR 0009: Pandoc + Gotenberg sidecars for import/export

  • Status: accepted
  • Date: 2026-07-04

Context

Requirements: Markdown as the primary import/export format (including copy/paste), import of Word (.docx) and OpenOffice/LibreOffice (.odt) documents, export to Word, OpenOffice, and PDF. Kickoff decision: import is best-effort structural — headings, paragraphs, lists, tables, images, links, bold/italic are preserved reliably; layout fidelity (columns, text boxes, exact spacing) is explicitly out of scope. Document conversion is CPU-heavy and full of parser edge cases; it must not run inside the Node API process.

Decision

  • Markdown conversion (both directions, including clipboard paste) is implemented in-process in TypeScript via prosemirror-markdown with extensions for our custom nodes (wikilinks, section styles). This is the primary, lossless-as-possible path.
  • pandoc-server (official pandoc image, HTTP server mode, internal network only) handles:
    • import: .docx / .odt → Markdown (+ extracted media, which the API stores as uploads and rewrites to image nodes),
    • export: Markdown → .docx / .odt (structure-true best effort).
  • Gotenberg (internal only) handles PDF export: the API renders the page to standalone HTML (same renderer as the public read-only HTML endpoint, with the pond's fonts inlined) and sends it to Gotenberg's Chromium route. PDF output is for reading/sharing, not print production.
  • The API's import-export module orchestrates conversions asynchronously (job table + polling endpoint) with size limits and timeouts; sidecar failures degrade gracefully into a user-visible error, never a crash.
  • Plugin blocks render their manifest-declared static fallback in all exports (ADR 0008).

Consequences

  • Two extra containers in the Compose stack; both are stateless, official images, and internal-only (no ingress). Self-hosting stays docker compose up.
  • Import fidelity is testable: a fixture corpus of .docx/.odt files with expected Markdown output lives in the repo (stories reference it).
  • Copy/paste from Word into the editor goes through the editor's HTML paste handling (structural, same fidelity philosophy), not through pandoc.

Alternatives considered

  • mammoth.js in-process for docx: good HTML output, but no .odt support and no export direction; pandoc covers all four directions with one tool.
  • LibreOffice headless for everything: heavyweight, slower startup, layout-oriented rather than structure-oriented output.
  • Browser-print PDF (client-side): inconsistent results across clients; server-side Chromium (Gotenberg) gives reproducible PDFs and enables "export without opening the page".