@office-kit/xlsx 0.15.0 → 0.17.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 +29 -8
- package/dist/cell/cell.d.ts +12 -19
- package/dist/{cell-Df3cKRIl.mjs → cell-B5OUUdfI.mjs} +13 -7
- package/dist/cell-B5OUUdfI.mjs.map +1 -0
- package/dist/{cell-style-CNsET6WU.mjs → cell-style-DjKKPWDL.mjs} +2 -2
- package/dist/{cell-style-CNsET6WU.mjs.map → cell-style-DjKKPWDL.mjs.map} +1 -1
- package/dist/cell.mjs +1 -1
- package/dist/{coordinate-Bt5Gni2I.mjs → coordinate-CxC88uBk.mjs} +31 -2
- package/dist/coordinate-CxC88uBk.mjs.map +1 -0
- package/dist/{defined-names-Ctu3F6ls.mjs → defined-names-Cmp8rbvI.mjs} +2 -2
- package/dist/{defined-names-Ctu3F6ls.mjs.map → defined-names-Cmp8rbvI.mjs.map} +1 -1
- package/dist/{differential-BPKa7wXi.mjs → differential-7KGzbliB.mjs} +2 -2
- package/dist/{differential-BPKa7wXi.mjs.map → differential-7KGzbliB.mjs.map} +1 -1
- package/dist/{drawing-ZJ3h4VHD.mjs → drawing-Cv6UtInY.mjs} +2 -2
- package/dist/{drawing-ZJ3h4VHD.mjs.map → drawing-Cv6UtInY.mjs.map} +1 -1
- package/dist/drawing.mjs +1 -1
- package/dist/{escape-rnWSPpWY.mjs → escape-rA5UpibT.mjs} +26 -3
- package/dist/escape-rA5UpibT.mjs.map +1 -0
- package/dist/io/load.d.ts +28 -0
- package/dist/io.mjs +2 -2
- package/dist/{load-CspGc399.mjs → load-DIdQ_zum.mjs} +66 -39
- package/dist/{load-CspGc399.mjs.map → load-DIdQ_zum.mjs.map} +1 -1
- package/dist/{manifest-pLmx7KYh.mjs → manifest-BHUYpjUG.mjs} +3 -3
- package/dist/{manifest-pLmx7KYh.mjs.map → manifest-BHUYpjUG.mjs.map} +1 -1
- package/dist/node.mjs +1 -1
- package/dist/packaging.mjs +1 -1
- package/dist/{reader-DHMxLBQV.mjs → reader-pC7bxYAs.mjs} +30 -7
- package/dist/reader-pC7bxYAs.mjs.map +1 -0
- package/dist/{save-DgPf1DyF.mjs → save-ax11Gsw0.mjs} +7 -7
- package/dist/{save-DgPf1DyF.mjs.map → save-ax11Gsw0.mjs.map} +1 -1
- package/dist/schema.mjs +1 -1
- package/dist/{serialize-BC2Wu3bR.mjs → serialize-Dmb6Vtt9.mjs} +23 -6
- package/dist/serialize-Dmb6Vtt9.mjs.map +1 -0
- package/dist/{serializer-C6nm0mRJ.mjs → serializer-dhv07Z6F.mjs} +2 -2
- package/dist/{serializer-C6nm0mRJ.mjs.map → serializer-dhv07Z6F.mjs.map} +1 -1
- package/dist/streaming/string-table.d.ts +7 -0
- package/dist/streaming.mjs +189 -83
- package/dist/streaming.mjs.map +1 -1
- package/dist/styles.mjs +3 -3
- package/dist/{stylesheet-writer-TxsinwbO.mjs → stylesheet-writer-BBYjNSMA.mjs} +201 -118
- package/dist/stylesheet-writer-BBYjNSMA.mjs.map +1 -0
- package/dist/{table-BrP0RwUd.mjs → table-BFi2WmgG.mjs} +2 -2
- package/dist/{table-BrP0RwUd.mjs.map → table-BFi2WmgG.mjs.map} +1 -1
- package/dist/utils/cell-number.d.ts +10 -0
- package/dist/utils/coordinate.d.ts +11 -0
- package/dist/utils/escape.d.ts +17 -0
- package/dist/utils/xsd-boolean.d.ts +9 -0
- package/dist/utils.mjs +2 -2
- package/dist/workbook/shared-strings.d.ts +2 -0
- package/dist/{workbook-DQCslHzY.mjs → workbook-CMel6UcI.mjs} +13 -12
- package/dist/workbook-CMel6UcI.mjs.map +1 -0
- package/dist/workbook.mjs +2 -2
- package/dist/worksheet/writer.d.ts +9 -5
- package/dist/{worksheet-B2NOQ6dM.mjs → worksheet-Cxn2c-jz.mjs} +3 -3
- package/dist/{worksheet-B2NOQ6dM.mjs.map → worksheet-Cxn2c-jz.mjs.map} +1 -1
- package/dist/worksheet.mjs +2 -2
- package/dist/xml.mjs +2 -2
- package/dist/zip.mjs +1 -1
- package/package.json +1 -1
- package/dist/cell-Df3cKRIl.mjs.map +0 -1
- package/dist/coordinate-Bt5Gni2I.mjs.map +0 -1
- package/dist/escape-rnWSPpWY.mjs.map +0 -1
- package/dist/reader-DHMxLBQV.mjs.map +0 -1
- package/dist/serialize-BC2Wu3bR.mjs.map +0 -1
- package/dist/stylesheet-writer-TxsinwbO.mjs.map +0 -1
- package/dist/workbook-DQCslHzY.mjs.map +0 -1
package/dist/cell.mjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { A as setSharedFormula, C as makeErrorValue, D as setCellValue, E as setArrayFormula, M as makeTextRun, N as richTextToString, O as setDataTableFormula, S as makeDurationValue, T as makeSharedFormula, _ as isRichTextCell, a as cellValueAsPrimitive, b as makeCell, c as getCoordinate, d as isEmptyCell, f as isErrorCell, g as isMergedCell, h as isFormulaValue, i as cellValueAsNumber, j as makeRichText, k as setFormula, l as getFormulaText, m as isFormulaCell, n as cellValueAsBoolean, o as cellValueAsString, p as isErrorValue, r as cellValueAsDate, s as getCachedFormulaValue, t as bindValue, u as isDurationValue, v as isRichTextValue, w as makeFormula, x as makeDataTableFormula, y as makeArrayFormula } from "./cell-
|
|
1
|
+
import { A as setSharedFormula, C as makeErrorValue, D as setCellValue, E as setArrayFormula, M as makeTextRun, N as richTextToString, O as setDataTableFormula, S as makeDurationValue, T as makeSharedFormula, _ as isRichTextCell, a as cellValueAsPrimitive, b as makeCell, c as getCoordinate, d as isEmptyCell, f as isErrorCell, g as isMergedCell, h as isFormulaValue, i as cellValueAsNumber, j as makeRichText, k as setFormula, l as getFormulaText, m as isFormulaCell, n as cellValueAsBoolean, o as cellValueAsString, p as isErrorValue, r as cellValueAsDate, s as getCachedFormulaValue, t as bindValue, u as isDurationValue, v as isRichTextValue, w as makeFormula, x as makeDataTableFormula, y as makeArrayFormula } from "./cell-B5OUUdfI.mjs";
|
|
2
2
|
export { bindValue, cellValueAsBoolean, cellValueAsDate, cellValueAsNumber, cellValueAsPrimitive, cellValueAsString, getCachedFormulaValue, getCoordinate, getFormulaText, isDurationValue, isEmptyCell, isErrorCell, isErrorValue, isFormulaCell, isFormulaValue, isMergedCell, isRichTextCell, isRichTextValue, makeArrayFormula, makeCell, makeDataTableFormula, makeDurationValue, makeErrorValue, makeFormula, makeRichText, makeSharedFormula, makeTextRun, richTextToString, setArrayFormula, setCellValue, setDataTableFormula, setFormula, setSharedFormula };
|
|
@@ -176,6 +176,35 @@ function isValidColumnLetter(s) {
|
|
|
176
176
|
function isValidRowNumber(n) {
|
|
177
177
|
return typeof n === "number" && Number.isInteger(n) && n >= 1 && n <= 1048576;
|
|
178
178
|
}
|
|
179
|
+
const ROW_ATTR_VALUE = /^[ \t\r\n]*\+?([0-9]+)[ \t\r\n]*$/;
|
|
180
|
+
/**
|
|
181
|
+
* Parse a `<row r="…">` value into a row number, or `undefined` when it is not
|
|
182
|
+
* a row number in `[1, MAX_ROW]`.
|
|
183
|
+
*/
|
|
184
|
+
function parseRowNumberAttr(raw) {
|
|
185
|
+
const digits = ROW_ATTR_VALUE.exec(raw)?.[1];
|
|
186
|
+
if (digits === void 0) return void 0;
|
|
187
|
+
const n = Number(digits);
|
|
188
|
+
return isValidRowNumber(n) ? n : void 0;
|
|
189
|
+
}
|
|
190
|
+
/**
|
|
191
|
+
* Row number carried by a `<row>`'s `@r`. A malformed or out-of-range value
|
|
192
|
+
* throws: renumbering it would shift every row after it, and the file is
|
|
193
|
+
* already one no reader can agree on.
|
|
194
|
+
*/
|
|
195
|
+
function rowNumberFromAttr(raw, where) {
|
|
196
|
+
const n = parseRowNumberAttr(raw);
|
|
197
|
+
if (n === void 0) throw new OpenXmlSchemaError(`${where}: <row r="${raw}"> is not a row number in [1, ${MAX_ROW}]`);
|
|
198
|
+
return n;
|
|
199
|
+
}
|
|
200
|
+
/**
|
|
201
|
+
* Row number for a `<row>` that omits `@r` and holds no cell to locate it:
|
|
202
|
+
* `fallback`, the slot after the highest row read so far.
|
|
203
|
+
*/
|
|
204
|
+
function derivedRowNumber(fallback, where) {
|
|
205
|
+
if (!isValidRowNumber(fallback)) throw new OpenXmlSchemaError(`${where}: <row> without @r falls past the last row (${MAX_ROW})`);
|
|
206
|
+
return fallback;
|
|
207
|
+
}
|
|
179
208
|
/**
|
|
180
209
|
* Predicate: true iff `n` is a valid 1-based column index in `[1, 16384]`.
|
|
181
210
|
* Non-finite / non-integer / out-of-bound fails.
|
|
@@ -282,6 +311,6 @@ function formatSheetQualifiedRef(sheet, ref) {
|
|
|
282
311
|
return `'${sheet.replace(/'/g, "''")}'!${ref}`;
|
|
283
312
|
}
|
|
284
313
|
//#endregion
|
|
285
|
-
export { columnLetterFromIndex as a,
|
|
314
|
+
export { rowNumberFromAttr as _, columnLetterFromIndex as a, derivedRowNumber as c, isValidColumnLetter as d, isValidColumnNumber as f, rangeBoundaries as g, parseSheetRange as h, columnIndexFromLetter as i, formatSheetQualifiedRef as l, isValidRowNumber as m, MAX_ROW as n, coordinateFromString as o, isValidRangeRef as p, boundariesToRangeString as r, coordinateToTuple as s, MAX_COL as t, isValidCellRef as u, tupleToCoordinate as v };
|
|
286
315
|
|
|
287
|
-
//# sourceMappingURL=coordinate-
|
|
316
|
+
//# sourceMappingURL=coordinate-CxC88uBk.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"coordinate-CxC88uBk.mjs","names":[],"sources":["../src/utils/coordinate.ts"],"sourcesContent":["// Worksheet coordinate utilities. Mirrors openpyxl/openpyxl/utils/cell.py.\n//\n// These functions are on the worksheet read/write hot path (millions of calls\n// when streaming a sheet) so the implementations stay branch-light, regex-based\n// only at the entry points, with bounded Map caches for the bidirectional\n// column letter <-> index mapping.\n\nimport { OpenXmlSchemaError } from './exceptions.js';\n\n/** Maximum column index Excel accepts (XFD). */\nexport const MAX_COL = 16384;\n/** Maximum row index Excel accepts. */\nexport const MAX_ROW = 1048576;\n\n// ---- column letter <-> 1-based index ---------------------------------------\n\nconst indexByLetter = new Map<string, number>();\nconst letterByIndex = new Map<number, string>();\n\n/**\n * 1-based column index → spreadsheet column letter (\"A\", \"Z\", \"AA\", \"XFD\").\n * Throws OpenXmlSchemaError when out of range.\n */\nexport function columnLetterFromIndex(n: number): string {\n const cached = letterByIndex.get(n);\n if (cached !== undefined) return cached;\n if (!Number.isInteger(n) || n < 1 || n > MAX_COL) {\n throw new OpenXmlSchemaError(`column index ${n} is out of range [1, ${MAX_COL}]`);\n }\n let m = n;\n let out = '';\n while (m > 0) {\n m -= 1; // shift to 0-based for the modulo\n out = String.fromCharCode(65 + (m % 26)) + out;\n m = Math.floor(m / 26);\n }\n letterByIndex.set(n, out);\n return out;\n}\n\n/**\n * Column letter → 1-based column index. Case-insensitive but at most 3 letters\n * (the spec ceiling). Throws on empty / non-A-Z / over-range.\n */\nexport function columnIndexFromLetter(letter: string): number {\n const cached = indexByLetter.get(letter);\n if (cached !== undefined) return cached;\n if (letter.length === 0 || letter.length > 3) {\n throw new OpenXmlSchemaError(`column letter \"${letter}\" is empty or too long`);\n }\n let n = 0;\n for (let i = 0; i < letter.length; i++) {\n const c = letter.charCodeAt(i);\n let v: number;\n if (c >= 65 && c <= 90)\n v = c - 64; // 'A' = 65\n else if (c >= 97 && c <= 122)\n v = c - 96; // 'a' = 97\n else throw new OpenXmlSchemaError(`column letter \"${letter}\" contains non-letter char`);\n n = n * 26 + v;\n }\n if (n < 1 || n > MAX_COL) {\n throw new OpenXmlSchemaError(`column letter \"${letter}\" expands to out-of-range index ${n}`);\n }\n // Normalise the cache key to upper-case so 'a' / 'A' share a slot.\n const key = letter.toUpperCase();\n indexByLetter.set(key, n);\n if (key !== letter) indexByLetter.set(letter, n);\n return n;\n}\n\n// ---- coordinate parsing ----------------------------------------------------\n\n/** A single-cell coordinate split into its letter and 1-based row. */\nexport interface CellCoordinate {\n column: string;\n row: number;\n}\n\n/** A single-cell coordinate split into 1-based numeric (col, row). */\nexport interface CellCoordinateNumeric {\n col: number;\n row: number;\n}\n\n/** A 1-based rectangular boundary. */\nexport interface CellRangeBoundaries {\n minCol: number;\n minRow: number;\n maxCol: number;\n maxRow: number;\n}\n\n/**\n * What every range-taking helper accepts: an A1 expression (`\"A4:H20\"`,\n * `\"A:A\"`, `\"1:1\"`) or already-computed 1-based bounds. Code that walks rows\n * and columns as integers passes the bounds directly instead of formatting a\n * string for the callee to parse straight back.\n *\n * Two spellings of one parameter, deliberately, rather than a second function\n * per helper: each capability keeps a single canonical path through the API.\n */\nexport type RangeRef = string | CellRangeBoundaries;\n\nconst COORD_RE = /^[$]?([A-Za-z]{1,3})[$]?([1-9][0-9]*)$/;\nconst COL_RANGE_RE = /^[$]?([A-Za-z]{1,3}):[$]?([A-Za-z]{1,3})$/;\nconst ROW_RANGE_RE = /^[$]?([1-9][0-9]*):[$]?([1-9][0-9]*)$/;\nconst SHEET_RANGE_RE = /^(?:'((?:[^']|'')+)'|([^'!]+))!(.+)$/;\n\n/**\n * Parse a single-cell coordinate string (\"A1\", \"$XFD$1048576\") into its column\n * letter (always uppercased) and 1-based row.\n */\nexport function coordinateFromString(coord: string): CellCoordinate {\n const m = COORD_RE.exec(coord);\n if (m === null || m[1] === undefined || m[2] === undefined) {\n throw new OpenXmlSchemaError(`coordinateFromString: invalid coordinate \"${coord}\"`);\n }\n const column = m[1].toUpperCase();\n const row = Number.parseInt(m[2], 10);\n if (row < 1 || row > MAX_ROW) {\n throw new OpenXmlSchemaError(`coordinateFromString: row ${row} out of range`);\n }\n // Validate column upper bound through the cached helper.\n columnIndexFromLetter(column);\n return { column, row };\n}\n\n/**\n * Same as {@link coordinateFromString} but returning the column as its 1-based\n * numeric index. Thin convenience for the worksheet read path.\n */\nexport function coordinateToTuple(coord: string): CellCoordinateNumeric {\n const c = coordinateFromString(coord);\n return { col: columnIndexFromLetter(c.column), row: c.row };\n}\n\n/** `$` placement for a composed A1 reference. */\nexport interface AbsoluteRefOptions {\n /** Prefix the column letter with `$` so it survives a fill-across. */\n absoluteCol?: boolean;\n /** Prefix the row number with `$` so it survives a fill-down. */\n absoluteRow?: boolean;\n}\n\n/**\n * Compose `\"A1\"` from a 1-based (col, row). Set `absoluteCol` / `absoluteRow`\n * to get the `$` forms formula text needs (`\"$B$5\"`, `\"B$5\"`), so callers\n * tracking integer coordinates never have to concatenate a column letter\n * themselves.\n */\nexport function tupleToCoordinate(col: number, row: number, opts: AbsoluteRefOptions = {}): string {\n if (!Number.isInteger(row) || row < 1 || row > MAX_ROW) {\n throw new OpenXmlSchemaError(`tupleToCoordinate: row ${row} out of range`);\n }\n const colMarker = opts.absoluteCol === true ? '$' : '';\n const rowMarker = opts.absoluteRow === true ? '$' : '';\n return `${colMarker}${columnLetterFromIndex(col)}${rowMarker}${row}`;\n}\n\n/**\n * Predicate: true iff `s` is a valid single-cell A1 coordinate (`\"A1\"`,\n * `\"XFD1048576\"`). Strings with `$` absolute markers, surrounding whitespace,\n * ranges (`A1:B2`), or out-of-bound row / column return false. Useful for\n * sanitising user input before passing to {@link coordinateToTuple} or\n * `setCellByCoord`.\n */\nexport function isValidCellRef(s: unknown): s is string {\n if (typeof s !== 'string') return false;\n const m = /^([A-Za-z]{1,3})([1-9][0-9]*)$/.exec(s);\n if (!m) return false;\n const colStr = m[1];\n const rowStr = m[2];\n if (colStr === undefined || rowStr === undefined) return false;\n const col = columnIndexFromLetterUnchecked(colStr);\n if (col < 1 || col > MAX_COL) return false;\n const row = Number.parseInt(rowStr, 10);\n if (!Number.isFinite(row) || row < 1 || row > MAX_ROW) return false;\n return true;\n}\n\n/**\n * Predicate: true iff `s` is a valid A1-style range expression — single cell,\n * two-corner range, whole column (`A:A`), or whole row (`1:1`). `$` markers,\n * whitespace, and out-of-bound bounds fail. Sanity-check before {@link\n * rangeBoundaries} / `parseRange`.\n */\nexport function isValidRangeRef(s: unknown): s is string {\n if (typeof s !== 'string' || s.length === 0) return false;\n if (isValidCellRef(s)) return true;\n const ab = /^([A-Za-z]{1,3})([1-9][0-9]*):([A-Za-z]{1,3})([1-9][0-9]*)$/.exec(s);\n if (ab) {\n const [, c1, r1, c2, r2] = ab;\n if (!c1 || !r1 || !c2 || !r2) return false;\n const col1 = columnIndexFromLetterUnchecked(c1);\n const col2 = columnIndexFromLetterUnchecked(c2);\n if (col1 < 1 || col1 > MAX_COL || col2 < 1 || col2 > MAX_COL) return false;\n const row1 = Number.parseInt(r1, 10);\n const row2 = Number.parseInt(r2, 10);\n return row1 >= 1 && row1 <= MAX_ROW && row2 >= 1 && row2 <= MAX_ROW;\n }\n const cc = /^([A-Za-z]{1,3}):([A-Za-z]{1,3})$/.exec(s);\n if (cc) {\n const [, c1, c2] = cc;\n if (!c1 || !c2) return false;\n const col1 = columnIndexFromLetterUnchecked(c1);\n const col2 = columnIndexFromLetterUnchecked(c2);\n return col1 >= 1 && col1 <= MAX_COL && col2 >= 1 && col2 <= MAX_COL;\n }\n const rr = /^([1-9][0-9]*):([1-9][0-9]*)$/.exec(s);\n if (rr) {\n const [, r1, r2] = rr;\n if (!r1 || !r2) return false;\n const row1 = Number.parseInt(r1, 10);\n const row2 = Number.parseInt(r2, 10);\n return row1 >= 1 && row1 <= MAX_ROW && row2 >= 1 && row2 <= MAX_ROW;\n }\n return false;\n}\n\nconst columnIndexFromLetterUnchecked = (letters: string): number => {\n let n = 0;\n const upper = letters.toUpperCase();\n for (let i = 0; i < upper.length; i++) {\n const ch = upper.charCodeAt(i);\n if (ch < 65 || ch > 90) return -1;\n n = n * 26 + (ch - 64);\n }\n return n;\n};\n\n/**\n * Predicate: true iff `s` is a valid 1..3-char column letter (`\"A\"` through\n * `\"XFD\"`, case-insensitive). Empty / over-long / out-of-bound / non-string\n * fails.\n */\nexport function isValidColumnLetter(s: unknown): s is string {\n if (typeof s !== 'string' || s.length === 0 || s.length > 3) return false;\n if (!/^[A-Za-z]+$/.test(s)) return false;\n const col = columnIndexFromLetterUnchecked(s);\n return col >= 1 && col <= MAX_COL;\n}\n\n/**\n * Predicate: true iff `n` is a valid 1-based row index in `[1, 1048576]`.\n * Non-finite / non-integer / out-of-bound fails.\n */\nexport function isValidRowNumber(n: unknown): n is number {\n return typeof n === 'number' && Number.isInteger(n) && n >= 1 && n <= MAX_ROW;\n}\n\n// Digits with an optional plus sign and XML whitespace, which `xsd:unsignedInt` collapses\n// away. The streaming row-offset index scans bytes for this exact shape, so a\n// looser rule here would let the index and the SAX walk number a row\n// differently, and a band query would then seek past rows it should yield.\nconst ROW_ATTR_VALUE = /^[ \\t\\r\\n]*\\+?([0-9]+)[ \\t\\r\\n]*$/;\n\n/**\n * Parse a `<row r=\"…\">` value into a row number, or `undefined` when it is not\n * a row number in `[1, MAX_ROW]`.\n */\nfunction parseRowNumberAttr(raw: string): number | undefined {\n const digits = ROW_ATTR_VALUE.exec(raw)?.[1];\n if (digits === undefined) return undefined;\n const n = Number(digits);\n return isValidRowNumber(n) ? n : undefined;\n}\n\n/**\n * Row number carried by a `<row>`'s `@r`. A malformed or out-of-range value\n * throws: renumbering it would shift every row after it, and the file is\n * already one no reader can agree on.\n */\nexport function rowNumberFromAttr(raw: string, where: string): number {\n const n = parseRowNumberAttr(raw);\n if (n === undefined) {\n throw new OpenXmlSchemaError(`${where}: <row r=\"${raw}\"> is not a row number in [1, ${MAX_ROW}]`);\n }\n return n;\n}\n\n/**\n * Row number for a `<row>` that omits `@r` and holds no cell to locate it:\n * `fallback`, the slot after the highest row read so far.\n */\nexport function derivedRowNumber(fallback: number, where: string): number {\n if (!isValidRowNumber(fallback)) {\n throw new OpenXmlSchemaError(`${where}: <row> without @r falls past the last row (${MAX_ROW})`);\n }\n return fallback;\n}\n\n/**\n * Predicate: true iff `n` is a valid 1-based column index in `[1, 16384]`.\n * Non-finite / non-integer / out-of-bound fails.\n */\nexport function isValidColumnNumber(n: unknown): n is number {\n return typeof n === 'number' && Number.isInteger(n) && n >= 1 && n <= MAX_COL;\n}\n\n/**\n * Parse \"A1:B5\" / \"A:A\" / \"1:1\" / single-cell into 1-based (minCol, minRow,\n * maxCol, maxRow). Whole-column ranges fill rows to [1, MAX_ROW]; whole-row\n * ranges fill cols to [1, MAX_COL].\n */\nexport function rangeBoundaries(range: string): CellRangeBoundaries {\n const trimmed = range.trim();\n if (trimmed.length === 0) throw new OpenXmlSchemaError('rangeBoundaries: empty range');\n\n const colon = trimmed.indexOf(':');\n if (colon < 0) {\n // Single-cell shorthand: \"A1\" → A1:A1.\n const c = coordinateFromString(trimmed);\n const col = columnIndexFromLetter(c.column);\n return { minCol: col, minRow: c.row, maxCol: col, maxRow: c.row };\n }\n\n const left = trimmed.slice(0, colon);\n const right = trimmed.slice(colon + 1);\n\n const colsOnly = COL_RANGE_RE.exec(trimmed);\n if (colsOnly !== null && colsOnly[1] !== undefined && colsOnly[2] !== undefined) {\n const minCol = columnIndexFromLetter(colsOnly[1]);\n const maxCol = columnIndexFromLetter(colsOnly[2]);\n return {\n minCol: Math.min(minCol, maxCol),\n minRow: 1,\n maxCol: Math.max(minCol, maxCol),\n maxRow: MAX_ROW,\n };\n }\n\n const rowsOnly = ROW_RANGE_RE.exec(trimmed);\n if (rowsOnly !== null && rowsOnly[1] !== undefined && rowsOnly[2] !== undefined) {\n const minRow = Number.parseInt(rowsOnly[1], 10);\n const maxRow = Number.parseInt(rowsOnly[2], 10);\n if (minRow < 1 || maxRow < 1 || minRow > MAX_ROW || maxRow > MAX_ROW) {\n throw new OpenXmlSchemaError(`rangeBoundaries: row out of range in \"${trimmed}\"`);\n }\n return {\n minCol: 1,\n minRow: Math.min(minRow, maxRow),\n maxCol: MAX_COL,\n maxRow: Math.max(minRow, maxRow),\n };\n }\n\n const a = coordinateFromString(left);\n const b = coordinateFromString(right);\n const ac = columnIndexFromLetter(a.column);\n const bc = columnIndexFromLetter(b.column);\n return {\n minCol: Math.min(ac, bc),\n minRow: Math.min(a.row, b.row),\n maxCol: Math.max(ac, bc),\n maxRow: Math.max(a.row, b.row),\n };\n}\n\n/**\n * Inverse of {@link rangeBoundaries} for the rectangular case. `opts` forwards\n * to {@link tupleToCoordinate}, so `{ absoluteCol: true, absoluteRow: true }`\n * yields the `\"$A$4:$H$20\"` form a formula or defined name wants.\n */\nexport function boundariesToRangeString(b: CellRangeBoundaries, opts: AbsoluteRefOptions = {}): string {\n const tl = tupleToCoordinate(b.minCol, b.minRow, opts);\n if (b.minCol === b.maxCol && b.minRow === b.maxRow) return tl;\n const br = tupleToCoordinate(b.maxCol, b.maxRow, opts);\n return `${tl}:${br}`;\n}\n\n/**\n * Parse a sheet-qualified range (\"Sheet1!A1:B5\" / \"'Quarter 1'!A1\"). Sheet\n * names with single quotes inside use SQL-style doubling (\"'Bob''s Sheet'!A1\")\n * — we unescape on the way out.\n */\nexport function parseSheetRange(input: string): {\n sheet: string;\n range: string;\n bounds: CellRangeBoundaries;\n} {\n const m = SHEET_RANGE_RE.exec(input);\n if (m === null) throw new OpenXmlSchemaError(`parseSheetRange: missing \"!\" delimiter in \"${input}\"`);\n const quoted = m[1];\n const bare = m[2];\n const range = m[3];\n if (range === undefined) throw new OpenXmlSchemaError(`parseSheetRange: empty range part in \"${input}\"`);\n const sheet = quoted !== undefined ? quoted.replace(/''/g, \"'\") : (bare ?? '');\n return { sheet, range, bounds: rangeBoundaries(range) };\n}\n\nconst BARE_SHEET_NAME = /^[A-Za-z_][A-Za-z0-9_]*$/;\n\n/**\n * Inverse of {@link parseSheetRange}: format a sheet title + range (or\n * single-cell ref) as `Sheet1!A1` or `'Quarter 1'!A1` per Excel's\n * sheet-qualified syntax. Single quotes inside the title are escaped by\n * doubling (`'Bob''s Sheet'`).\n *\n * Quoting rule: sheet titles consisting only of `[A-Za-z_][A-Za-z0-9_]*` are\n * emitted bare; everything else (spaces, digits-leading, hyphens, apostrophes,\n * punctuation…) gets wrapped in single quotes.\n */\nexport function formatSheetQualifiedRef(sheet: string, ref: string): string {\n if (BARE_SHEET_NAME.test(sheet)) return `${sheet}!${ref}`;\n return `'${sheet.replace(/'/g, \"''\")}'!${ref}`;\n}\n"],"mappings":"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"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { h as parseSheetRange } from "./coordinate-CxC88uBk.mjs";
|
|
2
2
|
import { r as normalizeFormulaText } from "./inference-BMvv9cUY.mjs";
|
|
3
3
|
//#region src/workbook/defined-names.ts
|
|
4
4
|
function makeDefinedName(opts) {
|
|
@@ -87,4 +87,4 @@ const listDefinedNames = (wb, opts = {}) => {
|
|
|
87
87
|
//#endregion
|
|
88
88
|
export { makeDefinedName as a, listDefinedNames as i, getDefinedName as n, removeDefinedName as o, getDefinedNameTarget as r, addDefinedName as t };
|
|
89
89
|
|
|
90
|
-
//# sourceMappingURL=defined-names-
|
|
90
|
+
//# sourceMappingURL=defined-names-Cmp8rbvI.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"defined-names-Ctu3F6ls.mjs","names":[],"sources":["../src/workbook/defined-names.ts"],"sourcesContent":["// Workbook-level defined names. Models the OOXML schema's `<definedName>` element.\n//\n// A defined name binds an identifier to a formula-style value (`'Sheet\n// 1'!$A$1:$B$10`, `SUM(A:A)`, etc). Workbook-scope names omit `localSheetId`;\n// sheet-scope names use the 0-based sheet index. Excel reserves a handful of\n// names with the `_xlnm.` prefix for built-in uses (Print_Area, Print_Titles,\n// Sheet_Title, etc) — those round-trip here as plain DefinedName entries since\n// the value semantics are the same.\n\nimport { OpenXmlSchemaError } from '../utils/exceptions.js';\nimport { normalizeFormulaText } from '../utils/formula-text.js';\n\nexport interface DefinedName {\n /** Identifier — `_xlnm.Print_Area` for built-ins, otherwise user-chosen. */\n name: string;\n /** The formula expression the name points at. */\n value: string;\n /** 0-based sheet index for sheet-scope names; undefined → workbook-scope. */\n scope?: number;\n /** Hidden from the Name Manager when true. */\n hidden?: boolean;\n /** Optional human-readable description. */\n comment?: string;\n}\n\nexport function makeDefinedName(opts: Partial<DefinedName> & { name: string; value: string }): DefinedName {\n return {\n name: opts.name,\n value: normalizeFormulaText(opts.value),\n ...(opts.scope !== undefined ? { scope: opts.scope } : {}),\n ...(opts.hidden !== undefined ? { hidden: opts.hidden } : {}),\n ...(opts.comment !== undefined ? { comment: opts.comment } : {}),\n };\n}\n\n// ---- Workbook ergonomic helpers -----------------------------------------\n\nimport { type CellRangeBoundaries, parseSheetRange } from '../utils/coordinate.js';\nimport type { Worksheet } from '../worksheet/worksheet.js';\nimport { getRangeAddress } from '../worksheet/worksheet.js';\nimport type { Workbook } from './workbook.js';\n\n/**\n * One parsed leg of a defined name's value. Defined-name values can be\n * comma-separated multi-range expressions (e.g. `_xlnm.Print_Titles` sets\n * `Sheet!$1:$1,Sheet!$A:$A`); this represents one such leg.\n */\nexport interface DefinedNameTarget {\n sheet: string;\n range: string;\n bounds: CellRangeBoundaries;\n}\n\n/**\n * Add a workbook-scope or sheet-scope defined name. If a defined name with the\n * same `name` (and `scope`) already exists, it's replaced — Excel allows one\n * workbook-scope and one per-sheet-scope name, but not two with the same scope.\n * Returns the resulting `DefinedName`.\n */\nexport const addDefinedName = (\n wb: Workbook,\n opts: Partial<DefinedName> & { name: string; value: string },\n): DefinedName => {\n const dn = makeDefinedName(opts);\n // Replace any existing entry with the same name + scope.\n const idx = wb.definedNames.findIndex((d) => d.name === dn.name && d.scope === dn.scope);\n if (idx >= 0) {\n wb.definedNames[idx] = dn;\n } else {\n wb.definedNames.push(dn);\n }\n return dn;\n};\n\n/**\n * High-level: register a defined name pointing at a worksheet range. Combines\n * {@link getRangeAddress} (sheet-qualified, properly quoted) with {@link\n * addDefinedName}, so the caller doesn't have to assemble the formula string by\n * hand.\n *\n * Pass `opts.localToSheet: true` to scope the name to the worksheet (instead of\n * the workbook). Re-using the same `name` + scope replaces the previous entry\n * (Excel's per-scope-uniqueness rule).\n *\n * Throws when `localToSheet: true` is set but the worksheet isn't on\n * `wb.sheets` — that would be a stale Worksheet reference.\n */\nexport const addDefinedNameForRange = (\n wb: Workbook,\n name: string,\n ws: Worksheet,\n range: string,\n opts: { localToSheet?: boolean; hidden?: boolean; comment?: string } = {},\n): DefinedName => {\n const value = getRangeAddress(ws, range);\n let scope: number | undefined;\n if (opts.localToSheet) {\n const idx = wb.sheets.findIndex((s) => s.sheet === ws);\n if (idx < 0) {\n throw new OpenXmlSchemaError(\n `addDefinedNameForRange: worksheet \"${ws.title}\" is not registered on this workbook`,\n );\n }\n scope = idx;\n }\n return addDefinedName(wb, {\n name,\n value,\n ...(scope !== undefined ? { scope } : {}),\n ...(opts.hidden !== undefined ? { hidden: opts.hidden } : {}),\n ...(opts.comment !== undefined ? { comment: opts.comment } : {}),\n });\n};\n\n/** Look up a defined name by identifier and (optional) sheet scope. */\nexport const getDefinedName = (\n wb: Workbook,\n name: string,\n scope?: number,\n): DefinedName | undefined => wb.definedNames.find((d) => d.name === name && d.scope === scope);\n\n/**\n * Resolve a defined name's `value` into one or more {@link DefinedNameTarget}s.\n * Comma-separated values (e.g. `_xlnm.Print_Titles` typically sets\n * `Sheet!$1:$1,Sheet!$A:$A`) yield one entry per leg; a plain `Sheet!A1:B5`\n * yields a single-element array.\n *\n * Returns `undefined` when the name doesn't exist; throws when the value can't\n * be parsed (e.g. a constant or a non-range formula — defined names are\n * sometimes used for things like `=42` or `=SUM(A:A)` which aren't ranges).\n */\nexport const getDefinedNameTarget = (\n wb: Workbook,\n name: string,\n scope?: number,\n): DefinedNameTarget[] | undefined => {\n const dn = getDefinedName(wb, name, scope);\n if (!dn) return undefined;\n // Defined-name values use `,` as the leg separator. Sheet titles can\n // themselves contain commas inside `'...'` quotes — split on commas that\n // aren't inside an unbalanced single-quoted segment.\n const legs: string[] = [];\n let current = '';\n let inQuote = false;\n for (let i = 0; i < dn.value.length; i++) {\n const c = dn.value[i];\n if (c === \"'\") {\n // Doubled `''` inside a quoted run is the escape for a literal apostrophe\n // — skip the second one without flipping the state.\n if (inQuote && dn.value[i + 1] === \"'\") {\n current += \"''\";\n i++;\n continue;\n }\n inQuote = !inQuote;\n current += c;\n continue;\n }\n if (c === ',' && !inQuote) {\n legs.push(current);\n current = '';\n continue;\n }\n current += c;\n }\n if (current.length > 0) legs.push(current);\n return legs.map((leg) => parseSheetRange(leg));\n};\n\n/**\n * Remove a defined name by identifier + scope. Returns true if any entry was\n * removed.\n */\nexport const removeDefinedName = (wb: Workbook, name: string, scope?: number): boolean => {\n const idx = wb.definedNames.findIndex((d) => d.name === name && d.scope === scope);\n if (idx < 0) return false;\n wb.definedNames.splice(idx, 1);\n return true;\n};\n\n/**\n * Read-only snapshot of every defined name. Pass `{ scope }` to narrow to\n * workbook-scope (`scope: undefined`) or one specific sheet (`scope: 0`) — omit\n * the option entirely to list all.\n */\nexport const listDefinedNames = (\n wb: Workbook,\n opts: { scope?: number | 'workbook' | 'all' } = {},\n): ReadonlyArray<DefinedName> => {\n const scope = opts.scope ?? 'all';\n if (scope === 'all') return wb.definedNames;\n if (scope === 'workbook') return wb.definedNames.filter((d) => d.scope === undefined);\n return wb.definedNames.filter((d) => d.scope === scope);\n};\n\n/**\n * Bulk-remove every defined name matching `predicate`. Returns the count\n * removed. Mirrors {@link removeDataValidations} on worksheets.\n */\nexport const removeDefinedNames = (\n wb: Workbook,\n predicate: (d: DefinedName) => boolean,\n): number => {\n const before = wb.definedNames.length;\n wb.definedNames = wb.definedNames.filter((d) => !predicate(d));\n return before - wb.definedNames.length;\n};\n\n/**\n * Rename a defined name, scoped or workbook-scope. Returns `true` when an entry\n * was renamed. Throws when `newName` is already taken with the same scope\n * (Excel forbids duplicates within a scope).\n */\nexport const renameDefinedName = (\n wb: Workbook,\n oldName: string,\n newName: string,\n scope?: number,\n): boolean => {\n const idx = wb.definedNames.findIndex((d) => d.name === oldName && d.scope === scope);\n if (idx < 0) return false;\n const conflict = wb.definedNames.findIndex((d, i) => i !== idx && d.name === newName && d.scope === scope);\n if (conflict >= 0) {\n throw new OpenXmlSchemaError(`renameDefinedName: \"${newName}\" is already in use at the same scope`);\n }\n const existing = wb.definedNames[idx];\n if (!existing) return false;\n wb.definedNames[idx] = { ...existing, name: newName };\n return true;\n};\n\n/**\n * Read-only snapshot of every `_xlnm.Print_Area` defined name. Each entry is\n * the raw DefinedName carrying `scope` (sheet index) and `value` (the\n * print-area expression like `'Sheet1'!$A$1:$D$10`).\n */\nexport const listPrintAreas = (wb: Workbook): ReadonlyArray<DefinedName> =>\n wb.definedNames.filter((d) => d.name === '_xlnm.Print_Area');\n\n/**\n * Read-only snapshot of every `_xlnm.Print_Titles` defined name. Each entry's\n * `value` is the title-row / title-col expression Excel re-uses on every\n * printed page.\n */\nexport const listPrintTitles = (wb: Workbook): ReadonlyArray<DefinedName> =>\n wb.definedNames.filter((d) => d.name === '_xlnm.Print_Titles');\n\n/**\n * Define the print-area for a given sheet. Excel uses the built-in\n * `_xlnm.Print_Area` defined name with sheet scope.\n */\nexport const setPrintArea = (wb: Workbook, sheetIndex: number, ref: string): DefinedName => {\n return addDefinedName(wb, {\n name: '_xlnm.Print_Area',\n value: ref,\n scope: sheetIndex,\n });\n};\n\n/**\n * Define print-title rows / columns on a sheet. Excel uses the\n * `_xlnm.Print_Titles` defined name. Pass `rows` (\"$1:$1\") to repeat row 1 on\n * every printed page; `cols` (\"$A:$A\") to repeat column A.\n */\nexport const setPrintTitles = (\n wb: Workbook,\n sheetIndex: number,\n opts: { rows?: string; cols?: string; sheetName: string },\n): DefinedName => {\n const parts: string[] = [];\n // The wire form is \"Sheet!$1:$1,Sheet!$A:$A\"; both refs share the sheet\n // prefix.\n if (opts.cols !== undefined) parts.push(`'${opts.sheetName}'!${opts.cols}`);\n if (opts.rows !== undefined) parts.push(`'${opts.sheetName}'!${opts.rows}`);\n if (parts.length === 0) {\n throw new OpenXmlSchemaError('setPrintTitles: at least one of rows or cols must be set');\n }\n return addDefinedName(wb, {\n name: '_xlnm.Print_Titles',\n value: parts.join(','),\n scope: sheetIndex,\n });\n};\n"],"mappings":";;;AAyBA,SAAgB,gBAAgB,MAA2E;CACzG,OAAO;EACL,MAAM,KAAK;EACX,OAAO,qBAAqB,KAAK,KAAK;EACtC,GAAI,KAAK,UAAU,KAAA,IAAY,EAAE,OAAO,KAAK,MAAM,IAAI,CAAC;EACxD,GAAI,KAAK,WAAW,KAAA,IAAY,EAAE,QAAQ,KAAK,OAAO,IAAI,CAAC;EAC3D,GAAI,KAAK,YAAY,KAAA,IAAY,EAAE,SAAS,KAAK,QAAQ,IAAI,CAAC;CAChE;AACF;;;;;;;AA0BA,MAAa,kBACX,IACA,SACgB;CAChB,MAAM,KAAK,gBAAgB,IAAI;CAE/B,MAAM,MAAM,GAAG,aAAa,WAAW,MAAM,EAAE,SAAS,GAAG,QAAQ,EAAE,UAAU,GAAG,KAAK;CACvF,IAAI,OAAO,GACT,GAAG,aAAa,OAAO;MAEvB,GAAG,aAAa,KAAK,EAAE;CAEzB,OAAO;AACT;;AA2CA,MAAa,kBACX,IACA,MACA,UAC4B,GAAG,aAAa,MAAM,MAAM,EAAE,SAAS,QAAQ,EAAE,UAAU,KAAK;;;;;;;;;;;AAY9F,MAAa,wBACX,IACA,MACA,UACoC;CACpC,MAAM,KAAK,eAAe,IAAI,MAAM,KAAK;CACzC,IAAI,CAAC,IAAI,OAAO,KAAA;CAIhB,MAAM,OAAiB,CAAC;CACxB,IAAI,UAAU;CACd,IAAI,UAAU;CACd,KAAK,IAAI,IAAI,GAAG,IAAI,GAAG,MAAM,QAAQ,KAAK;EACxC,MAAM,IAAI,GAAG,MAAM;EACnB,IAAI,MAAM,KAAK;GAGb,IAAI,WAAW,GAAG,MAAM,IAAI,OAAO,KAAK;IACtC,WAAW;IACX;IACA;GACF;GACA,UAAU,CAAC;GACX,WAAW;GACX;EACF;EACA,IAAI,MAAM,OAAO,CAAC,SAAS;GACzB,KAAK,KAAK,OAAO;GACjB,UAAU;GACV;EACF;EACA,WAAW;CACb;CACA,IAAI,QAAQ,SAAS,GAAG,KAAK,KAAK,OAAO;CACzC,OAAO,KAAK,KAAK,QAAQ,gBAAgB,GAAG,CAAC;AAC/C;;;;;AAMA,MAAa,qBAAqB,IAAc,MAAc,UAA4B;CACxF,MAAM,MAAM,GAAG,aAAa,WAAW,MAAM,EAAE,SAAS,QAAQ,EAAE,UAAU,KAAK;CACjF,IAAI,MAAM,GAAG,OAAO;CACpB,GAAG,aAAa,OAAO,KAAK,CAAC;CAC7B,OAAO;AACT;;;;;;AAOA,MAAa,oBACX,IACA,OAAgD,CAAC,MAClB;CAC/B,MAAM,QAAQ,KAAK,SAAS;CAC5B,IAAI,UAAU,OAAO,OAAO,GAAG;CAC/B,IAAI,UAAU,YAAY,OAAO,GAAG,aAAa,QAAQ,MAAM,EAAE,UAAU,KAAA,CAAS;CACpF,OAAO,GAAG,aAAa,QAAQ,MAAM,EAAE,UAAU,KAAK;AACxD"}
|
|
1
|
+
{"version":3,"file":"defined-names-Cmp8rbvI.mjs","names":[],"sources":["../src/workbook/defined-names.ts"],"sourcesContent":["// Workbook-level defined names. Models the OOXML schema's `<definedName>` element.\n//\n// A defined name binds an identifier to a formula-style value (`'Sheet\n// 1'!$A$1:$B$10`, `SUM(A:A)`, etc). Workbook-scope names omit `localSheetId`;\n// sheet-scope names use the 0-based sheet index. Excel reserves a handful of\n// names with the `_xlnm.` prefix for built-in uses (Print_Area, Print_Titles,\n// Sheet_Title, etc) — those round-trip here as plain DefinedName entries since\n// the value semantics are the same.\n\nimport { OpenXmlSchemaError } from '../utils/exceptions.js';\nimport { normalizeFormulaText } from '../utils/formula-text.js';\n\nexport interface DefinedName {\n /** Identifier — `_xlnm.Print_Area` for built-ins, otherwise user-chosen. */\n name: string;\n /** The formula expression the name points at. */\n value: string;\n /** 0-based sheet index for sheet-scope names; undefined → workbook-scope. */\n scope?: number;\n /** Hidden from the Name Manager when true. */\n hidden?: boolean;\n /** Optional human-readable description. */\n comment?: string;\n}\n\nexport function makeDefinedName(opts: Partial<DefinedName> & { name: string; value: string }): DefinedName {\n return {\n name: opts.name,\n value: normalizeFormulaText(opts.value),\n ...(opts.scope !== undefined ? { scope: opts.scope } : {}),\n ...(opts.hidden !== undefined ? { hidden: opts.hidden } : {}),\n ...(opts.comment !== undefined ? { comment: opts.comment } : {}),\n };\n}\n\n// ---- Workbook ergonomic helpers -----------------------------------------\n\nimport { type CellRangeBoundaries, parseSheetRange } from '../utils/coordinate.js';\nimport type { Worksheet } from '../worksheet/worksheet.js';\nimport { getRangeAddress } from '../worksheet/worksheet.js';\nimport type { Workbook } from './workbook.js';\n\n/**\n * One parsed leg of a defined name's value. Defined-name values can be\n * comma-separated multi-range expressions (e.g. `_xlnm.Print_Titles` sets\n * `Sheet!$1:$1,Sheet!$A:$A`); this represents one such leg.\n */\nexport interface DefinedNameTarget {\n sheet: string;\n range: string;\n bounds: CellRangeBoundaries;\n}\n\n/**\n * Add a workbook-scope or sheet-scope defined name. If a defined name with the\n * same `name` (and `scope`) already exists, it's replaced — Excel allows one\n * workbook-scope and one per-sheet-scope name, but not two with the same scope.\n * Returns the resulting `DefinedName`.\n */\nexport const addDefinedName = (\n wb: Workbook,\n opts: Partial<DefinedName> & { name: string; value: string },\n): DefinedName => {\n const dn = makeDefinedName(opts);\n // Replace any existing entry with the same name + scope.\n const idx = wb.definedNames.findIndex((d) => d.name === dn.name && d.scope === dn.scope);\n if (idx >= 0) {\n wb.definedNames[idx] = dn;\n } else {\n wb.definedNames.push(dn);\n }\n return dn;\n};\n\n/**\n * High-level: register a defined name pointing at a worksheet range. Combines\n * {@link getRangeAddress} (sheet-qualified, properly quoted) with {@link\n * addDefinedName}, so the caller doesn't have to assemble the formula string by\n * hand.\n *\n * Pass `opts.localToSheet: true` to scope the name to the worksheet (instead of\n * the workbook). Re-using the same `name` + scope replaces the previous entry\n * (Excel's per-scope-uniqueness rule).\n *\n * Throws when `localToSheet: true` is set but the worksheet isn't on\n * `wb.sheets` — that would be a stale Worksheet reference.\n */\nexport const addDefinedNameForRange = (\n wb: Workbook,\n name: string,\n ws: Worksheet,\n range: string,\n opts: { localToSheet?: boolean; hidden?: boolean; comment?: string } = {},\n): DefinedName => {\n const value = getRangeAddress(ws, range);\n let scope: number | undefined;\n if (opts.localToSheet) {\n const idx = wb.sheets.findIndex((s) => s.sheet === ws);\n if (idx < 0) {\n throw new OpenXmlSchemaError(\n `addDefinedNameForRange: worksheet \"${ws.title}\" is not registered on this workbook`,\n );\n }\n scope = idx;\n }\n return addDefinedName(wb, {\n name,\n value,\n ...(scope !== undefined ? { scope } : {}),\n ...(opts.hidden !== undefined ? { hidden: opts.hidden } : {}),\n ...(opts.comment !== undefined ? { comment: opts.comment } : {}),\n });\n};\n\n/** Look up a defined name by identifier and (optional) sheet scope. */\nexport const getDefinedName = (\n wb: Workbook,\n name: string,\n scope?: number,\n): DefinedName | undefined => wb.definedNames.find((d) => d.name === name && d.scope === scope);\n\n/**\n * Resolve a defined name's `value` into one or more {@link DefinedNameTarget}s.\n * Comma-separated values (e.g. `_xlnm.Print_Titles` typically sets\n * `Sheet!$1:$1,Sheet!$A:$A`) yield one entry per leg; a plain `Sheet!A1:B5`\n * yields a single-element array.\n *\n * Returns `undefined` when the name doesn't exist; throws when the value can't\n * be parsed (e.g. a constant or a non-range formula — defined names are\n * sometimes used for things like `=42` or `=SUM(A:A)` which aren't ranges).\n */\nexport const getDefinedNameTarget = (\n wb: Workbook,\n name: string,\n scope?: number,\n): DefinedNameTarget[] | undefined => {\n const dn = getDefinedName(wb, name, scope);\n if (!dn) return undefined;\n // Defined-name values use `,` as the leg separator. Sheet titles can\n // themselves contain commas inside `'...'` quotes — split on commas that\n // aren't inside an unbalanced single-quoted segment.\n const legs: string[] = [];\n let current = '';\n let inQuote = false;\n for (let i = 0; i < dn.value.length; i++) {\n const c = dn.value[i];\n if (c === \"'\") {\n // Doubled `''` inside a quoted run is the escape for a literal apostrophe\n // — skip the second one without flipping the state.\n if (inQuote && dn.value[i + 1] === \"'\") {\n current += \"''\";\n i++;\n continue;\n }\n inQuote = !inQuote;\n current += c;\n continue;\n }\n if (c === ',' && !inQuote) {\n legs.push(current);\n current = '';\n continue;\n }\n current += c;\n }\n if (current.length > 0) legs.push(current);\n return legs.map((leg) => parseSheetRange(leg));\n};\n\n/**\n * Remove a defined name by identifier + scope. Returns true if any entry was\n * removed.\n */\nexport const removeDefinedName = (wb: Workbook, name: string, scope?: number): boolean => {\n const idx = wb.definedNames.findIndex((d) => d.name === name && d.scope === scope);\n if (idx < 0) return false;\n wb.definedNames.splice(idx, 1);\n return true;\n};\n\n/**\n * Read-only snapshot of every defined name. Pass `{ scope }` to narrow to\n * workbook-scope (`scope: undefined`) or one specific sheet (`scope: 0`) — omit\n * the option entirely to list all.\n */\nexport const listDefinedNames = (\n wb: Workbook,\n opts: { scope?: number | 'workbook' | 'all' } = {},\n): ReadonlyArray<DefinedName> => {\n const scope = opts.scope ?? 'all';\n if (scope === 'all') return wb.definedNames;\n if (scope === 'workbook') return wb.definedNames.filter((d) => d.scope === undefined);\n return wb.definedNames.filter((d) => d.scope === scope);\n};\n\n/**\n * Bulk-remove every defined name matching `predicate`. Returns the count\n * removed. Mirrors {@link removeDataValidations} on worksheets.\n */\nexport const removeDefinedNames = (\n wb: Workbook,\n predicate: (d: DefinedName) => boolean,\n): number => {\n const before = wb.definedNames.length;\n wb.definedNames = wb.definedNames.filter((d) => !predicate(d));\n return before - wb.definedNames.length;\n};\n\n/**\n * Rename a defined name, scoped or workbook-scope. Returns `true` when an entry\n * was renamed. Throws when `newName` is already taken with the same scope\n * (Excel forbids duplicates within a scope).\n */\nexport const renameDefinedName = (\n wb: Workbook,\n oldName: string,\n newName: string,\n scope?: number,\n): boolean => {\n const idx = wb.definedNames.findIndex((d) => d.name === oldName && d.scope === scope);\n if (idx < 0) return false;\n const conflict = wb.definedNames.findIndex((d, i) => i !== idx && d.name === newName && d.scope === scope);\n if (conflict >= 0) {\n throw new OpenXmlSchemaError(`renameDefinedName: \"${newName}\" is already in use at the same scope`);\n }\n const existing = wb.definedNames[idx];\n if (!existing) return false;\n wb.definedNames[idx] = { ...existing, name: newName };\n return true;\n};\n\n/**\n * Read-only snapshot of every `_xlnm.Print_Area` defined name. Each entry is\n * the raw DefinedName carrying `scope` (sheet index) and `value` (the\n * print-area expression like `'Sheet1'!$A$1:$D$10`).\n */\nexport const listPrintAreas = (wb: Workbook): ReadonlyArray<DefinedName> =>\n wb.definedNames.filter((d) => d.name === '_xlnm.Print_Area');\n\n/**\n * Read-only snapshot of every `_xlnm.Print_Titles` defined name. Each entry's\n * `value` is the title-row / title-col expression Excel re-uses on every\n * printed page.\n */\nexport const listPrintTitles = (wb: Workbook): ReadonlyArray<DefinedName> =>\n wb.definedNames.filter((d) => d.name === '_xlnm.Print_Titles');\n\n/**\n * Define the print-area for a given sheet. Excel uses the built-in\n * `_xlnm.Print_Area` defined name with sheet scope.\n */\nexport const setPrintArea = (wb: Workbook, sheetIndex: number, ref: string): DefinedName => {\n return addDefinedName(wb, {\n name: '_xlnm.Print_Area',\n value: ref,\n scope: sheetIndex,\n });\n};\n\n/**\n * Define print-title rows / columns on a sheet. Excel uses the\n * `_xlnm.Print_Titles` defined name. Pass `rows` (\"$1:$1\") to repeat row 1 on\n * every printed page; `cols` (\"$A:$A\") to repeat column A.\n */\nexport const setPrintTitles = (\n wb: Workbook,\n sheetIndex: number,\n opts: { rows?: string; cols?: string; sheetName: string },\n): DefinedName => {\n const parts: string[] = [];\n // The wire form is \"Sheet!$1:$1,Sheet!$A:$A\"; both refs share the sheet\n // prefix.\n if (opts.cols !== undefined) parts.push(`'${opts.sheetName}'!${opts.cols}`);\n if (opts.rows !== undefined) parts.push(`'${opts.sheetName}'!${opts.rows}`);\n if (parts.length === 0) {\n throw new OpenXmlSchemaError('setPrintTitles: at least one of rows or cols must be set');\n }\n return addDefinedName(wb, {\n name: '_xlnm.Print_Titles',\n value: parts.join(','),\n scope: sheetIndex,\n });\n};\n"],"mappings":";;;AAyBA,SAAgB,gBAAgB,MAA2E;CACzG,OAAO;EACL,MAAM,KAAK;EACX,OAAO,qBAAqB,KAAK,KAAK;EACtC,GAAI,KAAK,UAAU,KAAA,IAAY,EAAE,OAAO,KAAK,MAAM,IAAI,CAAC;EACxD,GAAI,KAAK,WAAW,KAAA,IAAY,EAAE,QAAQ,KAAK,OAAO,IAAI,CAAC;EAC3D,GAAI,KAAK,YAAY,KAAA,IAAY,EAAE,SAAS,KAAK,QAAQ,IAAI,CAAC;CAChE;AACF;;;;;;;AA0BA,MAAa,kBACX,IACA,SACgB;CAChB,MAAM,KAAK,gBAAgB,IAAI;CAE/B,MAAM,MAAM,GAAG,aAAa,WAAW,MAAM,EAAE,SAAS,GAAG,QAAQ,EAAE,UAAU,GAAG,KAAK;CACvF,IAAI,OAAO,GACT,GAAG,aAAa,OAAO;MAEvB,GAAG,aAAa,KAAK,EAAE;CAEzB,OAAO;AACT;;AA2CA,MAAa,kBACX,IACA,MACA,UAC4B,GAAG,aAAa,MAAM,MAAM,EAAE,SAAS,QAAQ,EAAE,UAAU,KAAK;;;;;;;;;;;AAY9F,MAAa,wBACX,IACA,MACA,UACoC;CACpC,MAAM,KAAK,eAAe,IAAI,MAAM,KAAK;CACzC,IAAI,CAAC,IAAI,OAAO,KAAA;CAIhB,MAAM,OAAiB,CAAC;CACxB,IAAI,UAAU;CACd,IAAI,UAAU;CACd,KAAK,IAAI,IAAI,GAAG,IAAI,GAAG,MAAM,QAAQ,KAAK;EACxC,MAAM,IAAI,GAAG,MAAM;EACnB,IAAI,MAAM,KAAK;GAGb,IAAI,WAAW,GAAG,MAAM,IAAI,OAAO,KAAK;IACtC,WAAW;IACX;IACA;GACF;GACA,UAAU,CAAC;GACX,WAAW;GACX;EACF;EACA,IAAI,MAAM,OAAO,CAAC,SAAS;GACzB,KAAK,KAAK,OAAO;GACjB,UAAU;GACV;EACF;EACA,WAAW;CACb;CACA,IAAI,QAAQ,SAAS,GAAG,KAAK,KAAK,OAAO;CACzC,OAAO,KAAK,KAAK,QAAQ,gBAAgB,GAAG,CAAC;AAC/C;;;;;AAMA,MAAa,qBAAqB,IAAc,MAAc,UAA4B;CACxF,MAAM,MAAM,GAAG,aAAa,WAAW,MAAM,EAAE,SAAS,QAAQ,EAAE,UAAU,KAAK;CACjF,IAAI,MAAM,GAAG,OAAO;CACpB,GAAG,aAAa,OAAO,KAAK,CAAC;CAC7B,OAAO;AACT;;;;;;AAOA,MAAa,oBACX,IACA,OAAgD,CAAC,MAClB;CAC/B,MAAM,QAAQ,KAAK,SAAS;CAC5B,IAAI,UAAU,OAAO,OAAO,GAAG;CAC/B,IAAI,UAAU,YAAY,OAAO,GAAG,aAAa,QAAQ,MAAM,EAAE,UAAU,KAAA,CAAS;CACpF,OAAO,GAAG,aAAa,QAAQ,MAAM,EAAE,UAAU,KAAK;AACxD"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { en as stableStringify } from "./cell-style-
|
|
1
|
+
import { en as stableStringify } from "./cell-style-DjKKPWDL.mjs";
|
|
2
2
|
//#region src/styles/differential.ts
|
|
3
3
|
function makeDifferentialStyle(opts = {}) {
|
|
4
4
|
const out = {};
|
|
@@ -34,4 +34,4 @@ function getDxfs(ss) {
|
|
|
34
34
|
//#endregion
|
|
35
35
|
export { getDxfs as n, makeDifferentialStyle as r, addDxf as t };
|
|
36
36
|
|
|
37
|
-
//# sourceMappingURL=differential-
|
|
37
|
+
//# sourceMappingURL=differential-7KGzbliB.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"differential-
|
|
1
|
+
{"version":3,"file":"differential-7KGzbliB.mjs","names":[],"sources":["../src/styles/differential.ts"],"sourcesContent":["// DifferentialStyle (DXF) — partial style overlay used by conditional\n// formatting and Excel-table style elements. Mirrors\n// openpyxl/openpyxl/styles/differential.py.\n//\n// Where a NamedStyle / CellXf carries a *complete* style snapshot, a\n// DifferentialStyle carries only the components that override the base.\n// The Stylesheet keeps DXFs in their own pool (`dxfs`) and conditional-\n// formatting rules / table-style elements reference them by index.\n\nimport { stableStringify } from '../utils/stable-stringify.js';\nimport type { Alignment } from './alignment.js';\nimport type { Border } from './borders.js';\nimport type { Fill } from './fills.js';\nimport type { Font } from './fonts.js';\nimport type { NumberFormat } from './numbers.js';\nimport type { Protection } from './protection.js';\nimport type { Stylesheet } from './stylesheet.js';\n\n/**\n * Differential (\"partial\") style. Every component is optional; only\n * the set fields override the base style of whatever the DXF is\n * applied to.\n */\nexport interface DifferentialStyle {\n readonly font?: Font;\n readonly fill?: Fill;\n readonly border?: Border;\n readonly alignment?: Alignment;\n readonly protection?: Protection;\n /**\n * NumberFormat carries both the id and the format code so DXFs can\n * reference custom user-defined number formats without ambiguity.\n */\n readonly numFmt?: NumberFormat;\n}\n\nexport function makeDifferentialStyle(opts: Partial<DifferentialStyle> = {}): DifferentialStyle {\n const out: { -readonly [K in keyof DifferentialStyle]: DifferentialStyle[K] } = {};\n if (opts.font !== undefined) out.font = opts.font;\n if (opts.fill !== undefined) out.fill = opts.fill;\n if (opts.border !== undefined) out.border = opts.border;\n if (opts.alignment !== undefined) out.alignment = opts.alignment;\n if (opts.protection !== undefined) out.protection = opts.protection;\n if (opts.numFmt !== undefined) out.numFmt = opts.numFmt;\n return Object.freeze(out);\n}\n\n/**\n * Stylesheet pool extension. The DXF list is allocated lazily on first\n * use so empty stylesheets stay slim.\n */\nexport interface StylesheetWithDxfs extends Stylesheet {\n dxfs?: DifferentialStyle[];\n _dxfIdByKey?: Map<string, number>;\n}\n\n/**\n * Add a DifferentialStyle to the stylesheet's `dxfs` pool. Returns\n * the 0-based index. Idempotent on structural equality (via\n * stableStringify).\n */\nexport function addDxf(ss: Stylesheet, dxf: DifferentialStyle): number {\n const w = ss as StylesheetWithDxfs;\n if (w.dxfs === undefined) w.dxfs = [];\n if (w._dxfIdByKey === undefined) w._dxfIdByKey = new Map();\n const key = stableStringify(dxf);\n const cached = w._dxfIdByKey.get(key);\n if (cached !== undefined) return cached;\n const id = w.dxfs.length;\n w.dxfs.push(dxf);\n w._dxfIdByKey.set(key, id);\n return id;\n}\n\n/** Read-only access to the dxfs pool. Returns `[]` when none registered. */\nexport function getDxfs(ss: Stylesheet): ReadonlyArray<DifferentialStyle> {\n return (ss as StylesheetWithDxfs).dxfs ?? [];\n}\n"],"mappings":";;AAoCA,SAAgB,sBAAsB,OAAmC,CAAC,GAAsB;CAC9F,MAAM,MAA0E,CAAC;CACjF,IAAI,KAAK,SAAS,KAAA,GAAW,IAAI,OAAO,KAAK;CAC7C,IAAI,KAAK,SAAS,KAAA,GAAW,IAAI,OAAO,KAAK;CAC7C,IAAI,KAAK,WAAW,KAAA,GAAW,IAAI,SAAS,KAAK;CACjD,IAAI,KAAK,cAAc,KAAA,GAAW,IAAI,YAAY,KAAK;CACvD,IAAI,KAAK,eAAe,KAAA,GAAW,IAAI,aAAa,KAAK;CACzD,IAAI,KAAK,WAAW,KAAA,GAAW,IAAI,SAAS,KAAK;CACjD,OAAO,OAAO,OAAO,GAAG;AAC1B;;;;;;AAgBA,SAAgB,OAAO,IAAgB,KAAgC;CACrE,MAAM,IAAI;CACV,IAAI,EAAE,SAAS,KAAA,GAAW,EAAE,OAAO,CAAC;CACpC,IAAI,EAAE,gBAAgB,KAAA,GAAW,EAAE,8BAAc,IAAI,IAAI;CACzD,MAAM,MAAM,gBAAgB,GAAG;CAC/B,MAAM,SAAS,EAAE,YAAY,IAAI,GAAG;CACpC,IAAI,WAAW,KAAA,GAAW,OAAO;CACjC,MAAM,KAAK,EAAE,KAAK;CAClB,EAAE,KAAK,KAAK,GAAG;CACf,EAAE,YAAY,IAAI,KAAK,EAAE;CACzB,OAAO;AACT;;AAGA,SAAgB,QAAQ,IAAkD;CACxE,OAAQ,GAA0B,QAAQ,CAAC;AAC7C"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { i as OpenXmlIoError, o as OpenXmlSchemaError } from "./exceptions-D-CFwxgm.mjs";
|
|
2
|
-
import { i as columnIndexFromLetter } from "./coordinate-
|
|
2
|
+
import { i as columnIndexFromLetter } from "./coordinate-CxC88uBk.mjs";
|
|
3
3
|
import { l as emuFromPx } from "./units-rOMQqXh2.mjs";
|
|
4
4
|
//#region src/drawing/image.ts
|
|
5
5
|
/**
|
|
@@ -412,4 +412,4 @@ const removeAllCharts = (ws) => {
|
|
|
412
412
|
//#endregion
|
|
413
413
|
export { makeChartDrawingItem as a, removeAllCharts as c, makeAbsoluteAnchor as d, makeOneCellAnchor as f, loadImage as h, listImagesOnSheet as i, removeAllDrawingItems as l, IMAGE_FORMAT_MIME as m, addImageAt as n, makeDrawing as o, IMAGE_FORMAT_EXTENSION as p, listChartsOnSheet as r, makePictureDrawingItem as s, addChartAt as t, removeAllImages as u };
|
|
414
414
|
|
|
415
|
-
//# sourceMappingURL=drawing-
|
|
415
|
+
//# sourceMappingURL=drawing-Cv6UtInY.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"drawing-ZJ3h4VHD.mjs","names":[],"sources":["../src/drawing/image.ts","../src/drawing/anchor.ts","../src/drawing/drawing.ts"],"sourcesContent":["// Image embedding.\n//\n// `XlsxImage` is the workbook-level handle for any image referenced from a\n// worksheet drawing (or chart `<a:blipFill>`). Bytes are kept verbatim so\n// re-saving never re-encodes; format / width / height are detected from the\n// file header so callers don't have to specify them.\n\nimport { OpenXmlIoError } from '../utils/exceptions.js';\n\nexport type XlsxImageFormat = 'png' | 'jpeg' | 'gif' | 'bmp' | 'webp' | 'tiff' | 'svg' | 'emf' | 'wmf';\n\n/**\n * Map from image format to the Content-Types Default extension that Excel uses\n * in `[Content_Types].xml`. SVG / EMF / WMF use the same extension as the\n * format string; PNG/JPEG/GIF/BMP do too. WebP and TIFF use their canonical\n * short names.\n */\nexport const IMAGE_FORMAT_EXTENSION: Readonly<Record<XlsxImageFormat, string>> = {\n png: 'png',\n jpeg: 'jpeg',\n gif: 'gif',\n bmp: 'bmp',\n webp: 'webp',\n tiff: 'tiff',\n svg: 'svg',\n emf: 'emf',\n wmf: 'wmf',\n};\n\n/** Map from image format to its IANA `image/*` MIME type. */\nexport const IMAGE_FORMAT_MIME: Readonly<Record<XlsxImageFormat, string>> = {\n png: 'image/png',\n jpeg: 'image/jpeg',\n gif: 'image/gif',\n bmp: 'image/bmp',\n webp: 'image/webp',\n tiff: 'image/tiff',\n svg: 'image/svg+xml',\n emf: 'image/x-emf',\n wmf: 'image/x-wmf',\n};\n\nexport interface XlsxImage {\n bytes: Uint8Array;\n format: XlsxImageFormat;\n /** Pixel width. Zero when dimensions can't be determined for the format. */\n width: number;\n /** Pixel height. Zero when dimensions can't be determined for the format. */\n height: number;\n /** ZIP archive path, e.g. `xl/media/image3.png`. Set by the writer. */\n path?: string;\n /** rels-resolved id used by `<a:blip r:embed=\"...\">`. Set by the loader. */\n rId?: string;\n}\n\n// ---- Magic-byte format detection ------------------------------------------\n\nconst startsWith = (bytes: Uint8Array, sig: ReadonlyArray<number>, offset = 0): boolean => {\n if (bytes.length < offset + sig.length) return false;\n for (let i = 0; i < sig.length; i++) {\n if (bytes[offset + i] !== sig[i]) return false;\n }\n return true;\n};\n\nconst PNG_SIG = [0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a];\nconst JPEG_SIG = [0xff, 0xd8, 0xff];\nconst GIF87 = [0x47, 0x49, 0x46, 0x38, 0x37, 0x61];\nconst GIF89 = [0x47, 0x49, 0x46, 0x38, 0x39, 0x61];\nconst BMP_SIG = [0x42, 0x4d];\nconst RIFF_SIG = [0x52, 0x49, 0x46, 0x46];\nconst WEBP_TAG = [0x57, 0x45, 0x42, 0x50];\nconst TIFF_LE = [0x49, 0x49, 0x2a, 0x00];\nconst TIFF_BE = [0x4d, 0x4d, 0x00, 0x2a];\nconst EMF_SIG = [0x01, 0x00, 0x00, 0x00];\nconst EMF_TAG = [0x20, 0x45, 0x4d, 0x46]; // ' EMF' at offset 40\nconst WMF_SIG_PLACEABLE = [0xd7, 0xcd, 0xc6, 0x9a];\nconst WMF_SIG_BARE = [0x01, 0x00, 0x09, 0x00];\n\nconst isSvg = (bytes: Uint8Array): boolean => {\n // Inspect the first ~512 bytes for an `<svg` tag (with optional XML / DOCTYPE\n // preamble).\n const head = new TextDecoder('utf-8', { fatal: false }).decode(bytes.subarray(0, Math.min(bytes.length, 512)));\n return /<svg\\b/i.test(head);\n};\n\nexport function detectImageFormat(bytes: Uint8Array): XlsxImageFormat | undefined {\n if (startsWith(bytes, PNG_SIG)) return 'png';\n if (startsWith(bytes, JPEG_SIG)) return 'jpeg';\n if (startsWith(bytes, GIF87) || startsWith(bytes, GIF89)) return 'gif';\n if (startsWith(bytes, BMP_SIG)) return 'bmp';\n if (startsWith(bytes, RIFF_SIG) && startsWith(bytes, WEBP_TAG, 8)) return 'webp';\n if (startsWith(bytes, TIFF_LE) || startsWith(bytes, TIFF_BE)) return 'tiff';\n if (startsWith(bytes, EMF_SIG) && startsWith(bytes, EMF_TAG, 40)) return 'emf';\n if (startsWith(bytes, WMF_SIG_PLACEABLE) || startsWith(bytes, WMF_SIG_BARE)) return 'wmf';\n if (isSvg(bytes)) return 'svg';\n return undefined;\n}\n\n// ---- Per-format dimension extraction --------------------------------------\n\nconst readU32BE = (bytes: Uint8Array, offset: number): number =>\n ((bytes[offset] ?? 0) << 24) |\n ((bytes[offset + 1] ?? 0) << 16) |\n ((bytes[offset + 2] ?? 0) << 8) |\n (bytes[offset + 3] ?? 0);\n\nconst readU16LE = (bytes: Uint8Array, offset: number): number => (bytes[offset] ?? 0) | ((bytes[offset + 1] ?? 0) << 8);\n\nconst readU16BE = (bytes: Uint8Array, offset: number): number => ((bytes[offset] ?? 0) << 8) | (bytes[offset + 1] ?? 0);\n\ninterface Dimensions {\n width: number;\n height: number;\n}\n\nconst pngDimensions = (bytes: Uint8Array): Dimensions | undefined => {\n // PNG: 8-byte signature, 4-byte chunk-length, 4-byte type (\"IHDR\"), then\n // 4-byte width, 4-byte height (both big-endian).\n if (bytes.length < 24) return undefined;\n if (bytes[12] !== 0x49 || bytes[13] !== 0x48 || bytes[14] !== 0x44 || bytes[15] !== 0x52) {\n return undefined;\n }\n return { width: readU32BE(bytes, 16), height: readU32BE(bytes, 20) };\n};\n\nconst gifDimensions = (bytes: Uint8Array): Dimensions | undefined => {\n if (bytes.length < 10) return undefined;\n return { width: readU16LE(bytes, 6), height: readU16LE(bytes, 8) };\n};\n\nconst bmpDimensions = (bytes: Uint8Array): Dimensions | undefined => {\n if (bytes.length < 26) return undefined;\n // BITMAPINFOHEADER: width (LE int32) at offset 18, height at offset 22.\n // Height can be negative for top-down bitmaps; we report absolute pixels.\n const w = (bytes[18] ?? 0) | ((bytes[19] ?? 0) << 8) | ((bytes[20] ?? 0) << 16) | ((bytes[21] ?? 0) << 24);\n const hRaw = (bytes[22] ?? 0) | ((bytes[23] ?? 0) << 8) | ((bytes[24] ?? 0) << 16) | ((bytes[25] ?? 0) << 24);\n // Treat width as signed; for height take absolute value (top-down bitmaps\n // store negative).\n const wSigned = (w | 0) >>> 0 > 0x7fffffff ? (w | 0) - 0x100000000 : w;\n const hSigned = (hRaw | 0) >>> 0 > 0x7fffffff ? (hRaw | 0) - 0x100000000 : hRaw;\n return { width: wSigned, height: Math.abs(hSigned) };\n};\n\nconst jpegDimensions = (bytes: Uint8Array): Dimensions | undefined => {\n // Walk the marker stream until an SOFn (FFC0..FFCF, excluding FFC4/C8/CC).\n let i = 2; // skip SOI (FFD8)\n while (i + 9 < bytes.length) {\n if (bytes[i] !== 0xff) return undefined;\n let marker = bytes[i + 1] ?? 0;\n while (marker === 0xff) {\n // Padding bytes. Advance to the next non-FF.\n i += 1;\n marker = bytes[i + 1] ?? 0;\n }\n i += 2;\n const isSof = marker >= 0xc0 && marker <= 0xcf && marker !== 0xc4 && marker !== 0xc8 && marker !== 0xcc;\n const len = readU16BE(bytes, i);\n if (isSof) {\n // SOF segment: 2 bytes length, 1 byte precision, 2 bytes height, 2 bytes\n // width.\n if (i + 7 > bytes.length) return undefined;\n const height = readU16BE(bytes, i + 3);\n const width = readU16BE(bytes, i + 5);\n return { width, height };\n }\n i += len;\n }\n return undefined;\n};\n\nconst webpDimensions = (bytes: Uint8Array): Dimensions | undefined => {\n // RIFF header is 12 bytes; chunk type is at offset 12.\n if (bytes.length < 30) return undefined;\n // VP8 (lossy): \"VP8 \" chunk; width / height at offset 26 / 28 (LE 14-bit each\n // + 2 unused bits).\n if (\n bytes[12] === 0x56 && // V\n bytes[13] === 0x50 && // P\n bytes[14] === 0x38 && // 8\n bytes[15] === 0x20 // space\n ) {\n const w = readU16LE(bytes, 26) & 0x3fff;\n const h = readU16LE(bytes, 28) & 0x3fff;\n return { width: w, height: h };\n }\n // VP8L (lossless): 14-bit width-1 / height-1 packed little-endian starting at\n // offset 21.\n if (\n bytes[12] === 0x56 &&\n bytes[13] === 0x50 &&\n bytes[14] === 0x38 &&\n bytes[15] === 0x4c // L\n ) {\n if (bytes.length < 25) return undefined;\n const b0 = bytes[21] ?? 0;\n const b1 = bytes[22] ?? 0;\n const b2 = bytes[23] ?? 0;\n const b3 = bytes[24] ?? 0;\n const w = (b0 | (b1 << 8)) & 0x3fff;\n const h = ((b1 >> 6) | (b2 << 2) | ((b3 & 0x0f) << 10)) & 0x3fff;\n return { width: w + 1, height: h + 1 };\n }\n // VP8X (extended): width-1 (24 bits) / height-1 (24 bits) at offset 24 / 27.\n if (\n bytes[12] === 0x56 &&\n bytes[13] === 0x50 &&\n bytes[14] === 0x38 &&\n bytes[15] === 0x58 // X\n ) {\n if (bytes.length < 30) return undefined;\n const w = (bytes[24] ?? 0) | ((bytes[25] ?? 0) << 8) | ((bytes[26] ?? 0) << 16);\n const h = (bytes[27] ?? 0) | ((bytes[28] ?? 0) << 8) | ((bytes[29] ?? 0) << 16);\n return { width: w + 1, height: h + 1 };\n }\n return undefined;\n};\n\nexport function detectImageDimensions(bytes: Uint8Array, format: XlsxImageFormat): Dimensions {\n let dims: Dimensions | undefined;\n switch (format) {\n case 'png':\n dims = pngDimensions(bytes);\n break;\n case 'jpeg':\n dims = jpegDimensions(bytes);\n break;\n case 'gif':\n dims = gifDimensions(bytes);\n break;\n case 'bmp':\n dims = bmpDimensions(bytes);\n break;\n case 'webp':\n dims = webpDimensions(bytes);\n break;\n default:\n // tiff / svg / emf / wmf — readers can fall back to 0 and Excel will\n // assign defaults.\n dims = undefined;\n }\n return dims ?? { width: 0, height: 0 };\n}\n\n// ---- Public factory --------------------------------------------------------\n\n/**\n * Build an `XlsxImage` from raw bytes. Detects the format and dimensions\n * automatically. Caller may pass an explicit `format` to override detection\n * (e.g. when bytes were read from a `data:` URL with a known MIME type) or\n * `width`/`height` to skip the parser.\n */\nexport function loadImage(\n bytes: Uint8Array,\n opts: { format?: XlsxImageFormat; width?: number; height?: number } = {},\n): XlsxImage {\n const format = opts.format ?? detectImageFormat(bytes);\n if (!format) {\n throw new OpenXmlIoError('loadImage: could not determine image format from bytes');\n }\n const dims =\n opts.width !== undefined && opts.height !== undefined\n ? { width: opts.width, height: opts.height }\n : detectImageDimensions(bytes, format);\n return { bytes, format, width: dims.width, height: dims.height };\n}\n","// DrawingML anchors.\n//\n// An anchor positions a drawing (chart / image / shape) inside a worksheet.\n// ECMA-376 §20.5.2 defines three kinds:\n//\n// absolute — fixed (x, y) and (cx, cy) in EMU; ignores cell layout. oneCell —\n// pinned at `from` cell, fixed extent. Resizes with the\n// cell only on its top-left corner.\n// twoCell — anchored to `from` and `to` cells. Resizes / moves with\n// both corners depending on `editAs`.\n//\n// Coordinates are EMU (English Metric Units). 1 inch = 914400 EMU, 1 cm =\n// 360000 EMU, 1 px = 9525 EMU at 96 dpi.\n\nimport { columnIndexFromLetter } from '../utils/coordinate.js';\nimport { OpenXmlSchemaError } from '../utils/exceptions.js';\nimport { emuFromPx } from '../utils/units.js';\n\n/** EMU = English Metric Units. Drawing coordinates are stored in EMU on the wire. */\nexport interface Point2D {\n /** Horizontal offset in EMU. */\n x: number;\n /** Vertical offset in EMU. */\n y: number;\n}\n\nexport interface PositiveSize2D {\n cx: number;\n cy: number;\n}\n\n/**\n * `from` / `to` corners reference a cell by 0-based column + row index plus an\n * EMU offset within the cell. Excel's wire format is 0-based here even though\n * cell references in formulas / sheetData are 1-based.\n */\nexport interface AnchorMarker {\n col: number;\n colOff: number;\n row: number;\n rowOff: number;\n}\n\nexport type DrawingAnchor =\n | { kind: 'absolute'; pos: Point2D; ext: PositiveSize2D }\n | { kind: 'oneCell'; from: AnchorMarker; ext: PositiveSize2D }\n | { kind: 'twoCell'; from: AnchorMarker; to: AnchorMarker; editAs?: 'twoCell' | 'oneCell' | 'absolute' };\n\nconst A1_RE = /^([A-Za-z]{1,3})([1-9][0-9]*)$/;\n\n/**\n * Convert a cell ref (\"A1\", \"C5\") to a 0-based AnchorMarker with `colOff =\n * rowOff = 0`. Throws on malformed refs.\n */\nexport function anchorMarkerFromCellRef(ref: string): AnchorMarker {\n const m = A1_RE.exec(ref);\n if (!m || m[1] === undefined || m[2] === undefined) {\n throw new OpenXmlSchemaError(`anchorMarkerFromCellRef: invalid coordinate \"${ref}\"`);\n }\n const col = columnIndexFromLetter(m[1]) - 1; // 0-based\n const row = Number.parseInt(m[2], 10) - 1; // 0-based\n return { col, colOff: 0, row, rowOff: 0 };\n}\n\n/** Build an absolute anchor from an (x, y, cx, cy) EMU bundle. */\nexport function makeAbsoluteAnchor(opts: { x: number; y: number; cx: number; cy: number }): DrawingAnchor {\n return { kind: 'absolute', pos: { x: opts.x, y: opts.y }, ext: { cx: opts.cx, cy: opts.cy } };\n}\n\n/** Build a one-cell anchor pinned at `from` with an explicit pixel extent. */\nexport function makeOneCellAnchor(opts: {\n from: string | AnchorMarker;\n widthPx: number;\n heightPx: number;\n}): DrawingAnchor {\n const from = typeof opts.from === 'string' ? anchorMarkerFromCellRef(opts.from) : opts.from;\n return {\n kind: 'oneCell',\n from,\n ext: { cx: emuFromPx(opts.widthPx), cy: emuFromPx(opts.heightPx) },\n };\n}\n\n/**\n * Build a two-cell anchor from cell-ref pairs (or pre-built markers). Defaults\n * to `editAs='twoCell'` — drag both corners with the cells.\n */\nexport function makeTwoCellAnchor(opts: {\n from: string | AnchorMarker;\n to: string | AnchorMarker;\n editAs?: 'twoCell' | 'oneCell' | 'absolute';\n}): DrawingAnchor {\n const from = typeof opts.from === 'string' ? anchorMarkerFromCellRef(opts.from) : opts.from;\n const to = typeof opts.to === 'string' ? anchorMarkerFromCellRef(opts.to) : opts.to;\n return {\n kind: 'twoCell',\n from,\n to,\n ...(opts.editAs !== undefined ? { editAs: opts.editAs } : {}),\n };\n}\n","// Spreadsheet drawing data model.\n//\n// A `Drawing` is the per-worksheet `xl/drawings/drawingN.xml` part — a list of\n// anchor entries, each carrying a content variant (chart, picture, shape,\n// connector, group). Charts and pictures are modeled; everything else is kept\n// as the verbatim source XML so it survives a round-trip untouched.\n\nimport type { ChartSpace } from '../chart/chart.js';\nimport type { CxChartSpace } from '../chart/cx/chartex.js';\nimport type { DrawingAnchor } from './anchor.js';\nimport type { ShapeProperties } from './dml/shape-properties.js';\nimport type { XlsxImage } from './image.js';\n\n/** Reference to a chart part — the chart's drawing-rels rId resolves to xl/charts/chartN.xml. */\nexport interface ChartReference {\n /** Drawing-rels rId pointing at the chart part. Populated on read; the writer assigns its own. */\n rId?: string;\n /**\n * Legacy ECMA-376 chart payload (`c:chartSpace`). Stage-1 supports BarChart\n * end-to-end; other chart kinds populate this field as their parsers /\n * writers land.\n */\n space?: ChartSpace;\n /**\n * Excel-2016 chartex payload (`cx:chartSpace`). Mutually exclusive with\n * {@link space} for any given drawing item; the parser sniffs the root\n * element and populates whichever is appropriate.\n */\n cxSpace?: CxChartSpace;\n /**\n * `true` when the resolved chart part is a chartex (`cx:`) chart. Set by the\n * package writer so the drawing emitter knows to use the chartex\n * `<a:graphicData uri>` instead of the legacy chart URI — Excel rejects the\n * workbook when the URI doesn't match the chart's actual root namespace.\n */\n isCx?: boolean;\n}\n\n/** Reference to an embedded picture inside a worksheet drawing. */\nexport interface PictureReference {\n /** Drawing-rels rId pointing at the embedded image. Populated on read; the writer assigns its own. */\n rId?: string;\n /** Resolved image bytes + metadata. Populated on read; the writer reads it back. */\n image?: XlsxImage;\n /** Picture display name (`<xdr:cNvPr name=\"...\">`). */\n name?: string;\n /** Optional alt-text description. */\n descr?: string;\n /** Hidden flag (`<xdr:cNvPr hidden=\"1\"/>`). */\n hidden?: boolean;\n /** Per-picture shape properties (extra fill / line / rotation). */\n spPr?: ShapeProperties;\n}\n\nexport interface DrawingItem {\n anchor: DrawingAnchor;\n content:\n | { kind: 'chart'; chart: ChartReference }\n | { kind: 'picture'; picture: PictureReference }\n | { kind: 'unsupported'; rawTag: string };\n /**\n * The whole source anchor element, captured verbatim. Set by the reader for\n * `unsupported` content — a plain shape, a group, a connector — because the\n * model has no slot for its geometry, text or `<xdr:clientData>` attributes.\n * When present the writer emits it as-is instead of rebuilding the anchor\n * from {@link anchor} + {@link content}.\n */\n raw?: import('../xml/tree.js').XmlNode;\n}\n\nexport interface Drawing {\n items: DrawingItem[];\n /**\n * `<xdr:wsDr>` children that aren't anchors — in practice Excel's\n * `<mc:AlternateContent>` wrapper around an anchor that needs a newer\n * feature. Kept verbatim together with the `items` index they sat before, so\n * document order (which is z-order) survives a round-trip.\n */\n rawChildren?: Array<{ beforeItem: number; node: import('../xml/tree.js').XmlNode }>;\n /**\n * Drawing-rels entries the verbatim nodes above reference (`r:embed` on a\n * picture inside a group, `r:id` on a shape's hyperlink, …). Carried over\n * from the source part under their original ids so those references still\n * resolve after a re-save; the writer allocates its own ids around them.\n */\n rawRels?: import('../packaging/relationships.js').Relationships;\n}\n\nexport function makeDrawing(items: DrawingItem[] = []): Drawing {\n return { items };\n}\n\nexport function makeChartDrawingItem(anchor: DrawingAnchor, chart: ChartReference = {}): DrawingItem {\n return { anchor, content: { kind: 'chart', chart } };\n}\n\nexport function makePictureDrawingItem(anchor: DrawingAnchor, picture: PictureReference | XlsxImage): DrawingItem {\n // Distinguish raw image bytes from a full PictureReference by checking the\n // discriminator: XlsxImage carries `format`, PictureReference doesn't.\n const ref: PictureReference = 'format' in picture ? { image: picture as XlsxImage } : (picture as PictureReference);\n return { anchor, content: { kind: 'picture', picture: ref } };\n}\n\n// ---- Worksheet ergonomic helpers ----------------------------------------\n\nimport { loadImage } from './image.js';\nimport { makeOneCellAnchor } from './anchor.js';\nimport type { Worksheet } from '../worksheet/worksheet.js';\n\n/**\n * Drop an image onto a worksheet at a single-cell anchor. Lazy-allocates\n * `ws.drawing` (as `makeDrawing([])`) on first call and appends a picture\n * DrawingItem.\n *\n * `image` accepts either an `XlsxImage` (already loaded via `loadImage`) or raw\n * image bytes — in the bytes case, this helper sniffs the format with\n * `loadImage` itself.\n *\n * `at` is a cell ref like `\"C3\"`. Override `widthPx` / `heightPx` to scale;\n * otherwise the helper uses 96×96 defaults that look fine for typical icons.\n */\nexport const addImageAt = (\n ws: Worksheet,\n at: string,\n image: XlsxImage | Uint8Array,\n opts: { widthPx?: number; heightPx?: number } = {},\n): DrawingItem => {\n const xlsxImage: XlsxImage = image instanceof Uint8Array ? loadImage(image) : image;\n const anchor = makeOneCellAnchor({\n from: at,\n widthPx: opts.widthPx ?? 96,\n heightPx: opts.heightPx ?? 96,\n });\n const item = makePictureDrawingItem(anchor, xlsxImage);\n if (!ws.drawing) ws.drawing = makeDrawing([]);\n ws.drawing.items.push(item);\n return item;\n};\n\n/**\n * Anchor a chart to a worksheet at a single-cell ref. Lazy-allocates\n * `ws.drawing`. `chart` is the same `ChartReference` shape `makeChart\n * DrawingItem` accepts (`{ space }` for legacy chart, `{ cxSpace }` for\n * chartex).\n */\nexport const addChartAt = (\n ws: Worksheet,\n at: string,\n chart: ChartReference,\n opts: { widthPx?: number; heightPx?: number } = {},\n): DrawingItem => {\n const anchor = makeOneCellAnchor({\n from: at,\n widthPx: opts.widthPx ?? 480,\n heightPx: opts.heightPx ?? 320,\n });\n const item = makeChartDrawingItem(anchor, chart);\n if (!ws.drawing) ws.drawing = makeDrawing([]);\n ws.drawing.items.push(item);\n return item;\n};\n\n/**\n * Read-only snapshot of every picture DrawingItem on the sheet. Returns the\n * matching items (each with its anchor + picture reference). Empty array when\n * the sheet has no drawing or only non-picture items.\n */\nexport const listImagesOnSheet = (ws: Worksheet): ReadonlyArray<DrawingItem> => {\n if (!ws.drawing) return [];\n return ws.drawing.items.filter((it) => it.content.kind === 'picture');\n};\n\n/**\n * Read-only snapshot of every chart DrawingItem on the sheet. Each item has its\n * anchor + chart reference.\n */\nexport const listChartsOnSheet = (ws: Worksheet): ReadonlyArray<DrawingItem> => {\n if (!ws.drawing) return [];\n return ws.drawing.items.filter((it) => it.content.kind === 'chart');\n};\n\n/**\n * Drop every DrawingItem from the worksheet. Returns the count removed. The\n * `ws.drawing` field itself is left in place (empty) so subsequent `addImageAt`\n * / `addChartAt` calls don't have to re-allocate.\n */\nexport const removeAllDrawingItems = (ws: Worksheet): number => {\n if (!ws.drawing) return 0;\n const n = ws.drawing.items.length;\n ws.drawing.items = [];\n return n;\n};\n\n/**\n * Drop every picture DrawingItem from the worksheet, leaving charts and any\n * other content kinds untouched. Returns the count removed.\n */\nexport const removeAllImages = (ws: Worksheet): number => {\n if (!ws.drawing) return 0;\n const before = ws.drawing.items.length;\n ws.drawing.items = ws.drawing.items.filter((it) => it.content.kind !== 'picture');\n return before - ws.drawing.items.length;\n};\n\n/**\n * Drop every chart DrawingItem from the worksheet, leaving pictures and any\n * other content kinds untouched. Returns the count removed.\n */\nexport const removeAllCharts = (ws: Worksheet): number => {\n if (!ws.drawing) return 0;\n const before = ws.drawing.items.length;\n ws.drawing.items = ws.drawing.items.filter((it) => it.content.kind !== 'chart');\n return before - ws.drawing.items.length;\n};\n"],"mappings":";;;;;;;;;;AAiBA,MAAa,yBAAoE;CAC/E,KAAK;CACL,MAAM;CACN,KAAK;CACL,KAAK;CACL,MAAM;CACN,MAAM;CACN,KAAK;CACL,KAAK;CACL,KAAK;AACP;;AAGA,MAAa,oBAA+D;CAC1E,KAAK;CACL,MAAM;CACN,KAAK;CACL,KAAK;CACL,MAAM;CACN,MAAM;CACN,KAAK;CACL,KAAK;CACL,KAAK;AACP;AAiBA,MAAM,cAAc,OAAmB,KAA4B,SAAS,MAAe;CACzF,IAAI,MAAM,SAAS,SAAS,IAAI,QAAQ,OAAO;CAC/C,KAAK,IAAI,IAAI,GAAG,IAAI,IAAI,QAAQ,KAC9B,IAAI,MAAM,SAAS,OAAO,IAAI,IAAI,OAAO;CAE3C,OAAO;AACT;AAEA,MAAM,UAAU;CAAC;CAAM;CAAM;CAAM;CAAM;CAAM;CAAM;CAAM;AAAI;AAC/D,MAAM,WAAW;CAAC;CAAM;CAAM;AAAI;AAClC,MAAM,QAAQ;CAAC;CAAM;CAAM;CAAM;CAAM;CAAM;AAAI;AACjD,MAAM,QAAQ;CAAC;CAAM;CAAM;CAAM;CAAM;CAAM;AAAI;AACjD,MAAM,UAAU,CAAC,IAAM,EAAI;AAC3B,MAAM,WAAW;CAAC;CAAM;CAAM;CAAM;AAAI;AACxC,MAAM,WAAW;CAAC;CAAM;CAAM;CAAM;AAAI;AACxC,MAAM,UAAU;CAAC;CAAM;CAAM;CAAM;AAAI;AACvC,MAAM,UAAU;CAAC;CAAM;CAAM;CAAM;AAAI;AACvC,MAAM,UAAU;CAAC;CAAM;CAAM;CAAM;AAAI;AACvC,MAAM,UAAU;CAAC;CAAM;CAAM;CAAM;AAAI;AACvC,MAAM,oBAAoB;CAAC;CAAM;CAAM;CAAM;AAAI;AACjD,MAAM,eAAe;CAAC;CAAM;CAAM;CAAM;AAAI;AAE5C,MAAM,SAAS,UAA+B;CAG5C,MAAM,OAAO,IAAI,YAAY,SAAS,EAAE,OAAO,MAAM,CAAC,CAAC,CAAC,OAAO,MAAM,SAAS,GAAG,KAAK,IAAI,MAAM,QAAQ,GAAG,CAAC,CAAC;CAC7G,OAAO,UAAU,KAAK,IAAI;AAC5B;AAEA,SAAgB,kBAAkB,OAAgD;CAChF,IAAI,WAAW,OAAO,OAAO,GAAG,OAAO;CACvC,IAAI,WAAW,OAAO,QAAQ,GAAG,OAAO;CACxC,IAAI,WAAW,OAAO,KAAK,KAAK,WAAW,OAAO,KAAK,GAAG,OAAO;CACjE,IAAI,WAAW,OAAO,OAAO,GAAG,OAAO;CACvC,IAAI,WAAW,OAAO,QAAQ,KAAK,WAAW,OAAO,UAAU,CAAC,GAAG,OAAO;CAC1E,IAAI,WAAW,OAAO,OAAO,KAAK,WAAW,OAAO,OAAO,GAAG,OAAO;CACrE,IAAI,WAAW,OAAO,OAAO,KAAK,WAAW,OAAO,SAAS,EAAE,GAAG,OAAO;CACzE,IAAI,WAAW,OAAO,iBAAiB,KAAK,WAAW,OAAO,YAAY,GAAG,OAAO;CACpF,IAAI,MAAM,KAAK,GAAG,OAAO;AAE3B;AAIA,MAAM,aAAa,OAAmB,YAClC,MAAM,WAAW,MAAM,MACvB,MAAM,SAAS,MAAM,MAAM,MAC3B,MAAM,SAAS,MAAM,MAAM,KAC5B,MAAM,SAAS,MAAM;AAExB,MAAM,aAAa,OAAmB,YAA4B,MAAM,WAAW,MAAO,MAAM,SAAS,MAAM,MAAM;AAErH,MAAM,aAAa,OAAmB,YAA6B,MAAM,WAAW,MAAM,KAAM,MAAM,SAAS,MAAM;AAOrH,MAAM,iBAAiB,UAA8C;CAGnE,IAAI,MAAM,SAAS,IAAI,OAAO,KAAA;CAC9B,IAAI,MAAM,QAAQ,MAAQ,MAAM,QAAQ,MAAQ,MAAM,QAAQ,MAAQ,MAAM,QAAQ,IAClF;CAEF,OAAO;EAAE,OAAO,UAAU,OAAO,EAAE;EAAG,QAAQ,UAAU,OAAO,EAAE;CAAE;AACrE;AAEA,MAAM,iBAAiB,UAA8C;CACnE,IAAI,MAAM,SAAS,IAAI,OAAO,KAAA;CAC9B,OAAO;EAAE,OAAO,UAAU,OAAO,CAAC;EAAG,QAAQ,UAAU,OAAO,CAAC;CAAE;AACnE;AAEA,MAAM,iBAAiB,UAA8C;CACnE,IAAI,MAAM,SAAS,IAAI,OAAO,KAAA;CAG9B,MAAM,KAAK,MAAM,OAAO,MAAO,MAAM,OAAO,MAAM,KAAO,MAAM,OAAO,MAAM,MAAQ,MAAM,OAAO,MAAM;CACvG,MAAM,QAAQ,MAAM,OAAO,MAAO,MAAM,OAAO,MAAM,KAAO,MAAM,OAAO,MAAM,MAAQ,MAAM,OAAO,MAAM;CAG1G,MAAM,WAAW,IAAI,OAAO,IAAI,cAAc,IAAI,KAAK,aAAc;CACrE,MAAM,WAAW,OAAO,OAAO,IAAI,cAAc,OAAO,KAAK,aAAc;CAC3E,OAAO;EAAE,OAAO;EAAS,QAAQ,KAAK,IAAI,OAAO;CAAE;AACrD;AAEA,MAAM,kBAAkB,UAA8C;CAEpE,IAAI,IAAI;CACR,OAAO,IAAI,IAAI,MAAM,QAAQ;EAC3B,IAAI,MAAM,OAAO,KAAM,OAAO,KAAA;EAC9B,IAAI,SAAS,MAAM,IAAI,MAAM;EAC7B,OAAO,WAAW,KAAM;GAEtB,KAAK;GACL,SAAS,MAAM,IAAI,MAAM;EAC3B;EACA,KAAK;EACL,MAAM,QAAQ,UAAU,OAAQ,UAAU,OAAQ,WAAW,OAAQ,WAAW,OAAQ,WAAW;EACnG,MAAM,MAAM,UAAU,OAAO,CAAC;EAC9B,IAAI,OAAO;GAGT,IAAI,IAAI,IAAI,MAAM,QAAQ,OAAO,KAAA;GACjC,MAAM,SAAS,UAAU,OAAO,IAAI,CAAC;GAErC,OAAO;IAAE,OADK,UAAU,OAAO,IAAI,CACtB;IAAG;GAAO;EACzB;EACA,KAAK;CACP;AAEF;AAEA,MAAM,kBAAkB,UAA8C;CAEpE,IAAI,MAAM,SAAS,IAAI,OAAO,KAAA;CAG9B,IACE,MAAM,QAAQ,MACd,MAAM,QAAQ,MACd,MAAM,QAAQ,MACd,MAAM,QAAQ,IAId,OAAO;EAAE,OAFC,UAAU,OAAO,EAAE,IAAI;EAEd,QADT,UAAU,OAAO,EAAE,IAAI;CACJ;CAI/B,IACE,MAAM,QAAQ,MACd,MAAM,QAAQ,MACd,MAAM,QAAQ,MACd,MAAM,QAAQ,IACd;EACA,IAAI,MAAM,SAAS,IAAI,OAAO,KAAA;EAC9B,MAAM,KAAK,MAAM,OAAO;EACxB,MAAM,KAAK,MAAM,OAAO;EACxB,MAAM,KAAK,MAAM,OAAO;EACxB,MAAM,KAAK,MAAM,OAAO;EACxB,MAAM,KAAK,KAAM,MAAM,KAAM;EAC7B,MAAM,KAAM,MAAM,IAAM,MAAM,KAAO,KAAK,OAAS,MAAO;EAC1D,OAAO;GAAE,OAAO,IAAI;GAAG,QAAQ,IAAI;EAAE;CACvC;CAEA,IACE,MAAM,QAAQ,MACd,MAAM,QAAQ,MACd,MAAM,QAAQ,MACd,MAAM,QAAQ,IACd;EACA,IAAI,MAAM,SAAS,IAAI,OAAO,KAAA;EAC9B,MAAM,KAAK,MAAM,OAAO,MAAO,MAAM,OAAO,MAAM,KAAO,MAAM,OAAO,MAAM;EAC5E,MAAM,KAAK,MAAM,OAAO,MAAO,MAAM,OAAO,MAAM,KAAO,MAAM,OAAO,MAAM;EAC5E,OAAO;GAAE,OAAO,IAAI;GAAG,QAAQ,IAAI;EAAE;CACvC;AAEF;AAEA,SAAgB,sBAAsB,OAAmB,QAAqC;CAC5F,IAAI;CACJ,QAAQ,QAAR;EACE,KAAK;GACH,OAAO,cAAc,KAAK;GAC1B;EACF,KAAK;GACH,OAAO,eAAe,KAAK;GAC3B;EACF,KAAK;GACH,OAAO,cAAc,KAAK;GAC1B;EACF,KAAK;GACH,OAAO,cAAc,KAAK;GAC1B;EACF,KAAK;GACH,OAAO,eAAe,KAAK;GAC3B;EACF,SAGE,OAAO,KAAA;CACX;CACA,OAAO,QAAQ;EAAE,OAAO;EAAG,QAAQ;CAAE;AACvC;;;;;;;AAUA,SAAgB,UACd,OACA,OAAsE,CAAC,GAC5D;CACX,MAAM,SAAS,KAAK,UAAU,kBAAkB,KAAK;CACrD,IAAI,CAAC,QACH,MAAM,IAAI,eAAe,wDAAwD;CAEnF,MAAM,OACJ,KAAK,UAAU,KAAA,KAAa,KAAK,WAAW,KAAA,IACxC;EAAE,OAAO,KAAK;EAAO,QAAQ,KAAK;CAAO,IACzC,sBAAsB,OAAO,MAAM;CACzC,OAAO;EAAE;EAAO;EAAQ,OAAO,KAAK;EAAO,QAAQ,KAAK;CAAO;AACjE;;;ACzNA,MAAM,QAAQ;;;;;AAMd,SAAgB,wBAAwB,KAA2B;CACjE,MAAM,IAAI,MAAM,KAAK,GAAG;CACxB,IAAI,CAAC,KAAK,EAAE,OAAO,KAAA,KAAa,EAAE,OAAO,KAAA,GACvC,MAAM,IAAI,mBAAmB,gDAAgD,IAAI,EAAE;CAIrF,OAAO;EAAE,KAFG,sBAAsB,EAAE,EAAE,IAAI;EAE5B,QAAQ;EAAG,KADb,OAAO,SAAS,EAAE,IAAI,EAAE,IAAI;EACV,QAAQ;CAAE;AAC1C;;AAGA,SAAgB,mBAAmB,MAAuE;CACxG,OAAO;EAAE,MAAM;EAAY,KAAK;GAAE,GAAG,KAAK;GAAG,GAAG,KAAK;EAAE;EAAG,KAAK;GAAE,IAAI,KAAK;GAAI,IAAI,KAAK;EAAG;CAAE;AAC9F;;AAGA,SAAgB,kBAAkB,MAIhB;CAEhB,OAAO;EACL,MAAM;EACN,MAHW,OAAO,KAAK,SAAS,WAAW,wBAAwB,KAAK,IAAI,IAAI,KAAK;EAIrF,KAAK;GAAE,IAAI,UAAU,KAAK,OAAO;GAAG,IAAI,UAAU,KAAK,QAAQ;EAAE;CACnE;AACF;;;ACOA,SAAgB,YAAY,QAAuB,CAAC,GAAY;CAC9D,OAAO,EAAE,MAAM;AACjB;AAEA,SAAgB,qBAAqB,QAAuB,QAAwB,CAAC,GAAgB;CACnG,OAAO;EAAE;EAAQ,SAAS;GAAE,MAAM;GAAS;EAAM;CAAE;AACrD;AAEA,SAAgB,uBAAuB,QAAuB,SAAoD;CAIhH,OAAO;EAAE;EAAQ,SAAS;GAAE,MAAM;GAAW,SADf,YAAY,UAAU,EAAE,OAAO,QAAqB,IAAK;EAC7B;CAAE;AAC9D;;;;;;;;;;;;;AAoBA,MAAa,cACX,IACA,IACA,OACA,OAAgD,CAAC,MACjC;CAChB,MAAM,YAAuB,iBAAiB,aAAa,UAAU,KAAK,IAAI;CAM9E,MAAM,OAAO,uBALE,kBAAkB;EAC/B,MAAM;EACN,SAAS,KAAK,WAAW;EACzB,UAAU,KAAK,YAAY;CAC7B,CACyC,GAAG,SAAS;CACrD,IAAI,CAAC,GAAG,SAAS,GAAG,UAAU,YAAY,CAAC,CAAC;CAC5C,GAAG,QAAQ,MAAM,KAAK,IAAI;CAC1B,OAAO;AACT;;;;;;;AAQA,MAAa,cACX,IACA,IACA,OACA,OAAgD,CAAC,MACjC;CAMhB,MAAM,OAAO,qBALE,kBAAkB;EAC/B,MAAM;EACN,SAAS,KAAK,WAAW;EACzB,UAAU,KAAK,YAAY;CAC7B,CACuC,GAAG,KAAK;CAC/C,IAAI,CAAC,GAAG,SAAS,GAAG,UAAU,YAAY,CAAC,CAAC;CAC5C,GAAG,QAAQ,MAAM,KAAK,IAAI;CAC1B,OAAO;AACT;;;;;;AAOA,MAAa,qBAAqB,OAA8C;CAC9E,IAAI,CAAC,GAAG,SAAS,OAAO,CAAC;CACzB,OAAO,GAAG,QAAQ,MAAM,QAAQ,OAAO,GAAG,QAAQ,SAAS,SAAS;AACtE;;;;;AAMA,MAAa,qBAAqB,OAA8C;CAC9E,IAAI,CAAC,GAAG,SAAS,OAAO,CAAC;CACzB,OAAO,GAAG,QAAQ,MAAM,QAAQ,OAAO,GAAG,QAAQ,SAAS,OAAO;AACpE;;;;;;AAOA,MAAa,yBAAyB,OAA0B;CAC9D,IAAI,CAAC,GAAG,SAAS,OAAO;CACxB,MAAM,IAAI,GAAG,QAAQ,MAAM;CAC3B,GAAG,QAAQ,QAAQ,CAAC;CACpB,OAAO;AACT;;;;;AAMA,MAAa,mBAAmB,OAA0B;CACxD,IAAI,CAAC,GAAG,SAAS,OAAO;CACxB,MAAM,SAAS,GAAG,QAAQ,MAAM;CAChC,GAAG,QAAQ,QAAQ,GAAG,QAAQ,MAAM,QAAQ,OAAO,GAAG,QAAQ,SAAS,SAAS;CAChF,OAAO,SAAS,GAAG,QAAQ,MAAM;AACnC;;;;;AAMA,MAAa,mBAAmB,OAA0B;CACxD,IAAI,CAAC,GAAG,SAAS,OAAO;CACxB,MAAM,SAAS,GAAG,QAAQ,MAAM;CAChC,GAAG,QAAQ,QAAQ,GAAG,QAAQ,MAAM,QAAQ,OAAO,GAAG,QAAQ,SAAS,OAAO;CAC9E,OAAO,SAAS,GAAG,QAAQ,MAAM;AACnC"}
|
|
1
|
+
{"version":3,"file":"drawing-Cv6UtInY.mjs","names":[],"sources":["../src/drawing/image.ts","../src/drawing/anchor.ts","../src/drawing/drawing.ts"],"sourcesContent":["// Image embedding.\n//\n// `XlsxImage` is the workbook-level handle for any image referenced from a\n// worksheet drawing (or chart `<a:blipFill>`). Bytes are kept verbatim so\n// re-saving never re-encodes; format / width / height are detected from the\n// file header so callers don't have to specify them.\n\nimport { OpenXmlIoError } from '../utils/exceptions.js';\n\nexport type XlsxImageFormat = 'png' | 'jpeg' | 'gif' | 'bmp' | 'webp' | 'tiff' | 'svg' | 'emf' | 'wmf';\n\n/**\n * Map from image format to the Content-Types Default extension that Excel uses\n * in `[Content_Types].xml`. SVG / EMF / WMF use the same extension as the\n * format string; PNG/JPEG/GIF/BMP do too. WebP and TIFF use their canonical\n * short names.\n */\nexport const IMAGE_FORMAT_EXTENSION: Readonly<Record<XlsxImageFormat, string>> = {\n png: 'png',\n jpeg: 'jpeg',\n gif: 'gif',\n bmp: 'bmp',\n webp: 'webp',\n tiff: 'tiff',\n svg: 'svg',\n emf: 'emf',\n wmf: 'wmf',\n};\n\n/** Map from image format to its IANA `image/*` MIME type. */\nexport const IMAGE_FORMAT_MIME: Readonly<Record<XlsxImageFormat, string>> = {\n png: 'image/png',\n jpeg: 'image/jpeg',\n gif: 'image/gif',\n bmp: 'image/bmp',\n webp: 'image/webp',\n tiff: 'image/tiff',\n svg: 'image/svg+xml',\n emf: 'image/x-emf',\n wmf: 'image/x-wmf',\n};\n\nexport interface XlsxImage {\n bytes: Uint8Array;\n format: XlsxImageFormat;\n /** Pixel width. Zero when dimensions can't be determined for the format. */\n width: number;\n /** Pixel height. Zero when dimensions can't be determined for the format. */\n height: number;\n /** ZIP archive path, e.g. `xl/media/image3.png`. Set by the writer. */\n path?: string;\n /** rels-resolved id used by `<a:blip r:embed=\"...\">`. Set by the loader. */\n rId?: string;\n}\n\n// ---- Magic-byte format detection ------------------------------------------\n\nconst startsWith = (bytes: Uint8Array, sig: ReadonlyArray<number>, offset = 0): boolean => {\n if (bytes.length < offset + sig.length) return false;\n for (let i = 0; i < sig.length; i++) {\n if (bytes[offset + i] !== sig[i]) return false;\n }\n return true;\n};\n\nconst PNG_SIG = [0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a];\nconst JPEG_SIG = [0xff, 0xd8, 0xff];\nconst GIF87 = [0x47, 0x49, 0x46, 0x38, 0x37, 0x61];\nconst GIF89 = [0x47, 0x49, 0x46, 0x38, 0x39, 0x61];\nconst BMP_SIG = [0x42, 0x4d];\nconst RIFF_SIG = [0x52, 0x49, 0x46, 0x46];\nconst WEBP_TAG = [0x57, 0x45, 0x42, 0x50];\nconst TIFF_LE = [0x49, 0x49, 0x2a, 0x00];\nconst TIFF_BE = [0x4d, 0x4d, 0x00, 0x2a];\nconst EMF_SIG = [0x01, 0x00, 0x00, 0x00];\nconst EMF_TAG = [0x20, 0x45, 0x4d, 0x46]; // ' EMF' at offset 40\nconst WMF_SIG_PLACEABLE = [0xd7, 0xcd, 0xc6, 0x9a];\nconst WMF_SIG_BARE = [0x01, 0x00, 0x09, 0x00];\n\nconst isSvg = (bytes: Uint8Array): boolean => {\n // Inspect the first ~512 bytes for an `<svg` tag (with optional XML / DOCTYPE\n // preamble).\n const head = new TextDecoder('utf-8', { fatal: false }).decode(bytes.subarray(0, Math.min(bytes.length, 512)));\n return /<svg\\b/i.test(head);\n};\n\nexport function detectImageFormat(bytes: Uint8Array): XlsxImageFormat | undefined {\n if (startsWith(bytes, PNG_SIG)) return 'png';\n if (startsWith(bytes, JPEG_SIG)) return 'jpeg';\n if (startsWith(bytes, GIF87) || startsWith(bytes, GIF89)) return 'gif';\n if (startsWith(bytes, BMP_SIG)) return 'bmp';\n if (startsWith(bytes, RIFF_SIG) && startsWith(bytes, WEBP_TAG, 8)) return 'webp';\n if (startsWith(bytes, TIFF_LE) || startsWith(bytes, TIFF_BE)) return 'tiff';\n if (startsWith(bytes, EMF_SIG) && startsWith(bytes, EMF_TAG, 40)) return 'emf';\n if (startsWith(bytes, WMF_SIG_PLACEABLE) || startsWith(bytes, WMF_SIG_BARE)) return 'wmf';\n if (isSvg(bytes)) return 'svg';\n return undefined;\n}\n\n// ---- Per-format dimension extraction --------------------------------------\n\nconst readU32BE = (bytes: Uint8Array, offset: number): number =>\n ((bytes[offset] ?? 0) << 24) |\n ((bytes[offset + 1] ?? 0) << 16) |\n ((bytes[offset + 2] ?? 0) << 8) |\n (bytes[offset + 3] ?? 0);\n\nconst readU16LE = (bytes: Uint8Array, offset: number): number => (bytes[offset] ?? 0) | ((bytes[offset + 1] ?? 0) << 8);\n\nconst readU16BE = (bytes: Uint8Array, offset: number): number => ((bytes[offset] ?? 0) << 8) | (bytes[offset + 1] ?? 0);\n\ninterface Dimensions {\n width: number;\n height: number;\n}\n\nconst pngDimensions = (bytes: Uint8Array): Dimensions | undefined => {\n // PNG: 8-byte signature, 4-byte chunk-length, 4-byte type (\"IHDR\"), then\n // 4-byte width, 4-byte height (both big-endian).\n if (bytes.length < 24) return undefined;\n if (bytes[12] !== 0x49 || bytes[13] !== 0x48 || bytes[14] !== 0x44 || bytes[15] !== 0x52) {\n return undefined;\n }\n return { width: readU32BE(bytes, 16), height: readU32BE(bytes, 20) };\n};\n\nconst gifDimensions = (bytes: Uint8Array): Dimensions | undefined => {\n if (bytes.length < 10) return undefined;\n return { width: readU16LE(bytes, 6), height: readU16LE(bytes, 8) };\n};\n\nconst bmpDimensions = (bytes: Uint8Array): Dimensions | undefined => {\n if (bytes.length < 26) return undefined;\n // BITMAPINFOHEADER: width (LE int32) at offset 18, height at offset 22.\n // Height can be negative for top-down bitmaps; we report absolute pixels.\n const w = (bytes[18] ?? 0) | ((bytes[19] ?? 0) << 8) | ((bytes[20] ?? 0) << 16) | ((bytes[21] ?? 0) << 24);\n const hRaw = (bytes[22] ?? 0) | ((bytes[23] ?? 0) << 8) | ((bytes[24] ?? 0) << 16) | ((bytes[25] ?? 0) << 24);\n // Treat width as signed; for height take absolute value (top-down bitmaps\n // store negative).\n const wSigned = (w | 0) >>> 0 > 0x7fffffff ? (w | 0) - 0x100000000 : w;\n const hSigned = (hRaw | 0) >>> 0 > 0x7fffffff ? (hRaw | 0) - 0x100000000 : hRaw;\n return { width: wSigned, height: Math.abs(hSigned) };\n};\n\nconst jpegDimensions = (bytes: Uint8Array): Dimensions | undefined => {\n // Walk the marker stream until an SOFn (FFC0..FFCF, excluding FFC4/C8/CC).\n let i = 2; // skip SOI (FFD8)\n while (i + 9 < bytes.length) {\n if (bytes[i] !== 0xff) return undefined;\n let marker = bytes[i + 1] ?? 0;\n while (marker === 0xff) {\n // Padding bytes. Advance to the next non-FF.\n i += 1;\n marker = bytes[i + 1] ?? 0;\n }\n i += 2;\n const isSof = marker >= 0xc0 && marker <= 0xcf && marker !== 0xc4 && marker !== 0xc8 && marker !== 0xcc;\n const len = readU16BE(bytes, i);\n if (isSof) {\n // SOF segment: 2 bytes length, 1 byte precision, 2 bytes height, 2 bytes\n // width.\n if (i + 7 > bytes.length) return undefined;\n const height = readU16BE(bytes, i + 3);\n const width = readU16BE(bytes, i + 5);\n return { width, height };\n }\n i += len;\n }\n return undefined;\n};\n\nconst webpDimensions = (bytes: Uint8Array): Dimensions | undefined => {\n // RIFF header is 12 bytes; chunk type is at offset 12.\n if (bytes.length < 30) return undefined;\n // VP8 (lossy): \"VP8 \" chunk; width / height at offset 26 / 28 (LE 14-bit each\n // + 2 unused bits).\n if (\n bytes[12] === 0x56 && // V\n bytes[13] === 0x50 && // P\n bytes[14] === 0x38 && // 8\n bytes[15] === 0x20 // space\n ) {\n const w = readU16LE(bytes, 26) & 0x3fff;\n const h = readU16LE(bytes, 28) & 0x3fff;\n return { width: w, height: h };\n }\n // VP8L (lossless): 14-bit width-1 / height-1 packed little-endian starting at\n // offset 21.\n if (\n bytes[12] === 0x56 &&\n bytes[13] === 0x50 &&\n bytes[14] === 0x38 &&\n bytes[15] === 0x4c // L\n ) {\n if (bytes.length < 25) return undefined;\n const b0 = bytes[21] ?? 0;\n const b1 = bytes[22] ?? 0;\n const b2 = bytes[23] ?? 0;\n const b3 = bytes[24] ?? 0;\n const w = (b0 | (b1 << 8)) & 0x3fff;\n const h = ((b1 >> 6) | (b2 << 2) | ((b3 & 0x0f) << 10)) & 0x3fff;\n return { width: w + 1, height: h + 1 };\n }\n // VP8X (extended): width-1 (24 bits) / height-1 (24 bits) at offset 24 / 27.\n if (\n bytes[12] === 0x56 &&\n bytes[13] === 0x50 &&\n bytes[14] === 0x38 &&\n bytes[15] === 0x58 // X\n ) {\n if (bytes.length < 30) return undefined;\n const w = (bytes[24] ?? 0) | ((bytes[25] ?? 0) << 8) | ((bytes[26] ?? 0) << 16);\n const h = (bytes[27] ?? 0) | ((bytes[28] ?? 0) << 8) | ((bytes[29] ?? 0) << 16);\n return { width: w + 1, height: h + 1 };\n }\n return undefined;\n};\n\nexport function detectImageDimensions(bytes: Uint8Array, format: XlsxImageFormat): Dimensions {\n let dims: Dimensions | undefined;\n switch (format) {\n case 'png':\n dims = pngDimensions(bytes);\n break;\n case 'jpeg':\n dims = jpegDimensions(bytes);\n break;\n case 'gif':\n dims = gifDimensions(bytes);\n break;\n case 'bmp':\n dims = bmpDimensions(bytes);\n break;\n case 'webp':\n dims = webpDimensions(bytes);\n break;\n default:\n // tiff / svg / emf / wmf — readers can fall back to 0 and Excel will\n // assign defaults.\n dims = undefined;\n }\n return dims ?? { width: 0, height: 0 };\n}\n\n// ---- Public factory --------------------------------------------------------\n\n/**\n * Build an `XlsxImage` from raw bytes. Detects the format and dimensions\n * automatically. Caller may pass an explicit `format` to override detection\n * (e.g. when bytes were read from a `data:` URL with a known MIME type) or\n * `width`/`height` to skip the parser.\n */\nexport function loadImage(\n bytes: Uint8Array,\n opts: { format?: XlsxImageFormat; width?: number; height?: number } = {},\n): XlsxImage {\n const format = opts.format ?? detectImageFormat(bytes);\n if (!format) {\n throw new OpenXmlIoError('loadImage: could not determine image format from bytes');\n }\n const dims =\n opts.width !== undefined && opts.height !== undefined\n ? { width: opts.width, height: opts.height }\n : detectImageDimensions(bytes, format);\n return { bytes, format, width: dims.width, height: dims.height };\n}\n","// DrawingML anchors.\n//\n// An anchor positions a drawing (chart / image / shape) inside a worksheet.\n// ECMA-376 §20.5.2 defines three kinds:\n//\n// absolute — fixed (x, y) and (cx, cy) in EMU; ignores cell layout. oneCell —\n// pinned at `from` cell, fixed extent. Resizes with the\n// cell only on its top-left corner.\n// twoCell — anchored to `from` and `to` cells. Resizes / moves with\n// both corners depending on `editAs`.\n//\n// Coordinates are EMU (English Metric Units). 1 inch = 914400 EMU, 1 cm =\n// 360000 EMU, 1 px = 9525 EMU at 96 dpi.\n\nimport { columnIndexFromLetter } from '../utils/coordinate.js';\nimport { OpenXmlSchemaError } from '../utils/exceptions.js';\nimport { emuFromPx } from '../utils/units.js';\n\n/** EMU = English Metric Units. Drawing coordinates are stored in EMU on the wire. */\nexport interface Point2D {\n /** Horizontal offset in EMU. */\n x: number;\n /** Vertical offset in EMU. */\n y: number;\n}\n\nexport interface PositiveSize2D {\n cx: number;\n cy: number;\n}\n\n/**\n * `from` / `to` corners reference a cell by 0-based column + row index plus an\n * EMU offset within the cell. Excel's wire format is 0-based here even though\n * cell references in formulas / sheetData are 1-based.\n */\nexport interface AnchorMarker {\n col: number;\n colOff: number;\n row: number;\n rowOff: number;\n}\n\nexport type DrawingAnchor =\n | { kind: 'absolute'; pos: Point2D; ext: PositiveSize2D }\n | { kind: 'oneCell'; from: AnchorMarker; ext: PositiveSize2D }\n | { kind: 'twoCell'; from: AnchorMarker; to: AnchorMarker; editAs?: 'twoCell' | 'oneCell' | 'absolute' };\n\nconst A1_RE = /^([A-Za-z]{1,3})([1-9][0-9]*)$/;\n\n/**\n * Convert a cell ref (\"A1\", \"C5\") to a 0-based AnchorMarker with `colOff =\n * rowOff = 0`. Throws on malformed refs.\n */\nexport function anchorMarkerFromCellRef(ref: string): AnchorMarker {\n const m = A1_RE.exec(ref);\n if (!m || m[1] === undefined || m[2] === undefined) {\n throw new OpenXmlSchemaError(`anchorMarkerFromCellRef: invalid coordinate \"${ref}\"`);\n }\n const col = columnIndexFromLetter(m[1]) - 1; // 0-based\n const row = Number.parseInt(m[2], 10) - 1; // 0-based\n return { col, colOff: 0, row, rowOff: 0 };\n}\n\n/** Build an absolute anchor from an (x, y, cx, cy) EMU bundle. */\nexport function makeAbsoluteAnchor(opts: { x: number; y: number; cx: number; cy: number }): DrawingAnchor {\n return { kind: 'absolute', pos: { x: opts.x, y: opts.y }, ext: { cx: opts.cx, cy: opts.cy } };\n}\n\n/** Build a one-cell anchor pinned at `from` with an explicit pixel extent. */\nexport function makeOneCellAnchor(opts: {\n from: string | AnchorMarker;\n widthPx: number;\n heightPx: number;\n}): DrawingAnchor {\n const from = typeof opts.from === 'string' ? anchorMarkerFromCellRef(opts.from) : opts.from;\n return {\n kind: 'oneCell',\n from,\n ext: { cx: emuFromPx(opts.widthPx), cy: emuFromPx(opts.heightPx) },\n };\n}\n\n/**\n * Build a two-cell anchor from cell-ref pairs (or pre-built markers). Defaults\n * to `editAs='twoCell'` — drag both corners with the cells.\n */\nexport function makeTwoCellAnchor(opts: {\n from: string | AnchorMarker;\n to: string | AnchorMarker;\n editAs?: 'twoCell' | 'oneCell' | 'absolute';\n}): DrawingAnchor {\n const from = typeof opts.from === 'string' ? anchorMarkerFromCellRef(opts.from) : opts.from;\n const to = typeof opts.to === 'string' ? anchorMarkerFromCellRef(opts.to) : opts.to;\n return {\n kind: 'twoCell',\n from,\n to,\n ...(opts.editAs !== undefined ? { editAs: opts.editAs } : {}),\n };\n}\n","// Spreadsheet drawing data model.\n//\n// A `Drawing` is the per-worksheet `xl/drawings/drawingN.xml` part — a list of\n// anchor entries, each carrying a content variant (chart, picture, shape,\n// connector, group). Charts and pictures are modeled; everything else is kept\n// as the verbatim source XML so it survives a round-trip untouched.\n\nimport type { ChartSpace } from '../chart/chart.js';\nimport type { CxChartSpace } from '../chart/cx/chartex.js';\nimport type { DrawingAnchor } from './anchor.js';\nimport type { ShapeProperties } from './dml/shape-properties.js';\nimport type { XlsxImage } from './image.js';\n\n/** Reference to a chart part — the chart's drawing-rels rId resolves to xl/charts/chartN.xml. */\nexport interface ChartReference {\n /** Drawing-rels rId pointing at the chart part. Populated on read; the writer assigns its own. */\n rId?: string;\n /**\n * Legacy ECMA-376 chart payload (`c:chartSpace`). Stage-1 supports BarChart\n * end-to-end; other chart kinds populate this field as their parsers /\n * writers land.\n */\n space?: ChartSpace;\n /**\n * Excel-2016 chartex payload (`cx:chartSpace`). Mutually exclusive with\n * {@link space} for any given drawing item; the parser sniffs the root\n * element and populates whichever is appropriate.\n */\n cxSpace?: CxChartSpace;\n /**\n * `true` when the resolved chart part is a chartex (`cx:`) chart. Set by the\n * package writer so the drawing emitter knows to use the chartex\n * `<a:graphicData uri>` instead of the legacy chart URI — Excel rejects the\n * workbook when the URI doesn't match the chart's actual root namespace.\n */\n isCx?: boolean;\n}\n\n/** Reference to an embedded picture inside a worksheet drawing. */\nexport interface PictureReference {\n /** Drawing-rels rId pointing at the embedded image. Populated on read; the writer assigns its own. */\n rId?: string;\n /** Resolved image bytes + metadata. Populated on read; the writer reads it back. */\n image?: XlsxImage;\n /** Picture display name (`<xdr:cNvPr name=\"...\">`). */\n name?: string;\n /** Optional alt-text description. */\n descr?: string;\n /** Hidden flag (`<xdr:cNvPr hidden=\"1\"/>`). */\n hidden?: boolean;\n /** Per-picture shape properties (extra fill / line / rotation). */\n spPr?: ShapeProperties;\n}\n\nexport interface DrawingItem {\n anchor: DrawingAnchor;\n content:\n | { kind: 'chart'; chart: ChartReference }\n | { kind: 'picture'; picture: PictureReference }\n | { kind: 'unsupported'; rawTag: string };\n /**\n * The whole source anchor element, captured verbatim. Set by the reader for\n * `unsupported` content — a plain shape, a group, a connector — because the\n * model has no slot for its geometry, text or `<xdr:clientData>` attributes.\n * When present the writer emits it as-is instead of rebuilding the anchor\n * from {@link anchor} + {@link content}.\n */\n raw?: import('../xml/tree.js').XmlNode;\n}\n\nexport interface Drawing {\n items: DrawingItem[];\n /**\n * `<xdr:wsDr>` children that aren't anchors — in practice Excel's\n * `<mc:AlternateContent>` wrapper around an anchor that needs a newer\n * feature. Kept verbatim together with the `items` index they sat before, so\n * document order (which is z-order) survives a round-trip.\n */\n rawChildren?: Array<{ beforeItem: number; node: import('../xml/tree.js').XmlNode }>;\n /**\n * Drawing-rels entries the verbatim nodes above reference (`r:embed` on a\n * picture inside a group, `r:id` on a shape's hyperlink, …). Carried over\n * from the source part under their original ids so those references still\n * resolve after a re-save; the writer allocates its own ids around them.\n */\n rawRels?: import('../packaging/relationships.js').Relationships;\n}\n\nexport function makeDrawing(items: DrawingItem[] = []): Drawing {\n return { items };\n}\n\nexport function makeChartDrawingItem(anchor: DrawingAnchor, chart: ChartReference = {}): DrawingItem {\n return { anchor, content: { kind: 'chart', chart } };\n}\n\nexport function makePictureDrawingItem(anchor: DrawingAnchor, picture: PictureReference | XlsxImage): DrawingItem {\n // Distinguish raw image bytes from a full PictureReference by checking the\n // discriminator: XlsxImage carries `format`, PictureReference doesn't.\n const ref: PictureReference = 'format' in picture ? { image: picture as XlsxImage } : (picture as PictureReference);\n return { anchor, content: { kind: 'picture', picture: ref } };\n}\n\n// ---- Worksheet ergonomic helpers ----------------------------------------\n\nimport { loadImage } from './image.js';\nimport { makeOneCellAnchor } from './anchor.js';\nimport type { Worksheet } from '../worksheet/worksheet.js';\n\n/**\n * Drop an image onto a worksheet at a single-cell anchor. Lazy-allocates\n * `ws.drawing` (as `makeDrawing([])`) on first call and appends a picture\n * DrawingItem.\n *\n * `image` accepts either an `XlsxImage` (already loaded via `loadImage`) or raw\n * image bytes — in the bytes case, this helper sniffs the format with\n * `loadImage` itself.\n *\n * `at` is a cell ref like `\"C3\"`. Override `widthPx` / `heightPx` to scale;\n * otherwise the helper uses 96×96 defaults that look fine for typical icons.\n */\nexport const addImageAt = (\n ws: Worksheet,\n at: string,\n image: XlsxImage | Uint8Array,\n opts: { widthPx?: number; heightPx?: number } = {},\n): DrawingItem => {\n const xlsxImage: XlsxImage = image instanceof Uint8Array ? loadImage(image) : image;\n const anchor = makeOneCellAnchor({\n from: at,\n widthPx: opts.widthPx ?? 96,\n heightPx: opts.heightPx ?? 96,\n });\n const item = makePictureDrawingItem(anchor, xlsxImage);\n if (!ws.drawing) ws.drawing = makeDrawing([]);\n ws.drawing.items.push(item);\n return item;\n};\n\n/**\n * Anchor a chart to a worksheet at a single-cell ref. Lazy-allocates\n * `ws.drawing`. `chart` is the same `ChartReference` shape `makeChart\n * DrawingItem` accepts (`{ space }` for legacy chart, `{ cxSpace }` for\n * chartex).\n */\nexport const addChartAt = (\n ws: Worksheet,\n at: string,\n chart: ChartReference,\n opts: { widthPx?: number; heightPx?: number } = {},\n): DrawingItem => {\n const anchor = makeOneCellAnchor({\n from: at,\n widthPx: opts.widthPx ?? 480,\n heightPx: opts.heightPx ?? 320,\n });\n const item = makeChartDrawingItem(anchor, chart);\n if (!ws.drawing) ws.drawing = makeDrawing([]);\n ws.drawing.items.push(item);\n return item;\n};\n\n/**\n * Read-only snapshot of every picture DrawingItem on the sheet. Returns the\n * matching items (each with its anchor + picture reference). Empty array when\n * the sheet has no drawing or only non-picture items.\n */\nexport const listImagesOnSheet = (ws: Worksheet): ReadonlyArray<DrawingItem> => {\n if (!ws.drawing) return [];\n return ws.drawing.items.filter((it) => it.content.kind === 'picture');\n};\n\n/**\n * Read-only snapshot of every chart DrawingItem on the sheet. Each item has its\n * anchor + chart reference.\n */\nexport const listChartsOnSheet = (ws: Worksheet): ReadonlyArray<DrawingItem> => {\n if (!ws.drawing) return [];\n return ws.drawing.items.filter((it) => it.content.kind === 'chart');\n};\n\n/**\n * Drop every DrawingItem from the worksheet. Returns the count removed. The\n * `ws.drawing` field itself is left in place (empty) so subsequent `addImageAt`\n * / `addChartAt` calls don't have to re-allocate.\n */\nexport const removeAllDrawingItems = (ws: Worksheet): number => {\n if (!ws.drawing) return 0;\n const n = ws.drawing.items.length;\n ws.drawing.items = [];\n return n;\n};\n\n/**\n * Drop every picture DrawingItem from the worksheet, leaving charts and any\n * other content kinds untouched. Returns the count removed.\n */\nexport const removeAllImages = (ws: Worksheet): number => {\n if (!ws.drawing) return 0;\n const before = ws.drawing.items.length;\n ws.drawing.items = ws.drawing.items.filter((it) => it.content.kind !== 'picture');\n return before - ws.drawing.items.length;\n};\n\n/**\n * Drop every chart DrawingItem from the worksheet, leaving pictures and any\n * other content kinds untouched. Returns the count removed.\n */\nexport const removeAllCharts = (ws: Worksheet): number => {\n if (!ws.drawing) return 0;\n const before = ws.drawing.items.length;\n ws.drawing.items = ws.drawing.items.filter((it) => it.content.kind !== 'chart');\n return before - ws.drawing.items.length;\n};\n"],"mappings":"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"}
|
package/dist/drawing.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { a as makeChartDrawingItem, c as removeAllCharts, f as makeOneCellAnchor, h as loadImage, i as listImagesOnSheet, l as removeAllDrawingItems, n as addImageAt, o as makeDrawing, r as listChartsOnSheet, s as makePictureDrawingItem, t as addChartAt, u as removeAllImages } from "./drawing-
|
|
1
|
+
import { a as makeChartDrawingItem, c as removeAllCharts, f as makeOneCellAnchor, h as loadImage, i as listImagesOnSheet, l as removeAllDrawingItems, n as addImageAt, o as makeDrawing, r as listChartsOnSheet, s as makePictureDrawingItem, t as addChartAt, u as removeAllImages } from "./drawing-Cv6UtInY.mjs";
|
|
2
2
|
import { a as makeSchemeColor, i as makeColor, n as VALUED_COLOR_MOD_KINDS, o as makeSrgbColor, r as VALUELESS_COLOR_MOD_KINDS, t as SCHEME_COLOR_NAMES } from "./colors-ovWAwnZI.mjs";
|
|
3
3
|
//#region src/drawing/dml/shape-properties.ts
|
|
4
4
|
const makeShapeProperties = (opts = {}) => ({
|
|
@@ -1,5 +1,6 @@
|
|
|
1
|
+
import { o as OpenXmlSchemaError } from "./exceptions-D-CFwxgm.mjs";
|
|
1
2
|
//#region src/utils/escape.ts
|
|
2
|
-
const ILLEGAL_RE = /[\x00-\x1F\
|
|
3
|
+
const ILLEGAL_RE = /[\x00-\x1F\u{D800}-\u{DFFF}\u{FFFE}\u{FFFF}]/gu;
|
|
3
4
|
const ESCAPED_PATTERN_RE = /(_)(x[0-9A-Fa-f]{4}_)/g;
|
|
4
5
|
const toHex4 = (n) => n.toString(16).toUpperCase().padStart(4, "0");
|
|
5
6
|
/**
|
|
@@ -43,7 +44,29 @@ function escapeXmlText(s) {
|
|
|
43
44
|
function escapeXmlAttr(s) {
|
|
44
45
|
return s.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """);
|
|
45
46
|
}
|
|
47
|
+
const UNREPRESENTABLE_RE = /[\0-\x08\x0B\x0C\x0E-\x1F\p{Cs}\uFFFE\uFFFF]/u;
|
|
48
|
+
/**
|
|
49
|
+
* Escape text for an OOXML text node the reader hands back verbatim: `<f>`,
|
|
50
|
+
* `<formula>`, `<formula1>`, `<formula2>` and a `t="str"` cached formula
|
|
51
|
+
* result. The `_xHHHH_` convention of {@link escapeCellString} must not be
|
|
52
|
+
* applied to these: nothing decodes them on read, so an encoded sequence would
|
|
53
|
+
* reach Excel as the literal text `_x000D_`.
|
|
54
|
+
*
|
|
55
|
+
* Tab and LF stay literal; CR becomes a numeric character reference, which
|
|
56
|
+
* survives the CR-to-LF normalisation every conforming parser applies to raw
|
|
57
|
+
* text. A codepoint XML 1.0 cannot represent therefore has nowhere to go and is
|
|
58
|
+
* rejected rather than silently rewritten.
|
|
59
|
+
*
|
|
60
|
+
* `node` and `at` name the offending value in that error (`worksheet: formula`
|
|
61
|
+
* / `A1`). They stay separate so that a formula-heavy sheet does not build one
|
|
62
|
+
* message string per cell for an error it never raises.
|
|
63
|
+
*/
|
|
64
|
+
function escapeXmlTextVerbatim(s, node, at) {
|
|
65
|
+
const unrepresentable = UNREPRESENTABLE_RE.exec(s);
|
|
66
|
+
if (unrepresentable) throw new OpenXmlSchemaError(`${node} at ${at} contains U+${toHex4(s.charCodeAt(unrepresentable.index))}, which XML 1.0 cannot represent`);
|
|
67
|
+
return escapeXmlText(s).replace(/\r/g, " ");
|
|
68
|
+
}
|
|
46
69
|
//#endregion
|
|
47
|
-
export { unescapeCellString as i, escapeXmlAttr as n, escapeXmlText as r, escapeCellString as t };
|
|
70
|
+
export { unescapeCellString as a, escapeXmlTextVerbatim as i, escapeXmlAttr as n, escapeXmlText as r, escapeCellString as t };
|
|
48
71
|
|
|
49
|
-
//# sourceMappingURL=escape-
|
|
72
|
+
//# sourceMappingURL=escape-rA5UpibT.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"escape-rA5UpibT.mjs","names":[],"sources":["../src/utils/escape.ts"],"sourcesContent":["// XML 1.0 cell-string escaping. Mirrors openpyxl/openpyxl/utils/escape.py.\n//\n// Excel emits cell strings with control characters and other illegal\n// XML 1.0 codepoints encoded as `_xHHHH_` (uppercase hex). The\n// underscore itself is a literal in normal text but a sequence opener\n// in escape position; an existing `_xHHHH_` in the input is therefore\n// re-escaped to `_x005F_xHHHH_` so it round-trips losslessly.\n\nimport { OpenXmlSchemaError } from './exceptions.js';\n\n// Every codepoint XML 1.0 cannot carry: the C0 controls, an unpaired\n// surrogate (which has no UTF-8 encoding), and U+FFFE / U+FFFF. A character\n// reference is no way out either, since `�` is as illegal as the raw byte.\n// This range deliberately covers `\\t` (U+0009), `\\n` (U+000A) and `\\r`\n// (U+000D) even though XML 1.0 considers them legal whitespace: XML\n// parsers normalise CRLF / lone CR to LF on read, so a cell string\n// containing `\\r` would silently lose its CR without the `_x000D_`\n// encoding. openpyxl escapes the same `\\x01-\\x19` range; we add NUL\n// to keep the writer well-formed when callers feed in binary data.\n//\n// The `u` flag makes the class match code points rather than UTF-16 code\n// units, so a well-formed surrogate pair is a single character outside\n// D800-DFFF and passes through intact.\nconst ILLEGAL_RE =\n // biome-ignore lint/suspicious/noControlCharactersInRegex: by design, these are the codepoints we replace\n /[\\x00-\\x1F\\u{D800}-\\u{DFFF}\\u{FFFE}\\u{FFFF}]/gu;\nconst ESCAPED_PATTERN_RE = /(_)(x[0-9A-Fa-f]{4}_)/g;\n\nconst toHex4 = (n: number): string => n.toString(16).toUpperCase().padStart(4, '0');\n\n/**\n * Escape a string for safe storage in an OOXML cell. Already-escaped\n * sequences (`_xHHHH_`) are protected by escaping their leading\n * underscore; illegal codepoints are replaced with their `_xHHHH_`\n * representation.\n */\nexport function escapeCellString(s: string): string {\n // Re-escape any existing `_xHHHH_` so it round-trips; the underscore\n // becomes `_x005F_` and the rest of the sequence is left as-is.\n const protectedString = s.replace(ESCAPED_PATTERN_RE, '_x005F_$2');\n return protectedString.replace(ILLEGAL_RE, (ch) => `_x${toHex4(ch.charCodeAt(0))}_`);\n}\n\nconst UNESCAPE_RE = /_x([0-9A-Fa-f]{4})_/g;\n\n/**\n * Inverse of {@link escapeCellString}. Looking from left to right\n * we replace any `_xHHHH_` sequence with the corresponding code unit;\n * the protected `_x005F_` becomes a literal underscore which the\n * subsequent replacements skip safely (replace's regex is non-overlapping).\n */\nexport function unescapeCellString(s: string): string {\n return s.replace(UNESCAPE_RE, (_full, hex: string) => String.fromCharCode(Number.parseInt(hex, 16)));\n}\n\n// ---- XML escape helpers ----------------------------------------------------\n//\n// One canonical implementation for text-node and attribute escaping. The\n// previous codebase carried three near-identical copies (one each in save.ts,\n// xml/serializer.ts, xml/stream-writer.ts); they disagreed about `>`-in-attribute\n// handling and would have drifted further apart over time. Keeping them in a\n// single place also makes future fixes (e.g. surrogate-pair scrubbing) land\n// once rather than three times.\n\n/**\n * Escape a string for safe placement in an XML text node. Replaces the three\n * codepoints that would otherwise terminate the text region or open a markup\n * sequence (`&`, `<`, `>`); `\"` and `'` are not legal markup terminators\n * inside text and stay verbatim.\n */\nexport function escapeXmlText(s: string): string {\n return s.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>');\n}\n\n/**\n * Escape a string for safe placement inside a `\"`-quoted XML attribute.\n * Handles `&`, `<`, `>` and the `\"` that would otherwise close the value.\n *\n * Note: this deliberately does NOT escape `\\r` / `\\n` / `\\t` to numeric\n * character references. XML 1.0 attribute-value normalisation would\n * collapses them to spaces, but the DOM read path (fast-xml-parser) does not\n * apply that normalisation, so the literal bytes round-trip through\n * `loadWorkbook`. Leaving them literal also matches what Excel itself emits.\n */\nexport function escapeXmlAttr(s: string): string {\n return s\n .replace(/&/g, '&')\n .replace(/</g, '<')\n .replace(/>/g, '>')\n .replace(/\"/g, '"');\n}\n\n// The codepoints XML 1.0 cannot carry in a text node under any encoding: the\n// C0 controls apart from tab / LF / CR, unpaired surrogates, and U+FFFE / U+FFFF. A character\n// reference is no help either, since `�` is as illegal as the raw byte.\n// Matching on code points (`u` flag) is what keeps a well-formed surrogate\n// pair, and so every astral character, out of `\\p{Cs}`.\nconst UNREPRESENTABLE_RE =\n // biome-ignore lint/suspicious/noControlCharactersInRegex: by design, these are the codepoints being rejected\n /[\\0-\\x08\\x0B\\x0C\\x0E-\\x1F\\p{Cs}\\uFFFE\\uFFFF]/u;\n\n/**\n * Escape text for an OOXML text node the reader hands back verbatim: `<f>`,\n * `<formula>`, `<formula1>`, `<formula2>` and a `t=\"str\"` cached formula\n * result. The `_xHHHH_` convention of {@link escapeCellString} must not be\n * applied to these: nothing decodes them on read, so an encoded sequence would\n * reach Excel as the literal text `_x000D_`.\n *\n * Tab and LF stay literal; CR becomes a numeric character reference, which\n * survives the CR-to-LF normalisation every conforming parser applies to raw\n * text. A codepoint XML 1.0 cannot represent therefore has nowhere to go and is\n * rejected rather than silently rewritten.\n *\n * `node` and `at` name the offending value in that error (`worksheet: formula`\n * / `A1`). They stay separate so that a formula-heavy sheet does not build one\n * message string per cell for an error it never raises.\n */\nexport function escapeXmlTextVerbatim(s: string, node: string, at: string): string {\n const unrepresentable = UNREPRESENTABLE_RE.exec(s);\n if (unrepresentable) {\n const codePoint = toHex4(s.charCodeAt(unrepresentable.index));\n throw new OpenXmlSchemaError(`${node} at ${at} contains U+${codePoint}, which XML 1.0 cannot represent`);\n }\n return escapeXmlText(s).replace(/\\r/g, ' ');\n}\n"],"mappings":";;AAuBA,MAAM,aAEJ;AACF,MAAM,qBAAqB;AAE3B,MAAM,UAAU,MAAsB,EAAE,SAAS,EAAE,CAAC,CAAC,YAAY,CAAC,CAAC,SAAS,GAAG,GAAG;;;;;;;AAQlF,SAAgB,iBAAiB,GAAmB;CAIlD,OADwB,EAAE,QAAQ,oBAAoB,WACjC,CAAC,CAAC,QAAQ,aAAa,OAAO,KAAK,OAAO,GAAG,WAAW,CAAC,CAAC,EAAE,EAAE;AACrF;AAEA,MAAM,cAAc;;;;;;;AAQpB,SAAgB,mBAAmB,GAAmB;CACpD,OAAO,EAAE,QAAQ,cAAc,OAAO,QAAgB,OAAO,aAAa,OAAO,SAAS,KAAK,EAAE,CAAC,CAAC;AACrG;;;;;;;AAiBA,SAAgB,cAAc,GAAmB;CAC/C,OAAO,EAAE,QAAQ,MAAM,OAAO,CAAC,CAAC,QAAQ,MAAM,MAAM,CAAC,CAAC,QAAQ,MAAM,MAAM;AAC5E;;;;;;;;;;;AAYA,SAAgB,cAAc,GAAmB;CAC/C,OAAO,EACJ,QAAQ,MAAM,OAAO,CAAC,CACtB,QAAQ,MAAM,MAAM,CAAC,CACrB,QAAQ,MAAM,MAAM,CAAC,CACrB,QAAQ,MAAM,QAAQ;AAC3B;AAOA,MAAM,qBAEJ;;;;;;;;;;;;;;;;;AAkBF,SAAgB,sBAAsB,GAAW,MAAc,IAAoB;CACjF,MAAM,kBAAkB,mBAAmB,KAAK,CAAC;CACjD,IAAI,iBAEF,MAAM,IAAI,mBAAmB,GAAG,KAAK,MAAM,GAAG,cAD5B,OAAO,EAAE,WAAW,gBAAgB,KAAK,CACS,EAAE,iCAAiC;CAEzG,OAAO,cAAc,CAAC,CAAC,CAAC,QAAQ,OAAO,OAAO;AAChD"}
|
package/dist/io/load.d.ts
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import type { XlsxSource } from '../io/source.js';
|
|
2
|
+
import { type Relationships } from '../packaging/relationships.js';
|
|
2
3
|
import { type SheetState, type Workbook } from '../workbook/workbook.js';
|
|
3
4
|
import { type XmlNode } from '../xml/tree.js';
|
|
4
5
|
import type { DecompressionLimits } from '../zip/decompression-guard.js';
|
|
6
|
+
import { type ZipArchive } from '../zip/reader.js';
|
|
5
7
|
/**
|
|
6
8
|
* Options for {@link loadWorkbook}. Earlier drafts exposed `readOnly` /
|
|
7
9
|
* `keepLinks` / `keepVba` / `dataOnly` / `richText` placeholders that the
|
|
@@ -17,6 +19,8 @@ export interface LoadOptions {
|
|
|
17
19
|
*/
|
|
18
20
|
decompressionLimits?: DecompressionLimits | false;
|
|
19
21
|
}
|
|
22
|
+
/** Office Document relationship type: the package-root pointer to `xl/workbook.xml`. */
|
|
23
|
+
export declare const OFFICE_DOC_REL_TYPE = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument";
|
|
20
24
|
/**
|
|
21
25
|
* Resolve an OPC relationship target against its source part path.
|
|
22
26
|
*
|
|
@@ -25,6 +29,30 @@ export interface LoadOptions {
|
|
|
25
29
|
* - `..` segments collapse normally.
|
|
26
30
|
*/
|
|
27
31
|
export declare function resolveRelTarget(sourcePartPath: string, target: string): string;
|
|
32
|
+
/** A workbook-level part a package carries at most one of. */
|
|
33
|
+
interface OptionalWorkbookPart {
|
|
34
|
+
/** How the part is named in error messages. */
|
|
35
|
+
name: string;
|
|
36
|
+
/** Relationship type that binds the part to the workbook. */
|
|
37
|
+
relType: string;
|
|
38
|
+
/** Where Excel writes the part. A convention, not a rule. */
|
|
39
|
+
conventionalPath: string;
|
|
40
|
+
}
|
|
41
|
+
export declare const SHARED_STRINGS_PART: OptionalWorkbookPart;
|
|
42
|
+
export declare const STYLES_PART: OptionalWorkbookPart;
|
|
43
|
+
/**
|
|
44
|
+
* Read an optional workbook-level part, following the relationship that binds
|
|
45
|
+
* it to the workbook.
|
|
46
|
+
*
|
|
47
|
+
* The relationship decides. `xl/sharedStrings.xml` and its siblings are only
|
|
48
|
+
* where Excel happens to write these parts, and a producer may point the rel
|
|
49
|
+
* at any part in the package, so the conventional path serves the packages
|
|
50
|
+
* that ship the part with no relationship at all.
|
|
51
|
+
*
|
|
52
|
+
* A relationship that resolves to nothing is a malformed package rather than
|
|
53
|
+
* an absent part, so it throws instead of reading back as an empty table.
|
|
54
|
+
*/
|
|
55
|
+
export declare function readOptionalWorkbookPart(archive: ZipArchive, workbookPath: string, wbRels: Relationships, part: OptionalWorkbookPart): Uint8Array | undefined;
|
|
28
56
|
interface SheetEntry {
|
|
29
57
|
/** Display name (`sheet/@name`). */
|
|
30
58
|
name: string;
|
package/dist/io.mjs
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { i as OpenXmlIoError } from "./exceptions-D-CFwxgm.mjs";
|
|
2
|
-
import {
|
|
3
|
-
import { n as workbookToBytes, t as saveWorkbook } from "./save-
|
|
2
|
+
import { i as loadWorkbook } from "./load-DIdQ_zum.mjs";
|
|
3
|
+
import { n as workbookToBytes, t as saveWorkbook } from "./save-ax11Gsw0.mjs";
|
|
4
4
|
//#region src/io/browser.ts
|
|
5
5
|
/**
|
|
6
6
|
* Wrap a Blob (or File, since File extends Blob) as an XlsxSource. The
|