@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.
- package/COLLABORATION_ROADMAP.md +22 -12
- package/README.md +40 -15
- package/dist/{0~4705.js → 0~5093.js} +959 -10
- package/dist/0~document-editor.js +1 -0
- package/dist/0~spreadsheet-editor.js +189 -104
- package/dist/0~work-docx-export.js +225 -6
- package/dist/0~work-docx-import.js +26 -755
- package/dist/0~work-office-diagnostics.js +12 -8
- package/dist/0~work-pptx-import.js +2 -2
- package/dist/4104.js +330 -122
- package/dist/4476.js +82 -65
- package/dist/8715.js +41 -3
- package/dist/8928.js +448 -137
- package/dist/9356.js +126 -8
- package/dist/core.d.ts +1 -0
- package/dist/internal/features/work/editors/spreadsheet-editor-support.d.ts +1 -0
- package/dist/internal/features/work/editors/use-spreadsheet-collaboration.d.ts +2 -1
- package/dist/internal/features/work/spreadsheet-sparse.d.ts +5 -0
- package/dist/internal/features/work/work-document-file-io.d.ts +2 -1
- package/dist/internal/features/work/work-document-format-change-tracking.d.ts +1 -1
- package/dist/internal/features/work/work-document-paragraph-format-change-tracking.d.ts +7 -0
- package/dist/internal/features/work/work-document-paragraph-format-changes.d.ts +8 -0
- package/dist/internal/features/work/work-docx-import.d.ts +5 -2
- package/dist/internal/features/work/work-docx-paragraph-alignment-import.d.ts +1 -0
- package/dist/internal/features/work/work-docx-paragraph-borders-import.d.ts +1 -0
- package/dist/internal/features/work/work-docx-paragraph-direction-import.d.ts +1 -0
- package/dist/internal/features/work/work-docx-paragraph-format-change-export.d.ts +13 -0
- package/dist/internal/features/work/work-docx-paragraph-format-change-import.d.ts +17 -0
- package/dist/internal/features/work/work-docx-paragraph-indent-import.d.ts +1 -0
- package/dist/internal/features/work/work-docx-paragraph-pagination-import.d.ts +1 -0
- package/dist/internal/features/work/work-docx-paragraph-shading-import.d.ts +1 -0
- package/dist/internal/features/work/work-docx-paragraph-spacing-import.d.ts +1 -0
- package/dist/internal/features/work/work-docx-tab-stop-import.d.ts +1 -0
- package/dist/internal/features/work/work-file-data.d.ts +7 -1
- package/dist/internal/features/work/work-file-import.d.ts +28 -0
- package/dist/internal/features/work/work-file-io.d.ts +2 -1
- package/dist/internal/features/work/work-markdown-file-io.d.ts +2 -1
- package/dist/internal/features/work/work-office-diagnostics.d.ts +3 -2
- package/dist/internal/features/work/work-pptx-import.d.ts +1 -1
- package/dist/internal/features/work/work-presentation-file-io.d.ts +2 -1
- package/dist/internal/features/work/work-spreadsheet-protection.d.ts +2 -0
- package/dist/internal/features/work/work-types.d.ts +23 -1
- package/dist/internal/features/work/work-xlsx-interop.d.ts +4 -14
- package/dist/office-kernel.wasm +0 -0
- package/dist/styles.css +8 -0
- package/docs/latest/en/browser-editor-architecture.md +18 -0
- 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,
|
|
8
|
-
import { editableRangeRequiresCredentials, normalizeSpreadsheetConditionalIconSetFormat, defaultSpreadsheetDataBarOptions, normalizeSpreadsheetConditionalVisualOptions, normalizeSheetProtectionAuthority,
|
|
9
|
-
import {
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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('&', '&').replaceAll('<', '<').replaceAll('>', '>');
|
|
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
|
-
|
|
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
|
|
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
|
|
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,
|
|
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',
|
|
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
|
-
|
|
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
|
|
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
|
|
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
|
|
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
|
-
|
|
4521
|
-
const
|
|
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.
|
|
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
|
|
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
|
-
|
|
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
|
|
5540
|
-
|
|
5541
|
-
|
|
5542
|
-
|
|
5543
|
-
|
|
5544
|
-
|
|
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
|
-
|
|
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
|
-
|
|
5568
|
-
const
|
|
5569
|
-
const
|
|
5570
|
-
const
|
|
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
|
|
5574
|
-
|
|
5575
|
-
|
|
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 =
|
|
5588
|
-
|
|
5589
|
-
|
|
5590
|
-
|
|
5591
|
-
|
|
5592
|
-
|
|
5593
|
-
|
|
5594
|
-
|
|
5595
|
-
|
|
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
|
-
|
|
5616
|
-
applyPasswordlessEditableRanges(data, features?.protection.authority?.allowRangeList ?? [], rowCount, columnCount);
|
|
5725
|
+
data.length = Math.max(data.length, rowCount);
|
|
5617
5726
|
const config = fortuneSheetConfig(worksheet);
|
|
5618
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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
|
}
|