js.documents 1.81.8 → 1.81.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -629,7 +629,7 @@ To run a single test file: `pnpm vitest run src/path/to/file.test.ts`.
629
629
  - **`ContentStroke.style` (`solid`/`dashed`/`dotted`/`double`) is not written by any vector writer, ODF or DrawingML.** Nothing in this package produces one — `LayoutLine` and `LayoutPath` both carry a stroke of colour and width only (`document-schema.js`'s `layout.ts`), so no reconstruction path can populate it — and `a:prstDash` has no `double` member to map the fourth value onto regardless. A hand-built `ContentVector` setting it consequently paints solid. A real, bounded gap rather than an oversight.
630
630
  - **`pdfToOds` recovers what was printed, not what was entered.** `reconstructSpreadsheet` (`src/layout/reconstruct.ts`) tries a real gridline lattice first: it scans the page's `LayoutLine`/stroked-single-segment-`LayoutPath` items (see the `interpret.ts` gotcha above) for enough parallel horizontal and vertical lines at consistent positions to call it a printed grid (`MIN_GRIDLINE_COUNT_PER_AXIS = 3` per axis, i.e. at least a 2×2 grid, and a span-consistency check that rejects a scatter of unrelated short strokes — a page border or a couple of decorative rules — as not a genuine lattice), and uses those line positions DIRECTLY as cell boundaries when found. Absent a lattice, it clusters text into a grid from geometry alone instead: rows reuse `clusterIntoLines` verbatim (a spreadsheet cell's own text is never wrapped across lines, so a text line already IS a row), and columns generalise `clusterIntoParagraphs`'s own single `dominantLeftX` to several recurring x-position anchors, first merging directly-adjacent same-line fragments (`splitLineByLargeGaps`, the same >2em-gap signal `reconstructPresentation`'s own block clustering uses) so a cell whose text arrived as several run-level-split `LayoutText` items isn't scattered across spurious columns. Column widths and row heights are genuinely measured from whichever geometry was used (drawn gridline gaps, or measured text/anchor extents), never invented. Every recovered cell always carries its own extracted `displayText` verbatim, and additionally carries a **heuristically re-typed** `value` wherever `src/layout/cell-typing.ts` finds exactly one defensible reading of that string; a formula is still never claimed. See the dedicated heuristic-re-typing gotcha below and [Fidelity](#fidelity) for the full framing. `buildOdsPackage` (`src/edit/ods/content.ts`) is `pdfToOds`'s own package-building half, mirroring `buildOdtPackage`/`buildOdpPackage`/`buildOdgPackage`'s role for `pdfToOdt`/`pdfToOdp`/`pdfToOdg`.
