@a3s-lab/office 0.10.0 → 0.12.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.
Files changed (47) hide show
  1. package/COLLABORATION_ROADMAP.md +22 -12
  2. package/README.md +40 -15
  3. package/dist/{0~4705.js → 0~5093.js} +959 -10
  4. package/dist/0~document-editor.js +1 -0
  5. package/dist/0~spreadsheet-editor.js +189 -104
  6. package/dist/0~work-docx-export.js +225 -6
  7. package/dist/0~work-docx-import.js +26 -755
  8. package/dist/0~work-office-diagnostics.js +12 -8
  9. package/dist/0~work-pptx-import.js +2 -2
  10. package/dist/4104.js +330 -122
  11. package/dist/4476.js +82 -65
  12. package/dist/8715.js +41 -3
  13. package/dist/8928.js +448 -137
  14. package/dist/9356.js +126 -8
  15. package/dist/core.d.ts +1 -0
  16. package/dist/internal/features/work/editors/spreadsheet-editor-support.d.ts +1 -0
  17. package/dist/internal/features/work/editors/use-spreadsheet-collaboration.d.ts +2 -1
  18. package/dist/internal/features/work/spreadsheet-sparse.d.ts +5 -0
  19. package/dist/internal/features/work/work-document-file-io.d.ts +2 -1
  20. package/dist/internal/features/work/work-document-format-change-tracking.d.ts +1 -1
  21. package/dist/internal/features/work/work-document-paragraph-format-change-tracking.d.ts +7 -0
  22. package/dist/internal/features/work/work-document-paragraph-format-changes.d.ts +8 -0
  23. package/dist/internal/features/work/work-docx-import.d.ts +5 -2
  24. package/dist/internal/features/work/work-docx-paragraph-alignment-import.d.ts +1 -0
  25. package/dist/internal/features/work/work-docx-paragraph-borders-import.d.ts +1 -0
  26. package/dist/internal/features/work/work-docx-paragraph-direction-import.d.ts +1 -0
  27. package/dist/internal/features/work/work-docx-paragraph-format-change-export.d.ts +13 -0
  28. package/dist/internal/features/work/work-docx-paragraph-format-change-import.d.ts +17 -0
  29. package/dist/internal/features/work/work-docx-paragraph-indent-import.d.ts +1 -0
  30. package/dist/internal/features/work/work-docx-paragraph-pagination-import.d.ts +1 -0
  31. package/dist/internal/features/work/work-docx-paragraph-shading-import.d.ts +1 -0
  32. package/dist/internal/features/work/work-docx-paragraph-spacing-import.d.ts +1 -0
  33. package/dist/internal/features/work/work-docx-tab-stop-import.d.ts +1 -0
  34. package/dist/internal/features/work/work-file-data.d.ts +7 -1
  35. package/dist/internal/features/work/work-file-import.d.ts +28 -0
  36. package/dist/internal/features/work/work-file-io.d.ts +2 -1
  37. package/dist/internal/features/work/work-markdown-file-io.d.ts +2 -1
  38. package/dist/internal/features/work/work-office-diagnostics.d.ts +3 -2
  39. package/dist/internal/features/work/work-pptx-import.d.ts +1 -1
  40. package/dist/internal/features/work/work-presentation-file-io.d.ts +2 -1
  41. package/dist/internal/features/work/work-spreadsheet-protection.d.ts +2 -0
  42. package/dist/internal/features/work/work-types.d.ts +23 -1
  43. package/dist/internal/features/work/work-xlsx-interop.d.ts +4 -14
  44. package/dist/office-kernel.wasm +0 -0
  45. package/dist/styles.css +8 -0
  46. package/docs/latest/en/browser-editor-architecture.md +18 -0
  47. package/package.json +10 -4
package/dist/4104.js CHANGED
@@ -3,10 +3,10 @@ import { MarkdownManager } from "@tiptap/markdown";
3
3
  import { getSchema } from "@tiptap/core";
4
4
  import { OFFICE_COLLABORATION_PROTOCOL as WORK_OFFICE_COLLABORATION_PROTOCOL, OfficeCollaborationError as WorkOfficeCollaborationError, assertWorkOfficeCollaborationOrigin } from "./9787.js";
5
5
  import { createWorkDocumentModel, documentContentLayoutProperties, documentModelForContent, mountWorkLiveDocumentCapture, materializeWorkDocumentContent as work_document_model_codec_materializeWorkDocumentContent, createWorkDocumentExtensions, positionWorkLiveDocumentCapture } from "./8928.js";
