ooxml.js 4.6.0 → 4.7.0

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
@@ -299,7 +299,7 @@ The package layers a lossless core outward to lossy convenience views:
299
299
  - **A construct marker brackets whole blocks, never a sub-sequence of runs.** A construct covering a sub-sequence of one paragraph's runs lands on that paragraph's `constructs` field as a run range (see the fidelity-constructs section above) — bookmarks, fields, internal links, comment extents, note references, and `w:ffData` form fields alike. An inline SDT or a few inserted words inside an otherwise untouched paragraph are still read exactly as before — the text survives, the construct does not — as is a bookmark whose two halves sit in different paragraphs. So does a block extent that crosses another, or that straddles a section break or table-cell boundary: neither is expressible as balanced brackets (nor as a tree group, which is what the marker pair promotes to), and both are dropped — a drop `document-schema.js`'s extent-scope note ratifies — rather than emitted at a position that would decode to a different nesting.
300
300
  - **The docx pair round-trips `sections`, not a whole `DocxDocument`.** Numbering definitions, cell border styling/shading, section break kinds (`w:sectPr/w:type` onto `ContentSection.breakType`, both directions), and `w:themeColor` (without `themeShade`/`themeTint`) are read; images read into `ContentImageBlock` (floating `wp:anchor` position not recorded); `PAGE`/`NUMPAGES` fields resolve to Word's cached text. Headers and footers are read both as the flat per-part text arrays the reader always produced and structurally — each referenced part as block flow (`headerFooterParts`) plus per-section default/first/even references (`sectionHeaderFooters`); collapsing the flat arrays into that model wants a breaking change to a published shape and is tracked rather than taken. An inline `w:object`/`o:OLEObject` whose payload part is itself a ZIP archive — a modern producer's embedded xlsx/docx/pptx, detected by magic bytes through `archive-codec` — or a classic OLE compound-file `.bin` whose root storage carries the embedded file as an OLE-packaged `Package` stream, read through `archive-codec`'s bounded CFB reader, is decoded into a nested content document and emitted as a `ContentEmbeddedObjectBlock` beside its containing paragraph's own block, sized from `w:dxaOrig`/`w:dyaOrig`; the object's VML preview picture (`v:imagedata`) and a `w:object` inside a footnote (notes ride as text, with no block flow to lift an object into — a header/footer's own objects DO recover, those parts being walked as block flow) are not read, a compound file holding native legacy streams (BIFF and friends — no `Package` stream, or a packaged file that is not a ZIP) stays opaque, and a payload that does not decode as one of the three OOXML flavours (a plain archive, corrupt zip or compound-file data, a bodyless docx) degrades to no embedded block rather than failing the host read. The writer emits such a block back out as the inverse `w:object`/`o:OLEObject`: the nested document is re-serialised through its own format's builder into a fresh `word/embeddings/oleObject<N>.<docx|xlsx|pptx>` ZIP part carrying its relationship and content-type override, and `w:dxaOrig`/`w:dyaOrig` are derived from the block's frame (twips). An embedded presentation document serialises through an injected port — `BuildDocxContentOptions.serialiseEmbeddedPresentation`, a presentation `ContentDocument` → pptx-bytes function this package accepts because it has no PresentationML writer of its own, and the ecosystem's one pptx writer lives one layer up in documents.js (which exports `embeddedPresentationSerialiser`, wired from its own pptx builder, for exactly this call); with no serialiser injected, a `ContentDocument` carrying one is refused with a thrown error rather than a silent drop, and no VML preview picture is regenerated (the reader never read one into the model, and real previews are WMF/EMF this ecosystem has no writer for; Word shows a blank until activated). The writer emits no `styles.xml`, `numbering.xml`, comments, footnotes, endnotes, or headers/footers, so the `DocxDocument` fields outside `sections` do not come back — a paragraph's `styleId` is still written as a `w:pStyle` reference, resolving to nothing, since every property the style would have contributed is already spelled as direct formatting by then.
