@open-file-viewer/core 0.1.11 → 0.1.13
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +38 -0
- package/dist/index.cjs +230 -12
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +21 -2
- package/dist/index.d.ts +21 -2
- package/dist/index.js +230 -12
- package/dist/index.js.map +1 -1
- package/dist/style.css +56 -23
- package/package.json +1 -1
package/dist/index.d.cts
CHANGED
|
@@ -152,7 +152,26 @@ declare function epubPlugin(): PreviewPlugin;
|
|
|
152
152
|
|
|
153
153
|
declare function xpsPlugin(): PreviewPlugin;
|
|
154
154
|
|
|
155
|
-
|
|
155
|
+
interface OfficeConversionContext {
|
|
156
|
+
file: PreviewContext["file"];
|
|
157
|
+
arrayBuffer: ArrayBuffer;
|
|
158
|
+
extension: string;
|
|
159
|
+
detectedFormat?: "docx" | "xlsx" | "pptx";
|
|
160
|
+
reason: "complex-docx" | "legacy-office" | "manual";
|
|
161
|
+
}
|
|
162
|
+
type OfficeConversionResult = Blob | ArrayBuffer | string | {
|
|
163
|
+
blob?: Blob;
|
|
164
|
+
data?: Blob | ArrayBuffer;
|
|
165
|
+
url?: string;
|
|
166
|
+
fileName?: string;
|
|
167
|
+
mimeType?: string;
|
|
168
|
+
};
|
|
169
|
+
interface OfficePluginOptions {
|
|
170
|
+
convert?: (ctx: OfficeConversionContext) => Promise<OfficeConversionResult | null | undefined> | OfficeConversionResult | null | undefined;
|
|
171
|
+
preferConversion?: boolean | ((ctx: OfficeConversionContext) => boolean | Promise<boolean>);
|
|
172
|
+
pdf?: PdfPluginOptions;
|
|
173
|
+
}
|
|
174
|
+
declare function officePlugin(options?: OfficePluginOptions): PreviewPlugin;
|
|
156
175
|
|
|
157
176
|
declare function ofdPlugin(): PreviewPlugin;
|
|
158
177
|
|
|
@@ -211,4 +230,4 @@ declare function assetPlugin(): PreviewPlugin;
|
|
|
211
230
|
|
|
212
231
|
declare function fallbackPlugin(): PreviewPlugin;
|
|
213
232
|
|
|
214
|
-
export { type CadBinaryPreviewContext, type CadPluginOptions, type FileViewer, type LibreDwgPreviewOptions, type PreviewCommand, type PreviewContext, type PreviewFallback, type PreviewFile, type PreviewFit, type PreviewInstance, type PreviewItem, type PreviewOptions, type PreviewPlugin, type PreviewSize, type PreviewSource, type PreviewTheme, type PreviewToolbarActionId, type PreviewToolbarBuiltInAction, type PreviewToolbarCustomAction, type PreviewToolbarOptions, type PreviewToolbarRenderContext, archivePlugin, assetPlugin, audioPlugin, cadPlugin, createViewer, drawingPlugin, emailPlugin, epubPlugin, fallbackPlugin, gisPlugin, imagePlugin, model3dPlugin, ofdPlugin, officePlugin, pdfPlugin, textPlugin, videoPlugin, xpsPlugin };
|
|
233
|
+
export { type CadBinaryPreviewContext, type CadPluginOptions, type FileViewer, type LibreDwgPreviewOptions, type OfficeConversionContext, type OfficeConversionResult, type OfficePluginOptions, type PreviewCommand, type PreviewContext, type PreviewFallback, type PreviewFile, type PreviewFit, type PreviewInstance, type PreviewItem, type PreviewOptions, type PreviewPlugin, type PreviewSize, type PreviewSource, type PreviewTheme, type PreviewToolbarActionId, type PreviewToolbarBuiltInAction, type PreviewToolbarCustomAction, type PreviewToolbarOptions, type PreviewToolbarRenderContext, archivePlugin, assetPlugin, audioPlugin, cadPlugin, createViewer, drawingPlugin, emailPlugin, epubPlugin, fallbackPlugin, gisPlugin, imagePlugin, model3dPlugin, ofdPlugin, officePlugin, pdfPlugin, textPlugin, videoPlugin, xpsPlugin };
|
package/dist/index.d.ts
CHANGED
|
@@ -152,7 +152,26 @@ declare function epubPlugin(): PreviewPlugin;
|
|
|
152
152
|
|
|
153
153
|
declare function xpsPlugin(): PreviewPlugin;
|
|
154
154
|
|
|
155
|
-
|
|
155
|
+
interface OfficeConversionContext {
|
|
156
|
+
file: PreviewContext["file"];
|
|
157
|
+
arrayBuffer: ArrayBuffer;
|
|
158
|
+
extension: string;
|
|
159
|
+
detectedFormat?: "docx" | "xlsx" | "pptx";
|
|
160
|
+
reason: "complex-docx" | "legacy-office" | "manual";
|
|
161
|
+
}
|
|
162
|
+
type OfficeConversionResult = Blob | ArrayBuffer | string | {
|
|
163
|
+
blob?: Blob;
|
|
164
|
+
data?: Blob | ArrayBuffer;
|
|
165
|
+
url?: string;
|
|
166
|
+
fileName?: string;
|
|
167
|
+
mimeType?: string;
|
|
168
|
+
};
|
|
169
|
+
interface OfficePluginOptions {
|
|
170
|
+
convert?: (ctx: OfficeConversionContext) => Promise<OfficeConversionResult | null | undefined> | OfficeConversionResult | null | undefined;
|
|
171
|
+
preferConversion?: boolean | ((ctx: OfficeConversionContext) => boolean | Promise<boolean>);
|
|
172
|
+
pdf?: PdfPluginOptions;
|
|
173
|
+
}
|
|
174
|
+
declare function officePlugin(options?: OfficePluginOptions): PreviewPlugin;
|
|
156
175
|
|
|
157
176
|
declare function ofdPlugin(): PreviewPlugin;
|
|
158
177
|
|
|
@@ -211,4 +230,4 @@ declare function assetPlugin(): PreviewPlugin;
|
|
|
211
230
|
|
|
212
231
|
declare function fallbackPlugin(): PreviewPlugin;
|
|
213
232
|
|
|
214
|
-
export { type CadBinaryPreviewContext, type CadPluginOptions, type FileViewer, type LibreDwgPreviewOptions, type PreviewCommand, type PreviewContext, type PreviewFallback, type PreviewFile, type PreviewFit, type PreviewInstance, type PreviewItem, type PreviewOptions, type PreviewPlugin, type PreviewSize, type PreviewSource, type PreviewTheme, type PreviewToolbarActionId, type PreviewToolbarBuiltInAction, type PreviewToolbarCustomAction, type PreviewToolbarOptions, type PreviewToolbarRenderContext, archivePlugin, assetPlugin, audioPlugin, cadPlugin, createViewer, drawingPlugin, emailPlugin, epubPlugin, fallbackPlugin, gisPlugin, imagePlugin, model3dPlugin, ofdPlugin, officePlugin, pdfPlugin, textPlugin, videoPlugin, xpsPlugin };
|
|
233
|
+
export { type CadBinaryPreviewContext, type CadPluginOptions, type FileViewer, type LibreDwgPreviewOptions, type OfficeConversionContext, type OfficeConversionResult, type OfficePluginOptions, type PreviewCommand, type PreviewContext, type PreviewFallback, type PreviewFile, type PreviewFit, type PreviewInstance, type PreviewItem, type PreviewOptions, type PreviewPlugin, type PreviewSize, type PreviewSource, type PreviewTheme, type PreviewToolbarActionId, type PreviewToolbarBuiltInAction, type PreviewToolbarCustomAction, type PreviewToolbarOptions, type PreviewToolbarRenderContext, archivePlugin, assetPlugin, audioPlugin, cadPlugin, createViewer, drawingPlugin, emailPlugin, epubPlugin, fallbackPlugin, gisPlugin, imagePlugin, model3dPlugin, ofdPlugin, officePlugin, pdfPlugin, textPlugin, videoPlugin, xpsPlugin };
|
package/dist/index.js
CHANGED
|
@@ -682,7 +682,7 @@ function createViewer(options) {
|
|
|
682
682
|
if (destroyed || token !== renderToken) {
|
|
683
683
|
return;
|
|
684
684
|
}
|
|
685
|
-
currentInstance
|
|
685
|
+
destroyPreviewInstance(currentInstance);
|
|
686
686
|
currentInstance = void 0;
|
|
687
687
|
viewport.replaceChildren();
|
|
688
688
|
setLoading(true);
|
|
@@ -704,7 +704,7 @@ function createViewer(options) {
|
|
|
704
704
|
setError
|
|
705
705
|
});
|
|
706
706
|
if (destroyed || token !== renderToken) {
|
|
707
|
-
nextInstance
|
|
707
|
+
destroyPreviewInstance(nextInstance);
|
|
708
708
|
return;
|
|
709
709
|
}
|
|
710
710
|
currentInstance = nextInstance;
|
|
@@ -761,7 +761,7 @@ function createViewer(options) {
|
|
|
761
761
|
destroyed = true;
|
|
762
762
|
renderToken += 1;
|
|
763
763
|
resizeObserver.destroy();
|
|
764
|
-
currentInstance
|
|
764
|
+
destroyPreviewInstance(currentInstance);
|
|
765
765
|
toolbar?.destroy();
|
|
766
766
|
theme.destroy();
|
|
767
767
|
container.replaceChildren();
|
|
@@ -772,6 +772,16 @@ function createViewer(options) {
|
|
|
772
772
|
}
|
|
773
773
|
};
|
|
774
774
|
}
|
|
775
|
+
function destroyPreviewInstance(instance) {
|
|
776
|
+
if (!instance) {
|
|
777
|
+
return;
|
|
778
|
+
}
|
|
779
|
+
try {
|
|
780
|
+
instance.destroy();
|
|
781
|
+
} catch (error) {
|
|
782
|
+
console.error("Failed to destroy file preview instance:", error);
|
|
783
|
+
}
|
|
784
|
+
}
|
|
775
785
|
function normalizeQueue(options) {
|
|
776
786
|
if (options.files && options.files.length > 0) {
|
|
777
787
|
return options.files.map(
|
|
@@ -4575,7 +4585,7 @@ async function renderPdfDocumentPreview(options) {
|
|
|
4575
4585
|
},
|
|
4576
4586
|
destroy() {
|
|
4577
4587
|
options.viewport.classList.remove("ofv-center");
|
|
4578
|
-
documentTask
|
|
4588
|
+
destroyPdfResource(documentTask);
|
|
4579
4589
|
if (options.revokeUrlOnDestroy) {
|
|
4580
4590
|
revokeObjectUrl(options.fileUrl, Boolean(options.isExternal));
|
|
4581
4591
|
}
|
|
@@ -4808,13 +4818,27 @@ async function renderPdfDocumentPreview(options) {
|
|
|
4808
4818
|
}
|
|
4809
4819
|
});
|
|
4810
4820
|
pageStates.length = 0;
|
|
4811
|
-
|
|
4821
|
+
destroyPdfResource(pdfDocument);
|
|
4822
|
+
destroyPdfResource(documentTask);
|
|
4812
4823
|
if (options.revokeUrlOnDestroy) {
|
|
4813
4824
|
revokeObjectUrl(options.fileUrl, Boolean(options.isExternal));
|
|
4814
4825
|
}
|
|
4815
4826
|
}
|
|
4816
4827
|
};
|
|
4817
4828
|
}
|
|
4829
|
+
function destroyPdfResource(resource) {
|
|
4830
|
+
if (!resource || typeof resource !== "object") {
|
|
4831
|
+
return;
|
|
4832
|
+
}
|
|
4833
|
+
const candidate = resource;
|
|
4834
|
+
if (typeof candidate.destroy === "function") {
|
|
4835
|
+
void candidate.destroy();
|
|
4836
|
+
return;
|
|
4837
|
+
}
|
|
4838
|
+
if (typeof candidate.cleanup === "function") {
|
|
4839
|
+
void candidate.cleanup();
|
|
4840
|
+
}
|
|
4841
|
+
}
|
|
4818
4842
|
function getPdfOutputScale() {
|
|
4819
4843
|
if (typeof window === "undefined") {
|
|
4820
4844
|
return 1;
|
|
@@ -5802,7 +5826,7 @@ var officeMimeFormatMap = {
|
|
|
5802
5826
|
"application/vnd.oasis.opendocument.presentation-flat-xml": "fodp",
|
|
5803
5827
|
"application/vnd.apple.keynote": "key"
|
|
5804
5828
|
};
|
|
5805
|
-
function officePlugin() {
|
|
5829
|
+
function officePlugin(options = {}) {
|
|
5806
5830
|
return {
|
|
5807
5831
|
name: "office",
|
|
5808
5832
|
match(file) {
|
|
@@ -5815,7 +5839,11 @@ function officePlugin() {
|
|
|
5815
5839
|
const arrayBuffer = await readArrayBuffer(ctx.file);
|
|
5816
5840
|
const packageFormat = shouldSniffPackagedOffice(extension) ? await detectPackagedOfficeFormat(arrayBuffer) : void 0;
|
|
5817
5841
|
let disposeDocxFit;
|
|
5818
|
-
|
|
5842
|
+
let delegatedInstance;
|
|
5843
|
+
const conversionContext = await createOfficeConversionContext(ctx, arrayBuffer, extension, packageFormat);
|
|
5844
|
+
if (conversionContext && await shouldUseOfficeConversion(options, conversionContext)) {
|
|
5845
|
+
delegatedInstance = await renderConvertedOfficePreview(panel, ctx, options, conversionContext);
|
|
5846
|
+
} else if (packageFormat === "docx" && !fileIsDocx(extension)) {
|
|
5819
5847
|
disposeDocxFit = await renderDocx(panel, arrayBuffer);
|
|
5820
5848
|
} else if (packageFormat === "xlsx" && !sheetExtensions.has(extension)) {
|
|
5821
5849
|
await renderSheet(panel, arrayBuffer, "xlsx");
|
|
@@ -5849,12 +5877,13 @@ function officePlugin() {
|
|
|
5849
5877
|
ctx.toolbar?.refreshCommandSupport();
|
|
5850
5878
|
return {
|
|
5851
5879
|
canCommand(command) {
|
|
5852
|
-
return controller?.canCommand(command)
|
|
5880
|
+
return delegatedInstance?.canCommand?.(command) || controller?.canCommand(command) || false;
|
|
5853
5881
|
},
|
|
5854
5882
|
command(command) {
|
|
5855
|
-
return controller?.command(command)
|
|
5883
|
+
return delegatedInstance?.command?.(command) || controller?.command(command) || false;
|
|
5856
5884
|
},
|
|
5857
5885
|
destroy() {
|
|
5886
|
+
delegatedInstance?.destroy();
|
|
5858
5887
|
controller?.destroy();
|
|
5859
5888
|
disposeDocxFit?.();
|
|
5860
5889
|
panel.remove();
|
|
@@ -5863,6 +5892,104 @@ function officePlugin() {
|
|
|
5863
5892
|
}
|
|
5864
5893
|
};
|
|
5865
5894
|
}
|
|
5895
|
+
async function createOfficeConversionContext(ctx, arrayBuffer, extension, detectedFormat) {
|
|
5896
|
+
const effectiveFormat = detectedFormat || extension;
|
|
5897
|
+
if ((effectiveFormat === "docx" || fileIsDocx(extension)) && await docxShouldPreferTextboxLayoutFallback(arrayBuffer)) {
|
|
5898
|
+
return { file: ctx.file, arrayBuffer, extension, detectedFormat, reason: "complex-docx" };
|
|
5899
|
+
}
|
|
5900
|
+
if (isLegacyOfficeBinary(extension)) {
|
|
5901
|
+
return { file: ctx.file, arrayBuffer, extension, detectedFormat, reason: "legacy-office" };
|
|
5902
|
+
}
|
|
5903
|
+
return void 0;
|
|
5904
|
+
}
|
|
5905
|
+
async function shouldUseOfficeConversion(options, context) {
|
|
5906
|
+
if (!options.convert) {
|
|
5907
|
+
return false;
|
|
5908
|
+
}
|
|
5909
|
+
if (typeof options.preferConversion === "function") {
|
|
5910
|
+
return Boolean(await options.preferConversion(context));
|
|
5911
|
+
}
|
|
5912
|
+
if (options.preferConversion !== void 0) {
|
|
5913
|
+
return options.preferConversion;
|
|
5914
|
+
}
|
|
5915
|
+
return context.reason === "complex-docx" || context.reason === "legacy-office";
|
|
5916
|
+
}
|
|
5917
|
+
async function renderConvertedOfficePreview(panel, ctx, options, conversionContext) {
|
|
5918
|
+
if (!options.convert) {
|
|
5919
|
+
throw new Error("Office conversion handler is not configured.");
|
|
5920
|
+
}
|
|
5921
|
+
const converted = normalizeOfficeConversionResult(await options.convert(conversionContext), ctx.file.name);
|
|
5922
|
+
if (!converted) {
|
|
5923
|
+
throw new Error("Office conversion handler did not return a previewable file.");
|
|
5924
|
+
}
|
|
5925
|
+
if (converted.mimeType !== "application/pdf" && !converted.fileName.toLowerCase().endsWith(".pdf")) {
|
|
5926
|
+
throw new Error("Office conversion handler must return a PDF Blob, ArrayBuffer or URL.");
|
|
5927
|
+
}
|
|
5928
|
+
return renderPdfDocumentPreview({
|
|
5929
|
+
...options.pdf || {},
|
|
5930
|
+
fileName: converted.fileName,
|
|
5931
|
+
fileUrl: converted.fileUrl,
|
|
5932
|
+
fileSize: converted.fileSize,
|
|
5933
|
+
isExternal: !converted.revokeUrlOnDestroy,
|
|
5934
|
+
viewport: panel,
|
|
5935
|
+
size: ctx.size,
|
|
5936
|
+
fit: ctx.options.fit,
|
|
5937
|
+
toolbar: ctx.toolbar,
|
|
5938
|
+
title: "Office \u9AD8\u4FDD\u771F\u8F6C\u6362\u9884\u89C8",
|
|
5939
|
+
fallbackTitle: "Office \u8F6C\u6362\u540E\u7684 PDF \u65E0\u6CD5\u9884\u89C8",
|
|
5940
|
+
revokeUrlOnDestroy: converted.revokeUrlOnDestroy
|
|
5941
|
+
});
|
|
5942
|
+
}
|
|
5943
|
+
function normalizeOfficeConversionResult(result, sourceFileName) {
|
|
5944
|
+
if (!result) {
|
|
5945
|
+
return void 0;
|
|
5946
|
+
}
|
|
5947
|
+
const fallbackFileName = `${stripFileExtension(sourceFileName) || "office-preview"}.pdf`;
|
|
5948
|
+
if (typeof result === "string") {
|
|
5949
|
+
return {
|
|
5950
|
+
fileName: fallbackFileName,
|
|
5951
|
+
fileUrl: result,
|
|
5952
|
+
mimeType: "application/pdf",
|
|
5953
|
+
revokeUrlOnDestroy: false
|
|
5954
|
+
};
|
|
5955
|
+
}
|
|
5956
|
+
if (result instanceof ArrayBuffer) {
|
|
5957
|
+
const blob = new Blob([result], { type: "application/pdf" });
|
|
5958
|
+
return createConvertedOfficeBlobPreview(blob, fallbackFileName);
|
|
5959
|
+
}
|
|
5960
|
+
if (result instanceof Blob) {
|
|
5961
|
+
return createConvertedOfficeBlobPreview(result, fallbackFileName);
|
|
5962
|
+
}
|
|
5963
|
+
if (result.url) {
|
|
5964
|
+
return {
|
|
5965
|
+
fileName: result.fileName || fallbackFileName,
|
|
5966
|
+
fileUrl: result.url,
|
|
5967
|
+
mimeType: result.mimeType || "application/pdf",
|
|
5968
|
+
revokeUrlOnDestroy: false
|
|
5969
|
+
};
|
|
5970
|
+
}
|
|
5971
|
+
const data = result.blob || result.data;
|
|
5972
|
+
if (data instanceof ArrayBuffer) {
|
|
5973
|
+
const blob = new Blob([data], { type: result.mimeType || "application/pdf" });
|
|
5974
|
+
return createConvertedOfficeBlobPreview(blob, result.fileName || fallbackFileName);
|
|
5975
|
+
}
|
|
5976
|
+
if (data instanceof Blob) {
|
|
5977
|
+
return createConvertedOfficeBlobPreview(data, result.fileName || fallbackFileName);
|
|
5978
|
+
}
|
|
5979
|
+
return void 0;
|
|
5980
|
+
}
|
|
5981
|
+
function createConvertedOfficeBlobPreview(blob, fileName) {
|
|
5982
|
+
return {
|
|
5983
|
+
fileName,
|
|
5984
|
+
fileUrl: URL.createObjectURL(blob),
|
|
5985
|
+
fileSize: blob.size,
|
|
5986
|
+
mimeType: blob.type || "application/pdf",
|
|
5987
|
+
revokeUrlOnDestroy: true
|
|
5988
|
+
};
|
|
5989
|
+
}
|
|
5990
|
+
function stripFileExtension(fileName) {
|
|
5991
|
+
return fileName.replace(/\.[^.]+$/, "");
|
|
5992
|
+
}
|
|
5866
5993
|
function createOfficeZoomController(panel, ctx) {
|
|
5867
5994
|
const canZoom = Boolean(
|
|
5868
5995
|
panel.querySelector(".ofv-docx-document, .ofv-sheet, .ofv-pptx-viewer > div, .ofv-document, .ofv-text-block, .ofv-slide")
|
|
@@ -5959,7 +6086,7 @@ async function renderDocx(panel, arrayBuffer) {
|
|
|
5959
6086
|
useBase64URL: true
|
|
5960
6087
|
});
|
|
5961
6088
|
await normalizeDocxLayout(content, arrayBuffer);
|
|
5962
|
-
const shouldUseTextboxFallback = await docxPreviewLooksBlank(content, arrayBuffer) || await docxPreviewMissesRichTextboxContent(content, arrayBuffer);
|
|
6089
|
+
const shouldUseTextboxFallback = await docxPreviewLooksBlank(content, arrayBuffer) || await docxPreviewMissesRichTextboxContent(content, arrayBuffer) || await docxShouldPreferTextboxLayoutFallback(arrayBuffer);
|
|
5963
6090
|
if (shouldUseTextboxFallback) {
|
|
5964
6091
|
disposeFit?.();
|
|
5965
6092
|
styleContainer.remove();
|
|
@@ -6042,6 +6169,27 @@ async function docxHasRichTextboxContent(arrayBuffer) {
|
|
|
6042
6169
|
return false;
|
|
6043
6170
|
}
|
|
6044
6171
|
}
|
|
6172
|
+
async function docxShouldPreferTextboxLayoutFallback(arrayBuffer) {
|
|
6173
|
+
try {
|
|
6174
|
+
const zip = await JSZip3.loadAsync(arrayBuffer);
|
|
6175
|
+
const documentXml = await zip.file("word/document.xml")?.async("text");
|
|
6176
|
+
if (!documentXml || !/\btxbxContent\b/.test(documentXml)) {
|
|
6177
|
+
return false;
|
|
6178
|
+
}
|
|
6179
|
+
const blocks = extractDocxTextboxBlocks(documentXml);
|
|
6180
|
+
const meaningfulBlocks = blocks.filter((block) => block.paragraphs.length > 0);
|
|
6181
|
+
const sidebarBackgrounds = blocks.filter(
|
|
6182
|
+
(block) => block.paragraphs.length === 0 && block.fill && block.relativeV === "page" && block.x < 0 && block.width >= 120 && block.height >= 500
|
|
6183
|
+
);
|
|
6184
|
+
const pageAnchoredTextboxes = meaningfulBlocks.filter((block) => block.relativeV === "page");
|
|
6185
|
+
const paragraphAnchoredTextboxes = meaningfulBlocks.filter((block) => block.relativeV !== "page");
|
|
6186
|
+
const leftTextboxes = meaningfulBlocks.filter((block) => block.x < 0);
|
|
6187
|
+
const rightTextboxes = meaningfulBlocks.filter((block) => block.x >= 80);
|
|
6188
|
+
return sidebarBackgrounds.length >= 2 && meaningfulBlocks.length >= 8 && pageAnchoredTextboxes.length >= 4 && paragraphAnchoredTextboxes.length >= 2 && leftTextboxes.length >= 3 && rightTextboxes.length >= 3;
|
|
6189
|
+
} catch {
|
|
6190
|
+
return false;
|
|
6191
|
+
}
|
|
6192
|
+
}
|
|
6045
6193
|
async function renderDocxContentFallback(container, arrayBuffer, options = {}) {
|
|
6046
6194
|
if (options.showNote !== false) {
|
|
6047
6195
|
const fallbackNote = document.createElement("div");
|
|
@@ -6493,6 +6641,10 @@ function createDocxTextboxBlockElement(block) {
|
|
|
6493
6641
|
const paragraphs = normalizeDocxTextboxParagraphOrder(block);
|
|
6494
6642
|
const [first, ...rest] = paragraphs;
|
|
6495
6643
|
if (first) {
|
|
6644
|
+
const sectionKind = getDocxTextboxSectionKind(first);
|
|
6645
|
+
if (sectionKind) {
|
|
6646
|
+
section.classList.add(`ofv-docx-textbox-section-${sectionKind}`);
|
|
6647
|
+
}
|
|
6496
6648
|
const heading = document.createElement("h3");
|
|
6497
6649
|
heading.textContent = first;
|
|
6498
6650
|
section.append(heading);
|
|
@@ -6505,6 +6657,28 @@ function createDocxTextboxBlockElement(block) {
|
|
|
6505
6657
|
}
|
|
6506
6658
|
return section;
|
|
6507
6659
|
}
|
|
6660
|
+
function getDocxTextboxSectionKind(heading) {
|
|
6661
|
+
const text = normalizePreviewText(heading);
|
|
6662
|
+
if (text.includes("\u6559\u80B2\u80CC\u666F")) {
|
|
6663
|
+
return "education";
|
|
6664
|
+
}
|
|
6665
|
+
if (text.includes("\u4E13\u4E1A\u6280\u80FD")) {
|
|
6666
|
+
return "skills";
|
|
6667
|
+
}
|
|
6668
|
+
if (text.includes("\u5DE5\u4F5C\u7ECF\u5386")) {
|
|
6669
|
+
return "work";
|
|
6670
|
+
}
|
|
6671
|
+
if (text.includes("\u9879\u76EE\u7ECF\u9A8C")) {
|
|
6672
|
+
return "projects";
|
|
6673
|
+
}
|
|
6674
|
+
if (text.includes("\u81EA\u6211\u8BC4\u4EF7")) {
|
|
6675
|
+
return "summary";
|
|
6676
|
+
}
|
|
6677
|
+
if (text.includes("\u57FA\u672C\u4FE1\u606F")) {
|
|
6678
|
+
return "profile";
|
|
6679
|
+
}
|
|
6680
|
+
return "";
|
|
6681
|
+
}
|
|
6508
6682
|
function estimateDocxTextboxBlockHeight(block) {
|
|
6509
6683
|
return Math.max(block.height, 18 + block.paragraphs.length * 14);
|
|
6510
6684
|
}
|
|
@@ -6548,14 +6722,58 @@ function repairDocxHeadingShapeAlignment(page) {
|
|
|
6548
6722
|
if (!svg) {
|
|
6549
6723
|
continue;
|
|
6550
6724
|
}
|
|
6551
|
-
const width = parseCssPixelValue(svg.getAttribute("width") ||
|
|
6725
|
+
const width = parseCssPixelValue(svg.style.width) || parseCssPixelValue(svg.getAttribute("width") || "");
|
|
6552
6726
|
const marginLeft = parseCssPixelValue(svg.style.marginLeft);
|
|
6553
6727
|
if (width < 300 || marginLeft < 28 || marginLeft > 44) {
|
|
6554
6728
|
continue;
|
|
6555
6729
|
}
|
|
6556
|
-
|
|
6730
|
+
const textWidth = getDocxParagraphVisibleTextWidth(paragraph);
|
|
6731
|
+
svg.style.marginLeft = `${formatCssNumber(Math.max(48, marginLeft + textWidth * 0.68))}pt`;
|
|
6732
|
+
svg.style.marginTop = `${formatCssNumber(parseCssPixelValue(svg.style.marginTop) - 4)}pt`;
|
|
6733
|
+
normalizeDocxHeadingShapeFill(svg);
|
|
6734
|
+
repairDocxHeadingTextBackground(paragraph);
|
|
6557
6735
|
}
|
|
6558
6736
|
}
|
|
6737
|
+
function normalizeDocxHeadingShapeFill(svg) {
|
|
6738
|
+
const headingFill = "#3f4aa3";
|
|
6739
|
+
const fillNodes = svg.querySelectorAll("image[fill], rect[data-ofv-docx-shape-fill]");
|
|
6740
|
+
for (const node of fillNodes) {
|
|
6741
|
+
const fill = node.getAttribute("fill") || "";
|
|
6742
|
+
if (fill.toLowerCase() === "#38449a") {
|
|
6743
|
+
node.setAttribute("fill", headingFill);
|
|
6744
|
+
}
|
|
6745
|
+
}
|
|
6746
|
+
}
|
|
6747
|
+
function repairDocxHeadingTextBackground(paragraph) {
|
|
6748
|
+
const textSpans = Array.from(paragraph.querySelectorAll("span")).filter(
|
|
6749
|
+
(element) => normalizePreviewText(element.textContent || "")
|
|
6750
|
+
);
|
|
6751
|
+
const lastTextSpan = textSpans.at(-1);
|
|
6752
|
+
if (!lastTextSpan || !hasWhiteBackground(lastTextSpan)) {
|
|
6753
|
+
return;
|
|
6754
|
+
}
|
|
6755
|
+
lastTextSpan.style.paddingRight = "3pt";
|
|
6756
|
+
lastTextSpan.style.paddingTop = "2pt";
|
|
6757
|
+
lastTextSpan.style.paddingBottom = "2pt";
|
|
6758
|
+
lastTextSpan.style.boxDecorationBreak = "clone";
|
|
6759
|
+
}
|
|
6760
|
+
function hasWhiteBackground(element) {
|
|
6761
|
+
const background = element.style.backgroundColor.replace(/\s+/g, "").toLowerCase();
|
|
6762
|
+
return background === "white" || background === "#fff" || background === "#ffffff" || background === "rgb(255,255,255)";
|
|
6763
|
+
}
|
|
6764
|
+
function getDocxParagraphVisibleTextWidth(paragraph) {
|
|
6765
|
+
let textWidth = 0;
|
|
6766
|
+
for (const element of paragraph.querySelectorAll("span")) {
|
|
6767
|
+
if (!normalizePreviewText(element.textContent || "")) {
|
|
6768
|
+
continue;
|
|
6769
|
+
}
|
|
6770
|
+
textWidth += pxToPt(element.getBoundingClientRect().width);
|
|
6771
|
+
}
|
|
6772
|
+
return textWidth;
|
|
6773
|
+
}
|
|
6774
|
+
function pxToPt(value) {
|
|
6775
|
+
return value * 0.75;
|
|
6776
|
+
}
|
|
6559
6777
|
function repairDocxListIndentAlignment(page) {
|
|
6560
6778
|
for (const paragraph of page.querySelectorAll("p[class*='ofv-docx-num-']")) {
|
|
6561
6779
|
const text = normalizePreviewText(paragraph.textContent || "");
|