6
- import { contentTypeForPart, directChildren, bytesToDataUrl, OoxmlPackage, parseXml, firstDescendant, xmlNamespacePrefix, directChild, descendants, createArtifact as createWorkArtifact, createOfficeId as createWorkId, attribute } from "./5184.js";
7
- import { spreadsheetFormulaForXlsx, workSpreadsheetChartSupportsGrouping, normalizeSpreadsheetPrintArea, spreadsheetFormulaRangeConflict, formatSpreadsheetCellRanges, effectiveSpreadsheetCalculationSettings, normalizeWorkSpreadsheetChartSmoothLines, editableSpreadsheetFormula, spreadsheetCellAddress, spreadsheetFormulaRangeForCell, spreadsheetPivotFilterValueKey, normalizeWorkSpreadsheetBubbleScale, normalizeWorkSpreadsheetChartSeriesStyle, qualifySpreadsheetRange, normalizeSpreadsheetPrintTitleColumns, normalizeSpreadsheetPrintTitleRows, normalizeWorkSpreadsheetErrorBars, normalizeWorkSpreadsheetBubbleSizeRepresents, workSpreadsheetChartSupportsBarSpacing, workSpreadsheetChartSupportsTrendlines, workSpreadsheetChartGroupingIsStacked, normalizeWorkSpreadsheetChartGrouping, normalizeWorkSpreadsheetChartLegendOverlay, workSpreadsheetChartSupportsSmoothLines, normalizeWorkSpreadsheetChartGapWidth, xlsxTwoCellAnchorMarkers, xlsxDrawingAnchorToBounds, stripSpreadsheetSheetQualifier, workSpreadsheetChartSupportsErrorBars, refreshSpreadsheetPivotTables, normalizeWorkSpreadsheetChartAxisGroup, normalizeWorkSpreadsheetScatterStyle, workSpreadsheetChartAxisDefaultLabelPosition, isValidSpreadsheetDefinedName, spreadsheetPivotValidation, resolveSpreadsheetChart, workSpreadsheetChartAxisIsCategoryAxis, parseSpreadsheetCellRanges, spreadsheetPivotFields, normalizeWorkSpreadsheetDoughnutHoleSize, normalizeWorkSpreadsheetChartAxes, normalizeWorkSpreadsheetChartOverlap, workSpreadsheetChartUsesNumericXAxis, readXlsxDrawingAnchor, parseSpreadsheetPrintTitles, normalizeWorkSpreadsheetChartLegendPosition, normalizeWorkSpreadsheetChartColor, workSpreadsheetChartSupportsAxes, workSpreadsheetChartAxisShowsMajorGridlinesByDefault, normalizeWorkSpreadsheetTrendline, normalizeWorkSpreadsheetRadarStyle, normalizeWorkSpreadsheetCombinationSeriesType, normalizeWorkSpreadsheetDataLabels } from "./8715.js";
8
- import { editableRangeRequiresCredentials, normalizeSpreadsheetConditionalIconSetFormat, defaultSpreadsheetDataBarOptions, normalizeSpreadsheetConditionalVisualOptions, normalizeSheetProtectionAuthority, applySpreadsheetCellProtectionRanges, normalizeSpreadsheetPaperSize, defaultSpreadsheetColorScaleThresholds, applyPasswordlessEditableRanges, sheetHasProtectionState, spreadsheetConditionalComparisonNeedsUpperValue, isSpreadsheetConditionalComparisonOperator, defaultSpreadsheetConditionalIconThresholds, DEFAULT_PROTECTION_HINT, isSpreadsheetConditionalIconSetName, spreadsheetConditionalThresholdsEqual, spreadsheetConditionalIconSetCount } from "./4476.js";
9
- import { materializeWorkFile } from "./9356.js";
6
+ import { contentTypeForPart, directChildren, bytesToDataUrl, OoxmlPackage as work_ooxml_package_OoxmlPackage, parseXml, firstDescendant, xmlNamespacePrefix, directChild, descendants, createArtifact as createWorkArtifact, createOfficeId as createWorkId, attribute } from "./5184.js";
7
+ import { spreadsheetFormulaForXlsx, workSpreadsheetChartSupportsGrouping, normalizeSpreadsheetPrintArea, spreadsheetFormulaRangeConflict, formatSpreadsheetCellRanges, effectiveSpreadsheetCalculationSettings, normalizeWorkSpreadsheetChartSmoothLines, editableSpreadsheetFormula, spreadsheetCellAddress, spreadsheetFormulaRangeForCell, sparseArrayEntries, normalizeWorkSpreadsheetBubbleScale, normalizeWorkSpreadsheetChartSeriesStyle, qualifySpreadsheetRange, normalizeSpreadsheetPrintTitleColumns, normalizeSpreadsheetPrintTitleRows, spreadsheetPivotFilterValueKey, normalizeWorkSpreadsheetErrorBars, normalizeWorkSpreadsheetBubbleSizeRepresents, workSpreadsheetChartSupportsBarSpacing, workSpreadsheetChartSupportsTrendlines, workSpreadsheetChartGroupingIsStacked, normalizeWorkSpreadsheetChartGrouping, normalizeWorkSpreadsheetChartLegendOverlay, workSpreadsheetChartSupportsSmoothLines, normalizeWorkSpreadsheetChartGapWidth, xlsxTwoCellAnchorMarkers, xlsxDrawingAnchorToBounds, stripSpreadsheetSheetQualifier, workSpreadsheetChartSupportsErrorBars, sparseMatrixColumnCount, refreshSpreadsheetPivotTables, normalizeWorkSpreadsheetChartAxisGroup, normalizeWorkSpreadsheetScatterStyle, workSpreadsheetChartAxisDefaultLabelPosition, isValidSpreadsheetDefinedName, spreadsheetPivotValidation, resolveSpreadsheetChart, workSpreadsheetChartAxisIsCategoryAxis, parseSpreadsheetCellRanges, spreadsheetPivotFields, normalizeWorkSpreadsheetDoughnutHoleSize, normalizeWorkSpreadsheetChartAxes, normalizeWorkSpreadsheetChartOverlap, workSpreadsheetChartUsesNumericXAxis, readXlsxDrawingAnchor, parseSpreadsheetPrintTitles, normalizeWorkSpreadsheetChartLegendPosition, normalizeWorkSpreadsheetChartColor, workSpreadsheetChartSupportsAxes, workSpreadsheetChartAxisShowsMajorGridlinesByDefault, normalizeWorkSpreadsheetTrendline, normalizeWorkSpreadsheetRadarStyle, normalizeWorkSpreadsheetCombinationSeriesType, normalizeWorkSpreadsheetDataLabels } from "./8715.js";
8
+ import { editableRangeRequiresCredentials, normalizeSpreadsheetConditionalIconSetFormat, defaultSpreadsheetDataBarOptions, normalizeSpreadsheetConditionalVisualOptions, normalizeSheetProtectionAuthority, importedSheetProtectionAuthority, normalizeSpreadsheetPaperSize, defaultSpreadsheetColorScaleThresholds, sheetHasProtectionState, spreadsheetConditionalComparisonNeedsUpperValue, isSpreadsheetConditionalComparisonOperator, defaultSpreadsheetConditionalIconThresholds, DEFAULT_PROTECTION_HINT, isSpreadsheetConditionalIconSetName, spreadsheetConditionalThresholdsEqual, spreadsheetConditionalIconSetCount } from "./4476.js";
9
+ import { WorkFileImportController, materializeWorkFileSource } from "./9356.js";
10
10
  import "./2591.js";
11
11
  import "./3818.js";
12
12
  import "./4560.js";
@@ -495,13 +495,17 @@ async function workSourceFingerprint(bytes) {
495
495
  return `sha256:${Array.from(digest, (value)=>value.toString(16).padStart(2, '0')).join('')}`;
496
496
  }
497
497
  const DOCX_CONTENT_TYPE = 'application/vnd.openxmlformats-officedocument.wordprocessingml.document';
