Skip to content
This repository was archived by the owner on Aug 20, 2026. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@
"license": "MIT",
"packageManager": "pnpm@11.6.0",
"dependencies": {
"document-schema.js": "^3.3.0",
"document-schema.js": "^4.0.0",
"fast-xml-parser": "^5.10.1",
"fflate": "^0.8.3",
"zod": "^4.4.3"
Expand Down
10 changes: 5 additions & 5 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 0 additions & 2 deletions src/typed/formula/read.test.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import { describe, expect, it } from 'vitest';
import type { Package } from '../../model/package';
import type { XmlElement } from '../../model/node';
import { CONTENT_FORMAT_VERSION } from 'document-schema.js';
import { el, txt } from '../../xml/fragment';
import { readOdfFormula, readOdfFormulaDocument } from './read';

Expand Down Expand Up @@ -140,7 +139,6 @@ describe('readOdfFormulaDocument', () => {
if (document.kind !== 'formula') {
throw new Error('expected a formula-kind ContentDocument');
}
expect(document.formatVersion).toBe(CONTENT_FORMAT_VERSION);
expect(document.metadata.title).toBe('Pythagoras');
expect(document.formula.starMath).toBe('f(x) = {x^2} over {2} + sqrt {x}');
expect(document.formula.mathml).toEqual(readOdfFormula(realFormulaPackage()).mathml);
Expand Down
2 changes: 0 additions & 2 deletions src/typed/formula/read.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import type { ContentDocument, LayoutMetadata } from 'document-schema.js';
import { CONTENT_FORMAT_VERSION } from 'document-schema.js';
import type { XmlElement, XmlNode } from '../../model/node';
import type { Package } from '../../model/package';
import { attrValue, elementsWithTag, rootElement } from '../../xml/query';
Expand Down Expand Up @@ -100,7 +99,6 @@ export function readOdfFormulaDocument(pkg: Package): ContentDocument {

return {
kind: 'formula',
formatVersion: CONTENT_FORMAT_VERSION,
metadata,
formula: starMath === undefined ? { mathml } : { mathml, starMath },
};
Expand Down
2 changes: 1 addition & 1 deletion src/typed/odb/form.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import { subDocumentPackage } from './subdocument';
// EMPIRICALLY CONFIRMED against real, unmodified LibreOffice 26.2 output (src/typed/odb/fixtures/form-and-report.odb -- see typed/odb/read.ts's own top-of-file note for how that fixture was generated and cross-verified), not assumed:
//
// 1. A form sub-document is a COMPLETE, ordinary ODF TEXT document. Its own directory holds content.xml/styles.xml/settings.xml (plus a manifest.rdf), its manifest:media-type is "application/vnd.oasis.opendocument.text", and its content.xml root is the usual office:document-content/office:body/office:text. readOdt therefore reads it unmodified through a synthetic sub-Package (see subdocument.ts) -- no form-specific text reader needed, and the paragraphs/tables a form's designer laid out around its controls come back exactly as they would from a standalone .odt.
// 2. The control tree hangs off office:text/office:forms, NOT off the drawing layer. office:forms holds one form:form per top-level form; a control is a form:<kind> ELEMENT (form:text, form:formatted-text, form:listbox, form:fixed-text, form:checkbox, ...) whose own form:data-field names the bound column. The drawing layer separately carries a draw:control element per control, referencing the control by its form:id -- that is the control's own GEOMETRY (position/size/anchor), which readOdt already reads as ordinary document content and which this reader deliberately does not re-derive.
// 2. The control tree hangs off office:text/office:forms, NOT off the drawing layer. office:forms holds one form:form per top-level form; a control is a form:<kind> ELEMENT (form:text, form:formatted-text, form:listbox, form:fixed-text, form:checkbox, ...) whose own form:data-field names the bound column. The drawing layer separately carries a draw:control element per control, referencing the control by its form:id -- that is the control's own GEOMETRY (position/size/anchor), which no reader here resolves today: readBlocks (typed/odt/read.ts) has no draw:control branch, and the ods shape walker skips the element explicitly (see typed/ods/read.ts's collectAnchoredFrames note), so control geometry is dropped entirely rather than re-derived here.
// 3. A form:form can NEST another form:form (a real Base sub-form, bound to its own command -- the fixture's own "HighValueSubForm" is a genuine nested form:form bound to a QUERY while its parent is bound to a TABLE). Sub-forms are consequently modelled as their own recursive OdbFormDefinition list rather than flattened into the parent's controls.
// 4. form:properties (an untyped bag of form:property elements carrying UNO property values LibreOffice round-trips for its own benefit -- PropertyChangeNotificationEnabled, DefaultControl, ObjIDinMSO, ...) appears on the form and on most controls. It is deliberately never read: none of it is form STRUCTURE, and surfacing a producer-specific property bag would invite callers to depend on LibreOffice internals.
//
Expand Down
10 changes: 5 additions & 5 deletions src/typed/ods/read.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import type {
LayoutMetadata,
Margins,
} from 'document-schema.js';
import { CONTENT_FORMAT_VERSION, PAGE_SIZE_A4 } from 'document-schema.js';
import { PAGE_SIZE_A4 } from 'document-schema.js';
import type { XmlElement, XmlNode } from '../../model/node';
import type { Package } from '../../model/package';
import { attrValue, childrenWithTag, findChildElement, rootElement } from '../../xml/query';
Expand Down Expand Up @@ -225,19 +225,19 @@ function readEmbeddedObjectDocument(reference: EmbeddedDrawObject): ContentDocum
switch (reference.objectKind) {
case 'wordprocessing': {
const { metadata, sections } = readOdt(reference.package);
return { kind: 'wordprocessing', formatVersion: CONTENT_FORMAT_VERSION, metadata, sections };
return { kind: 'wordprocessing', metadata, sections };
}
case 'presentation': {
const { metadata, slides } = readOdp(reference.package);
return { kind: 'presentation', formatVersion: CONTENT_FORMAT_VERSION, metadata, slides };
return { kind: 'presentation', metadata, slides };
}
case 'drawing': {
const { metadata, pages } = readOdg(reference.package);
return { kind: 'drawing', formatVersion: CONTENT_FORMAT_VERSION, metadata, pages };
return { kind: 'drawing', metadata, pages };
}
case 'spreadsheet': {
const { metadata, sheets } = readOds(reference.package);
return { kind: 'spreadsheet', formatVersion: CONTENT_FORMAT_VERSION, metadata, sheets };
return { kind: 'spreadsheet', metadata, sheets };
}
case 'formula':
// The one embedded kind whose own reader already returns a finished ContentDocument (readOdfFormulaDocument), because a formula document has no per-format {metadata, sections/slides/pages/sheets} shape to re-wrap -- its whole content IS the MathML.
Expand Down
2 changes: 1 addition & 1 deletion src/typed/odt/read.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import { parseOdfLength } from '../shared/units';
//
// This reader is deliberately thin: paragraph/run reading (readOdfParagraph) and table reading (readOdfTable) already live in typed/shared/ -- built for reuse across odt/ods/odp/odg, not odt-specific -- so this module's own job is the odt-SPECIFIC structure those shared readers have no opinion on: walking office:text's actual block sequence (paragraphs interleaved with lists and tables, in document order), mapping text:h's own text:outline-level onto a docx-equivalent styleId alongside document-schema.js's own headingLevel field, and resolving the document's own page geometry from its first master page. readOdfParagraph is tag-agnostic (it never inspects which tag its own caller found it at) and reads text:h exactly as it reads text:p, so this reader calls straight through to it for both, then overrides ONLY the resulting heading identity (styleId plus headingLevel) for a heading -- see readParagraphOrHeading below.
//
// SCOPE, matching ooxml.js's own readDocx's already-established, deliberately narrower gaps (see that module's own top-of-file note for the identical reasoning applied to OOXML): footnotes/endnotes, annotations/comments, header/footer content, inline frames/images (draw:frame inside text flow -- odp/odg's job, not odt's), fields beyond their cached/last-computed text value, change tracking (text:change-*), cell borders, explicit page breaks (fo:break-before/fo:break-after -- not modelled by styles/properties.ts's StyleProperties, so the cascade this reader relies on can't surface it; a genuinely separate, bounded follow-on), and documents with more than one master page (only the first is read, in document order -- see readFirstMasterPageGeometry below). A text:h or a nested text:list/text:table inside a table cell is also out of scope here, inherited directly from readOdfTable's own cell reading (table:table-cell content there is read as text:p only) -- not a gap introduced by this module. src/typed/formula/read.ts does not exist yet at the time this reader was written, so there is no formula-embedding recursion to account for either. List marker GLYPHS (the exact bullet character or number format string) remain unread -- only the ordered-vs-bullet KIND is resolved (see typed/shared/list.ts's resolveOdfListKind), since that is what downstream consumers need to render <ol> vs <ul>.
// SCOPE, matching ooxml.js's own readDocx's already-established, deliberately narrower gaps (see that module's own top-of-file note for the identical reasoning applied to OOXML): footnotes/endnotes, annotations/comments, header/footer content, inline frames/images (draw:frame inside text flow -- odp/odg's job, not odt's), fields in their entirety (no field branch exists in run collection -- readOdfParagraph drops a field child without even its cached/last-computed text value), change tracking (text:change-*), explicit page breaks (fo:break-before/fo:break-after -- not modelled by styles/properties.ts's StyleProperties, so the cascade this reader relies on can't surface it; a genuinely separate, bounded follow-on), and documents with more than one master page (only the first is read, in document order -- see readFirstMasterPageGeometry below). A text:h or a nested text:list/text:table inside a table cell is also out of scope here, inherited directly from readOdfTable's own cell reading (table:table-cell content there is read as text:p only) -- not a gap introduced by this module. src/typed/formula/read.ts does not exist yet at the time this reader was written, so there is no formula-embedding recursion to account for either. List marker GLYPHS (the exact bullet character or number format string) remain unread -- only the ordered-vs-bullet KIND is resolved (see typed/shared/list.ts's resolveOdfListKind), since that is what downstream consumers need to render <ol> vs <ul>.
//
// LIST HANDLING: the numId minting convention (a monotonically increasing per-encounter counter, never text:style-name), the ordered:/bullet: kind prefix, and the text:list/text:list-item structural nesting walk itself all live in typed/shared/list.ts -- read that module's own top-of-file notes in full for the derivation -- because the odp reader meets the IDENTICAL text:list construct inside slide text frames and shares every line of it. This reader's own remaining list responsibility is the one genuinely odt-specific part: threading a single document-wide OdfListIdState through its office:text walk, so list identities are unique across the whole body exactly as they are across a whole presentation's slides.

Expand Down
Loading