631
631
  - **`OdsSheet.printSettings` (`src/edit/ods/print-settings.ts`) now round-trips every field `ContentSheetPrintSettingsSchema` carries, not just the five it started with.** `pageSize`/`margins`/`gridlines`/`headers`/`pageOrder` resolve through the `table:style-name` → `style:style[family="table"]` → `style:master-page-name` → `style:master-page` → `style:page-layout` → `style:page-layout-properties` chain (`odf.js`'s own exported `findStyleElement`/`resolvePageLayoutProperties`/`parsePageSize`/`parseMargins`); the setter mints a fresh `style:page-layout` + `style:master-page` + `style:style[family="table"]` triple and repoints the sheet's own `table:style-name` to it on every call, the same append-only style-editing convention `src/edit/odg/style.ts` already documents. The remaining, previously-unimplemented fields are now implemented too: `printRange` reads/writes `table:print-ranges` directly on `table:table`; `scalePercent`/`fitToPages` read/write `style:scale-to` and `style:scale-to-X`/`style:scale-to-Y` on the page-layout-properties element; `repeatColumns`/`repeatRows` are read via `scanTableStructure`, a scoped-down mirror of `odf.js`'s own private `readTable`'s table-wide column/row cursor tracking (the same walk that function performs before ever calling its own `readPrintSettings`), and written by moving the real `table:table-column`/`table:table-row` elements covering the given range into a fresh `table:table-header-columns`/`table:table-header-rows` wrapper; `manualBreaks` read/write `fo:break-before="page"` on the named row/column's own style. Writing `repeatRows`/`repeatColumns` required teaching `address.ts`'s row/column addressing that a row/column may now live nested one level inside a header wrapper rather than as a direct `table:table` child, so a subsequent cell/column/row write against a wrapped index finds the real element instead of creating a spurious duplicate outside it; the width/height and manual-break writers all target the same `style:table-column-properties`/`style:table-row-properties` element, so each reads the column/row's current style first and mints a fresh style carrying the merged result, rather than a naive single-property mint clobbering whatever an earlier call had already set. No known gap remains in `ContentSheetPrintSettingsSchema` coverage.
632
- - **`OdsSheet` now has a real column-width/row-height setter (`setColumnWidth`/`setRowHeight`, `src/edit/ods/column-row.ts`), closing a gap that escalated from cosmetic to a genuine correctness bug once `xlsxToPdf`/`pdfToXlsx` started composing through `buildOdsPackage` internally.** `OdsSheet.cell()`'s own column/row-materialisation (`address.ts`) creates a real, explicit `table:table-column`/`table:table-row` element for any position a caller ever addresses, but previously never gave it a width/height style. This is a genuinely different failure shape from a column/row with NO element at all: `sheets.ts`'s own `resolveAxis` only falls back to `DEFAULT_COLUMN_WIDTH_PT`/`DEFAULT_ROW_HEIGHT_PT` for an index with no `ContentSheetColumn`/`ContentSheetRow` entry whatsoever — an explicit-but-unstyled element reads back at `widthPt`/`heightPt` 0 (`odf.js`'s own `resolveColumnWidthPt`/`readRowLayout`), and that explicit zero wins over the fallback. While `buildOdsPackage`'s own output was only ever a terminal deliverable (`pdfToOds`, or a caller's own `readOdsContent` round trip), this was cosmetic: a real app reopening it would use its own defaults instead of the source's. `xlsxToPdf` (`xlsxToOds` then `odsToPdf`) made it a real bug instead — the intermediate ods bytes get laid out again by `convertSpreadsheetToLayout`, and a zero-size grid collapses every cell onto the same physical position rather than merely losing precision. `setColumnWidth`/`setRowHeight` mint a fresh `style:style[family="table-column"|"table-row"]` per column/row and repoint its own `table:style-name`, the same append-only style-minting convention `writeSheetPrintSettings`/`src/edit/odg/style.ts` already establish; `buildOdsPackage` now calls both for every `ContentSheetColumn`/`ContentSheetRow` a source sheet carries. Column/row HIDDEN state and `ContentSheetImage`/formula `embeddedObjects` are no longer gaps either, closed in the same phase: `OdsSheet.setColumnHidden`/`setRowHidden` set or clear `table:visibility="collapse"` directly on the `table:table-column`/`table:table-row` element — a plain attribute, not a style property, so it never interacts with the width/height setters above — and `buildOdsPackage` calls one of these for every column/row whose `hidden` field is `true`. `OdsSheet.addImage` (`src/edit/ods/floating.ts`) writes a real floating `draw:frame`/`draw:image` into `table:shapes` (the ODF 1.3 content-model container for spreadsheet floating shapes, always `table:table`'s own first child in a package this editor builds), resolving a `ContentSheetImage`'s `anchorRow`/`anchorColumn` plus `offsetXPt`/`offsetYPt` to an absolute `svg:x`/`svg:y` by summing the real, currently-declared width/height of every column/row strictly before the anchor (header-wrapper-aware, hidden columns/rows contributing zero, falling back to the same default column/row size the layout engine assumes once the walk runs past what the sheet has declared) — reusing `addImageMedia` for the binary part and manifest entry, the same mechanism `src/edit/odp/image.ts` already uses for a slide. `OdsSheet.addEmbeddedObject` writes a real embedded ODF formula sub-document for `objectKind === 'formula'` (reusing `addFormulaObject`, the same mechanism `OdtBody.appendFormula` already uses); every other `objectKind` (`wordprocessing`/`presentation`/`spreadsheet`/`drawing`) is left unwritten, a documented, bounded gap mirroring `buildOdtPackage`'s identical narrowing for a `'drawing'` embedded object, since embedding one would mean writing that document's own package as a nested OLE sub-object, which no writer in this codebase implements. `buildOdsPackage` calls both for every sheet's images/embedded objects, after every column/row width/height/hidden call, so an image's own anchor resolves against the sheet's final, real column/row sizing. This is no longer write-only: `odf.js` 2.2.0's own `readOds` reads a sheet's floating shapes and embedded objects back (it previously hardcoded `images: []` and never set `embeddedObjects`), so a written image now verifies as a genuine `ContentDocument` re-read round trip — bytes, declared size, and anchor quartet — on top of the direct written-XML structural checks these tests already made.
632
+ - **`OdsSheet` now has a real column-width/row-height setter (`setColumnWidth`/`setRowHeight`, `src/edit/ods/column-row.ts`), closing a gap that escalated from cosmetic to a genuine correctness bug once `xlsxToPdf`/`pdfToXlsx` started composing through `buildOdsPackage` internally.** `OdsSheet.cell()`'s own column/row-materialisation (`address.ts`) creates a real, explicit `table:table-column`/`table:table-row` element for any position a caller ever addresses, but previously never gave it a width/height style. This is a genuinely different failure shape from a column/row with NO element at all: `sheets.ts`'s own `resolveAxis` only falls back to `DEFAULT_COLUMN_WIDTH_PT`/`DEFAULT_ROW_HEIGHT_PT` for an index with no `ContentSheetColumn`/`ContentSheetRow` entry whatsoever — an explicit-but-unstyled element reads back at `widthPt`/`heightPt` 0 (`odf.js`'s own `resolveColumnWidthPt`/`readRowLayout`), and that explicit zero wins over the fallback. While `buildOdsPackage`'s own output was only ever a terminal deliverable (`pdfToOds`, or a caller's own `readOdsContent` round trip), this was cosmetic: a real app reopening it would use its own defaults instead of the source's. `xlsxToPdf` (`xlsxToOds` then `odsToPdf`) made it a real bug instead — the intermediate ods bytes get laid out again by `convertSpreadsheetToLayout`, and a zero-size grid collapses every cell onto the same physical position rather than merely losing precision. `setColumnWidth`/`setRowHeight` mint a fresh `style:style[family="table-column"|"table-row"]` per column/row and repoint its own `table:style-name`, the same append-only style-minting convention `writeSheetPrintSettings`/`src/edit/odg/style.ts` already establish; `buildOdsPackage` now calls both for every `ContentSheetColumn`/`ContentSheetRow` a source sheet carries. Column/row HIDDEN state and `ContentSheetImage`/formula `embeddedObjects` are no longer gaps either, closed in the same phase: `OdsSheet.setColumnHidden`/`setRowHidden` set or clear `table:visibility="collapse"` directly on the `table:table-column`/`table:table-row` element — a plain attribute, not a style property, so it never interacts with the width/height setters above — and `buildOdsPackage` calls one of these for every column/row whose `hidden` field is `true`. **The "explicit but unstyled" hazard itself is now closed at its source, not just worked around by callers who remember to call `setColumnWidth`/`setRowHeight` explicitly.** `column-row.ts`'s `ensureColumnDefaultWidth`/`ensureRowDefaultHeight` stamp `DEFAULT_COLUMN_WIDTH_PT`/`DEFAULT_ROW_HEIGHT_PT` (matching `sheets.ts`'s own fallback values exactly) onto a column/row the moment any of `OdsSheet.cell()`, `mergeCells()`, `setColumnHidden()`, or `setRowHidden()` individuates it for the first time — but only when it has no width/height style at all yet, never overwriting a width a caller (or a real source document opened via `openOds`) already set. A plain `createOds()` sheet touched only through `.cell()` now reads back with a real, positive `widthPt`/`heightPt` on every column/row it addressed, never the ambiguous 0 that used to require an explicit `setColumnWidth`/`setRowHeight` call to avoid. One narrower instance of the identical hazard remains open, deliberately not folded into this fix: `print-settings.ts`'s `wrapRepeatRange` (the `repeatColumns`/`repeatRows` writer) still individuates its own boundary columns/rows via a bare, unstyled `replaceRun`, and — unlike `cell()`/`mergeCells()` — stamping every position that boundary individuation touches would mean materializing an unbounded range rather than the two boundary positions it is deliberately designed to touch, a genuinely different performance tradeoff this fix does not attempt to resolve. `OdsSheet.addImage` (`src/edit/ods/floating.ts`) writes a real floating `draw:frame`/`draw:image` into `table:shapes` (the ODF 1.3 content-model container for spreadsheet floating shapes, always `table:table`'s own first child in a package this editor builds), resolving a `ContentSheetImage`'s `anchorRow`/`anchorColumn` plus `offsetXPt`/`offsetYPt` to an absolute `svg:x`/`svg:y` by summing the real, currently-declared width/height of every column/row strictly before the anchor (header-wrapper-aware, hidden columns/rows contributing zero, falling back to the same default column/row size the layout engine assumes once the walk runs past what the sheet has declared) — reusing `addImageMedia` for the binary part and manifest entry, the same mechanism `src/edit/odp/image.ts` already uses for a slide. `OdsSheet.addEmbeddedObject` writes a real embedded ODF formula sub-document for `objectKind === 'formula'` (reusing `addFormulaObject`, the same mechanism `OdtBody.appendFormula` already uses); every other `objectKind` (`wordprocessing`/`presentation`/`spreadsheet`/`drawing`) is left unwritten, a documented, bounded gap mirroring `buildOdtPackage`'s identical narrowing for a `'drawing'` embedded object, since embedding one would mean writing that document's own package as a nested OLE sub-object, which no writer in this codebase implements. `buildOdsPackage` calls both for every sheet's images/embedded objects, after every column/row width/height/hidden call, so an image's own anchor resolves against the sheet's final, real column/row sizing. This is no longer write-only: `odf.js` 2.2.0's own `readOds` reads a sheet's floating shapes and embedded objects back (it previously hardcoded `images: []` and never set `embeddedObjects`), so a written image now verifies as a genuine `ContentDocument` re-read round trip — bytes, declared size, and anchor quartet — on top of the direct written-XML structural checks these tests already made.
633
633
  - **`reconstructDrawing` maps recovered geometry back onto ODF shapes near-1:1, with no clustering — and every vector kind in this package's own `.odg` fixture now survives the round trip, where a stroked rect, an ellipse, and a line used to collapse to a generic `path`.** Every painted `LayoutItem` maps onto a `ContentVector`/`ContentShape` directly, in the exact z-order it was recovered — `LayoutRect` → `rect`, `LayoutEllipse` → `ellipse`, `LayoutLine` → `line`, `LayoutPath` → `path`, `LayoutText`/`LayoutImage` → `ContentShape` — a fundamentally more tractable problem than `reconstructWordprocessing`/`reconstructPresentation`'s own paragraph/shape geometry clustering, since a drawing has no semantic structure to infer at all. How much *kind* information survives is decided upstream, by what `readPdf` can hand it: pdf-codec's own shape-pattern detection (see the gotcha above) now recovers a rect under any fill/stroke combination, a real ellipse from the four kappa-ratio cubics `writeEllipse` emits, and a real line, so `reconstructDrawing` receives — and therefore emits — the original kind in each case. What still narrows: a rotation that is not a multiple of 90° leaves no axis-aligned pattern to match, so a rect turned by 30° comes back as a `path` carrying its four rotated corners exactly. Position, size, and fill/stroke colour survive regardless of kind (within ordinary floating-point/string-formatting tolerance). A `path` vector's own reconstructed `frame` is a further, separate approximation: it is the *tight* bounding box of every recovered point, cubic control points included (a cubic curve is guaranteed to lie within their convex hull, so this never clips the curve) — which can legitimately be *larger* than whatever frame the original path's own author declared, if that frame didn't tightly bound its own control points to begin with (a real, valid ODF/SVG authoring pattern: a `viewBox`/frame is a declared coordinate window, not a guaranteed tight bounding box). A single original drawing text box that PDF's own greedy line-wrapper split across several lines does **not** reconstruct as one multi-line shape: `reconstructDrawing` maps each recovered `LayoutText` item to its own separate `ContentShape` (the same one-`LayoutItem`-to-one-shape rule every other kind follows), so a wrapped multi-line text box comes back as several small, independently-positioned text boxes, one per original line — confirmed visually against real LibreOffice (see the real-file verification note below); the full text content still survives, just redistributed. `buildOdgPackage` (`src/edit/odg/content.ts`) is `pdfToOdg`'s own package-building half, mirroring `buildOdtPackage`/`buildOdpPackage`'s role for `pdfToOdt`/`pdfToOdp`.
634
634
  - **Two real, confirmed-against-actual-LibreOffice-rendering fill bugs were fixed as part of building `reconstructDrawing`/`pdfToOdg`, not by it.** Both are pre-existing gaps in code that `reconstructDrawing`'s own real-file verification exposed, not something the reconstruction algorithm itself introduced, and both apply to every `.odg` this package writes, not only a reconstructed one: (1) `src/edit/odg/style.ts`'s `graphicPropertyAttrs` wrote `draw:fill-color` alone, with no accompanying `draw:fill="solid"` — real LibreOffice 26.2 fills a `draw:rect`/`draw:ellipse` that way fine, but silently renders a `draw:path` with the identical omission as unfilled, even with a fill colour declared. `draw:fill="solid"` is now written explicitly whenever a fill is set, for every vector kind. (2) `writeEllipse` (pdf-codec's own `content-write.ts`) never emitted a PDF closepath (`h`) operator, even though its four Bezier arcs already return exactly to their own starting point — PDF fill operators close every subpath implicitly regardless (ISO 32000-1 8.5.3.1), but `readPdf`'s own general path tracking only marks a subpath `closed: true` when it actually sees an explicit `h`, so a PDF-round-tripped ellipse came back with `closed: false`, which correctly-behaving ODF/SVG consumers then refuse to fill even with `draw:fill="solid"` set. `writeEllipse` now emits `h` before its paint operator, drawing no additional ink (the path was already geometrically closed) but recording that closure explicitly.
635
635
  - **A vector primitive's own fill/stroke needed a self-contained graphic-family style writer, not `odf.js`'s own `StyleRegistry`.** `'graphic'` is a recognised `StyleFamily` member (`odf.js`'s `src/styles/registry.ts`), but `StylePropertiesSchema`/`buildStylePropertyElements` (`properties.ts`/`serialize.ts`) only ever model text/paragraph formatting and never emit a `style:graphic-properties` element for any family — extending that shared package for one narrow, documents.js-local need (`draw:fill(-color)`/`draw:stroke` + `svg:stroke-color`/`svg:stroke-width`) would be scope creep into a foreign package for a two-attribute-group writer this package can express directly. `src/edit/odg/style.ts` is that writer: it still reuses `odf.js`'s general append-only style-editing invariant (a setter always mints a fresh `style:style` and repoints `draw:style-name`, never mutates an existing entry — verified by the same `assertAutomaticStylesOnlyAppended` helper `OdpEditor`'s own live-view fidelity test uses) and `src/edit/odt/automatic-styles.ts`'s `ensureAutomaticStyles`/`nextStyleName` (the "find-or-create `office:automatic-styles`, mint the next unused name" logic every other hand-rolled style helper in this package already shares), rather than a third reimplementation of either.
@@ -8,6 +8,8 @@ let odf_js = require("odf.js");
8
8
  //#region src/edit/ods/column-row.ts
9
9
  const VISIBILITY_ATTR = "table:visibility";
10
10
  const VISIBILITY_COLLAPSE = "collapse";
11
+ const DEFAULT_COLUMN_WIDTH_PT = 64;
12
+ const DEFAULT_ROW_HEIGHT_PT = 15;
11
13
  function currentColumnStyleProperties(pkg, columnElement) {
12
14
  const styleName = (0, ooxml_js.attr)(columnElement, "table:style-name");
13
15
  const styleElement = styleName === void 0 ? void 0 : (0, odf_js.findStyleElement)(styleName, "table-column", pkg);
@@ -67,24 +69,34 @@ function writeColumnWidth(pkg, tableElement, index, widthPt) {
67
69
  function writeRowHeight(pkg, tableElement, index, heightPt) {
68
70
  applyRowStyleProperties(pkg, tableElement, index, { heightPt });
69
71
  }
72
+ function ensureColumnDefaultWidth(pkg, tableElement, index) {
73
+ require_edit_ods_address.ensureColumnCoverage(tableElement, index + 1);
74
+ if (currentColumnStyleProperties(pkg, require_edit_ods_address.replaceRun(tableElement.children, require_edit_ods_address.isElementWithTag("table:table-column"), index, "table:number-columns-repeated", () => require_xml_fragment.el("table:table-column"), "table:table-header-columns")).widthPt === void 0) writeColumnWidth(pkg, tableElement, index, DEFAULT_COLUMN_WIDTH_PT);
75
+ }
76
+ function ensureRowDefaultHeight(pkg, tableElement, index) {
77
+ if (currentRowStyleProperties(pkg, require_edit_ods_address.replaceRun(tableElement.children, require_edit_ods_address.isElementWithTag("table:table-row"), index, "table:number-rows-repeated", () => require_xml_fragment.el("table:table-row"), "table:table-header-rows")).heightPt === void 0) writeRowHeight(pkg, tableElement, index, DEFAULT_ROW_HEIGHT_PT);
78
+ }
70
79
  function writeColumnManualBreak(pkg, tableElement, index) {
71
80
  applyColumnStyleProperties(pkg, tableElement, index, { manualBreak: true });
72
81
  }
73
82
  function writeRowManualBreak(pkg, tableElement, index) {
74
83
  applyRowStyleProperties(pkg, tableElement, index, { manualBreak: true });
75
84
  }
76
- function writeColumnHidden(tableElement, index, hidden) {
77
- require_edit_ods_address.ensureColumnCoverage(tableElement, index + 1);
85
+ function writeColumnHidden(pkg, tableElement, index, hidden) {
86
+ ensureColumnDefaultWidth(pkg, tableElement, index);
78
87
  const columnElement = require_edit_ods_address.replaceRun(tableElement.children, require_edit_ods_address.isElementWithTag(require_edit_ods_address.COLUMN_TAG), index, require_edit_ods_address.COLUMN_REPEAT_ATTR, () => require_xml_fragment.el(require_edit_ods_address.COLUMN_TAG), require_edit_ods_address.HEADER_COLUMNS_TAG);
79
88
  if (hidden) require_xml_edit.setAttr(columnElement, VISIBILITY_ATTR, VISIBILITY_COLLAPSE);
80
89
  else require_xml_edit.removeAttr(columnElement, VISIBILITY_ATTR);
81
90
  }
82
- function writeRowHidden(tableElement, index, hidden) {
91
+ function writeRowHidden(pkg, tableElement, index, hidden) {
92
+ ensureRowDefaultHeight(pkg, tableElement, index);
83
93
  const rowElement = require_edit_ods_address.replaceRun(tableElement.children, require_edit_ods_address.isElementWithTag(require_edit_ods_address.ROW_TAG), index, require_edit_ods_address.ROW_REPEAT_ATTR, () => require_xml_fragment.el(require_edit_ods_address.ROW_TAG), require_edit_ods_address.HEADER_ROWS_TAG);
84
94
  if (hidden) require_xml_edit.setAttr(rowElement, VISIBILITY_ATTR, VISIBILITY_COLLAPSE);
85
95
  else require_xml_edit.removeAttr(rowElement, VISIBILITY_ATTR);
86
96
  }
87
97
  //#endregion
98
+ exports.ensureColumnDefaultWidth = ensureColumnDefaultWidth;
99
+ exports.ensureRowDefaultHeight = ensureRowDefaultHeight;
88
100
  exports.writeColumnHidden = writeColumnHidden;
89
101
  exports.writeColumnManualBreak = writeColumnManualBreak;
90
102
  exports.writeColumnWidth = writeColumnWidth;
@@ -2,9 +2,11 @@ import { Package, XmlElement } from "odf.js";
2
2
  //#region src/edit/ods/column-row.d.ts
3
3
  declare function writeColumnWidth(pkg: Package, tableElement: XmlElement, index: number, widthPt: number): void;
4
4
  declare function writeRowHeight(pkg: Package, tableElement: XmlElement, index: number, heightPt: number): void;
5
+ declare function ensureColumnDefaultWidth(pkg: Package, tableElement: XmlElement, index: number): void;
6
+ declare function ensureRowDefaultHeight(pkg: Package, tableElement: XmlElement, index: number): void;
5
7
  declare function writeColumnManualBreak(pkg: Package, tableElement: XmlElement, index: number): void;
6
8
  declare function writeRowManualBreak(pkg: Package, tableElement: XmlElement, index: number): void;
7
- declare function writeColumnHidden(tableElement: XmlElement, index: number, hidden: boolean): void;
8
- declare function writeRowHidden(tableElement: XmlElement, index: number, hidden: boolean): void;
9
+ declare function writeColumnHidden(pkg: Package, tableElement: XmlElement, index: number, hidden: boolean): void;
10
+ declare function writeRowHidden(pkg: Package, tableElement: XmlElement, index: number, hidden: boolean): void;
9
11
  //#endregion
10
- export { writeColumnHidden, writeColumnManualBreak, writeColumnWidth, writeRowHeight, writeRowHidden, writeRowManualBreak };
12
+ export { ensureColumnDefaultWidth, ensureRowDefaultHeight, writeColumnHidden, writeColumnManualBreak, writeColumnWidth, writeRowHeight, writeRowHidden, writeRowManualBreak };
@@ -2,9 +2,11 @@ import { Package, XmlElement } from "odf.js";
2
2
  //#region src/edit/ods/column-row.d.ts
3
3
  declare function writeColumnWidth(pkg: Package, tableElement: XmlElement, index: number, widthPt: number): void;
4
4
  declare function writeRowHeight(pkg: Package, tableElement: XmlElement, index: number, heightPt: number): void;
5
+ declare function ensureColumnDefaultWidth(pkg: Package, tableElement: XmlElement, index: number): void;
6
+ declare function ensureRowDefaultHeight(pkg: Package, tableElement: XmlElement, index: number): void;
5
7
  declare function writeColumnManualBreak(pkg: Package, tableElement: XmlElement, index: number): void;
6
8
  declare function writeRowManualBreak(pkg: Package, tableElement: XmlElement, index: number): void;
7
- declare function writeColumnHidden(tableElement: XmlElement, index: number, hidden: boolean): void;
8
- declare function writeRowHidden(tableElement: XmlElement, index: number, hidden: boolean): void;
9
+ declare function writeColumnHidden(pkg: Package, tableElement: XmlElement, index: number, hidden: boolean): void;
10
+ declare function writeRowHidden(pkg: Package, tableElement: XmlElement, index: number, hidden: boolean): void;
9
11
  //#endregion
10
- export { writeColumnHidden, writeColumnManualBreak, writeColumnWidth, writeRowHeight, writeRowHidden, writeRowManualBreak };
12
+ export { ensureColumnDefaultWidth, ensureRowDefaultHeight, writeColumnHidden, writeColumnManualBreak, writeColumnWidth, writeRowHeight, writeRowHidden, writeRowManualBreak };
@@ -7,6 +7,8 @@ import { findStyleElement, formatOdfLength, parseOdfLength } from "odf.js";
7
7
  //#region src/edit/ods/column-row.ts
8
8
  const VISIBILITY_ATTR = "table:visibility";
9
9
  const VISIBILITY_COLLAPSE = "collapse";
10
+ const DEFAULT_COLUMN_WIDTH_PT = 64;
11
+ const DEFAULT_ROW_HEIGHT_PT = 15;
10
12
  function currentColumnStyleProperties(pkg, columnElement) {
11
13
  const styleName = attr(columnElement, "table:style-name");
12
14
  const styleElement = styleName === void 0 ? void 0 : findStyleElement(styleName, "table-column", pkg);
@@ -66,22 +68,30 @@ function writeColumnWidth(pkg, tableElement, index, widthPt) {
66
68
  function writeRowHeight(pkg, tableElement, index, heightPt) {
67
69
  applyRowStyleProperties(pkg, tableElement, index, { heightPt });
68
70
  }
71
+ function ensureColumnDefaultWidth(pkg, tableElement, index) {
72
+ ensureColumnCoverage(tableElement, index + 1);
73
+ if (currentColumnStyleProperties(pkg, replaceRun(tableElement.children, isElementWithTag("table:table-column"), index, "table:number-columns-repeated", () => el("table:table-column"), "table:table-header-columns")).widthPt === void 0) writeColumnWidth(pkg, tableElement, index, DEFAULT_COLUMN_WIDTH_PT);
74
+ }
75
+ function ensureRowDefaultHeight(pkg, tableElement, index) {
76
+ if (currentRowStyleProperties(pkg, replaceRun(tableElement.children, isElementWithTag("table:table-row"), index, "table:number-rows-repeated", () => el("table:table-row"), "table:table-header-rows")).heightPt === void 0) writeRowHeight(pkg, tableElement, index, DEFAULT_ROW_HEIGHT_PT);
77
+ }
69
78
  function writeColumnManualBreak(pkg, tableElement, index) {
70
79
  applyColumnStyleProperties(pkg, tableElement, index, { manualBreak: true });
71
80
  }
72
81
  function writeRowManualBreak(pkg, tableElement, index) {
73
82
  applyRowStyleProperties(pkg, tableElement, index, { manualBreak: true });
74
83
  }
75
- function writeColumnHidden(tableElement, index, hidden) {
76
- ensureColumnCoverage(tableElement, index + 1);
84
+ function writeColumnHidden(pkg, tableElement, index, hidden) {
85
+ ensureColumnDefaultWidth(pkg, tableElement, index);
77
86
  const columnElement = replaceRun(tableElement.children, isElementWithTag(COLUMN_TAG), index, COLUMN_REPEAT_ATTR, () => el(COLUMN_TAG), HEADER_COLUMNS_TAG);
78
87
  if (hidden) setAttr(columnElement, VISIBILITY_ATTR, VISIBILITY_COLLAPSE);
79
88
  else removeAttr(columnElement, VISIBILITY_ATTR);
80
89
  }
81
- function writeRowHidden(tableElement, index, hidden) {
90
+ function writeRowHidden(pkg, tableElement, index, hidden) {
91
+ ensureRowDefaultHeight(pkg, tableElement, index);
82
92
  const rowElement = replaceRun(tableElement.children, isElementWithTag(ROW_TAG), index, ROW_REPEAT_ATTR, () => el(ROW_TAG), HEADER_ROWS_TAG);
83
93
  if (hidden) setAttr(rowElement, VISIBILITY_ATTR, VISIBILITY_COLLAPSE);
84
94
  else removeAttr(rowElement, VISIBILITY_ATTR);
85
95
  }
86
96
  //#endregion
87
- export { writeColumnHidden, writeColumnManualBreak, writeColumnWidth, writeRowHeight, writeRowHidden, writeRowManualBreak };
97
+ export { ensureColumnDefaultWidth, ensureRowDefaultHeight, writeColumnHidden, writeColumnManualBreak, writeColumnWidth, writeRowHeight, writeRowHidden, writeRowManualBreak };
@@ -44,10 +44,10 @@ var OdsSheet = class {
44
44
  require_edit_ods_column_row.writeRowHeight(this.pkg, this.live(), index, heightPt);
45
45
  }
46
46
  setColumnHidden(index, hidden) {
47
- require_edit_ods_column_row.writeColumnHidden(this.live(), index, hidden);
47
+ require_edit_ods_column_row.writeColumnHidden(this.pkg, this.live(), index, hidden);
48
48
  }
49
49
  setRowHidden(index, hidden) {
50
- require_edit_ods_column_row.writeRowHidden(this.live(), index, hidden);
50
+ require_edit_ods_column_row.writeRowHidden(this.pkg, this.live(), index, hidden);
51
51
  }
52
52
  addImage(image) {
53
53
  require_edit_ods_floating.insertSheetImage(this.pkg, this.live(), image);
@@ -56,8 +56,11 @@ var OdsSheet = class {
56
56
  require_edit_ods_floating.insertSheetEmbeddedObject(this.pkg, this.live(), object);
57
57
  }
58
58
  cell(row, column) {
59
- const node = require_edit_ods_address.resolveCellNode(this.live(), row, column);
59
+ const tableElement = this.live();
60
+ const node = require_edit_ods_address.resolveCellNode(tableElement, row, column);
60
61
  if (node.tag === "table:covered-table-cell") throw new Error(`cell (${row}, ${column}) is covered by a merged range -- address the merge's own anchor cell instead`);
62
+ require_edit_ods_column_row.ensureColumnDefaultWidth(this.pkg, tableElement, column);
63
+ require_edit_ods_column_row.ensureRowDefaultHeight(this.pkg, tableElement, row);
61
64
  return new require_edit_ods_cell.OdsCell(node, this.pkg);
62
65
  }
63
66
  cellAt(reference) {
@@ -70,6 +73,8 @@ var OdsSheet = class {
70
73
  const tableElement = this.live();
71
74
  const anchorNode = require_edit_ods_address.resolveCellNode(tableElement, startRow, startColumn);
72
75
  if (anchorNode.tag === "table:covered-table-cell") throw new Error(`mergeCells: (${startRow}, ${startColumn}) is already covered by another merged range`);
76
+ for (let rowOffset = 0; rowOffset < rowSpan; rowOffset++) require_edit_ods_column_row.ensureRowDefaultHeight(this.pkg, tableElement, startRow + rowOffset);
77
+ for (let columnOffset = 0; columnOffset < colSpan; columnOffset++) require_edit_ods_column_row.ensureColumnDefaultWidth(this.pkg, tableElement, startColumn + columnOffset);
73
78
  if (rowSpan > 1) require_xml_edit.setAttr(anchorNode, "table:number-rows-spanned", String(rowSpan));
74
79
  if (colSpan > 1) require_xml_edit.setAttr(anchorNode, "table:number-columns-spanned", String(colSpan));
75
80
  for (let rowOffset = 0; rowOffset < rowSpan; rowOffset++) for (let columnOffset = 0; columnOffset < colSpan; columnOffset++) {
@@ -1,7 +1,7 @@
1
1
  import { removeChild, setAttr } from "../../xml/edit.js";
2
2
  import { COVERED_CELL_TAG, resolveCellNode } from "./address.js";
3
3
  import { OdsCell } from "./cell.js";
4
- import { writeColumnHidden, writeColumnWidth, writeRowHeight, writeRowHidden } from "./column-row.js";
4
+ import { ensureColumnDefaultWidth, ensureRowDefaultHeight, writeColumnHidden, writeColumnWidth, writeRowHeight, writeRowHidden } from "./column-row.js";
5
5
  import { insertSheetEmbeddedObject, insertSheetImage } from "./floating.js";
6
6
  import { readSheetPrintSettings, writeSheetPrintSettings } from "./print-settings.js";
7
7
  import { attr } from "ooxml.js";
@@ -43,10 +43,10 @@ var OdsSheet = class {
43
43
  writeRowHeight(this.pkg, this.live(), index, heightPt);
44
44
  }
45
45
  setColumnHidden(index, hidden) {
46
- writeColumnHidden(this.live(), index, hidden);
46
+ writeColumnHidden(this.pkg, this.live(), index, hidden);
47
47
  }
48
48
  setRowHidden(index, hidden) {
49
- writeRowHidden(this.live(), index, hidden);
49
+ writeRowHidden(this.pkg, this.live(), index, hidden);
50
50
  }
51
51
  addImage(image) {
52
52
  insertSheetImage(this.pkg, this.live(), image);
@@ -55,8 +55,11 @@ var OdsSheet = class {
55
55
  insertSheetEmbeddedObject(this.pkg, this.live(), object);
56
56
  }
57
57
  cell(row, column) {
58
- const node = resolveCellNode(this.live(), row, column);
58
+ const tableElement = this.live();
59
+ const node = resolveCellNode(tableElement, row, column);
59
60
  if (node.tag === "table:covered-table-cell") throw new Error(`cell (${row}, ${column}) is covered by a merged range -- address the merge's own anchor cell instead`);
61
+ ensureColumnDefaultWidth(this.pkg, tableElement, column);
62
+ ensureRowDefaultHeight(this.pkg, tableElement, row);
60
63
  return new OdsCell(node, this.pkg);
61
64
  }
62
65
  cellAt(reference) {
@@ -69,6 +72,8 @@ var OdsSheet = class {
69
72
  const tableElement = this.live();
70
73
  const anchorNode = resolveCellNode(tableElement, startRow, startColumn);
71
74
  if (anchorNode.tag === "table:covered-table-cell") throw new Error(`mergeCells: (${startRow}, ${startColumn}) is already covered by another merged range`);
75
+ for (let rowOffset = 0; rowOffset < rowSpan; rowOffset++) ensureRowDefaultHeight(this.pkg, tableElement, startRow + rowOffset);
76
+ for (let columnOffset = 0; columnOffset < colSpan; columnOffset++) ensureColumnDefaultWidth(this.pkg, tableElement, startColumn + columnOffset);
72
77
  if (rowSpan > 1) setAttr(anchorNode, "table:number-rows-spanned", String(rowSpan));
73
78
  if (colSpan > 1) setAttr(anchorNode, "table:number-columns-spanned", String(colSpan));
74
79
  for (let rowOffset = 0; rowOffset < rowSpan; rowOffset++) for (let columnOffset = 0; columnOffset < colSpan; columnOffset++) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "js.documents",
3
- "version": "1.81.8",
3
+ "version": "1.81.9",
4
4
  "description": "Bidirectional docx/pptx <-> PDF conversion and a read+write editable OOXML document model, built on ooxml.js and Zod 4 codecs.",
5
5
  "type": "module",
6
6
  "repository": {