498
- async function importWorkDocumentFile(file, extension) {
498
+ async function importWorkDocumentFile(file, extension, context) {
499
499
  let html;
500
500
  if ('docx' === extension) {
501
501
  const mammoth = await import("mammoth");
502
- const arrayBuffer = await file.arrayBuffer();
502
+ const arrayBuffer = context?.bytes ?? await file.arrayBuffer();
503
+ context?.controller.report('parsing', 0.1);
503
504
  const { applyDocxSectionsToHtml, prepareDocxImport, readDocxLayout } = await import("./0~work-docx-import.js");
504
- const prepared = await prepareDocxImport(arrayBuffer).catch(()=>null);
505
+ const { OoxmlPackage } = await import("./5184.js");
506
+ const sourcePackage = await OoxmlPackage.load(arrayBuffer).catch(()=>null);
507
+ const prepared = await prepareDocxImport(arrayBuffer, sourcePackage ?? void 0).catch(()=>null);
508
+ context?.controller.report('parsing', 0.45);
505
509
  const conversionBuffer = prepared?.conversionBuffer ?? arrayBuffer;
506
510
  const input = {
507
511
  arrayBuffer: conversionBuffer,
@@ -512,7 +516,8 @@ async function importWorkDocumentFile(file, extension) {
512
516
  "br[type='page'] => hr.work-page-break[data-page-break='true']:fresh"
513
517
  ]
514
518
  });
515
- html = prepared ? applyDocxSectionsToHtml(result.value, prepared.sections, prepared.captionMarkers, prepared.bookmarkMarkers, prepared.changeMarkers, prepared.commentMarkers, prepared.fieldMarkers, prepared.equationMarkers, prepared.citationMarkers, prepared.listMarkers, prepared.imageLayoutMarkers, prepared.paragraphIdentityMarkers, prepared.paragraphAlignmentMarkers, prepared.runFormattingMarkers, prepared.paragraphDirectionMarkers, prepared.paragraphIndentMarkers, prepared.paragraphSpacingMarkers, prepared.paragraphBorderMarkers, prepared.paragraphShadingMarkers, prepared.paragraphPaginationMarkers, prepared.bibliography, prepared.tabStopMarkers, prepared.tableCellMarkers, prepared.tableRowMarkers, prepared.tableSizingMarkers) : result.value;
519
+ context?.controller.report('parsing', 0.8);
520
+ html = prepared ? applyDocxSectionsToHtml(result.value, prepared.sections, prepared.captionMarkers, prepared.bookmarkMarkers, prepared.changeMarkers, prepared.commentMarkers, prepared.fieldMarkers, prepared.equationMarkers, prepared.citationMarkers, prepared.listMarkers, prepared.imageLayoutMarkers, prepared.paragraphIdentityMarkers, prepared.paragraphFormattingChangeMarkers, prepared.paragraphAlignmentMarkers, prepared.runFormattingMarkers, prepared.paragraphDirectionMarkers, prepared.paragraphIndentMarkers, prepared.paragraphSpacingMarkers, prepared.paragraphBorderMarkers, prepared.paragraphShadingMarkers, prepared.paragraphPaginationMarkers, prepared.bibliography, prepared.tabStopMarkers, prepared.tableCellMarkers, prepared.tableRowMarkers, prepared.tableSizingMarkers) : result.value;
516
521
  const layout = prepared ? {
517
522
  ...documentContentLayoutProperties(prepared.sections[0].layout),
518
523
  ...prepared.pageColor ? {
@@ -538,7 +543,9 @@ async function importWorkDocumentFile(file, extension) {
538
543
  bibliography: prepared.bibliography
539
544
  } : {}
540
545
  });
541
- artifact.compatibility = await analyzeDocxCompatibility(file, result.messages);
546
+ context?.controller.report('analyzing', 0);
547
+ artifact.compatibility = await analyzeDocxCompatibility(file, result.messages, sourcePackage);
548
+ context?.controller.report('analyzing', 1);
542
549
  artifact.source = {
543
550
  name: file.name,
544
551
  contentType: file.type || DOCX_CONTENT_TYPE,
@@ -549,7 +556,7 @@ async function importWorkDocumentFile(file, extension) {
549
556
  rememberWorkSourceBlob(artifact.id, file);
550
557
  return artifact;
551
558
  }
552
- const source = await file.text();
559
+ const source = context ? new TextDecoder().decode(context.bytes) : await file.text();
553
560
  html = 'html' === extension || 'htm' === extension ? source : textToHtml(source);
554
561
  const artifact = createWorkArtifact('blank-document');
555
562
  artifact.title = fileNameWithoutExtension(file.name);
@@ -558,6 +565,7 @@ async function importWorkDocumentFile(file, extension) {
558
565
  html,
559
566
  pageSize: 'a4'
560
567
  });
568
+ context?.controller.report('parsing', 1);
561
569
  return artifact;
562
570
  }
563
571
  async function createWorkDocumentBlob(artifact) {
@@ -583,13 +591,14 @@ function escapeHtml(value) {
583
591
  return value.replaceAll('&', '&amp;').replaceAll('<', '&lt;').replaceAll('>', '&gt;');
584
592
  }
585
593
  const MARKDOWN_CONTENT_TYPE = 'text/markdown;charset=utf-8';
586
- async function importWorkMarkdownFile(file) {
594
+ async function importWorkMarkdownFile(file, context) {
587
595
  const artifact = createWorkArtifact('blank-markdown');
588
596
  artifact.title = fileNameWithoutExtension(file.name);
589
597
  artifact.content = {
590
598
  type: 'markdown',
591
- markdown: await file.text()
599
+ markdown: context ? new TextDecoder().decode(context.bytes) : await file.text()
592
600
  };
601
+ context?.controller.report('parsing', 1);
593
602
  return artifact;
594
603
  }
595
604
  function createWorkMarkdownBlob(artifact) {
@@ -602,9 +611,12 @@ function createWorkMarkdownBlob(artifact) {
602
611
  }
603
612
  const presentationRuntimePromises = new Map();
604
613
  const defaultPptxRuntimeUrl = new URL('./pptxgen.bundle.js', import.meta.url).href;
605
- async function importWorkPresentationFile(file) {
614
+ async function importWorkPresentationFile(file, context) {
606
615
  const { importPptxPresentation } = await import("./0~work-pptx-import.js");
607
- const imported = await importPptxPresentation(file);
616
+ context?.controller.report('parsing', 0.1);
617
+ const imported = await importPptxPresentation(file, context?.bytes);
618
+ context?.controller.report('parsing', 1);
619
+ context?.controller.report('analyzing', 1);
608
620
  const artifact = createWorkArtifact('blank-presentation');
609
621
  artifact.title = fileNameWithoutExtension(file.name);
610
622
  artifact.content = imported.content;
@@ -2214,9 +2226,6 @@ function encodeSpreadsheetColumn(index) {
2214
2226
  }
2215
2227
  return label;
2216
2228
  }
2217
- async function readXlsxFormulaFeatures(buffer) {
2218
- return readXlsxFormulaFeaturesFromPackage(await OoxmlPackage.load(buffer));
2219
- }
2220
2229
  async function readXlsxFormulaFeaturesFromPackage(archive) {
2221
2230
  if (!archive.has('xl/workbook.xml')) return {
2222
2231
  calculation: effectiveSpreadsheetCalculationSettings(void 0),
@@ -2295,7 +2304,7 @@ function createXlsxErrorCell(cell) {
2295
2304
  };
2296
2305
  }
2297
2306
  async function patchXlsxFormulaFeatures(buffer, content) {
2298
- const archive = await OoxmlPackage.load(buffer);
2307
+ const archive = await work_ooxml_package_OoxmlPackage.load(buffer);
2299
2308
  const zip = await jszip.loadAsync(buffer);
2300
2309
  if (archive.has('xl/workbook.xml')) {
2301
2310
  const entry = zip.file('xl/workbook.xml');
@@ -2639,7 +2648,7 @@ function xlsxWorksheetImagesToSheet(images, config) {
2639
2648
  }
2640
2649
  async function patchXlsxWorksheetDrawings(buffer, content) {
2641
2650
  if (!content.sheets.some((sheet)=>sheet.images?.some((image)=>serializableImage(image)) || (sheet.charts?.length ?? 0) > 0)) return buffer;
2642
- const archive = await OoxmlPackage.load(buffer);
2651
+ const archive = await work_ooxml_package_OoxmlPackage.load(buffer);
2643
2652
  const worksheetParts = await exportedWorksheetParts(archive);
2644
2653
  const zip = await jszip.loadAsync(buffer);
2645
2654
  const drawingPaths = [];
@@ -3503,7 +3512,10 @@ function writeXlsxProtection(worksheet, sheet, styles) {
3503
3512
  'extLst'
3504
3513
  ]);
3505
3514
  }
3506
- writeProtectedRanges(worksheet, authority.allowRangeList);
3515
+ writeProtectedRanges(worksheet, [
3516
+ ...authority.allowRangeList,
3517
+ ...generatedEditableProtectionRanges(authority)
3518
+ ]);
3507
3519
  if (styles) writeCellProtectionStyles(worksheet, sheet, styles);
3508
3520
  }
3509
3521
  class XlsxCellProtectionWriter {
@@ -3771,13 +3783,17 @@ function writeProtectionAttributes(element, authority) {
3771
3783
  function writeProtectedRanges(document1, ranges) {
3772
3784
  const root = document1.documentElement;
3773
3785
  const container = document1.createElementNS(root.namespaceURI, 'protectedRanges');
3786
+ const written = new Set();
3774
3787
  for (const range of ranges){
3775
3788
  const parsed = parseSpreadsheetCellRanges(range.sqref);
3776
3789
  if (!range.name.trim() || !parsed?.length) continue;
3790
+ const reference = formatSpreadsheetCellRanges(parsed).replaceAll(',', ' ');
3791
+ if (written.has(reference)) continue;
3792
+ written.add(reference);
3777
3793
  const element = document1.createElementNS(root.namespaceURI, 'protectedRange');
3778
3794
  copyAttributes(element, range.xlsxAttributes);
3779
3795
  element.setAttribute('name', range.name.trim());
3780
- element.setAttribute('sqref', formatSpreadsheetCellRanges(parsed).replaceAll(',', ' '));
3796
+ element.setAttribute('sqref', reference);
3781
3797
  container.append(element);
3782
3798
  }
3783
3799
  if (!container.children.length) return;
@@ -3803,16 +3819,88 @@ function writeProtectedRanges(document1, ranges) {
3803
3819
  ]);
3804
3820
  }
3805
3821
  function writeCellProtectionStyles(worksheet, sheet, styles) {
3806
- for (const [row, values] of (sheet.data ?? []).entries())for (const [column, source] of values.entries()){
3822
+ const authority = normalizeSheetProtectionAuthority(sheet.config?.authority);
3823
+ for (const [row, values] of sparseArrayEntries(sheet.data))for (const [column, source] of sparseArrayEntries(values)){
3807
3824
  const cell = source;
3808
- if (!cell || void 0 === cell.lo && void 0 === cell.hi) continue;
3825
+ if (!cell) continue;
3826
+ const compact = compactProtectionAt(authority, row, column);
3827
+ if (void 0 === cell.lo && void 0 === cell.hi && void 0 === compact) continue;
3809
3828
  const element = ensureCellElement(worksheet, row, column);
3810
3829
  const baseStyle = nonNegativeInteger(attribute(element, 's')) ?? 0;
3811
- const styleId = styles.styleId(baseStyle, 0 !== cell.lo, 1 === cell.hi);
3830
+ const styleId = styles.styleId(baseStyle, void 0 === cell.lo ? compact?.locked ?? true : 0 !== cell.lo, void 0 === cell.hi ? compact?.hidden ?? false : 1 === cell.hi);
3812
3831
  if (styleId) element.setAttribute('s', String(styleId));
3813
3832
  else element.removeAttribute('s');
3814
3833
  }
3815
3834
  }
3835
+ function generatedEditableProtectionRanges(authority) {
3836
+ let editable = [];
3837
+ for (const item of authority.cellProtectionRanges)if (item.locked) editable = editable.flatMap((range)=>subtractSpreadsheetRange(range, item.range));
3838
+ else editable.push(item.range);
3839
+ return editable.map((range, index)=>({
3840
+ name: `Work editable range ${index + 1}`,
3841
+ sqref: formatSpreadsheetCellRanges([
3842
+ range
3843
+ ])
3844
+ }));
3845
+ }
3846
+ function compactProtectionAt(authority, row, column) {
3847
+ for(let index = authority.cellProtectionRanges.length - 1; index >= 0; index -= 1){
3848
+ const item = authority.cellProtectionRanges[index];
3849
+ if (row >= item.range.row[0] && row <= item.range.row[1] && column >= item.range.column[0] && column <= item.range.column[1]) return {
3850
+ locked: item.locked,
3851
+ hidden: item.hidden
3852
+ };
3853
+ }
3854
+ }
3855
+ function subtractSpreadsheetRange(source, removed) {
3856
+ const top = Math.max(source.row[0], removed.row[0]);
3857
+ const bottom = Math.min(source.row[1], removed.row[1]);
3858
+ const left = Math.max(source.column[0], removed.column[0]);
3859
+ const right = Math.min(source.column[1], removed.column[1]);
3860
+ if (top > bottom || left > right) return [
3861
+ source
3862
+ ];
3863
+ const remaining = [];
3864
+ if (source.row[0] < top) remaining.push({
3865
+ row: [
3866
+ source.row[0],
3867
+ top - 1
3868
+ ],
3869
+ column: [
3870
+ ...source.column
3871
+ ]
3872
+ });
3873
+ if (bottom < source.row[1]) remaining.push({
3874
+ row: [
3875
+ bottom + 1,
3876
+ source.row[1]
3877
+ ],
3878
+ column: [
3879
+ ...source.column
3880
+ ]
3881
+ });
3882
+ if (source.column[0] < left) remaining.push({
3883
+ row: [
3884
+ top,
3885
+ bottom
3886
+ ],
3887
+ column: [
3888
+ source.column[0],
3889
+ left - 1
3890
+ ]
3891
+ });
3892
+ if (right < source.column[1]) remaining.push({
3893
+ row: [
3894
+ top,
3895
+ bottom
3896
+ ],
3897
+ column: [
3898
+ right + 1,
3899
+ source.column[1]
3900
+ ]
3901
+ });
3902
+ return remaining;
3903
+ }
3816
3904
  function ensureCellElement(document1, rowIndex, columnIndex) {
3817
3905
  const sheetData = directChild(document1.documentElement, 'sheetData');
3818
3906
  if (!sheetData) throw new Error('XLSX worksheet does not contain sheetData');
@@ -4352,8 +4440,7 @@ function inchesToMillimeters(value) {
4352
4440
  function millimetersToInches(value) {
4353
4441
  return String(Number((value / 25.4).toFixed(4)));
4354
4442
  }
4355
- async function readXlsxSheetFeatures(buffer) {
4356
- const archive = await OoxmlPackage.load(buffer);
4443
+ async function readXlsxSheetFeaturesFromPackage(archive) {
4357
4444
  const worksheetParts = await work_xlsx_interop_readWorksheetParts(archive);
4358
4445
  const styles = archive.has('xl/styles.xml') ? await archive.xml('xl/styles.xml') : null;
4359
4446
  const differentialFormats = readXlsxDifferentialFormats(styles);
@@ -4387,8 +4474,8 @@ async function patchXlsxSheetFeatures(buffer, content) {
4387
4474
  pageSetup.sheetId,
4388
4475
  pageSetup
4389
4476
  ]));
4390
- if (!sheets.some((sheet)=>sheet.frozen || Object.keys(sheet.dataVerification ?? {}).length || sheet.luckysheet_conditionformat_save?.length || sheetHasProtectionState(sheet) || Boolean(sheet.id && (pageBreaksBySheetId.get(sheet.id)?.rows?.length ?? 0) + (pageBreaksBySheetId.get(sheet.id)?.columns?.length ?? 0)) || Boolean(sheet.id && pageSetupsBySheetId.has(sheet.id)))) return buffer;
4391
- const archive = await OoxmlPackage.load(buffer);
4477
+ if (!sheets.some((sheet)=>sheet.frozen || Object.keys(sheet.dataVerification ?? {}).length || sheet.dataValidationRanges?.length || sheet.luckysheet_conditionformat_save?.length || sheetHasProtectionState(sheet) || Boolean(sheet.id && (pageBreaksBySheetId.get(sheet.id)?.rows?.length ?? 0) + (pageBreaksBySheetId.get(sheet.id)?.columns?.length ?? 0)) || Boolean(sheet.id && pageSetupsBySheetId.has(sheet.id)))) return buffer;
4478
+ const archive = await work_ooxml_package_OoxmlPackage.load(buffer);
4392
4479
  const worksheetParts = await work_xlsx_interop_readWorksheetParts(archive);
4393
4480
  const zip = await jszip.loadAsync(buffer);
4394
4481
  const styles = archive.has('xl/styles.xml') ? await archive.xml('xl/styles.xml') : null;
@@ -4397,14 +4484,14 @@ async function patchXlsxSheetFeatures(buffer, content) {
4397
4484
  for (const sheet of sheets){
4398
4485
  const pageBreaks = sheet.id ? pageBreaksBySheetId.get(sheet.id) : void 0;
4399
4486
  const pageSetup = sheet.id ? pageSetupsBySheetId.get(sheet.id) : void 0;
4400
- if (!sheet.frozen && !Object.keys(sheet.dataVerification ?? {}).length && !sheet.luckysheet_conditionformat_save?.length && !sheetHasProtectionState(sheet) && !(pageBreaks?.rows?.length || pageBreaks?.columns?.length) && !pageSetup) continue;
4487
+ if (!sheet.frozen && !Object.keys(sheet.dataVerification ?? {}).length && !sheet.dataValidationRanges?.length && !sheet.luckysheet_conditionformat_save?.length && !sheetHasProtectionState(sheet) && !(pageBreaks?.rows?.length || pageBreaks?.columns?.length) && !pageSetup) continue;
4401
4488
  const exportedName = sheet.name.slice(0, 31) || '工作表';
4402
4489
  const partPath = worksheetParts.get(exportedName);
4403
4490
  const entry = partPath ? zip.file(partPath) : null;
4404
4491
  if (!partPath || !entry) continue;
4405
4492
  const document1 = parseXml(await entry.async('text'), partPath);
4406
4493
  if (sheet.frozen) writeFrozenPane(document1, sheet.frozen);
4407
- writeDataValidations(document1, sheet.dataVerification);
4494
+ writeDataValidations(document1, sheet.dataVerification, sheet.dataValidationRanges);
4408
4495
  writeXlsxConditionalFormats(document1, sheet.luckysheet_conditionformat_save, differentialFormats);
4409
4496
  writeXlsxProtection(document1, sheet, cellProtection);
4410
4497
  writeXlsxPageSetup(document1, pageSetup);
@@ -4475,10 +4562,10 @@ function parseDataValidations(document1) {
4475
4562
  ];
4476
4563
  });
4477
4564
  }
4478
- function writeDataValidations(document1, source) {
4565
+ function writeDataValidations(document1, source, compact) {
4479
4566
  const root = document1.documentElement;
4480
4567
  for (const existing of directChildren(root, 'dataValidations'))existing.remove();
4481
- const grouped = groupDataValidations(source);
4568
+ const grouped = groupDataValidations(source, compact);
4482
4569
  if (!grouped.length) return;
4483
4570
  const namespace = root.namespaceURI;
4484
4571
  const container = document1.createElementNS(namespace, 'dataValidations');
@@ -4514,31 +4601,42 @@ function writeDataValidations(document1, source) {
4514
4601
  ].includes(child.localName));
4515
4602
  root.insertBefore(container, anchor ?? null);
4516
4603
  }
4517
- function groupDataValidations(source) {
4518
- if (!source || 'object' != typeof source) return [];
4604
+ function groupDataValidations(source, compact) {
4519
4605
  const groups = new Map();
4520
- for (const [key, value] of Object.entries(source)){
4521
- const match = /^(\d+)_(\d+)$/.exec(key);
4522
- const item = fortuneDataValidationItem(value);
4523
- if (!match || !item || !xlsxValidationType(item.type)) continue;
4524
- const signature = JSON.stringify({
4525
- type: item.type,
4526
- type2: item.type2,
4527
- value1: item.value1,
4528
- value2: item.value2,
4529
- prohibitInput: item.prohibitInput,
4530
- hintShow: item.hintShow,
4531
- hintValue: item.hintValue
4532
- });
4606
+ const append = (item, references)=>{
4607
+ const signature = validationSignature(item);
4533
4608
  const group = groups.get(signature) ?? {
4534
4609
  item,
4535
4610
  references: []
4536
4611
  };
4537
- group.references.push(work_xlsx_interop_encodeCell(Number(match[1]), Number(match[2])));
4612
+ for (const reference of references)if (!group.references.includes(reference)) group.references.push(reference);
4538
4613
  groups.set(signature, group);
4614
+ };
4615
+ for (const entry of compact ?? []){
4616
+ const item = fortuneDataValidationItem(entry.item);
4617
+ if (item && xlsxValidationType(item.type)) append(item, entry.ranges.map((range)=>`${work_xlsx_interop_encodeCell(range.row[0], range.column[0])}:${work_xlsx_interop_encodeCell(range.row[1], range.column[1])}`));
4618
+ }
4619
+ if (!source || 'object' != typeof source) return Array.from(groups.values());
4620
+ for (const [key, value] of Object.entries(source)){
4621
+ const match = /^(\d+)_(\d+)$/.exec(key);
4622
+ const item = fortuneDataValidationItem(value);
4623
+ if (match && item && xlsxValidationType(item.type)) append(item, [
4624
+ work_xlsx_interop_encodeCell(Number(match[1]), Number(match[2]))
4625
+ ]);
4539
4626
  }
4540
4627
  return Array.from(groups.values());
4541
4628
  }
4629
+ function validationSignature(item) {
4630
+ return JSON.stringify({
4631
+ type: item.type,
4632
+ type2: item.type2,
4633
+ value1: item.value1,
4634
+ value2: item.value2,
4635
+ prohibitInput: item.prohibitInput,
4636
+ hintShow: item.hintShow,
4637
+ hintValue: item.hintValue
4638
+ });
4639
+ }
4542
4640
  function fortuneDataValidationItem(value) {
4543
4641
  if (!value || 'object' != typeof value) return null;
4544
4642
  const item = value;
@@ -4969,7 +5067,7 @@ const PIVOT_CACHE_DEFINITION_CONTENT_TYPE = 'application/vnd.openxmlformats-offi
4969
5067
  const PIVOT_CACHE_RECORDS_CONTENT_TYPE = 'application/vnd.openxmlformats-officedocument.spreadsheetml.pivotCacheRecords+xml';
4970
5068
  async function patchXlsxPivotTables(buffer, content) {
4971
5069
  if (!content.sheets.some((sheet)=>sheet.pivotTables?.length)) return buffer;
4972
- const archive = await OoxmlPackage.load(buffer);
5070
+ const archive = await work_ooxml_package_OoxmlPackage.load(buffer);
4973
5071
  const worksheetParts = await workbookWorksheetParts(archive);
4974
5072
  const candidates = content.sheets.flatMap((ownerSheet)=>(ownerSheet.pivotTables ?? []).flatMap((pivot)=>{
4975
5073
  if (!ownerSheet.id || !spreadsheetPivotValidation(content, ownerSheet.id, pivot).valid) return [];
@@ -5055,9 +5153,6 @@ async function patchXlsxPivotTables(buffer, content) {
5055
5153
  compression: 'DEFLATE'
5056
5154
  });
5057
5155
  }
5058
- async function readXlsxPivotTables(buffer) {
5059
- return inspectXlsxPivotTables(await OoxmlPackage.load(buffer));
5060
- }
5061
5156
  async function inspectXlsxPivotTables(archive) {
5062
5157
  if (!archive.has('xl/workbook.xml')) return {
5063
5158
  tables: [],
@@ -5533,15 +5628,23 @@ const PRESENTATION_EXTENSIONS = new Set([
5533
5628
  const PDF_EXTENSIONS = new Set([
5534
5629
  'pdf'
5535
5630
  ]);
5536
- async function importWorkFile(file) {
5631
+ const MAX_INLINE_DATA_VALIDATION_CELLS = 10000;
5632
+ async function importWorkFile(file, options = {}) {
5537
5633
  const extension = file.name.split('.').pop()?.toLowerCase() ?? '';
5538
5634
  if (!MARKDOWN_EXTENSIONS.has(extension) && !DOCUMENT_EXTENSIONS.has(extension) && !SPREADSHEET_EXTENSIONS.has(extension) && !PRESENTATION_EXTENSIONS.has(extension) && !PDF_EXTENSIONS.has(extension)) throw new Error('目前可导入 DOCX、XLSX、XLS、ODS、CSV、PPTX、PDF、HTML、Markdown 和文本文件。');
5539
- const source = await materializeWorkFile(file);
5540
- if (MARKDOWN_EXTENSIONS.has(extension)) return importWorkMarkdownFile(source);
5541
- if (DOCUMENT_EXTENSIONS.has(extension)) return importWorkDocumentFile(source, extension);
5542
- if (SPREADSHEET_EXTENSIONS.has(extension)) return importSpreadsheet(source, extension);
5543
- if (PRESENTATION_EXTENSIONS.has(extension)) return importWorkPresentationFile(source);
5544
- return importPdf(source);
5635
+ const controller = new WorkFileImportController(options, file.size);
5636
+ const source = await materializeWorkFileSource(file, controller);
5637
+ const context = {
5638
+ bytes: source.bytes,
5639
+ controller
5640
+ };
5641
+ controller.report('parsing', 0);
5642
+ let artifact;
5643
+ artifact = MARKDOWN_EXTENSIONS.has(extension) ? await importWorkMarkdownFile(source.file, context) : DOCUMENT_EXTENSIONS.has(extension) ? await importWorkDocumentFile(source.file, extension, context) : SPREADSHEET_EXTENSIONS.has(extension) ? await importSpreadsheet(source.file, extension, context) : PRESENTATION_EXTENSIONS.has(extension) ? await importWorkPresentationFile(source.file, context) : await importPdf(source.file);
5644
+ controller.report('analyzing', 1);
5645
+ controller.report('finalizing', 0);
5646
+ controller.complete();
5647
+ return artifact;
5545
5648
  }
5546
5649
  async function exportWorkArtifact(artifact, options) {
5547
5650
  const blob = await createWorkArtifactBlob(artifact, options);
@@ -5554,9 +5657,10 @@ async function createWorkArtifactBlob(artifact, options) {
5554
5657
  if ('presentation' === artifact.kind) return createWorkPresentationBlob(artifact, options);
5555
5658
  return readWorkSourceBlob(artifact);
5556
5659
  }
5557
- async function importSpreadsheet(file, extension) {
5660
+ async function importSpreadsheet(file, extension, context) {
5558
5661
  const XLSX = await import("xlsx");
5559
- const arrayBuffer = await file.arrayBuffer();
5662
+ context.controller.report('parsing', 0.05);
5663
+ const arrayBuffer = context.bytes;
5560
5664
  const workbook = XLSX.read(arrayBuffer, {
5561
5665
  type: 'array',
5562
5666
  cellDates: true,
@@ -5564,39 +5668,44 @@ async function importSpreadsheet(file, extension) {
5564
5668
  cellStyles: true,
5565
5669
  xlfn: true
5566
5670
  });
5567
- const sheetFeatures = 'xlsx' === extension ? await readXlsxSheetFeatures(arrayBuffer).catch(()=>new Map()) : new Map();
5568
- const formulaFeatures = 'xlsx' === extension ? await readXlsxFormulaFeatures(arrayBuffer).catch(()=>null) : null;
5569
- const pivotFeatures = 'xlsx' === extension ? await readXlsxPivotTables(arrayBuffer).catch(()=>null) : null;
5570
- const sheets = workbook.SheetNames.map((name, index)=>{
5671
+ await context.controller.checkpoint('parsing', 0.2);
5672
+ const archive = 'xlsx' === extension ? await work_ooxml_package_OoxmlPackage.load(arrayBuffer).catch(()=>null) : null;
5673
+ const sheetFeatures = archive ? await readXlsxSheetFeaturesFromPackage(archive).catch(()=>new Map()) : new Map();
5674
+ const formulaFeatures = archive ? await readXlsxFormulaFeaturesFromPackage(archive).catch(()=>null) : null;
5675
+ const pivotFeatures = archive ? await inspectXlsxPivotTables(archive).catch(()=>null) : null;
5676
+ await context.controller.checkpoint('parsing', 0.4);
5677
+ const sheets = [];
5678
+ for (const [index, name] of workbook.SheetNames.entries()){
5571
5679
  const worksheet = workbook.Sheets[name];
5680
+ if (!worksheet) continue;
5572
5681
  const features = sheetFeatures.get(name);
5573
- const range = worksheet['!ref'] ? XLSX.utils.decode_range(worksheet['!ref']) : {
5574
- s: {
5575
- r: 0,
5576
- c: 0
5577
- },
5578
- e: {
5579
- r: 39,
5580
- c: 11
5581
- }
5582
- };
5583
- const rowCount = Math.max(range.e.r + 1, 40);
5584
- const columnCount = Math.max(range.e.c + 1, 12);
5682
+ const range = safeSpreadsheetRange(worksheet, XLSX);
5683
+ let rowCount = Math.max(range.e.r + 1, 40);
5684
+ let columnCount = Math.max(range.e.c + 1, 12);
5585
5685
  const id = createWorkId('sheet');
5586
5686
  const hyperlinks = {};
5587
- const data = Array.from({
5588
- length: rowCount
5589
- }, ()=>Array(columnCount).fill(null));
5590
- for(let row = range.s.r; row <= range.e.r; row += 1)for(let column = range.s.c; column <= range.e.c; column += 1){
5591
- const source = worksheet[XLSX.utils.encode_cell({
5592
- r: row,
5593
- c: column
5594
- })];
5595
- if (!source) continue;
5687
+ const data = [];
5688
+ const entries = Object.entries(worksheet).filter(([address, value])=>!address.startsWith('!') && Boolean(value) && 'object' == typeof value);
5689
+ const sheetProgressStart = 0.4 + index / Math.max(1, workbook.SheetNames.length) * 0.45;
5690
+ const sheetProgressSize = 0.45 / Math.max(1, workbook.SheetNames.length);
5691
+ for (const [entryIndex, [address, value]] of entries.entries()){
5692
+ let position;
5693
+ try {
5694
+ position = XLSX.utils.decode_cell(address);
5695
+ } catch {
5696
+ continue;
5697
+ }
5698
+ if (position.r < 0 || position.c < 0) continue;
5699
+ const source = value;
5700
+ const row = position.r;
5701
+ const column = position.c;
5702
+ rowCount = Math.max(rowCount, row + 1);
5703
+ columnCount = Math.max(columnCount, column + 1);
5596
5704
  const hyperlink = fortuneSheetHyperlink(source.l?.Target);
5597
5705
  const comment = importXlsxCellComment(source.c);
5598
5706
  const style = fortuneCellStyle(source);
5599
5707
  if (hyperlink) hyperlinks[`${row}_${column}`] = hyperlink;
5708
+ data[row] ??= [];
5600
5709
  data[row][column] = {
5601
5710
  v: source.v,
5602
5711
  m: source.w ?? String(source.v ?? ''),
@@ -5611,14 +5720,16 @@ async function importSpreadsheet(file, extension) {
5611
5720
  fc: hyperlink ? style.fc ?? '#0563c1' : style.fc,
5612
5721
  un: hyperlink ? style.un ?? 1 : style.un
5613
5722
  };
5723
+ if ((entryIndex + 1) % 2048 === 0) await context.controller.checkpoint('parsing', sheetProgressStart + sheetProgressSize * ((entryIndex + 1) / Math.max(1, entries.length)));
5614
5724
  }
5615
- applySpreadsheetCellProtectionRanges(data, features?.protection.cellProtectionRanges ?? [], rowCount, columnCount);
5616
- applyPasswordlessEditableRanges(data, features?.protection.authority?.allowRangeList ?? [], rowCount, columnCount);
5725
+ data.length = Math.max(data.length, rowCount);
5617
5726
  const config = fortuneSheetConfig(worksheet);
5618
- if (features?.protection.authority) config.authority = features.protection.authority;
5727
+ const protectionAuthority = importedSheetProtectionAuthority(features?.protection.authority, features?.protection.cellProtectionRanges ?? []);
5728
+ if (protectionAuthority) config.authority = protectionAuthority;
5619
5729
  const filterSelect = fortuneSheetFilter(worksheet, XLSX);
5620
5730
  const dataVerification = fortuneSheetDataVerification(features?.validations ?? [], rowCount, columnCount, XLSX);
5621
- return {
5731
+ const dataValidationRanges = fortuneSheetDataValidationRanges(features?.validations ?? [], rowCount, columnCount, XLSX);
5732
+ sheets.push({
5622
5733
  id,
5623
5734
  name,
5624
5735
  order: index,
@@ -5633,12 +5744,14 @@ async function importSpreadsheet(file, extension) {
5633
5744
  frozen: features?.frozen,
5634
5745
  hyperlink: Object.keys(hyperlinks).length ? hyperlinks : void 0,
5635
5746
  dataVerification: Object.keys(dataVerification).length ? dataVerification : void 0,
5747
+ dataValidationRanges: dataValidationRanges.length ? dataValidationRanges : void 0,
5636
5748
  luckysheet_conditionformat_save: features?.conditionalFormats.length ? features.conditionalFormats : void 0,
5637
5749
  images: features?.images.length ? xlsxWorksheetImagesToSheet(features.images, config) : void 0,
5638
5750
  charts: features?.charts.length ? xlsxWorksheetChartsToSheet(features.charts, config) : void 0,
5639
5751
  formulaMetadata: createSpreadsheetFormulaMetadata(worksheet, formulaFeatures?.sheets.get(name))
5640
- };
5641
- });
5752
+ });
5753
+ await context.controller.checkpoint('parsing', sheetProgressStart + sheetProgressSize);
5754
+ }
5642
5755
  const workbookMetadata = importXlsxDefinedNames(workbook, sheets);
5643
5756
  const pageBreaks = sheets.flatMap((sheet)=>{
5644
5757
  const imported = sheetFeatures.get(sheet.name)?.pageBreaks;
@@ -5671,7 +5784,9 @@ async function importSpreadsheet(file, extension) {
5671
5784
  pageSetups: pageSetups.length ? pageSetups : void 0
5672
5785
  }, pivotFeatures);
5673
5786
  const { analyzeSpreadsheetCompatibility } = await import("./0~work-office-diagnostics.js");
5674
- artifact.compatibility = await analyzeSpreadsheetCompatibility(file, extension, workbook);
5787
+ context.controller.report('analyzing', 0);
5788
+ artifact.compatibility = await analyzeSpreadsheetCompatibility(file, extension, workbook, archive);
5789
+ context.controller.report('analyzing', 1);
5675
5790
  return artifact;
5676
5791
  }
5677
5792
  async function importPdf(file) {
@@ -5696,6 +5811,21 @@ async function importPdf(file) {
5696
5811
  rememberWorkSourceBlob(artifact.id, source);
5697
5812
  return artifact;
5698
5813
  }
5814
+ function safeSpreadsheetRange(worksheet, XLSX) {
5815
+ if (worksheet['!ref']) try {
5816
+ return XLSX.utils.decode_range(worksheet['!ref']);
5817
+ } catch {}
5818
+ return {
5819
+ s: {
5820
+ r: 0,
5821
+ c: 0
5822
+ },
5823
+ e: {
5824
+ r: 39,
5825
+ c: 11
5826
+ }
5827
+ };
5828
+ }
5699
5829
  function fortuneSheetConfig(worksheet) {
5700
5830
  const config = {};
5701
5831
  for (const range of worksheet['!merges'] ?? []){
@@ -5764,36 +5894,8 @@ async function createSpreadsheetBlob(artifact) {
5764
5894
  const XLSX = await import("xlsx");
5765
5895
  const workbook = XLSX.utils.book_new();
5766
5896
  for (const sheet of content.sheets){
5767
- const data = sheet.data ?? [];
5768
- const worksheet = XLSX.utils.aoa_to_sheet(data.map((row, rowIndex)=>row.map((cell, columnIndex)=>{
5769
- if (!cell) return null;
5770
- if (cell.f) return createXlsxFormulaCell(cell, rowIndex, columnIndex, sheet);
5771
- if (cell.ct?.t === 'e') return createXlsxErrorCell(cell);
5772
- if (cell.ps && void 0 === cell.v && void 0 === cell.m) return {
5773
- t: 's',
5774
- v: ''
5775
- };
5776
- return cell.v ?? cell.m ?? null;
5777
- })));
5897
+ const worksheet = createSparseXlsxWorksheet(sheet, XLSX);
5778
5898
  applySpreadsheetLayout(worksheet, sheet);
5779
- for (const [rowIndex, row] of data.entries())for (const [columnIndex, cell] of row.entries()){
5780
- if (!cell) continue;
5781
- const address = XLSX.utils.encode_cell({
5782
- r: rowIndex,
5783
- c: columnIndex
5784
- });
5785
- const target = worksheet[address];
5786
- if (!target) continue;
5787
- if (cell.ct?.fa) target.z = cell.ct.fa;
5788
- const style = xlsxCellStyle(cell);
5789
- if (style) target.s = style;
5790
- const comment = exportXlsxCellComment(cell.ps);
5791
- if (comment) target.c = comment;
5792
- const hyperlink = sheet.hyperlink?.[`${rowIndex}_${columnIndex}`];
5793
- if (hyperlink) target.l = {
5794
- Target: xlsxHyperlinkTarget(hyperlink)
5795
- };
5796
- }
5797
5899
  XLSX.utils.book_append_sheet(workbook, worksheet, sheet.name.slice(0, 31) || '工作表');
5798
5900
  }
5799
5901
  workbook.Workbook = {
@@ -5819,6 +5921,73 @@ async function createSpreadsheetBlob(artifact) {
5819
5921
  type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
5820
5922
  });
5821
5923
  }
5924
+ function createSparseXlsxWorksheet(sheet, XLSX) {
5925
+ const data = sheet.data ?? [];
5926
+ const worksheet = {
5927
+ '!a3sSparse': true
5928
+ };
5929
+ let rowCount = Math.max(1, sheet.row ?? 0, data.length);
5930
+ let columnCount = Math.max(1, sheet.column ?? 0, sparseMatrixColumnCount(data));
5931
+ for (const [rowIndex, row] of sparseArrayEntries(data)){
5932
+ rowCount = Math.max(rowCount, rowIndex + 1);
5933
+ for (const [columnIndex, cell] of sparseArrayEntries(row)){
5934
+ if (!cell) continue;
5935
+ columnCount = Math.max(columnCount, columnIndex + 1);
5936
+ const address = XLSX.utils.encode_cell({
5937
+ r: rowIndex,
5938
+ c: columnIndex
5939
+ });
5940
+ const style = xlsxCellStyle(cell);
5941
+ const comment = exportXlsxCellComment(cell.ps);
5942
+ const hyperlink = sheet.hyperlink?.[`${rowIndex}_${columnIndex}`];
5943
+ const target = xlsxCellObject(cell, rowIndex, columnIndex, sheet) ?? (style || comment || hyperlink ? {
5944
+ t: 's',
5945
+ v: ''
5946
+ } : null);
5947
+ if (target) {
5948
+ if (cell.ct?.fa) target.z = cell.ct.fa;
5949
+ if (style) target.s = style;
5950
+ if (comment) target.c = comment;
5951
+ if (hyperlink) target.l = {
5952
+ Target: xlsxHyperlinkTarget(hyperlink)
5953
+ };
5954
+ worksheet[address] = target;
5955
+ }
5956
+ }
5957
+ }
5958
+ worksheet['!ref'] = XLSX.utils.encode_range({
5959
+ s: {
5960
+ r: 0,
5961
+ c: 0
5962
+ },
5963
+ e: {
5964
+ r: rowCount - 1,
5965
+ c: columnCount - 1
5966
+ }
5967
+ });
5968
+ return worksheet;
5969
+ }
5970
+ function xlsxCellObject(cell, row, column, sheet) {
5971
+ if (cell.f) return createXlsxFormulaCell(cell, row, column, sheet);
5972
+ if (cell.ct?.t === 'e') return createXlsxErrorCell(cell);
5973
+ const value = cell.v ?? cell.m;
5974
+ if (null == value) return cell.ps ? {
5975
+ t: 's',
5976
+ v: ''
5977
+ } : null;
5978
+ if ('boolean' == typeof value) return {
5979
+ t: 'b',
5980
+ v: value
5981
+ };
5982
+ if ('number' == typeof value) return {
5983
+ t: 'n',
5984
+ v: value
5985
+ };
5986
+ return {
5987
+ t: 's',
5988
+ v: String(value)
5989
+ };
5990
+ }
5822
5991
  function applySpreadsheetLayout(worksheet, sheet) {
5823
5992
  const merges = Object.values(sheet.config?.merge ?? {});
5824
5993
  if (merges.length) worksheet['!merges'] = merges.map((merge)=>({
@@ -5904,10 +6073,14 @@ function quoteSheetName(value) {
5904
6073
  }
5905
6074
  function fortuneSheetDataVerification(validations, rowCount, columnCount, XLSX) {
5906
6075
  const result = {};
6076
+ let remaining = MAX_INLINE_DATA_VALIDATION_CELLS;
5907
6077
  for (const validation of validations)for (const reference of validation.references)try {
5908
6078
  const range = XLSX.utils.decode_range(reference);
5909
6079
  const lastRow = Math.min(range.e.r, rowCount - 1);
5910
6080
  const lastColumn = Math.min(range.e.c, columnCount - 1);
6081
+ const cells = (lastRow - Math.max(0, range.s.r) + 1) * (lastColumn - Math.max(0, range.s.c) + 1);
6082
+ if (cells <= 0 || cells > remaining) continue;
6083
+ remaining -= cells;
5911
6084
  for(let row = Math.max(0, range.s.r); row <= lastRow; row += 1)for(let column = Math.max(0, range.s.c); column <= lastColumn; column += 1)result[`${row}_${column}`] = {
5912
6085
  ...validation.item,
5913
6086
  rangeTxt: reference
@@ -5915,6 +6088,41 @@ function fortuneSheetDataVerification(validations, rowCount, columnCount, XLSX)
5915
6088
  } catch {}
5916
6089
  return result;
5917
6090
  }
6091
+ function fortuneSheetDataValidationRanges(validations, rowCount, columnCount, XLSX) {
6092
+ return validations.flatMap((validation)=>{
6093
+ const ranges = validation.references.flatMap((reference)=>{
6094
+ try {
6095
+ const range = XLSX.utils.decode_range(reference);
6096
+ const startRow = Math.max(0, range.s.r);
6097
+ const endRow = Math.min(rowCount - 1, range.e.r);
6098
+ const startColumn = Math.max(0, range.s.c);
6099
+ const endColumn = Math.min(columnCount - 1, range.e.c);
6100
+ return startRow <= endRow && startColumn <= endColumn ? [
6101
+ {
6102
+ row: [
6103
+ startRow,
6104
+ endRow
6105
+ ],
6106
+ column: [
6107
+ startColumn,
6108
+ endColumn
6109
+ ]
6110
+ }
6111
+ ] : [];
6112
+ } catch {
6113
+ return [];
6114
+ }
6115
+ });
6116
+ return ranges.length ? [
6117
+ {
6118
+ ranges,
6119
+ item: {
6120
+ ...validation.item
6121
+ }
6122
+ }
6123
+ ] : [];
6124
+ });
6125
+ }
5918
6126
  function encodeSpreadsheetRange(range) {
5919
6127
  return `${encodeSpreadsheetCell(range.row[0], range.column[0])}:${encodeSpreadsheetCell(range.row[1], range.column[1])}`;
5920
6128
  }