ooxml.js 2.6.18 → 2.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -194,28 +194,55 @@ function buildStylesPart(cellFormats) {
194
194
  }));
195
195
  children.push(require_xml_fragment.el("numFmts", { count: String(numFmtElements.length) }, numFmtElements));
196
196
  }
197
- children.push(require_xml_fragment.el("fonts", { count: "1" }, [require_xml_fragment.el("font", {}, [require_xml_fragment.el("sz", { val: "11" }), require_xml_fragment.el("name", { val: "Calibri" })])]), require_xml_fragment.el("fills", { count: "2" }, [require_xml_fragment.el("fill", {}, [require_xml_fragment.el("patternFill", { patternType: "none" })]), require_xml_fragment.el("fill", {}, [require_xml_fragment.el("patternFill", { patternType: "gray125" })])]), require_xml_fragment.el("borders", { count: "1" }, [require_xml_fragment.el("border", {}, [
198
- require_xml_fragment.el("left"),
199
- require_xml_fragment.el("right"),
200
- require_xml_fragment.el("top"),
201
- require_xml_fragment.el("bottom"),
202
- require_xml_fragment.el("diagonal")
203
- ])]), require_xml_fragment.el("cellStyleXfs", { count: "1" }, [require_xml_fragment.el("xf", {
197
+ const fillElements = cellFormats.fillDeclarations().map((fill) => {
198
+ if (fill.patternType === "solid") {
199
+ const rgb = fill.rgb ?? "000000";
200
+ return require_xml_fragment.el("fill", {}, [require_xml_fragment.el("patternFill", { patternType: "solid" }, [require_xml_fragment.el("fgColor", { rgb: `FF${rgb}` }), require_xml_fragment.el("bgColor", { indexed: "64" })])]);
201
+ }
202
+ return require_xml_fragment.el("fill", {}, [require_xml_fragment.el("patternFill", { patternType: fill.patternType })]);
203
+ });
204
+ const borderElements = cellFormats.borderDeclarations().map((border) => {
205
+ const edgeElements = [];
206
+ for (const edge of [
207
+ "left",
208
+ "right",
209
+ "top",
210
+ "bottom"
211
+ ]) {
212
+ const present = border.edges[edge];
213
+ if (present !== void 0) edgeElements.push(require_xml_fragment.el(edge, { style: present.style }, [require_xml_fragment.el("color", { rgb: `FF${present.rgb}` })]));
214
+ else edgeElements.push(require_xml_fragment.el(edge));
215
+ }
216
+ edgeElements.push(require_xml_fragment.el("diagonal"));
217
+ return require_xml_fragment.el("border", {}, edgeElements);
218
+ });
219
+ children.push(require_xml_fragment.el("fonts", { count: "1" }, [require_xml_fragment.el("font", {}, [require_xml_fragment.el("sz", { val: "11" }), require_xml_fragment.el("name", { val: "Calibri" })])]), require_xml_fragment.el("fills", { count: String(fillElements.length) }, fillElements), require_xml_fragment.el("borders", { count: String(borderElements.length) }, borderElements), require_xml_fragment.el("cellStyleXfs", { count: "1" }, [require_xml_fragment.el("xf", {
204
220
  numFmtId: "0",
205
221
  fontId: "0",
206
222
  fillId: "0",
207
223
  borderId: "0"
208
224
  })]));
209
- const xfElements = cellFormats.cellFormats().map((numFmtId) => {
225
+ const xfElements = cellFormats.cellFormatRecords().map((record) => {
210
226
  const attrs = {
211
- numFmtId: String(numFmtId),
227
+ numFmtId: String(record.numFmtId),
212
228
  fontId: "0",
213
- fillId: "0",
214
- borderId: "0",
229
+ fillId: String(record.fillId),
230
+ borderId: String(record.borderId),
215
231
  xfId: "0"
216
232
  };
217
- if (numFmtId !== 0) attrs.applyNumberFormat = require_typed_xlsx_util.writeXmlBool(true);
218
- return require_xml_fragment.el("xf", attrs);
233
+ if (record.numFmtId !== 0) attrs.applyNumberFormat = require_typed_xlsx_util.writeXmlBool(true);
234
+ if (record.fillId !== require_typed_xlsx_styles.RESERVED_FILL_INDICES.none) attrs.applyFill = require_typed_xlsx_util.writeXmlBool(true);
235
+ if (record.borderId !== require_typed_xlsx_styles.RESERVED_BORDER_INDICES.empty) attrs.applyBorder = require_typed_xlsx_util.writeXmlBool(true);
236
+ const xfChildren = [];
237
+ if (record.alignment !== void 0) {
238
+ attrs.applyAlignment = require_typed_xlsx_util.writeXmlBool(true);
239
+ const alignmentAttrs = {};
240
+ if (record.alignment.horizontal !== void 0) alignmentAttrs.horizontal = record.alignment.horizontal;
241
+ if (record.alignment.vertical === "top") alignmentAttrs.vertical = "top";
242
+ else if (record.alignment.vertical === "middle") alignmentAttrs.vertical = "center";
243
+ xfChildren.push(require_xml_fragment.el("alignment", alignmentAttrs));
244
+ }
245
+ return require_xml_fragment.el("xf", attrs, xfChildren);
219
246
  });
220
247
  children.push(require_xml_fragment.el("cellXfs", { count: String(xfElements.length) }, xfElements));
221
248
  children.push(require_xml_fragment.el("cellStyles", { count: "1" }, [require_xml_fragment.el("cellStyle", {
@@ -332,7 +359,17 @@ function renderTemporal(serial, iso, format, isFormulaResult, sharedStrings) {
332
359
  function buildCellElement(cell, sharedStrings, cellFormats) {
333
360
  const children = [];
334
361
  const rendered = renderCellValue(cell.value, cell.formula !== void 0, sharedStrings);
335
- const styleIndex = rendered?.format === void 0 ? 0 : cellFormats.intern(rendered.format);
362
+ const decoration = cell.background !== void 0 || cell.borders !== void 0 || cell.alignment !== void 0 || cell.verticalAlignment !== void 0 ? {
363
+ background: cell.background,
364
+ borders: cell.borders,
365
+ alignment: cell.alignment,
366
+ verticalAlignment: cell.verticalAlignment
367
+ } : void 0;
368
+ const format = rendered?.format;
369
+ const styleIndex = format === void 0 && decoration === void 0 ? 0 : cellFormats.intern(format ?? {
370
+ kind: "builtin",
371
+ id: 0
372
+ }, decoration);
336
373
  const attrs = {
337
374
  r: require_typed_xlsx_a1.cellReference(cell.row, cell.column),
338
375
  s: String(styleIndex)
@@ -8,7 +8,7 @@ import { BOOLEAN_NUMBER_FORMAT, DATE_NUMBER_FORMAT, DATE_TIME_NUMBER_FORMAT, PER
8
8
  import { pageSizeToPaperSizeCode, ptToUniversalMeasure, writeXmlBool } from "./util.js";
9
9
  import { DEFAULT_HEADER_FOOTER_MARGIN_PT } from "./print-settings.js";
10
10
  import { isoDateTimeToSerial, isoDateToSerial, isoTimeToSerial } from "./serial.js";
11
- import { CellFormatTable } from "./styles.js";
11
+ import { CellFormatTable, RESERVED_BORDER_INDICES, RESERVED_FILL_INDICES } from "./styles.js";
12
12
  import { ptToColumnWidthChars } from "./units.js";
13
13
  //#region src/typed/xlsx/build.ts
14
14
  const SML_NS = "http://schemas.openxmlformats.org/spreadsheetml/2006/main";
@@ -193,28 +193,55 @@ function buildStylesPart(cellFormats) {
193
193
  }));
194
194
  children.push(el("numFmts", { count: String(numFmtElements.length) }, numFmtElements));
195
195
  }
196
- children.push(el("fonts", { count: "1" }, [el("font", {}, [el("sz", { val: "11" }), el("name", { val: "Calibri" })])]), el("fills", { count: "2" }, [el("fill", {}, [el("patternFill", { patternType: "none" })]), el("fill", {}, [el("patternFill", { patternType: "gray125" })])]), el("borders", { count: "1" }, [el("border", {}, [
197
- el("left"),
198
- el("right"),
199
- el("top"),
200
- el("bottom"),
201
- el("diagonal")
202
- ])]), el("cellStyleXfs", { count: "1" }, [el("xf", {
196
+ const fillElements = cellFormats.fillDeclarations().map((fill) => {
197
+ if (fill.patternType === "solid") {
198
+ const rgb = fill.rgb ?? "000000";
199
+ return el("fill", {}, [el("patternFill", { patternType: "solid" }, [el("fgColor", { rgb: `FF${rgb}` }), el("bgColor", { indexed: "64" })])]);
200
+ }
201
+ return el("fill", {}, [el("patternFill", { patternType: fill.patternType })]);
202
+ });
203
+ const borderElements = cellFormats.borderDeclarations().map((border) => {
204
+ const edgeElements = [];
205
+ for (const edge of [
206
+ "left",
207
+ "right",
208
+ "top",
209
+ "bottom"
210
+ ]) {
211
+ const present = border.edges[edge];
212
+ if (present !== void 0) edgeElements.push(el(edge, { style: present.style }, [el("color", { rgb: `FF${present.rgb}` })]));
213
+ else edgeElements.push(el(edge));
214
+ }
215
+ edgeElements.push(el("diagonal"));
216
+ return el("border", {}, edgeElements);
217
+ });
218
+ children.push(el("fonts", { count: "1" }, [el("font", {}, [el("sz", { val: "11" }), el("name", { val: "Calibri" })])]), el("fills", { count: String(fillElements.length) }, fillElements), el("borders", { count: String(borderElements.length) }, borderElements), el("cellStyleXfs", { count: "1" }, [el("xf", {
203
219
  numFmtId: "0",
204
220
  fontId: "0",
205
221
  fillId: "0",
206
222
  borderId: "0"
207
223
  })]));
208
- const xfElements = cellFormats.cellFormats().map((numFmtId) => {
224
+ const xfElements = cellFormats.cellFormatRecords().map((record) => {
209
225
  const attrs = {
210
- numFmtId: String(numFmtId),
226
+ numFmtId: String(record.numFmtId),
211
227
  fontId: "0",
212
- fillId: "0",
213
- borderId: "0",
228
+ fillId: String(record.fillId),
229
+ borderId: String(record.borderId),
214
230
  xfId: "0"
215
231
  };
216
- if (numFmtId !== 0) attrs.applyNumberFormat = writeXmlBool(true);
217
- return el("xf", attrs);
232
+ if (record.numFmtId !== 0) attrs.applyNumberFormat = writeXmlBool(true);
233
+ if (record.fillId !== RESERVED_FILL_INDICES.none) attrs.applyFill = writeXmlBool(true);
234
+ if (record.borderId !== RESERVED_BORDER_INDICES.empty) attrs.applyBorder = writeXmlBool(true);
235
+ const xfChildren = [];
236
+ if (record.alignment !== void 0) {
237
+ attrs.applyAlignment = writeXmlBool(true);
238
+ const alignmentAttrs = {};
239
+ if (record.alignment.horizontal !== void 0) alignmentAttrs.horizontal = record.alignment.horizontal;
240
+ if (record.alignment.vertical === "top") alignmentAttrs.vertical = "top";
241
+ else if (record.alignment.vertical === "middle") alignmentAttrs.vertical = "center";
242
+ xfChildren.push(el("alignment", alignmentAttrs));
243
+ }
244
+ return el("xf", attrs, xfChildren);
218
245
  });
219
246
  children.push(el("cellXfs", { count: String(xfElements.length) }, xfElements));
220
247
  children.push(el("cellStyles", { count: "1" }, [el("cellStyle", {
@@ -331,7 +358,17 @@ function renderTemporal(serial, iso, format, isFormulaResult, sharedStrings) {
331
358
  function buildCellElement(cell, sharedStrings, cellFormats) {
332
359
  const children = [];
333
360
  const rendered = renderCellValue(cell.value, cell.formula !== void 0, sharedStrings);
334
- const styleIndex = rendered?.format === void 0 ? 0 : cellFormats.intern(rendered.format);
361
+ const decoration = cell.background !== void 0 || cell.borders !== void 0 || cell.alignment !== void 0 || cell.verticalAlignment !== void 0 ? {
362
+ background: cell.background,
363
+ borders: cell.borders,
364
+ alignment: cell.alignment,
365
+ verticalAlignment: cell.verticalAlignment
366
+ } : void 0;
367
+ const format = rendered?.format;
368
+ const styleIndex = format === void 0 && decoration === void 0 ? 0 : cellFormats.intern(format ?? {
369
+ kind: "builtin",
370
+ id: 0
371
+ }, decoration);
335
372
  const attrs = {
336
373
  r: cellReference(cell.row, cell.column),
337
374
  s: String(styleIndex)
@@ -98,16 +98,21 @@ function deriveDisplayText(value) {
98
98
  }
99
99
  const DEFAULT_CELL_STYLE_INDEX = 0;
100
100
  const PLAIN_NUMBER = { kind: "number" };
101
- function readNumericFormatContext(pkg) {
101
+ function readCellFormatContext(pkg) {
102
102
  return {
103
- classes: require_typed_xlsx_styles.readCellFormatCodes(pkg).map((code) => code === void 0 ? PLAIN_NUMBER : require_typed_xlsx_number_format.classifyNumberFormat(code)),
103
+ entries: require_typed_xlsx_styles.readCellStyles(pkg),
104
104
  date1904: require_typed_xlsx_serial.readDate1904(pkg)
105
105
  };
106
106
  }
107
- function numberFormatOf(cell, context) {
107
+ function entryOf(cell, context) {
108
108
  const raw = require_typed_util.attr(cell, "s");
109
109
  const index = raw === void 0 ? DEFAULT_CELL_STYLE_INDEX : Number.parseInt(raw, 10);
110
- return (Number.isInteger(index) ? context.classes[index] : void 0) ?? PLAIN_NUMBER;
110
+ return Number.isInteger(index) ? context.entries[index] : void 0;
111
+ }
112
+ function numberFormatOf(cell, context) {
113
+ const entry = entryOf(cell, context);
114
+ if (entry?.numberFormatCode === void 0) return PLAIN_NUMBER;
115
+ return require_typed_xlsx_number_format.classifyNumberFormat(entry.numberFormatCode);
111
116
  }
112
117
  function resolveNumericValue(num, cell, context) {
113
118
  const format = numberFormatOf(cell, context);
@@ -231,13 +236,13 @@ function readCellValue(cell, sharedStrings, numericFormats) {
231
236
  displayText: deriveDisplayText(value)
232
237
  };
233
238
  }
234
- function readCell(cell, sharedStrings, numericFormats) {
239
+ function readCell(cell, sharedStrings, context) {
235
240
  const reference = require_typed_util.attr(cell, "r");
236
241
  const position = reference === void 0 ? void 0 : require_typed_xlsx_a1.parseCellReference(reference);
237
242
  if (position === void 0) return;
238
243
  const formulaEl = require_typed_util.childrenWithTag(cell, "f")[0];
239
244
  const formula = formulaEl === void 0 ? void 0 : require_typed_util.textContent(formulaEl);
240
- const resolved = readCellValue(cell, sharedStrings, numericFormats);
245
+ const resolved = readCellValue(cell, sharedStrings, context);
241
246
  if (resolved === void 0) {
242
247
  if (formula === void 0) return;
243
248
  return {
@@ -255,6 +260,13 @@ function readCell(cell, sharedStrings, numericFormats) {
255
260
  displayText: resolved.displayText
256
261
  };
257
262
  if (formula !== void 0) cellEntry.formula = formula;
263
+ const entry = entryOf(cell, context);
264
+ if (entry !== void 0) {
265
+ if (entry.background !== void 0) cellEntry.background = entry.background;
266
+ if (entry.borders !== void 0) cellEntry.borders = entry.borders;
267
+ if (entry.alignment !== void 0) cellEntry.alignment = entry.alignment;
268
+ if (entry.verticalAlignment !== void 0) cellEntry.verticalAlignment = entry.verticalAlignment;
269
+ }
258
270
  return cellEntry;
259
271
  }
260
272
  function applyMergedRanges(worksheet, cells) {
@@ -274,18 +286,18 @@ function applyMergedRanges(worksheet, cells) {
274
286
  if (rowSpan > 1) anchor.rowSpan = rowSpan;
275
287
  }
276
288
  }
277
- function readCells(worksheet, sharedStrings, numericFormats) {
289
+ function readCells(worksheet, sharedStrings, context) {
278
290
  const sheetData = require_typed_util.childrenWithTag(worksheet, "sheetData")[0];
279
291
  if (sheetData === void 0) return [];
280
292
  const cells = [];
281
293
  for (const row of require_typed_util.childrenWithTag(sheetData, "row")) for (const cell of require_typed_util.childrenWithTag(row, "c")) {
282
- const read = readCell(cell, sharedStrings, numericFormats);
294
+ const read = readCell(cell, sharedStrings, context);
283
295
  if (read !== void 0) cells.push(read);
284
296
  }
285
297
  applyMergedRanges(worksheet, cells);
286
298
  return cells;
287
299
  }
288
- function readSheet(pkg, entry, sheetIndex, sharedStrings, definedNamesBySheet, numericFormats) {
300
+ function readSheet(pkg, entry, sheetIndex, sharedStrings, definedNamesBySheet, context) {
289
301
  const worksheet = require_typed_util.rootElement(pkg.parts[entry.path]);
290
302
  if (worksheet === void 0) return {
291
303
  name: entry.name,
@@ -297,7 +309,7 @@ function readSheet(pkg, entry, sheetIndex, sharedStrings, definedNamesBySheet, n
297
309
  };
298
310
  return {
299
311
  name: entry.name,
300
- cells: readCells(worksheet, sharedStrings, numericFormats),
312
+ cells: readCells(worksheet, sharedStrings, context),
301
313
  columns: readColumns(worksheet),
302
314
  rows: readRows(worksheet),
303
315
  images: [],
@@ -315,8 +327,8 @@ function fallbackEmptyWorksheet() {
315
327
  function readXlsxContent(pkg) {
316
328
  const sharedStrings = require_typed_xlsx_shared_strings.loadSharedStrings(pkg);
317
329
  const definedNamesBySheet = require_typed_xlsx_defined_names.readDefinedNamesBySheet(pkg);
318
- const numericFormats = readNumericFormatContext(pkg);
319
- const sheets = resolveSheetEntries(pkg).map((entry, sheetIndex) => readSheet(pkg, entry, sheetIndex, sharedStrings, definedNamesBySheet, numericFormats));
330
+ const context = readCellFormatContext(pkg);
331
+ const sheets = resolveSheetEntries(pkg).map((entry, sheetIndex) => readSheet(pkg, entry, sheetIndex, sharedStrings, definedNamesBySheet, context));
320
332
  return {
321
333
  kind: "spreadsheet",
322
334
  formatVersion: document_schema_js.CONTENT_FORMAT_VERSION,
@@ -7,7 +7,7 @@ import { classifyNumberFormat } from "./number-format.js";
7
7
  import { readXmlBool } from "./util.js";
8
8
  import { readPrintSettings } from "./print-settings.js";
9
9
  import { readDate1904, serialToIsoDate, serialToIsoDateTime, serialToIsoTime } from "./serial.js";
10
- import { readCellFormatCodes } from "./styles.js";
10
+ import { readCellStyles } from "./styles.js";
11
11
  import { columnWidthCharsToPt } from "./units.js";
12
12
  import { CONTENT_FORMAT_VERSION } from "document-schema.js";
13
13
  //#region src/typed/xlsx/content.ts
@@ -97,16 +97,21 @@ function deriveDisplayText(value) {
97
97
  }
98
98
  const DEFAULT_CELL_STYLE_INDEX = 0;
99
99
  const PLAIN_NUMBER = { kind: "number" };
100
- function readNumericFormatContext(pkg) {
100
+ function readCellFormatContext(pkg) {
101
101
  return {
102
- classes: readCellFormatCodes(pkg).map((code) => code === void 0 ? PLAIN_NUMBER : classifyNumberFormat(code)),
102
+ entries: readCellStyles(pkg),
103
103
  date1904: readDate1904(pkg)
104
104
  };
105
105
  }
106
- function numberFormatOf(cell, context) {
106
+ function entryOf(cell, context) {
107
107
  const raw = attr(cell, "s");
108
108
  const index = raw === void 0 ? DEFAULT_CELL_STYLE_INDEX : Number.parseInt(raw, 10);
109
- return (Number.isInteger(index) ? context.classes[index] : void 0) ?? PLAIN_NUMBER;
109
+ return Number.isInteger(index) ? context.entries[index] : void 0;
110
+ }
111
+ function numberFormatOf(cell, context) {
112
+ const entry = entryOf(cell, context);
113
+ if (entry?.numberFormatCode === void 0) return PLAIN_NUMBER;
114
+ return classifyNumberFormat(entry.numberFormatCode);
110
115
  }
111
116
  function resolveNumericValue(num, cell, context) {
112
117
  const format = numberFormatOf(cell, context);
@@ -230,13 +235,13 @@ function readCellValue(cell, sharedStrings, numericFormats) {
230
235
  displayText: deriveDisplayText(value)
231
236
  };
232
237
  }
233
- function readCell(cell, sharedStrings, numericFormats) {
238
+ function readCell(cell, sharedStrings, context) {
234
239
  const reference = attr(cell, "r");
235
240
  const position = reference === void 0 ? void 0 : parseCellReference(reference);
236
241
  if (position === void 0) return;
237
242
  const formulaEl = childrenWithTag(cell, "f")[0];
238
243
  const formula = formulaEl === void 0 ? void 0 : textContent(formulaEl);
239
- const resolved = readCellValue(cell, sharedStrings, numericFormats);
244
+ const resolved = readCellValue(cell, sharedStrings, context);
240
245
  if (resolved === void 0) {
241
246
  if (formula === void 0) return;
242
247
  return {
@@ -254,6 +259,13 @@ function readCell(cell, sharedStrings, numericFormats) {
254
259
  displayText: resolved.displayText
255
260
  };
256
261
  if (formula !== void 0) cellEntry.formula = formula;
262
+ const entry = entryOf(cell, context);
263
+ if (entry !== void 0) {
264
+ if (entry.background !== void 0) cellEntry.background = entry.background;
265
+ if (entry.borders !== void 0) cellEntry.borders = entry.borders;
266
+ if (entry.alignment !== void 0) cellEntry.alignment = entry.alignment;
267
+ if (entry.verticalAlignment !== void 0) cellEntry.verticalAlignment = entry.verticalAlignment;
268
+ }
257
269
  return cellEntry;
258
270
  }
259
271
  function applyMergedRanges(worksheet, cells) {
@@ -273,18 +285,18 @@ function applyMergedRanges(worksheet, cells) {
273
285
  if (rowSpan > 1) anchor.rowSpan = rowSpan;
274
286
  }
275
287
  }
276
- function readCells(worksheet, sharedStrings, numericFormats) {
288
+ function readCells(worksheet, sharedStrings, context) {
277
289
  const sheetData = childrenWithTag(worksheet, "sheetData")[0];
278
290
  if (sheetData === void 0) return [];
279
291
  const cells = [];
280
292
  for (const row of childrenWithTag(sheetData, "row")) for (const cell of childrenWithTag(row, "c")) {
281
- const read = readCell(cell, sharedStrings, numericFormats);
293
+ const read = readCell(cell, sharedStrings, context);
282
294
  if (read !== void 0) cells.push(read);
283
295
  }
284
296
  applyMergedRanges(worksheet, cells);
285
297
  return cells;
286
298
  }
287
- function readSheet(pkg, entry, sheetIndex, sharedStrings, definedNamesBySheet, numericFormats) {
299
+ function readSheet(pkg, entry, sheetIndex, sharedStrings, definedNamesBySheet, context) {
288
300
  const worksheet = rootElement(pkg.parts[entry.path]);
289
301
  if (worksheet === void 0) return {
290
302
  name: entry.name,
@@ -296,7 +308,7 @@ function readSheet(pkg, entry, sheetIndex, sharedStrings, definedNamesBySheet, n
296
308
  };
297
309
  return {
298
310
  name: entry.name,
299
- cells: readCells(worksheet, sharedStrings, numericFormats),
311
+ cells: readCells(worksheet, sharedStrings, context),
300
312
  columns: readColumns(worksheet),
301
313
  rows: readRows(worksheet),
302
314
  images: [],
@@ -314,8 +326,8 @@ function fallbackEmptyWorksheet() {
314
326
  function readXlsxContent(pkg) {
315
327
  const sharedStrings = loadSharedStrings(pkg);
316
328
  const definedNamesBySheet = readDefinedNamesBySheet(pkg);
317
- const numericFormats = readNumericFormatContext(pkg);
318
- const sheets = resolveSheetEntries(pkg).map((entry, sheetIndex) => readSheet(pkg, entry, sheetIndex, sharedStrings, definedNamesBySheet, numericFormats));
329
+ const context = readCellFormatContext(pkg);
330
+ const sheets = resolveSheetEntries(pkg).map((entry, sheetIndex) => readSheet(pkg, entry, sheetIndex, sharedStrings, definedNamesBySheet, context));
319
331
  return {
320
332
  kind: "spreadsheet",
321
333
  formatVersion: CONTENT_FORMAT_VERSION,
@@ -1,6 +1,7 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
2
  const require_typed_util = require("../util.cjs");
3
3
  const require_typed_xlsx_number_format = require("./number-format.cjs");
4
+ let document_schema_js = require("document-schema.js");
4
5
  //#region src/typed/xlsx/styles.ts
5
6
  const STYLES_PATH = "xl/styles.xml";
6
7
  const GENERAL_NUM_FMT_ID = 0;
@@ -17,37 +18,249 @@ function readNumberFormatCodesById(styleSheet) {
17
18
  }
18
19
  return codes;
19
20
  }
20
- function readCellFormatCodes(pkg) {
21
+ const BORDER_WIDTH_PT = {
22
+ hair: .5,
23
+ thin: .75,
24
+ medium: 1.5,
25
+ thick: 2.25
26
+ };
27
+ const XLSX_BORDER_STYLE = {
28
+ thin: {
29
+ weight: "thin",
30
+ pattern: "solid"
31
+ },
32
+ medium: {
33
+ weight: "medium",
34
+ pattern: "solid"
35
+ },
36
+ thick: {
37
+ weight: "thick",
38
+ pattern: "solid"
39
+ },
40
+ hair: {
41
+ weight: "hair",
42
+ pattern: "solid"
43
+ },
44
+ dashed: {
45
+ weight: "thin",
46
+ pattern: "dashed"
47
+ },
48
+ dotted: {
49
+ weight: "thin",
50
+ pattern: "dotted"
51
+ },
52
+ double: {
53
+ weight: "thin",
54
+ pattern: "double"
55
+ },
56
+ mediumDashed: {
57
+ weight: "medium",
58
+ pattern: "dashed"
59
+ },
60
+ dashDot: {
61
+ weight: "thin",
62
+ pattern: "dashed"
63
+ },
64
+ mediumDashDot: {
65
+ weight: "medium",
66
+ pattern: "dashed"
67
+ },
68
+ dashDotDot: {
69
+ weight: "thin",
70
+ pattern: "dashed"
71
+ },
72
+ mediumDashDotDot: {
73
+ weight: "medium",
74
+ pattern: "dashed"
75
+ },
76
+ slantDashDot: {
77
+ weight: "medium",
78
+ pattern: "dashed"
79
+ }
80
+ };
81
+ const BORDER_WEIGHT_UPPER_PT = {
82
+ hair: .625,
83
+ thin: 1.125,
84
+ medium: 1.875
85
+ };
86
+ function readColorRgb(container, colorTag) {
87
+ const colorEl = require_typed_util.childrenWithTag(container, colorTag)[0];
88
+ if (colorEl === void 0) return;
89
+ const raw = require_typed_util.attr(colorEl, "rgb");
90
+ if (raw === void 0) return;
91
+ const hex = raw.length >= 6 ? raw.slice(-6) : raw;
92
+ if (!/^[0-9a-fA-F]{6}$/.test(hex)) return;
93
+ try {
94
+ return (0, document_schema_js.rgbHexToColor)(hex);
95
+ } catch {
96
+ return;
97
+ }
98
+ }
99
+ function readFillBackground(fill) {
100
+ const patternFill = require_typed_util.childrenWithTag(fill, "patternFill")[0];
101
+ if (patternFill === void 0 || require_typed_util.attr(patternFill, "patternType") !== "solid") return;
102
+ return readColorRgb(patternFill, "fgColor") ?? readColorRgb(patternFill, "bgColor");
103
+ }
104
+ function readFills(styleSheet) {
105
+ const fillsEl = require_typed_util.childrenWithTag(styleSheet, "fills")[0];
106
+ if (fillsEl === void 0) return [];
107
+ return require_typed_util.childrenWithTag(fillsEl, "fill").map(readFillBackground);
108
+ }
109
+ function readBorderEdge(border, edge) {
110
+ const edgeEl = require_typed_util.childrenWithTag(border, edge)[0];
111
+ if (edgeEl === void 0) return;
112
+ const styleToken = require_typed_util.attr(edgeEl, "style");
113
+ if (styleToken === void 0 || styleToken === "none") return;
114
+ const resolved = XLSX_BORDER_STYLE[styleToken];
115
+ if (resolved === void 0) return;
116
+ const color = readColorRgb(edgeEl, "color");
117
+ if (color === void 0) return;
118
+ const result = {
119
+ color,
120
+ widthPt: BORDER_WIDTH_PT[resolved.weight]
121
+ };
122
+ if (resolved.pattern !== "solid") result.style = resolved.pattern;
123
+ return result;
124
+ }
125
+ function readBorders(styleSheet) {
126
+ const bordersEl = require_typed_util.childrenWithTag(styleSheet, "borders")[0];
127
+ if (bordersEl === void 0) return [];
128
+ return require_typed_util.childrenWithTag(bordersEl, "border").map((border) => {
129
+ const left = readBorderEdge(border, "left");
130
+ const right = readBorderEdge(border, "right");
131
+ const top = readBorderEdge(border, "top");
132
+ const bottom = readBorderEdge(border, "bottom");
133
+ if (left === void 0 && right === void 0 && top === void 0 && bottom === void 0) return;
134
+ const result = {};
135
+ if (left !== void 0) result.left = left;
136
+ if (right !== void 0) result.right = right;
137
+ if (top !== void 0) result.top = top;
138
+ if (bottom !== void 0) result.bottom = bottom;
139
+ return result;
140
+ });
141
+ }
142
+ function readHorizontalAlignment(alignment) {
143
+ const value = require_typed_util.attr(alignment, "horizontal");
144
+ if (value === "left" || value === "center" || value === "right" || value === "justify") return value;
145
+ }
146
+ function readVerticalAlignment(alignment) {
147
+ const value = require_typed_util.attr(alignment, "vertical");
148
+ if (value === "top") return "top";
149
+ if (value === "center") return "middle";
150
+ }
151
+ function readAlignment(xf) {
152
+ const alignment = require_typed_util.childrenWithTag(xf, "alignment")[0];
153
+ if (alignment === void 0) return {};
154
+ return {
155
+ alignment: readHorizontalAlignment(alignment),
156
+ verticalAlignment: readVerticalAlignment(alignment)
157
+ };
158
+ }
159
+ function readCellStyles(pkg) {
21
160
  const styleSheet = require_typed_util.rootElement(pkg.parts[STYLES_PATH]);
22
161
  if (styleSheet === void 0) return [];
23
162
  const cellXfsEl = require_typed_util.childrenWithTag(styleSheet, "cellXfs")[0];
24
163
  if (cellXfsEl === void 0) return [];
25
164
  const codes = readNumberFormatCodesById(styleSheet);
165
+ const fills = readFills(styleSheet);
166
+ const borders = readBorders(styleSheet);
26
167
  return require_typed_util.childrenWithTag(cellXfsEl, "xf").map((xf) => {
27
- const raw = require_typed_util.attr(xf, "numFmtId");
28
- const id = raw === void 0 ? 0 : Number.parseInt(raw, 10);
29
- return Number.isInteger(id) ? codes.get(id) : void 0;
168
+ const numFmtRaw = require_typed_util.attr(xf, "numFmtId");
169
+ const numFmtId = numFmtRaw === void 0 ? 0 : Number.parseInt(numFmtRaw, 10);
170
+ const entry = {};
171
+ if (Number.isInteger(numFmtId)) {
172
+ const code = codes.get(numFmtId);
173
+ if (code !== void 0) entry.numberFormatCode = code;
174
+ }
175
+ const fillId = parseChildIndex(require_typed_util.attr(xf, "fillId"));
176
+ if (fillId !== void 0) entry.background = fills[fillId];
177
+ const borderId = parseChildIndex(require_typed_util.attr(xf, "borderId"));
178
+ if (borderId !== void 0) entry.borders = borders[borderId];
179
+ const { alignment, verticalAlignment } = readAlignment(xf);
180
+ if (alignment !== void 0) entry.alignment = alignment;
181
+ if (verticalAlignment !== void 0) entry.verticalAlignment = verticalAlignment;
182
+ return entry;
30
183
  });
31
184
  }
185
+ function parseChildIndex(value) {
186
+ if (value === void 0) return;
187
+ const n = Number.parseInt(value, 10);
188
+ return Number.isInteger(n) ? n : void 0;
189
+ }
190
+ function readCellFormatCodes(pkg) {
191
+ return readCellStyles(pkg).map((entry) => entry.numberFormatCode);
192
+ }
32
193
  const FIRST_CUSTOM_NUM_FMT_ID = 164;
33
194
  const DEFAULT_CELL_FORMAT_INDEX = 0;
34
- function signatureOf(format) {
195
+ const NONE_FILL_INDEX = 0;
196
+ const GRAY125_FILL_INDEX = 1;
197
+ const FIRST_REAL_FILL_INDEX = 2;
198
+ const EMPTY_BORDER_INDEX = 0;
199
+ const FIRST_REAL_BORDER_INDEX = 1;
200
+ function signatureOfNumberFormat(format) {
35
201
  return format.kind === "builtin" ? `builtin:${format.id}` : `custom:${format.code}`;
36
202
  }
203
+ const EMPTY_DECORATION = {};
204
+ function signatureOfDecoration(decoration) {
205
+ let sig = "";
206
+ if (decoration.background !== void 0) sig += `|bg:${(0, document_schema_js.colorToRgbHex)(decoration.background)}`;
207
+ const borders = decoration.borders;
208
+ if (borders !== void 0) for (const edge of [
209
+ "left",
210
+ "right",
211
+ "top",
212
+ "bottom"
213
+ ]) {
214
+ const border = borders[edge];
215
+ if (border !== void 0) sig += `|${edge}:${border.style ?? "solid"}:${(0, document_schema_js.colorToRgbHex)(border.color)}:${border.widthPt.toFixed(4)}`;
216
+ }
217
+ if (decoration.alignment !== void 0) sig += `|h:${decoration.alignment}`;
218
+ if (decoration.verticalAlignment !== void 0) sig += `|v:${decoration.verticalAlignment}`;
219
+ return sig;
220
+ }
221
+ function borderToXlsxStyle(border) {
222
+ switch (border.style) {
223
+ case "double": return "double";
224
+ case "dotted": return "dotted";
225
+ case "dashed": return border.widthPt >= BORDER_WEIGHT_UPPER_PT.thin ? "mediumDashed" : "dashed";
226
+ case "solid":
227
+ case void 0:
228
+ if (border.widthPt < BORDER_WEIGHT_UPPER_PT.hair) return "hair";
229
+ if (border.widthPt < BORDER_WEIGHT_UPPER_PT.thin) return "thin";
230
+ if (border.widthPt < BORDER_WEIGHT_UPPER_PT.medium) return "medium";
231
+ return "thick";
232
+ }
233
+ }
37
234
  var CellFormatTable = class {
38
- indexBySignature = /* @__PURE__ */ new Map([[signatureOf({
235
+ indexBySignature = /* @__PURE__ */ new Map([[signatureOfNumberFormat({
39
236
  kind: "builtin",
40
237
  id: 0
41
- }), 0]]);
42
- numFmtIdByIndex = [0];
238
+ }) + signatureOfDecoration(EMPTY_DECORATION), 0]]);
239
+ records = [{
240
+ numFmtId: 0,
241
+ fillId: NONE_FILL_INDEX,
242
+ borderId: EMPTY_BORDER_INDEX
243
+ }];
43
244
  declared = [];
44
- intern(format) {
45
- const signature = signatureOf(format);
245
+ fillIndexByRgb = /* @__PURE__ */ new Map();
246
+ fills = [{ patternType: "none" }, { patternType: "gray125" }];
247
+ borderIndexBySignature = /* @__PURE__ */ new Map();
248
+ borders = [{ edges: {} }];
249
+ intern(format, decoration = EMPTY_DECORATION) {
250
+ const signature = signatureOfNumberFormat(format) + signatureOfDecoration(decoration);
46
251
  const existing = this.indexBySignature.get(signature);
47
252
  if (existing !== void 0) return existing;
48
- const numFmtId = format.kind === "builtin" ? format.id : this.declare(format.code);
49
- const index = this.numFmtIdByIndex.length;
50
- this.numFmtIdByIndex.push(numFmtId);
253
+ const record = {
254
+ numFmtId: format.kind === "builtin" ? format.id : this.declareNumberFormat(format.code),
255
+ fillId: decoration.background === void 0 ? NONE_FILL_INDEX : this.internFill(decoration.background),
256
+ borderId: decoration.borders === void 0 ? EMPTY_BORDER_INDEX : this.internBorder(decoration.borders)
257
+ };
258
+ if (decoration.alignment !== void 0 || decoration.verticalAlignment !== void 0) record.alignment = {
259
+ horizontal: decoration.alignment,
260
+ vertical: decoration.verticalAlignment
261
+ };
262
+ const index = this.records.length;
263
+ this.records.push(record);
51
264
  this.indexBySignature.set(signature, index);
52
265
  return index;
53
266
  }
@@ -55,9 +268,18 @@ var CellFormatTable = class {
55
268
  return this.declared;
56
269
  }
57
270
  cellFormats() {
58
- return this.numFmtIdByIndex;
271
+ return this.records.map((record) => record.numFmtId);
59
272
  }
60
- declare(code) {
273
+ cellFormatRecords() {
274
+ return this.records;
275
+ }
276
+ fillDeclarations() {
277
+ return this.fills;
278
+ }
279
+ borderDeclarations() {
280
+ return this.borders;
281
+ }
282
+ declareNumberFormat(code) {
61
283
  const id = FIRST_CUSTOM_NUM_FMT_ID + this.declared.length;
62
284
  this.declared.push({
63
285
  id,
@@ -65,9 +287,60 @@ var CellFormatTable = class {
65
287
  });
66
288
  return id;
67
289
  }
290
+ internFill(background) {
291
+ const rgb = (0, document_schema_js.colorToRgbHex)(background);
292
+ const existing = this.fillIndexByRgb.get(rgb);
293
+ if (existing !== void 0) return existing;
294
+ const index = this.fills.length;
295
+ this.fills.push({
296
+ patternType: "solid",
297
+ rgb
298
+ });
299
+ this.fillIndexByRgb.set(rgb, index);
300
+ return index;
301
+ }
302
+ internBorder(borders) {
303
+ const edges = {};
304
+ let signature = "";
305
+ for (const edge of [
306
+ "left",
307
+ "right",
308
+ "top",
309
+ "bottom"
310
+ ]) {
311
+ const border = borders[edge];
312
+ if (border !== void 0) {
313
+ const style = borderToXlsxStyle(border);
314
+ const rgb = (0, document_schema_js.colorToRgbHex)(border.color);
315
+ edges[edge] = {
316
+ style,
317
+ rgb
318
+ };
319
+ signature += `|${edge}:${style}:${rgb}`;
320
+ }
321
+ }
322
+ const existing = this.borderIndexBySignature.get(signature);
323
+ if (existing !== void 0) return existing;
324
+ const index = this.borders.length;
325
+ this.borders.push({ edges });
326
+ this.borderIndexBySignature.set(signature, index);
327
+ return index;
328
+ }
329
+ };
330
+ const RESERVED_FILL_INDICES = {
331
+ none: NONE_FILL_INDEX,
332
+ gray125: GRAY125_FILL_INDEX,
333
+ firstReal: FIRST_REAL_FILL_INDEX
334
+ };
335
+ const RESERVED_BORDER_INDICES = {
336
+ empty: EMPTY_BORDER_INDEX,
337
+ firstReal: FIRST_REAL_BORDER_INDEX
68
338
  };
69
339
  //#endregion
70
340
  exports.CellFormatTable = CellFormatTable;
71
341
  exports.DEFAULT_CELL_FORMAT_INDEX = DEFAULT_CELL_FORMAT_INDEX;
72
342
  exports.GENERAL_NUM_FMT_ID = GENERAL_NUM_FMT_ID;
343
+ exports.RESERVED_BORDER_INDICES = RESERVED_BORDER_INDICES;
344
+ exports.RESERVED_FILL_INDICES = RESERVED_FILL_INDICES;
73
345
  exports.readCellFormatCodes = readCellFormatCodes;
346
+ exports.readCellStyles = readCellStyles;
@@ -1,21 +1,87 @@
1
1
  import { r as Package } from "../../package-L24lkba-.cjs";
2
2
  import { CellNumberFormat } from "./number-format.cjs";
3
+ import { Alignment, Color, ContentCellBorders } from "document-schema.js";
3
4
  //#region src/typed/xlsx/styles.d.ts
4
5
  declare const GENERAL_NUM_FMT_ID = 0;
6
+ interface CellStyleEntry {
7
+ numberFormatCode?: string;
8
+ background?: Color;
9
+ borders?: ContentCellBorders;
10
+ alignment?: Alignment;
11
+ verticalAlignment?: 'top' | 'middle' | 'bottom';
12
+ }
13
+ declare function readCellStyles(pkg: Package): readonly CellStyleEntry[];
5
14
  declare function readCellFormatCodes(pkg: Package): readonly (string | undefined)[];
6
15
  declare const DEFAULT_CELL_FORMAT_INDEX = 0;
7
16
  interface DeclaredNumberFormat {
8
17
  id: number;
9
18
  code: string;
10
19
  }
20
+ interface CellFormatDecoration {
21
+ background?: Color;
22
+ borders?: ContentCellBorders;
23
+ alignment?: Alignment;
24
+ verticalAlignment?: 'top' | 'middle' | 'bottom';
25
+ }
26
+ interface DeclaredFill {
27
+ patternType: 'none' | 'gray125' | 'solid';
28
+ rgb?: string;
29
+ }
30
+ interface DeclaredBorder {
31
+ edges: {
32
+ left?: {
33
+ style: string;
34
+ rgb: string;
35
+ };
36
+ right?: {
37
+ style: string;
38
+ rgb: string;
39
+ };
40
+ top?: {
41
+ style: string;
42
+ rgb: string;
43
+ };
44
+ bottom?: {
45
+ style: string;
46
+ rgb: string;
47
+ };
48
+ };
49
+ }
50
+ interface CellFormatRecord {
51
+ numFmtId: number;
52
+ fillId: number;
53
+ borderId: number;
54
+ alignment?: {
55
+ horizontal?: Alignment;
56
+ vertical?: 'top' | 'middle' | 'bottom';
57
+ };
58
+ }
11
59
  declare class CellFormatTable {
12
60
  private readonly indexBySignature;
13
- private readonly numFmtIdByIndex;
61
+ private readonly records;
14
62
  private readonly declared;
15
- intern(format: CellNumberFormat): number;
63
+ private readonly fillIndexByRgb;
64
+ private readonly fills;
65
+ private readonly borderIndexBySignature;
66
+ private readonly borders;
67
+ intern(format: CellNumberFormat, decoration?: CellFormatDecoration): number;
16
68
  declarations(): readonly DeclaredNumberFormat[];
17
69
  cellFormats(): readonly number[];
18
- private declare;
70
+ cellFormatRecords(): readonly CellFormatRecord[];
71
+ fillDeclarations(): readonly DeclaredFill[];
72
+ borderDeclarations(): readonly DeclaredBorder[];
73
+ private declareNumberFormat;
74
+ private internFill;
75
+ private internBorder;
19
76
  }
77
+ declare const RESERVED_FILL_INDICES: {
78
+ readonly none: 0;
79
+ readonly gray125: 1;
80
+ readonly firstReal: 2;
81
+ };
82
+ declare const RESERVED_BORDER_INDICES: {
83
+ readonly empty: 0;
84
+ readonly firstReal: 1;
85
+ };
20
86
  //#endregion
21
- export { CellFormatTable, DEFAULT_CELL_FORMAT_INDEX, DeclaredNumberFormat, GENERAL_NUM_FMT_ID, readCellFormatCodes };
87
+ export { CellFormatDecoration, CellFormatRecord, CellFormatTable, CellStyleEntry, DEFAULT_CELL_FORMAT_INDEX, DeclaredBorder, DeclaredFill, DeclaredNumberFormat, GENERAL_NUM_FMT_ID, RESERVED_BORDER_INDICES, RESERVED_FILL_INDICES, readCellFormatCodes, readCellStyles };
@@ -1,21 +1,87 @@
1
1
  import { r as Package } from "../../package-BUojjTXf.js";
2
2
  import { CellNumberFormat } from "./number-format.js";
3
+ import { Alignment, Color, ContentCellBorders } from "document-schema.js";
3
4
  //#region src/typed/xlsx/styles.d.ts
4
5
  declare const GENERAL_NUM_FMT_ID = 0;
6
+ interface CellStyleEntry {
7
+ numberFormatCode?: string;
8
+ background?: Color;
9
+ borders?: ContentCellBorders;
10
+ alignment?: Alignment;
11
+ verticalAlignment?: 'top' | 'middle' | 'bottom';
12
+ }
13
+ declare function readCellStyles(pkg: Package): readonly CellStyleEntry[];
5
14
  declare function readCellFormatCodes(pkg: Package): readonly (string | undefined)[];
6
15
  declare const DEFAULT_CELL_FORMAT_INDEX = 0;
7
16
  interface DeclaredNumberFormat {
8
17
  id: number;
9
18
  code: string;
10
19
  }
20
+ interface CellFormatDecoration {
21
+ background?: Color;
22
+ borders?: ContentCellBorders;
23
+ alignment?: Alignment;
24
+ verticalAlignment?: 'top' | 'middle' | 'bottom';
25
+ }
26
+ interface DeclaredFill {
27
+ patternType: 'none' | 'gray125' | 'solid';
28
+ rgb?: string;
29
+ }
30
+ interface DeclaredBorder {
31
+ edges: {
32
+ left?: {
33
+ style: string;
34
+ rgb: string;
35
+ };
36
+ right?: {
37
+ style: string;
38
+ rgb: string;
39
+ };
40
+ top?: {
41
+ style: string;
42
+ rgb: string;
43
+ };
44
+ bottom?: {
45
+ style: string;
46
+ rgb: string;
47
+ };
48
+ };
49
+ }
50
+ interface CellFormatRecord {
51
+ numFmtId: number;
52
+ fillId: number;
53
+ borderId: number;
54
+ alignment?: {
55
+ horizontal?: Alignment;
56
+ vertical?: 'top' | 'middle' | 'bottom';
57
+ };
58
+ }
11
59
  declare class CellFormatTable {
12
60
  private readonly indexBySignature;
13
- private readonly numFmtIdByIndex;
61
+ private readonly records;
14
62
  private readonly declared;
15
- intern(format: CellNumberFormat): number;
63
+ private readonly fillIndexByRgb;
64
+ private readonly fills;
65
+ private readonly borderIndexBySignature;
66
+ private readonly borders;
67
+ intern(format: CellNumberFormat, decoration?: CellFormatDecoration): number;
16
68
  declarations(): readonly DeclaredNumberFormat[];
17
69
  cellFormats(): readonly number[];
18
- private declare;
70
+ cellFormatRecords(): readonly CellFormatRecord[];
71
+ fillDeclarations(): readonly DeclaredFill[];
72
+ borderDeclarations(): readonly DeclaredBorder[];
73
+ private declareNumberFormat;
74
+ private internFill;
75
+ private internBorder;
19
76
  }
77
+ declare const RESERVED_FILL_INDICES: {
78
+ readonly none: 0;
79
+ readonly gray125: 1;
80
+ readonly firstReal: 2;
81
+ };
82
+ declare const RESERVED_BORDER_INDICES: {
83
+ readonly empty: 0;
84
+ readonly firstReal: 1;
85
+ };
20
86
  //#endregion
21
- export { CellFormatTable, DEFAULT_CELL_FORMAT_INDEX, DeclaredNumberFormat, GENERAL_NUM_FMT_ID, readCellFormatCodes };
87
+ export { CellFormatDecoration, CellFormatRecord, CellFormatTable, CellStyleEntry, DEFAULT_CELL_FORMAT_INDEX, DeclaredBorder, DeclaredFill, DeclaredNumberFormat, GENERAL_NUM_FMT_ID, RESERVED_BORDER_INDICES, RESERVED_FILL_INDICES, readCellFormatCodes, readCellStyles };
@@ -1,5 +1,6 @@
1
1
  import { attr, childrenWithTag, decodeEntities, rootElement } from "../util.js";
2
2
  import { BUILTIN_NUMBER_FORMATS } from "./number-format.js";
3
+ import { colorToRgbHex, rgbHexToColor } from "document-schema.js";
3
4
  //#region src/typed/xlsx/styles.ts
4
5
  const STYLES_PATH = "xl/styles.xml";
5
6
  const GENERAL_NUM_FMT_ID = 0;
@@ -16,37 +17,249 @@ function readNumberFormatCodesById(styleSheet) {
16
17
  }
17
18
  return codes;
18
19
  }
19
- function readCellFormatCodes(pkg) {
20
+ const BORDER_WIDTH_PT = {
21
+ hair: .5,
22
+ thin: .75,
23
+ medium: 1.5,
24
+ thick: 2.25
25
+ };
26
+ const XLSX_BORDER_STYLE = {
27
+ thin: {
28
+ weight: "thin",
29
+ pattern: "solid"
30
+ },
31
+ medium: {
32
+ weight: "medium",
33
+ pattern: "solid"
34
+ },
35
+ thick: {
36
+ weight: "thick",
37
+ pattern: "solid"
38
+ },
39
+ hair: {
40
+ weight: "hair",
41
+ pattern: "solid"
42
+ },
43
+ dashed: {
44
+ weight: "thin",
45
+ pattern: "dashed"
46
+ },
47
+ dotted: {
48
+ weight: "thin",
49
+ pattern: "dotted"
50
+ },
51
+ double: {
52
+ weight: "thin",
53
+ pattern: "double"
54
+ },
55
+ mediumDashed: {
56
+ weight: "medium",
57
+ pattern: "dashed"
58
+ },
59
+ dashDot: {
60
+ weight: "thin",
61
+ pattern: "dashed"
62
+ },
63
+ mediumDashDot: {
64
+ weight: "medium",
65
+ pattern: "dashed"
66
+ },
67
+ dashDotDot: {
68
+ weight: "thin",
69
+ pattern: "dashed"
70
+ },
71
+ mediumDashDotDot: {
72
+ weight: "medium",
73
+ pattern: "dashed"
74
+ },
75
+ slantDashDot: {
76
+ weight: "medium",
77
+ pattern: "dashed"
78
+ }
79
+ };
80
+ const BORDER_WEIGHT_UPPER_PT = {
81
+ hair: .625,
82
+ thin: 1.125,
83
+ medium: 1.875
84
+ };
85
+ function readColorRgb(container, colorTag) {
86
+ const colorEl = childrenWithTag(container, colorTag)[0];
87
+ if (colorEl === void 0) return;
88
+ const raw = attr(colorEl, "rgb");
89
+ if (raw === void 0) return;
90
+ const hex = raw.length >= 6 ? raw.slice(-6) : raw;
91
+ if (!/^[0-9a-fA-F]{6}$/.test(hex)) return;
92
+ try {
93
+ return rgbHexToColor(hex);
94
+ } catch {
95
+ return;
96
+ }
97
+ }
98
+ function readFillBackground(fill) {
99
+ const patternFill = childrenWithTag(fill, "patternFill")[0];
100
+ if (patternFill === void 0 || attr(patternFill, "patternType") !== "solid") return;
101
+ return readColorRgb(patternFill, "fgColor") ?? readColorRgb(patternFill, "bgColor");
102
+ }
103
+ function readFills(styleSheet) {
104
+ const fillsEl = childrenWithTag(styleSheet, "fills")[0];
105
+ if (fillsEl === void 0) return [];
106
+ return childrenWithTag(fillsEl, "fill").map(readFillBackground);
107
+ }
108
+ function readBorderEdge(border, edge) {
109
+ const edgeEl = childrenWithTag(border, edge)[0];
110
+ if (edgeEl === void 0) return;
111
+ const styleToken = attr(edgeEl, "style");
112
+ if (styleToken === void 0 || styleToken === "none") return;
113
+ const resolved = XLSX_BORDER_STYLE[styleToken];
114
+ if (resolved === void 0) return;
115
+ const color = readColorRgb(edgeEl, "color");
116
+ if (color === void 0) return;
117
+ const result = {
118
+ color,
119
+ widthPt: BORDER_WIDTH_PT[resolved.weight]
120
+ };
121
+ if (resolved.pattern !== "solid") result.style = resolved.pattern;
122
+ return result;
123
+ }
124
+ function readBorders(styleSheet) {
125
+ const bordersEl = childrenWithTag(styleSheet, "borders")[0];
126
+ if (bordersEl === void 0) return [];
127
+ return childrenWithTag(bordersEl, "border").map((border) => {
128
+ const left = readBorderEdge(border, "left");
129
+ const right = readBorderEdge(border, "right");
130
+ const top = readBorderEdge(border, "top");
131
+ const bottom = readBorderEdge(border, "bottom");
132
+ if (left === void 0 && right === void 0 && top === void 0 && bottom === void 0) return;
133
+ const result = {};
134
+ if (left !== void 0) result.left = left;
135
+ if (right !== void 0) result.right = right;
136
+ if (top !== void 0) result.top = top;
137
+ if (bottom !== void 0) result.bottom = bottom;
138
+ return result;
139
+ });
140
+ }
141
+ function readHorizontalAlignment(alignment) {
142
+ const value = attr(alignment, "horizontal");
143
+ if (value === "left" || value === "center" || value === "right" || value === "justify") return value;
144
+ }
145
+ function readVerticalAlignment(alignment) {
146
+ const value = attr(alignment, "vertical");
147
+ if (value === "top") return "top";
148
+ if (value === "center") return "middle";
149
+ }
150
+ function readAlignment(xf) {
151
+ const alignment = childrenWithTag(xf, "alignment")[0];
152
+ if (alignment === void 0) return {};
153
+ return {
154
+ alignment: readHorizontalAlignment(alignment),
155
+ verticalAlignment: readVerticalAlignment(alignment)
156
+ };
157
+ }
158
+ function readCellStyles(pkg) {
20
159
  const styleSheet = rootElement(pkg.parts[STYLES_PATH]);
21
160
  if (styleSheet === void 0) return [];
22
161
  const cellXfsEl = childrenWithTag(styleSheet, "cellXfs")[0];
23
162
  if (cellXfsEl === void 0) return [];
24
163
  const codes = readNumberFormatCodesById(styleSheet);
164
+ const fills = readFills(styleSheet);
165
+ const borders = readBorders(styleSheet);
25
166
  return childrenWithTag(cellXfsEl, "xf").map((xf) => {
26
- const raw = attr(xf, "numFmtId");
27
- const id = raw === void 0 ? 0 : Number.parseInt(raw, 10);
28
- return Number.isInteger(id) ? codes.get(id) : void 0;
167
+ const numFmtRaw = attr(xf, "numFmtId");
168
+ const numFmtId = numFmtRaw === void 0 ? 0 : Number.parseInt(numFmtRaw, 10);
169
+ const entry = {};
170
+ if (Number.isInteger(numFmtId)) {
171
+ const code = codes.get(numFmtId);
172
+ if (code !== void 0) entry.numberFormatCode = code;
173
+ }
174
+ const fillId = parseChildIndex(attr(xf, "fillId"));
175
+ if (fillId !== void 0) entry.background = fills[fillId];
176
+ const borderId = parseChildIndex(attr(xf, "borderId"));
177
+ if (borderId !== void 0) entry.borders = borders[borderId];
178
+ const { alignment, verticalAlignment } = readAlignment(xf);
179
+ if (alignment !== void 0) entry.alignment = alignment;
180
+ if (verticalAlignment !== void 0) entry.verticalAlignment = verticalAlignment;
181
+ return entry;
29
182
  });
30
183
  }
184
+ function parseChildIndex(value) {
185
+ if (value === void 0) return;
186
+ const n = Number.parseInt(value, 10);
187
+ return Number.isInteger(n) ? n : void 0;
188
+ }
189
+ function readCellFormatCodes(pkg) {
190
+ return readCellStyles(pkg).map((entry) => entry.numberFormatCode);
191
+ }
31
192
  const FIRST_CUSTOM_NUM_FMT_ID = 164;
32
193
  const DEFAULT_CELL_FORMAT_INDEX = 0;
33
- function signatureOf(format) {
194
+ const NONE_FILL_INDEX = 0;
195
+ const GRAY125_FILL_INDEX = 1;
196
+ const FIRST_REAL_FILL_INDEX = 2;
197
+ const EMPTY_BORDER_INDEX = 0;
198
+ const FIRST_REAL_BORDER_INDEX = 1;
199
+ function signatureOfNumberFormat(format) {
34
200
  return format.kind === "builtin" ? `builtin:${format.id}` : `custom:${format.code}`;
35
201
  }
202
+ const EMPTY_DECORATION = {};
203
+ function signatureOfDecoration(decoration) {
204
+ let sig = "";
205
+ if (decoration.background !== void 0) sig += `|bg:${colorToRgbHex(decoration.background)}`;
206
+ const borders = decoration.borders;
207
+ if (borders !== void 0) for (const edge of [
208
+ "left",
209
+ "right",
210
+ "top",
211
+ "bottom"
212
+ ]) {
213
+ const border = borders[edge];
214
+ if (border !== void 0) sig += `|${edge}:${border.style ?? "solid"}:${colorToRgbHex(border.color)}:${border.widthPt.toFixed(4)}`;
215
+ }
216
+ if (decoration.alignment !== void 0) sig += `|h:${decoration.alignment}`;
217
+ if (decoration.verticalAlignment !== void 0) sig += `|v:${decoration.verticalAlignment}`;
218
+ return sig;
219
+ }
220
+ function borderToXlsxStyle(border) {
221
+ switch (border.style) {
222
+ case "double": return "double";
223
+ case "dotted": return "dotted";
224
+ case "dashed": return border.widthPt >= BORDER_WEIGHT_UPPER_PT.thin ? "mediumDashed" : "dashed";
225
+ case "solid":
226
+ case void 0:
227
+ if (border.widthPt < BORDER_WEIGHT_UPPER_PT.hair) return "hair";
228
+ if (border.widthPt < BORDER_WEIGHT_UPPER_PT.thin) return "thin";
229
+ if (border.widthPt < BORDER_WEIGHT_UPPER_PT.medium) return "medium";
230
+ return "thick";
231
+ }
232
+ }
36
233
  var CellFormatTable = class {
37
- indexBySignature = /* @__PURE__ */ new Map([[signatureOf({
234
+ indexBySignature = /* @__PURE__ */ new Map([[signatureOfNumberFormat({
38
235
  kind: "builtin",
39
236
  id: 0
40
- }), 0]]);
41
- numFmtIdByIndex = [0];
237
+ }) + signatureOfDecoration(EMPTY_DECORATION), 0]]);
238
+ records = [{
239
+ numFmtId: 0,
240
+ fillId: NONE_FILL_INDEX,
241
+ borderId: EMPTY_BORDER_INDEX
242
+ }];
42
243
  declared = [];
43
- intern(format) {
44
- const signature = signatureOf(format);
244
+ fillIndexByRgb = /* @__PURE__ */ new Map();
245
+ fills = [{ patternType: "none" }, { patternType: "gray125" }];
246
+ borderIndexBySignature = /* @__PURE__ */ new Map();
247
+ borders = [{ edges: {} }];
248
+ intern(format, decoration = EMPTY_DECORATION) {
249
+ const signature = signatureOfNumberFormat(format) + signatureOfDecoration(decoration);
45
250
  const existing = this.indexBySignature.get(signature);
46
251
  if (existing !== void 0) return existing;
47
- const numFmtId = format.kind === "builtin" ? format.id : this.declare(format.code);
48
- const index = this.numFmtIdByIndex.length;
49
- this.numFmtIdByIndex.push(numFmtId);
252
+ const record = {
253
+ numFmtId: format.kind === "builtin" ? format.id : this.declareNumberFormat(format.code),
254
+ fillId: decoration.background === void 0 ? NONE_FILL_INDEX : this.internFill(decoration.background),
255
+ borderId: decoration.borders === void 0 ? EMPTY_BORDER_INDEX : this.internBorder(decoration.borders)
256
+ };
257
+ if (decoration.alignment !== void 0 || decoration.verticalAlignment !== void 0) record.alignment = {
258
+ horizontal: decoration.alignment,
259
+ vertical: decoration.verticalAlignment
260
+ };
261
+ const index = this.records.length;
262
+ this.records.push(record);
50
263
  this.indexBySignature.set(signature, index);
51
264
  return index;
52
265
  }
@@ -54,9 +267,18 @@ var CellFormatTable = class {
54
267
  return this.declared;
55
268
  }
56
269
  cellFormats() {
57
- return this.numFmtIdByIndex;
270
+ return this.records.map((record) => record.numFmtId);
58
271
  }
59
- declare(code) {
272
+ cellFormatRecords() {
273
+ return this.records;
274
+ }
275
+ fillDeclarations() {
276
+ return this.fills;
277
+ }
278
+ borderDeclarations() {
279
+ return this.borders;
280
+ }
281
+ declareNumberFormat(code) {
60
282
  const id = FIRST_CUSTOM_NUM_FMT_ID + this.declared.length;
61
283
  this.declared.push({
62
284
  id,
@@ -64,6 +286,54 @@ var CellFormatTable = class {
64
286
  });
65
287
  return id;
66
288
  }
289
+ internFill(background) {
290
+ const rgb = colorToRgbHex(background);
291
+ const existing = this.fillIndexByRgb.get(rgb);
292
+ if (existing !== void 0) return existing;
293
+ const index = this.fills.length;
294
+ this.fills.push({
295
+ patternType: "solid",
296
+ rgb
297
+ });
298
+ this.fillIndexByRgb.set(rgb, index);
299
+ return index;
300
+ }
301
+ internBorder(borders) {
302
+ const edges = {};
303
+ let signature = "";
304
+ for (const edge of [
305
+ "left",
306
+ "right",
307
+ "top",
308
+ "bottom"
309
+ ]) {
310
+ const border = borders[edge];
311
+ if (border !== void 0) {
312
+ const style = borderToXlsxStyle(border);
313
+ const rgb = colorToRgbHex(border.color);
314
+ edges[edge] = {
315
+ style,
316
+ rgb
317
+ };
318
+ signature += `|${edge}:${style}:${rgb}`;
319
+ }
320
+ }
321
+ const existing = this.borderIndexBySignature.get(signature);
322
+ if (existing !== void 0) return existing;
323
+ const index = this.borders.length;
324
+ this.borders.push({ edges });
325
+ this.borderIndexBySignature.set(signature, index);
326
+ return index;
327
+ }
328
+ };
329
+ const RESERVED_FILL_INDICES = {
330
+ none: NONE_FILL_INDEX,
331
+ gray125: GRAY125_FILL_INDEX,
332
+ firstReal: FIRST_REAL_FILL_INDEX
333
+ };
334
+ const RESERVED_BORDER_INDICES = {
335
+ empty: EMPTY_BORDER_INDEX,
336
+ firstReal: FIRST_REAL_BORDER_INDEX
67
337
  };
68
338
  //#endregion
69
- export { CellFormatTable, DEFAULT_CELL_FORMAT_INDEX, GENERAL_NUM_FMT_ID, readCellFormatCodes };
339
+ export { CellFormatTable, DEFAULT_CELL_FORMAT_INDEX, GENERAL_NUM_FMT_ID, RESERVED_BORDER_INDICES, RESERVED_FILL_INDICES, readCellFormatCodes, readCellStyles };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ooxml.js",
3
- "version": "2.6.18",
3
+ "version": "2.7.0",
4
4
  "description": "Type-safe, lossless round-trip conversion between OOXML packages (docx, pptx, xlsx) and JSON, built on Zod 4 codecs.",
5
5
  "type": "module",
6
6
  "repository": {
@@ -69,7 +69,7 @@
69
69
  "license": "MIT",
70
70
  "packageManager": "pnpm@11.6.0",
71
71
  "dependencies": {
72
- "document-schema.js": "^2.3.3",
72
+ "document-schema.js": "^2.3.4",
73
73
  "fast-xml-parser": "^5.10.1",
74
74
  "fflate": "^0.8.3",
75
75
  "zod": "^4.4.3"