newdoc

package
v0.56.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Oct 11, 2026 License: MIT Imports: 9 Imported by: 0

Documentation

Overview

Package newdoc answers one question: what are the BYTES of a brand-new, empty document of type X?

Why this is not trivial

For a .md or a .csv the answer is "none" — a zero-byte file is a valid, empty text file and every editor opens it. For a .docx it is not: an Office document is a ZIP of XML parts, and a zero-byte file named report.docx is rejected by Word, by OnlyOffice and by LibreOffice alike. The same is true of .xlsx, .pptx and the OpenDocument trio. So "create an empty file" splits into two genuinely different jobs and this package owns the hard one.

Why the bytes are embedded rather than generated

The obvious alternative is to shell out to `soffice --headless --convert-to` at create time. It was rejected because it is not available where the product runs: the full image (ghcr.io/brf-tech/filex:vX) carries LibreOffice, but `:slim` and the bare `filex` binary deliberately carry no external tools at all — that is the documented point of those distributions. A feature that silently works on one image and 500s on another is worse than a feature with a narrower promise, so the bytes come from the binary itself: a few KB of XML, identical on every distribution, with no process to spawn and nothing to be absent.

Why the templates are XML files and not .docx blobs

The parts live under templates/ as readable XML and are zipped in memory on demand. A committed .docx would be an opaque binary in the diff — nobody could review a change to it, and nobody could tell a corrupted one from a deliberate one. As text, every part is reviewable and the ZIP container is reconstructed by code that is itself tested.

⚠ The ZIP is built with an explicit part manifest, NOT by walking the embedded tree. Two of the formats care about entry ORDER: an OpenDocument file must carry `mimetype` as its FIRST entry, STORED (uncompressed) and with no extra field, because that is what puts the media type at byte offset 38 where every magic-byte sniffer looks for it. A lexical walk puts META-INF first and the file stops being recognisable as an ODF document while still being a perfectly valid ZIP — which is exactly the kind of failure that passes every test that only asks "does it unzip?".

Index

Constants

This section is empty.

Variables

View Source
var ErrUnknownType = errors.New("newdoc: unknown document type")

ErrUnknownType is returned for an extension this build cannot create.

Functions

func Bytes

func Bytes(ext string) ([]byte, error)

Bytes returns the content of a brand-new file of this type.

The returned slice is shared with an internal cache and MUST NOT be modified; callers stream it and never own it. An empty, non-nil slice is a correct answer — it is what a new .md is.

Types

type AppDoc added in v0.48.0

type AppDoc struct {
	Plugin string            `json:"plugin"`
	View   string            `json:"view"`
	Label  map[string]string `json:"label"`
}

AppDoc is the app behind an app's row of the "New" menu.

type Group

type Group string

Group is the coarse family a type belongs to. It exists for the picker's section headings; it is NOT the capability gate (see Type.Requires).

const (
	// GroupDocument — office documents. Real bytes, real editor.
	GroupDocument Group = "document"
	// GroupText — text-shaped files where empty is the correct content.
	GroupText Group = "text"
	// GroupDiagram — diagram sources.
	GroupDiagram Group = "diagram"
	// GroupApp — a kind an installed app adds (`new_documents`): the app's
	// own row, made and opened by the app, not by this registry.
	GroupApp Group = "app"
)

type Requirement

type Requirement string

Requirement names the external service a client needs before it should offer this type to a person.

⚠ The server states the dependency; it does not resolve it. Whether OnlyOffice is actually reachable is already answered by /api/files/capabilities (external.onlyoffice), and an embedder may point at a document server this process cannot see — so the client is the only place that knows the true answer. What the client must NOT do is re-derive the dependency from a hardcoded extension list of its own: that list would rot the moment this registry grows a type.

const (
	// RequiresNothing — a built-in editor handles it (code / markdown).
	RequiresNothing Requirement = ""
	// RequiresOnlyOffice — only the OnlyOffice surface can open it.
	RequiresOnlyOffice Requirement = "onlyoffice"
	// RequiresDrawio — only the drawio surface can open it.
	RequiresDrawio Requirement = "drawio"
	// RequiresApp — an app's interface opens it; the server lists the row
	// only while the app runs, so there is nothing for a client to resolve.
	RequiresApp Requirement = "app"
)

type Type

type Type struct {
	Ext      string      `json:"ext"`
	Group    Group       `json:"group"`
	MIME     string      `json:"mime"`
	Requires Requirement `json:"requires,omitempty"`
	// ExtRequired says the file must carry this extension (#56). True for the
	// containers — an office document or a diagram is a ZIP or an XML file
	// that its editor finds BY extension, so "report" with no extension is a
	// document nothing opens. False for text: the new file is zero bytes and
	// is just as valid called LICENSE, Makefile or test.conf, so the person
	// names it whatever they like.
	//
	// ⚠ Never `omitempty`. A client tells a server from before #56 by this
	// key being ABSENT (that server appends the extension to every type), so
	// `false` has to be on the wire.
	ExtRequired bool `json:"ext_required"`
	// Key is what a create request names when it is not Ext: an app's row
	// (`app:<plugin>:<ext>`), whose extension may be one this registry also
	// makes. Empty for the registry's own types.
	Key string `json:"key,omitempty"`
	// App says whose row it is and which of its views opens the new file,
	// with the app's own label for it. Nil for the registry's own types.
	App *AppDoc `json:"app,omitempty"`
}

Type is one creatable document type, as published to clients.

There is deliberately no label here. Labels are localised, the catalogues live in the frontend package, and a server-rendered English string would be the one piece of this dialog that could not be translated.

func Lookup

func Lookup(ext string) (Type, bool)

Lookup resolves an extension (with or without a leading dot, any case).

func Types

func Types() []Type

Types returns every type this build can create, in picker order.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL