xlvane 1.2.2 → 1.3.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.
@@ -1,4 +1,4 @@
1
- import { $t as metricsOf, A as limitExceeded, Bt as resolveLimits, C as Bytes, D as TAIL_BYTES, Dt as normalizeTable, E as NotLoaded, Et as TABLE_CONTENT_TYPE, F as validateLocalHeaders, Ft as normalizeStyle, Ht as printNames, I as WarningCollector, Jt as DRAWING_CONTENT_TYPE, K as SheetRels, L as sheetNotFound, N as readCentralDirectory, O as checkSource$1, On as XlvError, Ot as checkDefinedName, P as unsupported, Q as createSharedStrings, R as createWorksheetParser, Rt as DEFAULT_READ_LIMITS, S as workbookPartsToLoad, St as openPackage, T as HEAD_BYTES, Ut as printXml, Wt as sheetProtectionXml, X as cellXml, Xt as MediaParts, Y as buildStyles, Yt as IMAGE_CONTENT_TYPES, Z as contentTypesXml, Zt as drawingParts, _ as isoDate, _t as FormulaGroups, a as DATE_FORMAT, an as MAX_COLUMNS, cn as formatCellRef, ct as sheetHead, d as convertCellInput, dt as sheetTail, en as resolveImage, f as createWorkbook, fn as APP_PROPERTIES_PART, gt as workbookXml, h as detectHeader, hn as corePropertiesXml, ht as workbookRelsXml, i as readOptions, it as noteParts, j as locateCentralDirectory, k as corrupt, kt as checkUniqueName, lt as sheetRelsPartName, mn as appPropertiesXml, n as oneOfOption, nt as hyperlinksXml, o as DATE_TIME_FORMAT, on as MAX_ROWS, ot as rootRelsXml, p as sortedNotes, pn as CORE_PROPERTIES_PART, pt as tableParts, rn as classifyRelationship, rt as nameTitles, sn as columnLetters, st as rowStart, u as checkBookViews, ut as sheetRulesXml, v as analyzeWorkbook, w as CORRUPT_HINT, wt as partKey, y as readNotes, yn as createXmlByteStreamParser, yt as crc32, zt as checkOutputPositions } from "./options-Ba1iWD-Z.js";
1
+ import { $t as metricsOf, A as checkSource$1, B as createWorksheetParser, Bt as resolveLimits, Cn as CORE_PROPERTIES_PART, Ct as relationshipsPartName, D as HEAD_BYTES, Dt as checkUniqueName, E as CORRUPT_HINT, Et as checkDefinedName, F as readCentralDirectory, Ht as printNames, I as unsupported, Jt as DRAWING_CONTENT_TYPE, K as SheetRels, L as validateLocalHeaders, Lt as DEFAULT_READ_LIMITS, M as limitExceeded, N as locateCentralDirectory, Nn as clip, O as NotLoaded, Ot as nameScopeKey, Pt as normalizeStyle, Q as createSharedStrings, R as WarningCollector, Rt as checkKeyText, Sn as APP_PROPERTIES_PART, St as openPackage, T as Bytes, Tn as corePropertiesXml, Tt as normalizeTable, Ut as printXml, Wt as sheetProtectionXml, X as cellXml, Xt as MediaParts, Y as buildStyles, Yt as IMAGE_CONTENT_TYPES, Z as contentTypesXml, Zt as drawingParts, _t as FormulaGroups, a as DATE_FORMAT, b as readNotes, bn as partKey, cn as chartPartXml, ct as sheetHead, d as convertCellInput, dt as sheetTail, en as resolveAnchor, f as createWorkbook, g as detectHeader, gn as formatCellRef, gt as workbookXml, hn as columnLetters, ht as workbookRelsXml, i as readOptions, in as classifyRelationship, it as noteParts, j as corrupt, k as TAIL_BYTES, kn as createXmlByteStreamParser, ln as chartSheetNames, lt as sheetRelsPartName, mn as MAX_ROWS, n as oneOfOption, nt as hyperlinksXml, o as DATE_TIME_FORMAT, on as CHART_CONTENT_TYPE, ot as rootRelsXml, p as sortedNotes, pn as MAX_COLUMNS, pt as tableParts, rt as nameTitles, sn as chartPartName, st as rowStart, tn as resolveImage, u as checkBookViews, un as resolveChartSheets, ut as sheetRulesXml, v as isoDate, w as workbookPartsToLoad, wn as appPropertiesXml, wt as TABLE_CONTENT_TYPE, xn as zipNameToPartName, y as analyzeWorkbook, yt as crc32, z as sheetNotFound, zn as XlvError, zt as checkOutputPositions } from "./options-Cxt-LJE4.js";
2
2
  import { Deflate, Inflate } from "fflate";
3
3
  // src/zip/stream.ts
4
4
  /**
@@ -396,7 +396,8 @@ function createZipStream(entries, level = 6) {
396
396
  // src/stream-read.ts
397
397
  const GiB = 1073741824;
398
398
  /**
399
- * Limits that differ in streaming mode (D-401): `maxCells` does not apply, and single parts may
399
+ * Limits that differ in streaming mode (D-401): `maxCells` does not apply (but one `<row>` element
400
+ * may hold at most 16 384 cells, D-J01), and single parts may
400
401
  * decompress to 4 GiB (8 GiB in total). All other `DEFAULT_READ_LIMITS` stay.
401
402
  *
402
403
  * @example
@@ -423,32 +424,69 @@ var PreloadedZip = class {
423
424
  entries;
424
425
  bytesRead = 0;
425
426
  data = /* @__PURE__ */ new Map();
427
+ /**
428
+ * ZIP entries by the part name they map to, as openPackage maps them: percent-encoded
429
+ * non-ASCII characters of an item name are decoded (Part 2 §7.3.5; F-OPC-12).
430
+ */
431
+ byPart = /* @__PURE__ */ new Map();
426
432
  constructor(zip, maxBufferedBytes) {
427
433
  this.zip = zip;
428
434
  this.maxBufferedBytes = maxBufferedBytes;
429
435
  this.entries = zip.entries;
436
+ for (const entry of zip.entries) {
437
+ if (entry.name.endsWith("/")) continue;
438
+ const key = partKey(zipNameToPartName(entry.name));
439
+ if (!this.byPart.has(key)) this.byPart.set(key, entry);
440
+ }
441
+ }
442
+ /** The ZIP entry that stores a part, or undefined. */
443
+ entryOf(partName) {
444
+ return this.byPart.get(partKey(partName));
430
445
  }
431
- async load(names) {
432
- for (const name of names) {
433
- const entry = this.zip.find(name);
446
+ async load(entries) {
447
+ for (const entry of entries) {
434
448
  if (entry === void 0 || this.data.has(entry.name)) continue;
435
449
  const bytes = await this.zip.read(entry, this.maxBufferedBytes);
436
450
  this.data.set(entry.name, bytes);
437
451
  this.bytesRead += bytes.length;
438
452
  }
439
453
  }
454
+ /** Loads the parts with these names that exist. */
455
+ async loadParts(partNames) {
456
+ await this.load([...partNames].map((name) => this.entryOf(name)));
457
+ }
458
+ /** Loads the Relationships parts of these sources (null: the package) that exist. */
459
+ async loadRelationships(sources) {
460
+ await this.loadParts([...sources].map(relationshipsPartName));
461
+ }
440
462
  find(name) {
441
463
  return this.zip.find(name);
442
464
  }
443
465
  read(entry) {
444
466
  const name = typeof entry === "string" ? this.zip.find(entry)?.name ?? entry : entry.name;
445
467
  const bytes = this.data.get(name);
446
- if (bytes === void 0) throw new Error(`internal: ZIP entry "${name}" was not preloaded`);
468
+ if (bytes === void 0) throw new XlvError("XLV_E_INTERNAL", `The ZIP entry "${clip(name)}" was read before it was loaded.`, {
469
+ location: { part: `/${clip(name)}` },
470
+ hint: "This is a bug in xlvane. Please report it, with the file if you can share it; readWorkbook reads the file meanwhile."
471
+ });
447
472
  return bytes;
448
473
  }
449
474
  };
450
- function zipName(partName) {
451
- return partName.slice(1);
475
+ /**
476
+ * The internal targets of the workbook part's relationships: the sheets among them, whose
477
+ * Relationships parts analyzeWorkbook reads. Only these, and not every .rels entry of the ZIP,
478
+ * are loaded (D-H01).
479
+ */
480
+ function workbookTargets(pkg, workbookPart) {
481
+ const seen = /* @__PURE__ */ new Set();
482
+ const out = [];
483
+ for (const rel of pkg.relationships(workbookPart)) {
484
+ const key = partKey(rel.target);
485
+ if (rel.external || seen.has(key) || !pkg.hasPart(rel.target)) continue;
486
+ seen.add(key);
487
+ out.push(rel.target);
488
+ }
489
+ return out;
452
490
  }
453
491
  function checkSource(source) {
454
492
  if (source instanceof Uint8Array || source instanceof ArrayBuffer) return source;
@@ -486,9 +524,15 @@ async function openWorkbookStream(source, options) {
486
524
  });
487
525
  const zip = await openZipSource(input, limits);
488
526
  const preloaded = new PreloadedZip(zip, userLimits?.maxEntryBytes ?? DEFAULT_READ_LIMITS.maxEntryBytes);
489
- await preloaded.load(zip.entries.filter((e) => e.name.toLowerCase() === "[content_types].xml" || e.name.toLowerCase().endsWith(".rels")).map((e) => e.name));
527
+ await preloaded.load(zip.entries.filter((e) => e.name.toLowerCase() === "[content_types].xml"));
528
+ await preloaded.loadRelationships([null]);
490
529
  const pkg = openPackage(preloaded, limits);
491
- await preloaded.load(workbookPartsToLoad(pkg).map(zipName));
530
+ const workbookPart = pkg.relationships(null).find((r) => !r.external && classifyRelationship(r.type)?.name === "officeDocument")?.target;
531
+ if (workbookPart !== void 0) {
532
+ await preloaded.loadRelationships([workbookPart]);
533
+ await preloaded.loadParts(workbookPartsToLoad(pkg));
534
+ await preloaded.loadRelationships(workbookTargets(pkg, workbookPart));
535
+ }
492
536
  const warnings = new WarningCollector();
493
537
  const info = analyzeWorkbook(pkg, limits, warnings);
494
538
  const formatCache = /* @__PURE__ */ new Map();
@@ -504,7 +548,7 @@ async function openWorkbookStream(source, options) {
504
548
  const cached = noteCache.get(entry.part);
505
549
  if (cached !== void 0) return cached;
506
550
  const parts = pkg.relationships(entry.part).filter((r) => !r.external && ["comments", "vmlDrawing"].includes(classifyRelationship(r.type)?.name ?? "") && pkg.hasPart(r.target) && !entry.foreignParts.has(partKey(r.target)));
507
- if (parts.length > 0) await preloaded.load(parts.map((r) => zipName(r.target)));
551
+ if (parts.length > 0) await preloaded.loadParts(parts.map((r) => r.target));
508
552
  const notes = Object.freeze(readNotes(pkg, entry.part, entry.name, limits, warnings, entry.foreignParts));
509
553
  noteCache.set(entry.part, notes);
510
554
  return notes;
@@ -553,7 +597,15 @@ async function openWorkbookStream(source, options) {
553
597
  part,
554
598
  limits
555
599
  });
556
- const reader = zip.stream(zipName(part)).getReader();
600
+ const stored = preloaded.entryOf(part);
601
+ if (stored === void 0) throw new XlvError("XLV_E_PART_MISSING", `The part ${clip(part)} of sheet "${entry.name}" is not in the archive.`, {
602
+ location: {
603
+ part: clip(part),
604
+ sheet: entry.name
605
+ },
606
+ hint: "The workbook is incomplete. Re-save it in a spreadsheet application."
607
+ });
608
+ const reader = zip.stream(stored).getReader();
557
609
  try {
558
610
  for (;;) {
559
611
  const { done, value } = await reader.read();
@@ -640,6 +692,7 @@ async function* readTableStream(source, options) {
640
692
  const limits = o.get("limits");
641
693
  const book = await openWorkbookStream(source, limits === void 0 ? void 0 : { limits });
642
694
  const outputCells = limits?.maxCells ?? DEFAULT_READ_LIMITS.maxCells;
695
+ const keyTextLimit = limits?.maxSharedStringChars ?? DEFAULT_READ_LIMITS.maxSharedStringChars;
643
696
  const sheetName = o.get("sheet") ?? (book.sheets.find((s) => s.kind === "worksheet" && s.state === "visible") ?? book.sheets[0])?.name;
644
697
  if (sheetName === void 0) return;
645
698
  const buffered = [];
@@ -692,6 +745,16 @@ async function* readTableStream(source, options) {
692
745
  };
693
746
  let positions = 0;
694
747
  let cellsRead = 0;
748
+ let byKey = null;
749
+ let byKeyOf = null;
750
+ const columnOf = (cols, key) => {
751
+ if (byKey === null || byKeyOf !== cols) {
752
+ byKey = /* @__PURE__ */ new Map();
753
+ for (const c of cols) if (!byKey.has(c.key)) byKey.set(c.key, c);
754
+ byKeyOf = cols;
755
+ }
756
+ return byKey.get(key);
757
+ };
695
758
  const emit = async (r, cols) => {
696
759
  if (headerRow !== null && r.number <= headerRow) return null;
697
760
  cellsRead += r.cells.length;
@@ -699,6 +762,7 @@ async function* readTableStream(source, options) {
699
762
  checkOutputPositions(positions, cellsRead, outputCells, "The table", sheetName);
700
763
  const byCol = new Map(r.cells.map((c) => [c.col, c]));
701
764
  if (cols.every((c) => byCol.get(c.col)?.value == null)) return null;
765
+ checkKeyText(cols, 1, keyTextLimit, sheetName);
702
766
  const row = {};
703
767
  for (const c of cols) setOwn(row, c.key, value(byCol.get(c.col)));
704
768
  if (schema === void 0) return {
@@ -717,7 +781,7 @@ async function* readTableStream(source, options) {
717
781
  const key = typeof seg === "object" && seg !== null ? seg.key : seg;
718
782
  return typeof key === "number" ? key : String(key);
719
783
  });
720
- const column = typeof path[0] === "string" ? cols.find((c) => c.key === path[0]) : void 0;
784
+ const column = typeof path[0] === "string" ? columnOf(cols, path[0]) : void 0;
721
785
  return {
722
786
  row: r.number,
723
787
  cell: column ? formatCellRef(r.number, column.col) : null,
@@ -833,6 +897,7 @@ function writeWorkbookStream(options) {
833
897
  details: { option: "definedNames" }
834
898
  });
835
899
  const names = [];
900
+ const nameKeys = /* @__PURE__ */ new Map();
836
901
  rawNames.forEach((raw, i) => {
837
902
  const r = readOptions(raw, [
838
903
  "name",
@@ -843,8 +908,9 @@ function writeWorkbookStream(options) {
843
908
  ], `definedNames[${String(i)}]`);
844
909
  const { name, refersTo, ...rest } = Object.fromEntries(r);
845
910
  const n = checkDefinedName(name, refersTo, rest, null);
846
- checkUniqueName(n, names);
911
+ checkUniqueName(n, nameKeys);
847
912
  names.push(n);
913
+ nameKeys.set(nameScopeKey(n), n);
848
914
  });
849
915
  const addNames = () => {
850
916
  for (const n of names) book.defineName(n.name, n.refersTo, {
@@ -886,6 +952,7 @@ function writeWorkbookStream(options) {
886
952
  "tables",
887
953
  "images",
888
954
  "background",
955
+ "charts",
889
956
  "rows"
890
957
  ], `sheets[${String(i)}]`);
891
958
  const state = s.get("state");
@@ -942,7 +1009,7 @@ function writeWorkbookStream(options) {
942
1009
  for (const v of listOf("tables", "{ name: \"Sales\", ref: \"A1:B10\", columns: [{ name: \"Region\" }, { name: \"Revenue\" }] }")) {
943
1010
  const t = normalizeTable(v, sheet.name, true);
944
1011
  book.checkTable(t, sheet);
945
- sheet.tableList.push(t);
1012
+ sheet.pushTable(t);
946
1013
  }
947
1014
  const merges = s.get("merges");
948
1015
  if (merges !== void 0) {
@@ -992,6 +1059,7 @@ function writeWorkbookStream(options) {
992
1059
  sheet.addImage(image, anchor, options);
993
1060
  }
994
1061
  if (s.has("background")) sheet.background(s.get("background"));
1062
+ for (const v of listOf("charts", "{ type: \"column\", anchor: \"E2:L18\", series: [{ values: \"Data!B2:B13\" }] }")) sheet.addChart(v);
995
1063
  return {
996
1064
  spec: raw,
997
1065
  sheet,
@@ -1008,6 +1076,12 @@ function writeWorkbookStream(options) {
1008
1076
  const prepared = dynamic ? [] : specs.map(prepare);
1009
1077
  if (!dynamic && !prepared.some((p) => p.sheet.state === "visible")) throw noVisibleSheet();
1010
1078
  if (!dynamic) addNames();
1079
+ if (!dynamic) {
1080
+ const lookup = chartSheetNames(prepared.map((q) => q.sheet.name));
1081
+ for (const p of prepared) for (const c of p.sheet.chartList) resolveChartSheets(c.chart, lookup, p.sheet.name);
1082
+ }
1083
+ /** Charts in workbook order (CH-01); their parts are written once every sheet name is known. */
1084
+ const chartQueue = [];
1011
1085
  const strings = shared ? createSharedStrings() : null;
1012
1086
  const styleCache = /* @__PURE__ */ new WeakMap();
1013
1087
  const dateStyle = /* @__PURE__ */ new Map();
@@ -1162,7 +1236,19 @@ function writeWorkbookStream(options) {
1162
1236
  conditionalFormats: p.sheet.conditionalList,
1163
1237
  dataValidations: p.sheet.validationList
1164
1238
  }, n);
1165
- const drawing = drawingParts(p.sheet.imageList.map((i) => resolveImage(i, metricsOf(p.sheet.columnMap, p.sheet.rowMap))), p.sheet.backgroundImage, media, ++drawingCount, p.rels);
1239
+ const frames = p.sheet.chartList.map((c) => {
1240
+ chartQueue.push({
1241
+ chart: c.chart,
1242
+ sheet: p.sheet.name
1243
+ });
1244
+ return {
1245
+ ...resolveAnchor(c.anchor, metricsOf(p.sheet.columnMap, p.sheet.rowMap)),
1246
+ name: c.chart.name,
1247
+ description: c.chart.description,
1248
+ target: `../charts/chart${String(chartQueue.length)}.xml`
1249
+ };
1250
+ });
1251
+ const drawing = drawingParts(p.sheet.imageList.map((i) => resolveImage(i, metricsOf(p.sheet.columnMap, p.sheet.rowMap))), p.sheet.backgroundImage, media, ++drawingCount, p.rels, frames);
1166
1252
  if (drawing.part === null) drawingCount--;
1167
1253
  p.drawing = drawing.part;
1168
1254
  const sheetTables = tableParts(p.sheet.tableList, tableCount + 1, p.rels);
@@ -1279,10 +1365,16 @@ function writeWorkbookStream(options) {
1279
1365
  if (prepared.length === 0) throw new XlvError("XLV_E_INVALID_INPUT", "The workbook has no sheets.", { hint: "Add at least one sheet before closing the stream." });
1280
1366
  if (!selected) throw noVisibleSheet();
1281
1367
  if (dynamic) addNames();
1368
+ const names = chartSheetNames(prepared.map((p) => p.sheet.name));
1369
+ const chartParts = chartQueue.map((c, k) => ({
1370
+ name: chartPartName(k + 1),
1371
+ xml: chartPartXml(resolveChartSheets(c.chart, names, c.sheet), null)
1372
+ }));
1282
1373
  const app = appXml() !== null;
1283
1374
  const overrides = [
1284
1375
  ...tableTypes(),
1285
1376
  ...prepared.flatMap((p) => p.drawing === null ? [] : [[p.drawing.name, DRAWING_CONTENT_TYPE]]),
1377
+ ...chartParts.map((c) => [c.name, CHART_CONTENT_TYPE]),
1286
1378
  ...media.entries().map((m) => [m.name, IMAGE_CONTENT_TYPES[m.name.slice(m.name.lastIndexOf(".") + 1)]])
1287
1379
  ];
1288
1380
  yield {
@@ -1294,6 +1386,10 @@ function writeWorkbookStream(options) {
1294
1386
  data: encoder.encode(rootRelsXml(core !== null, app))
1295
1387
  };
1296
1388
  yield* media.entries();
1389
+ for (const c of chartParts) yield {
1390
+ name: c.name,
1391
+ data: encoder.encode(c.xml)
1392
+ };
1297
1393
  yield* closingParts();
1298
1394
  }
1299
1395
  return createZipStream(entries(), level);