301
301
  - **pptx is read-only.** Connector shapes (`p:cxnSp`) are skipped; shape rotation composes through groups; a dynamic field (`a:fld`, e.g. slidenum/datetime) reads as an ordinary run plus a `field` run construct carrying `@type` as the instruction and the cached `a:t` as the result; an internal slide-jump link (`a:hlinkClick` resolving to a slide relationship, no `TargetMode`) records a `link` run construct with an internal target naming the destination slide's package part path (e.g. `ppt/slides/slide2.xml` — PresentationML addresses slides by part relationship, never by name; the flat run's `hyperlink` field stays reserved for external URIs, and an action-only `ppaction://hlinkshowjump` names no target part and records nothing); a chart graphic frame reads its chart part's cached series/category model into a table block (header row = series names, one row per category index); a SmartArt graphic frame reads its diagram data model's node text as paragraphs in diagram order (depth-first over `parOf` connections, siblings by `srcOrd`); an OLE graphic frame reads the fallback picture its `mc:Fallback` carries (or, with none reachable, a paragraph naming the `p:oleObj`'s `progId`), and when the payload part is itself a ZIP archive — a modern producer's embedded xlsx/docx/pptx, detected by magic bytes through `archive-codec` — or a classic OLE compound-file `.bin` whose root storage carries the embedded file as an OLE-packaged `Package` stream, read through `archive-codec`'s bounded CFB reader, also decodes it into a nested content document and emits it as an `embeddedObject` block beside the picture, sized to the frame's own geometry; a compound file holding native legacy streams (no `Package` stream, or a packaged file that is not a ZIP) stays opaque external-application data, and a payload that does not decode as one of the three OOXML flavours degrades to no embedded block rather than failing the slide read.
302
- - **xlsx has no native percentage/currency/date/time cell type.** Both directions are closed via the number-format engine: reading classifies style → format code → kind; writing emits interned `numFmt` codes, fed back through the classifier in tests. `displayText` is the typed-value spelling, not the producer's rendered string. A chart graphic frame reads as an embedded `chart` object (`embeddedObjects`, one row per series over the shared category axis, values verbatim `c:v` text, frame resolved through the sheet's own column widths and row heights), and a drawing picture (`xdr:pic`) as a `ContentSheetImage` (`images`, media bytes sniffed through the drawing part's relationships, frame resolved the same way) — one-way rows, since the writer emits no drawing part. A workbook's general defined names and table/List objects ride the tree reader's root `definitions` table (`readXlsx`), the landing the schema's own verdict gives a named range; the flat reader and the write pair carry neither. `dataValidation` and `conditionalFormatting` rules quarantine verbatim on their range's anchor cell through the residue channel, pending the corpus-gated vocabulary the construct inventory defers.
302
+ - **xlsx has no native percentage/currency/date/time cell type.** Both directions are closed via the number-format engine: reading classifies style → format code → kind; writing emits interned `numFmt` codes, fed back through the classifier in tests. `displayText` is the typed-value spelling, not the producer's rendered string. A chart graphic frame reads as an embedded `chart` object (`embeddedObjects`, one row per series over the shared category axis, values verbatim `c:v` text, frame resolved through the sheet's own column widths and row heights), and a drawing picture (`xdr:pic`) as a `ContentSheetImage` (`images`, media bytes sniffed through the drawing part's relationships, frame resolved the same way) — both rows under any of the drawing's three anchor spellings (`twoCellAnchor` marker pairs, `oneCellAnchor` sized by its own `xdr:ext`, and `absoluteAnchor`, whose page-absolute `xdr:pos` is re-based into the cell anchor vocabulary through that same grid geometry) — one-way rows, since the writer emits no drawing part. A workbook's general defined names and table/List objects ride the tree reader's root `definitions` table (`readXlsx`), the landing the schema's own verdict gives a named range; the flat reader and the write pair carry neither. `dataValidation` and `conditionalFormatting` rules quarantine verbatim on their range's anchor cell through the residue channel, pending the corpus-gated vocabulary the construct inventory defers.
303
303
  - **`test:smoke` depends on a fresh build.** It runs `tsdown && vitest run --project smoke`, always rebuilding `dist/` first. A bare `vitest` runs both projects; `smoke` fails loudly (`Cannot find module '../dist/index.js'`) if `dist/` is unbuilt.
304
304
  - **Binary-vs-XML part classification is a byte sniff, not an extension check.** `looksLikeXml` looks for a leading `<` after skipping a UTF-8 BOM and whitespace; any future binary format starting with `<` would misclassify.
305
305
  - **`Array.isArray` narrows `unknown` to `any[]`, not `unknown[]`.** Indexing the result reintroduces `any` and trips `no-unsafe-assignment`. `compact.ts` and `xml/parse.ts` each define a local `isUnknownArray` guard (`value is unknown[]`) — use it wherever the narrowed element is read.
@@ -55,6 +55,34 @@ var SheetGridGeometry = class {
55
55
  for (let index = 0; index < row; index++) yPt += this.rowHeightPt(index);
56
56
  return yPt;
57
57
  }
58
+ locateColumn(xPt) {
59
+ let index = 0;
60
+ let boundaryPt = 0;
61
+ let widthPt = this.columnWidthPt(index);
62
+ while (boundaryPt + widthPt <= xPt) {
63
+ boundaryPt += widthPt;
64
+ index += 1;
65
+ widthPt = this.columnWidthPt(index);
66
+ }
67
+ return {
68
+ index,
69
+ offsetPt: xPt - boundaryPt
70
+ };
71
+ }
72
+ locateRow(yPt) {
73
+ let index = 0;
74
+ let boundaryPt = 0;
75
+ let heightPt = this.rowHeightPt(index);
76
+ while (boundaryPt + heightPt <= yPt) {
77
+ boundaryPt += heightPt;
78
+ index += 1;
79
+ heightPt = this.rowHeightPt(index);
80
+ }
81
+ return {
82
+ index,
83
+ offsetPt: yPt - boundaryPt
84
+ };
85
+ }
58
86
  };
59
87
  function readAnchorChild(marker, tag) {
60
88
  const child = require_typed_util.childrenWithTag(marker, tag)[0];
@@ -62,6 +90,11 @@ function readAnchorChild(marker, tag) {
62
90
  const parsed = text === void 0 || text === "" ? NaN : Number(text);
63
91
  return Number.isFinite(parsed) ? parsed : 0;
64
92
  }
93
+ function numericAttr(element, name) {
94
+ const raw = require_typed_util.attr(element, name);
95
+ const parsed = raw === void 0 ? NaN : Number(raw);
96
+ return Number.isFinite(parsed) ? parsed : 0;
97
+ }
65
98
  function readAnchorMarker(parent, tag) {
66
99
  const marker = require_typed_util.childrenWithTag(parent, tag)[0];
67
100
  if (marker === void 0) return;
@@ -72,6 +105,65 @@ function readAnchorMarker(parent, tag) {
72
105
  rowOffEmu: readAnchorChild(marker, "xdr:rowOff")
73
106
  };
74
107
  }
108
+ function readAnchorExtEmu(anchor) {
109
+ const ext = require_typed_util.childrenWithTag(anchor, "xdr:ext")[0];
110
+ if (ext === void 0) return;
111
+ return {
112
+ cxEmu: numericAttr(ext, "cx"),
113
+ cyEmu: numericAttr(ext, "cy")
114
+ };
115
+ }
116
+ function readAnchorPlacement(anchor, geometry) {
117
+ if (anchor.tag === "xdr:twoCellAnchor") {
118
+ const from = readAnchorMarker(anchor, "xdr:from");
119
+ const to = readAnchorMarker(anchor, "xdr:to");
120
+ if (from === void 0 || to === void 0) return;
121
+ const xPt = geometry.xPt(from.column, from.colOffEmu);
122
+ const yPt = geometry.yPt(from.row, from.rowOffEmu);
123
+ return {
124
+ xPt,
125
+ yPt,
126
+ widthPt: geometry.xPt(to.column, to.colOffEmu) - xPt,
127
+ heightPt: geometry.yPt(to.row, to.rowOffEmu) - yPt,
128
+ anchorRow: from.row,
129
+ anchorColumn: from.column,
130
+ offsetXPt: require_typed_shared_units.emuToPt(from.colOffEmu),
131
+ offsetYPt: require_typed_shared_units.emuToPt(from.rowOffEmu)
132
+ };
133
+ }
134
+ if (anchor.tag === "xdr:oneCellAnchor") {
135
+ const from = readAnchorMarker(anchor, "xdr:from");
136
+ const ext = readAnchorExtEmu(anchor);
137
+ if (from === void 0 || ext === void 0) return;
138
+ return {
139
+ xPt: geometry.xPt(from.column, from.colOffEmu),
140
+ yPt: geometry.yPt(from.row, from.rowOffEmu),
141
+ widthPt: require_typed_shared_units.emuToPt(ext.cxEmu),
142
+ heightPt: require_typed_shared_units.emuToPt(ext.cyEmu),
143
+ anchorRow: from.row,
144
+ anchorColumn: from.column,
145
+ offsetXPt: require_typed_shared_units.emuToPt(from.colOffEmu),
146
+ offsetYPt: require_typed_shared_units.emuToPt(from.rowOffEmu)
147
+ };
148
+ }
149
+ const pos = require_typed_util.childrenWithTag(anchor, "xdr:pos")[0];
150
+ const ext = readAnchorExtEmu(anchor);
151
+ if (pos === void 0 || ext === void 0) return;
152
+ const xPt = require_typed_shared_units.emuToPt(numericAttr(pos, "x"));
153
+ const yPt = require_typed_shared_units.emuToPt(numericAttr(pos, "y"));
154
+ const column = geometry.locateColumn(xPt);
155
+ const row = geometry.locateRow(yPt);
156
+ return {
157
+ xPt,
158
+ yPt,
159
+ widthPt: require_typed_shared_units.emuToPt(ext.cxEmu),
160
+ heightPt: require_typed_shared_units.emuToPt(ext.cyEmu),
161
+ anchorRow: row.index,
162
+ anchorColumn: column.index,
163
+ offsetXPt: column.offsetPt,
164
+ offsetYPt: row.offsetPt
165
+ };
166
+ }
75
167
  function emptyWorksheet() {
76
168
  return {
77
169
  type: "element",
@@ -100,6 +192,11 @@ function chartCells(chartRoot, frame) {
100
192
  });
101
193
  return cells;
102
194
  }
195
+ const ANCHOR_TAGS = [
196
+ "xdr:twoCellAnchor",
197
+ "xdr:oneCellAnchor",
198
+ "xdr:absoluteAnchor"
199
+ ];
103
200
  function readSheetDrawing(pkg, worksheetPath, worksheet) {
104
201
  let drawingPath;
105
202
  for (const rel of require_typed_util.resolveRelationships(pkg, worksheetPath).values()) if (rel.type.endsWith(DRAWING_REL_SUFFIX)) {
@@ -119,45 +216,43 @@ function readSheetDrawing(pkg, worksheetPath, worksheet) {
119
216
  const geometry = new SheetGridGeometry(worksheet);
120
217
  const objects = [];
121
218
  const images = [];
122
- for (const anchor of require_typed_util.childrenWithTag(drawingRoot, "xdr:twoCellAnchor")) {
123
- const from = readAnchorMarker(anchor, "xdr:from");
124
- const to = readAnchorMarker(anchor, "xdr:to");
125
- if (from === void 0 || to === void 0) continue;
126
- const xPt = geometry.xPt(from.column, from.colOffEmu);
127
- const yPt = geometry.yPt(from.row, from.rowOffEmu);
219
+ for (const node of drawingRoot.children) {
220
+ if (node.type !== "element" || !ANCHOR_TAGS.includes(node.tag)) continue;
221
+ const placement = readAnchorPlacement(node, geometry);
222
+ if (placement === void 0) continue;
128
223
  const frameBox = {
129
- xPt,
130
- yPt,
131
- widthPt: geometry.xPt(to.column, to.colOffEmu) - xPt,
132
- heightPt: geometry.yPt(to.row, to.rowOffEmu) - yPt
224
+ xPt: placement.xPt,
225
+ yPt: placement.yPt,
226
+ widthPt: placement.widthPt,
227
+ heightPt: placement.heightPt
133
228
  };
134
- for (const frame of require_typed_util.elementsWithTag([anchor], "xdr:graphicFrame")) {
229
+ for (const frame of require_typed_util.elementsWithTag([node], "xdr:graphicFrame")) {
135
230
  const chart = readChartFrame(pkg, frame, drawingRels);
136
231
  if (chart === void 0) continue;
137
232
  objects.push({
138
233
  objectKind: "chart",
139
234
  document: chartDocument(pkg, chart.root, frameBox, chart.name),
140
235
  frame: frameBox,
141
- anchorRow: from.row,
142
- anchorColumn: from.column,
143
- offsetXPt: require_typed_shared_units.emuToPt(from.colOffEmu),
144
- offsetYPt: require_typed_shared_units.emuToPt(from.rowOffEmu)
236
+ anchorRow: placement.anchorRow,
237
+ anchorColumn: placement.anchorColumn,
238
+ offsetXPt: placement.offsetXPt,
239
+ offsetYPt: placement.offsetYPt
145
240
  });
146
241
  }
147
- for (const pic of require_typed_util.elementsWithTag([anchor], "xdr:pic")) {
242
+ for (const pic of require_typed_util.elementsWithTag([node], "xdr:pic")) {
148
243
  const media = readPictureMedia(pkg, pic, drawingRels);
149
244
  if (media === void 0) continue;
150
- if (frameBox.widthPt <= 0 || frameBox.heightPt <= 0) continue;
245
+ if (placement.widthPt <= 0 || placement.heightPt <= 0) continue;
151
246
  images.push({
152
247
  kind: "image",
153
248
  format: media.format,
154
249
  base64: media.base64,
155
- widthPt: frameBox.widthPt,
156
- heightPt: frameBox.heightPt,
157
- anchorRow: from.row,
158
- anchorColumn: from.column,
159
- offsetXPt: require_typed_shared_units.emuToPt(from.colOffEmu),
160
- offsetYPt: require_typed_shared_units.emuToPt(from.rowOffEmu)
250
+ widthPt: placement.widthPt,
251
+ heightPt: placement.heightPt,
252
+ anchorRow: placement.anchorRow,
253
+ anchorColumn: placement.anchorColumn,
254
+ offsetXPt: placement.offsetXPt,
255
+ offsetYPt: placement.offsetYPt
161
256
  });
162
257
  }
163
258
  }
@@ -54,6 +54,34 @@ var SheetGridGeometry = class {
54
54
  for (let index = 0; index < row; index++) yPt += this.rowHeightPt(index);
55
55
  return yPt;
56
56
  }
57
+ locateColumn(xPt) {
58
+ let index = 0;
59
+ let boundaryPt = 0;
60
+ let widthPt = this.columnWidthPt(index);
61
+ while (boundaryPt + widthPt <= xPt) {
62
+ boundaryPt += widthPt;
63
+ index += 1;
64
+ widthPt = this.columnWidthPt(index);
65
+ }
66
+ return {
67
+ index,
68
+ offsetPt: xPt - boundaryPt
69
+ };
70
+ }
71
+ locateRow(yPt) {
72
+ let index = 0;
73
+ let boundaryPt = 0;
74
+ let heightPt = this.rowHeightPt(index);
75
+ while (boundaryPt + heightPt <= yPt) {
76
+ boundaryPt += heightPt;
77
+ index += 1;
78
+ heightPt = this.rowHeightPt(index);
79
+ }
80
+ return {
81
+ index,
82
+ offsetPt: yPt - boundaryPt
83
+ };
84
+ }
57
85
  };
58
86
  function readAnchorChild(marker, tag) {
59
87
  const child = childrenWithTag(marker, tag)[0];
@@ -61,6 +89,11 @@ function readAnchorChild(marker, tag) {
61
89
  const parsed = text === void 0 || text === "" ? NaN : Number(text);
62
90
  return Number.isFinite(parsed) ? parsed : 0;
63
91
  }
92
+ function numericAttr(element, name) {
93
+ const raw = attr(element, name);
94
+ const parsed = raw === void 0 ? NaN : Number(raw);
95
+ return Number.isFinite(parsed) ? parsed : 0;
96
+ }
64
97
  function readAnchorMarker(parent, tag) {
65
98
  const marker = childrenWithTag(parent, tag)[0];
66
99
  if (marker === void 0) return;
@@ -71,6 +104,65 @@ function readAnchorMarker(parent, tag) {
71
104
  rowOffEmu: readAnchorChild(marker, "xdr:rowOff")
72
105
  };
73
106
  }
107
+ function readAnchorExtEmu(anchor) {
108
+ const ext = childrenWithTag(anchor, "xdr:ext")[0];
109
+ if (ext === void 0) return;
110
+ return {
111
+ cxEmu: numericAttr(ext, "cx"),
112
+ cyEmu: numericAttr(ext, "cy")
113
+ };
114
+ }
115
+ function readAnchorPlacement(anchor, geometry) {
116
+ if (anchor.tag === "xdr:twoCellAnchor") {
117
+ const from = readAnchorMarker(anchor, "xdr:from");
118
+ const to = readAnchorMarker(anchor, "xdr:to");
119
+ if (from === void 0 || to === void 0) return;
120
+ const xPt = geometry.xPt(from.column, from.colOffEmu);
121
+ const yPt = geometry.yPt(from.row, from.rowOffEmu);
122
+ return {
123
+ xPt,
124
+ yPt,
125
+ widthPt: geometry.xPt(to.column, to.colOffEmu) - xPt,
126
+ heightPt: geometry.yPt(to.row, to.rowOffEmu) - yPt,
127
+ anchorRow: from.row,
128
+ anchorColumn: from.column,
129
+ offsetXPt: emuToPt(from.colOffEmu),
130
+ offsetYPt: emuToPt(from.rowOffEmu)
131
+ };
132
+ }
133
+ if (anchor.tag === "xdr:oneCellAnchor") {
134
+ const from = readAnchorMarker(anchor, "xdr:from");
135
+ const ext = readAnchorExtEmu(anchor);
136
+ if (from === void 0 || ext === void 0) return;
137
+ return {
138
+ xPt: geometry.xPt(from.column, from.colOffEmu),
139
+ yPt: geometry.yPt(from.row, from.rowOffEmu),
140
+ widthPt: emuToPt(ext.cxEmu),
141
+ heightPt: emuToPt(ext.cyEmu),
142
+ anchorRow: from.row,
143
+ anchorColumn: from.column,
144
+ offsetXPt: emuToPt(from.colOffEmu),
145
+ offsetYPt: emuToPt(from.rowOffEmu)
146
+ };
147
+ }
148
+ const pos = childrenWithTag(anchor, "xdr:pos")[0];
149
+ const ext = readAnchorExtEmu(anchor);
150
+ if (pos === void 0 || ext === void 0) return;
151
+ const xPt = emuToPt(numericAttr(pos, "x"));
152
+ const yPt = emuToPt(numericAttr(pos, "y"));
153
+ const column = geometry.locateColumn(xPt);
154
+ const row = geometry.locateRow(yPt);
155
+ return {
156
+ xPt,
157
+ yPt,
158
+ widthPt: emuToPt(ext.cxEmu),
159
+ heightPt: emuToPt(ext.cyEmu),
160
+ anchorRow: row.index,
161
+ anchorColumn: column.index,
162
+ offsetXPt: column.offsetPt,
163
+ offsetYPt: row.offsetPt
164
+ };
165
+ }
74
166
  function emptyWorksheet() {
75
167
  return {
76
168
  type: "element",
@@ -99,6 +191,11 @@ function chartCells(chartRoot, frame) {
99
191
  });
100
192
  return cells;
101
193
  }
194
+ const ANCHOR_TAGS = [
195
+ "xdr:twoCellAnchor",
196
+ "xdr:oneCellAnchor",
197
+ "xdr:absoluteAnchor"
198
+ ];
102
199
  function readSheetDrawing(pkg, worksheetPath, worksheet) {
103
200
  let drawingPath;
104
201
  for (const rel of resolveRelationships(pkg, worksheetPath).values()) if (rel.type.endsWith(DRAWING_REL_SUFFIX)) {
@@ -118,45 +215,43 @@ function readSheetDrawing(pkg, worksheetPath, worksheet) {
118
215
  const geometry = new SheetGridGeometry(worksheet);
119
216
  const objects = [];
120
217
  const images = [];
121
- for (const anchor of childrenWithTag(drawingRoot, "xdr:twoCellAnchor")) {
122
- const from = readAnchorMarker(anchor, "xdr:from");
123
- const to = readAnchorMarker(anchor, "xdr:to");
124
- if (from === void 0 || to === void 0) continue;
125
- const xPt = geometry.xPt(from.column, from.colOffEmu);
126
- const yPt = geometry.yPt(from.row, from.rowOffEmu);
218
+ for (const node of drawingRoot.children) {
219
+ if (node.type !== "element" || !ANCHOR_TAGS.includes(node.tag)) continue;
220
+ const placement = readAnchorPlacement(node, geometry);
221
+ if (placement === void 0) continue;
127
222
  const frameBox = {
128
- xPt,
129
- yPt,
130
- widthPt: geometry.xPt(to.column, to.colOffEmu) - xPt,
131
- heightPt: geometry.yPt(to.row, to.rowOffEmu) - yPt
223
+ xPt: placement.xPt,
224
+ yPt: placement.yPt,
225
+ widthPt: placement.widthPt,
226
+ heightPt: placement.heightPt
132
227
  };
133
- for (const frame of elementsWithTag([anchor], "xdr:graphicFrame")) {
228
+ for (const frame of elementsWithTag([node], "xdr:graphicFrame")) {
134
229
  const chart = readChartFrame(pkg, frame, drawingRels);
135
230
  if (chart === void 0) continue;
136
231
  objects.push({
137
232
  objectKind: "chart",
138
233
  document: chartDocument(pkg, chart.root, frameBox, chart.name),
139
234
  frame: frameBox,
140
- anchorRow: from.row,
141
- anchorColumn: from.column,
142
- offsetXPt: emuToPt(from.colOffEmu),
143
- offsetYPt: emuToPt(from.rowOffEmu)
235
+ anchorRow: placement.anchorRow,
236
+ anchorColumn: placement.anchorColumn,
237
+ offsetXPt: placement.offsetXPt,
238
+ offsetYPt: placement.offsetYPt
144
239
  });
145
240
  }
146
- for (const pic of elementsWithTag([anchor], "xdr:pic")) {
241
+ for (const pic of elementsWithTag([node], "xdr:pic")) {
147
242
  const media = readPictureMedia(pkg, pic, drawingRels);
148
243
  if (media === void 0) continue;
149
- if (frameBox.widthPt <= 0 || frameBox.heightPt <= 0) continue;
244
+ if (placement.widthPt <= 0 || placement.heightPt <= 0) continue;
150
245
  images.push({
151
246
  kind: "image",
152
247
  format: media.format,
153
248
  base64: media.base64,
154
- widthPt: frameBox.widthPt,
155
- heightPt: frameBox.heightPt,
156
- anchorRow: from.row,
157
- anchorColumn: from.column,
158
- offsetXPt: emuToPt(from.colOffEmu),
159
- offsetYPt: emuToPt(from.rowOffEmu)
249
+ widthPt: placement.widthPt,
250
+ heightPt: placement.heightPt,
251
+ anchorRow: placement.anchorRow,
252
+ anchorColumn: placement.anchorColumn,
253
+ offsetXPt: placement.offsetXPt,
254
+ offsetYPt: placement.offsetYPt
160
255
  });
161
256
  }
162
257
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ooxml.js",
3
- "version": "4.6.0",
3
+ "version": "4.7.0",
4
4
  "description": "Type-safe, lossless round-trip conversion between OOXML packages (docx, pptx, xlsx) and JSON, built on Zod 4 codecs.",
5
5
  "type": "module",
6
6
  "repository": {