@extend-ai/react-docx 0.7.0-alpha.7 → 0.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +8 -1
- package/dist/index.cjs +1115 -98
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +27 -1
- package/dist/index.d.ts +27 -1
- package/dist/index.js +1115 -98
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -4403,6 +4403,14 @@ var THUMBNAIL_EXCLUDED_CLONE_SELECTOR = [
|
|
|
4403
4403
|
var THUMBNAIL_IMAGE_DOWNSCALE_MIN_DATA_URI_LENGTH = 32768;
|
|
4404
4404
|
var THUMBNAIL_IMAGE_DOWNSCALE_MAX_DIMENSION_PX = 512;
|
|
4405
4405
|
var THUMBNAIL_IMAGE_JPEG_QUALITY = 0.78;
|
|
4406
|
+
var THUMBNAIL_DIRECT_DEFAULT_FONT_FAMILY = "Calibri, Arial, sans-serif";
|
|
4407
|
+
var THUMBNAIL_DIRECT_DEFAULT_TEXT_COLOR = "#111827";
|
|
4408
|
+
var THUMBNAIL_DIRECT_TABLE_BORDER_COLOR = "#d1d5db";
|
|
4409
|
+
var THUMBNAIL_DIRECT_IMAGE_BACKGROUND = "#f3f4f6";
|
|
4410
|
+
var THUMBNAIL_DIRECT_MAX_ELEMENTS = 320;
|
|
4411
|
+
var THUMBNAIL_DIRECT_MAX_TEXT_CHARS = 640;
|
|
4412
|
+
var THUMBNAIL_DIRECT_MAX_LINES = 14;
|
|
4413
|
+
var THUMBNAIL_DIRECT_MAX_LAYOUT_LINES = 80;
|
|
4406
4414
|
function thumbnailSvgDataUri(svg) {
|
|
4407
4415
|
return `data:image/svg+xml;charset=utf-8,${encodeURIComponent(svg)}`;
|
|
4408
4416
|
}
|
|
@@ -4466,6 +4474,346 @@ function getDownscaledThumbnailImageDataUri(src) {
|
|
|
4466
4474
|
downscaledThumbnailImageCache.set(src, pending);
|
|
4467
4475
|
return pending;
|
|
4468
4476
|
}
|
|
4477
|
+
function directThumbnailPositivePx(value, fallback = 1) {
|
|
4478
|
+
return Number.isFinite(value) && value > 0 ? Math.max(1, Number(value)) : fallback;
|
|
4479
|
+
}
|
|
4480
|
+
function setCanvasFillStyle(context, color, fallback) {
|
|
4481
|
+
try {
|
|
4482
|
+
context.fillStyle = color || fallback;
|
|
4483
|
+
} catch {
|
|
4484
|
+
context.fillStyle = fallback;
|
|
4485
|
+
}
|
|
4486
|
+
}
|
|
4487
|
+
function setCanvasStrokeStyle(context, color, fallback) {
|
|
4488
|
+
try {
|
|
4489
|
+
context.strokeStyle = color || fallback;
|
|
4490
|
+
} catch {
|
|
4491
|
+
context.strokeStyle = fallback;
|
|
4492
|
+
}
|
|
4493
|
+
}
|
|
4494
|
+
function directThumbnailFont(run, fallbackFontSizePx) {
|
|
4495
|
+
const fontSizePx = Math.max(
|
|
4496
|
+
6,
|
|
4497
|
+
Math.min(
|
|
4498
|
+
36,
|
|
4499
|
+
Math.round(
|
|
4500
|
+
directThumbnailPositivePx(run?.fontSizePx, fallbackFontSizePx)
|
|
4501
|
+
)
|
|
4502
|
+
)
|
|
4503
|
+
);
|
|
4504
|
+
const fontStyle = run?.italic ? "italic " : "";
|
|
4505
|
+
const fontWeight = run?.bold ? "700 " : "";
|
|
4506
|
+
return `${fontStyle}${fontWeight}${fontSizePx}px ${run?.fontFamily || THUMBNAIL_DIRECT_DEFAULT_FONT_FAMILY}`;
|
|
4507
|
+
}
|
|
4508
|
+
var THUMBNAIL_DIRECT_TOKEN_REGEX = /(\r\n|\n|\t|[^\S\r\n\t]+|[^\s\r\n\t]+)/g;
|
|
4509
|
+
var THUMBNAIL_DIRECT_LEADING_WHITESPACE_REGEX = /^\s/;
|
|
4510
|
+
var THUMBNAIL_DIRECT_TEXT_MEASURE_CACHE_MAX_ENTRIES = 4096;
|
|
4511
|
+
var directThumbnailTextMeasureCache = /* @__PURE__ */ new Map();
|
|
4512
|
+
function measureDirectThumbnailToken(context, font, text) {
|
|
4513
|
+
if (!text) {
|
|
4514
|
+
return 0;
|
|
4515
|
+
}
|
|
4516
|
+
const cacheKey = `${font}\0${text}`;
|
|
4517
|
+
const cached = directThumbnailTextMeasureCache.get(cacheKey);
|
|
4518
|
+
if (cached !== void 0) {
|
|
4519
|
+
return cached;
|
|
4520
|
+
}
|
|
4521
|
+
const width = context.measureText(text).width;
|
|
4522
|
+
if (directThumbnailTextMeasureCache.size >= THUMBNAIL_DIRECT_TEXT_MEASURE_CACHE_MAX_ENTRIES) {
|
|
4523
|
+
const oldestKey = directThumbnailTextMeasureCache.keys().next().value;
|
|
4524
|
+
if (oldestKey !== void 0) {
|
|
4525
|
+
directThumbnailTextMeasureCache.delete(oldestKey);
|
|
4526
|
+
}
|
|
4527
|
+
}
|
|
4528
|
+
directThumbnailTextMeasureCache.set(cacheKey, width);
|
|
4529
|
+
return width;
|
|
4530
|
+
}
|
|
4531
|
+
function appendDirectThumbnailTextLine(lines, currentSegments, currentWidthPx) {
|
|
4532
|
+
const line = {
|
|
4533
|
+
segments: currentSegments,
|
|
4534
|
+
widthPx: currentWidthPx
|
|
4535
|
+
};
|
|
4536
|
+
lines.push(line);
|
|
4537
|
+
return line;
|
|
4538
|
+
}
|
|
4539
|
+
function layoutDirectThumbnailTextRuns(params) {
|
|
4540
|
+
const { context, runs, fallbackFontSizePx } = params;
|
|
4541
|
+
const widthPx = Math.max(1, params.widthPx);
|
|
4542
|
+
const maxLineCount = Math.max(
|
|
4543
|
+
1,
|
|
4544
|
+
Math.min(THUMBNAIL_DIRECT_MAX_LAYOUT_LINES, params.maxLineCount)
|
|
4545
|
+
);
|
|
4546
|
+
const lines = [];
|
|
4547
|
+
let currentSegments = [];
|
|
4548
|
+
let currentWidthPx = 0;
|
|
4549
|
+
let remainingChars = THUMBNAIL_DIRECT_MAX_TEXT_CHARS;
|
|
4550
|
+
const flushLine = () => {
|
|
4551
|
+
appendDirectThumbnailTextLine(lines, currentSegments, currentWidthPx);
|
|
4552
|
+
currentSegments = [];
|
|
4553
|
+
currentWidthPx = 0;
|
|
4554
|
+
};
|
|
4555
|
+
for (const run of runs) {
|
|
4556
|
+
if (lines.length >= maxLineCount || remainingChars <= 0) {
|
|
4557
|
+
break;
|
|
4558
|
+
}
|
|
4559
|
+
const text = run.text.slice(0, remainingChars);
|
|
4560
|
+
remainingChars -= text.length;
|
|
4561
|
+
const runFont = directThumbnailFont(run, fallbackFontSizePx);
|
|
4562
|
+
context.font = runFont;
|
|
4563
|
+
const tokens = text.match(THUMBNAIL_DIRECT_TOKEN_REGEX) ?? [];
|
|
4564
|
+
for (const token of tokens) {
|
|
4565
|
+
if (lines.length >= maxLineCount) {
|
|
4566
|
+
break;
|
|
4567
|
+
}
|
|
4568
|
+
if (token === "\n" || token === "\r\n") {
|
|
4569
|
+
flushLine();
|
|
4570
|
+
continue;
|
|
4571
|
+
}
|
|
4572
|
+
const drawableToken = token === " " ? " " : token;
|
|
4573
|
+
const tokenWidthPx = measureDirectThumbnailToken(
|
|
4574
|
+
context,
|
|
4575
|
+
runFont,
|
|
4576
|
+
drawableToken
|
|
4577
|
+
);
|
|
4578
|
+
const tokenIsWhitespace = THUMBNAIL_DIRECT_LEADING_WHITESPACE_REGEX.test(drawableToken);
|
|
4579
|
+
if (currentSegments.length > 0 && currentWidthPx + tokenWidthPx > widthPx && !tokenIsWhitespace) {
|
|
4580
|
+
flushLine();
|
|
4581
|
+
}
|
|
4582
|
+
if (currentSegments.length === 0 && tokenIsWhitespace) {
|
|
4583
|
+
continue;
|
|
4584
|
+
}
|
|
4585
|
+
currentSegments.push({
|
|
4586
|
+
run,
|
|
4587
|
+
text: drawableToken,
|
|
4588
|
+
widthPx: tokenWidthPx,
|
|
4589
|
+
font: runFont
|
|
4590
|
+
});
|
|
4591
|
+
currentWidthPx += tokenWidthPx;
|
|
4592
|
+
}
|
|
4593
|
+
}
|
|
4594
|
+
if (currentSegments.length > 0 || lines.length === 0) {
|
|
4595
|
+
flushLine();
|
|
4596
|
+
}
|
|
4597
|
+
return lines;
|
|
4598
|
+
}
|
|
4599
|
+
function directThumbnailAlignedX(params) {
|
|
4600
|
+
const { xPx, widthPx, lineWidthPx, align } = params;
|
|
4601
|
+
if (align === "center") {
|
|
4602
|
+
return xPx + Math.max(0, (widthPx - lineWidthPx) / 2);
|
|
4603
|
+
}
|
|
4604
|
+
if (align === "right") {
|
|
4605
|
+
return xPx + Math.max(0, widthPx - lineWidthPx);
|
|
4606
|
+
}
|
|
4607
|
+
return xPx;
|
|
4608
|
+
}
|
|
4609
|
+
function drawDirectThumbnailTextRuns(params) {
|
|
4610
|
+
const {
|
|
4611
|
+
context,
|
|
4612
|
+
runs,
|
|
4613
|
+
xPx,
|
|
4614
|
+
yPx,
|
|
4615
|
+
widthPx,
|
|
4616
|
+
heightPx,
|
|
4617
|
+
align,
|
|
4618
|
+
startLineIndex
|
|
4619
|
+
} = params;
|
|
4620
|
+
const safeWidthPx = Math.max(1, widthPx);
|
|
4621
|
+
const safeHeightPx = Math.max(1, heightPx);
|
|
4622
|
+
const fallbackFontSizePx = Math.max(
|
|
4623
|
+
7,
|
|
4624
|
+
Math.min(
|
|
4625
|
+
20,
|
|
4626
|
+
Math.round(
|
|
4627
|
+
runs.find((run) => Number.isFinite(run.fontSizePx))?.fontSizePx ?? 12
|
|
4628
|
+
)
|
|
4629
|
+
)
|
|
4630
|
+
);
|
|
4631
|
+
const lineHeightPx = Math.max(
|
|
4632
|
+
fallbackFontSizePx + 1,
|
|
4633
|
+
Math.round(params.lineHeightPx ?? fallbackFontSizePx * 1.25)
|
|
4634
|
+
);
|
|
4635
|
+
const skippedLineCount = Math.max(
|
|
4636
|
+
0,
|
|
4637
|
+
Math.min(THUMBNAIL_DIRECT_MAX_LAYOUT_LINES - 1, Math.trunc(startLineIndex ?? 0))
|
|
4638
|
+
);
|
|
4639
|
+
const visibleLineCount = Math.max(
|
|
4640
|
+
1,
|
|
4641
|
+
Math.min(
|
|
4642
|
+
THUMBNAIL_DIRECT_MAX_LINES,
|
|
4643
|
+
Math.ceil(safeHeightPx / Math.max(1, lineHeightPx)) + 1
|
|
4644
|
+
)
|
|
4645
|
+
);
|
|
4646
|
+
const lines = layoutDirectThumbnailTextRuns({
|
|
4647
|
+
context,
|
|
4648
|
+
runs,
|
|
4649
|
+
widthPx: safeWidthPx,
|
|
4650
|
+
fallbackFontSizePx,
|
|
4651
|
+
maxLineCount: skippedLineCount + visibleLineCount
|
|
4652
|
+
}).slice(skippedLineCount, skippedLineCount + visibleLineCount);
|
|
4653
|
+
context.save();
|
|
4654
|
+
context.beginPath();
|
|
4655
|
+
context.rect(xPx, yPx, safeWidthPx, safeHeightPx);
|
|
4656
|
+
context.clip();
|
|
4657
|
+
context.textBaseline = "alphabetic";
|
|
4658
|
+
let lastAppliedFont;
|
|
4659
|
+
lines.forEach((line, lineIndex) => {
|
|
4660
|
+
const lineTopPx = yPx + lineIndex * lineHeightPx;
|
|
4661
|
+
if (lineTopPx > yPx + safeHeightPx) {
|
|
4662
|
+
return;
|
|
4663
|
+
}
|
|
4664
|
+
let cursorXPx = directThumbnailAlignedX({
|
|
4665
|
+
xPx,
|
|
4666
|
+
widthPx: safeWidthPx,
|
|
4667
|
+
lineWidthPx: line.widthPx,
|
|
4668
|
+
align
|
|
4669
|
+
});
|
|
4670
|
+
const baselineYPx = lineTopPx + Math.max(1, Math.round(lineHeightPx * 0.78));
|
|
4671
|
+
line.segments.forEach((segment) => {
|
|
4672
|
+
const segmentWidthPx = segment.widthPx;
|
|
4673
|
+
if (segment.run.backgroundColor) {
|
|
4674
|
+
setCanvasFillStyle(context, segment.run.backgroundColor, "transparent");
|
|
4675
|
+
context.fillRect(cursorXPx, lineTopPx + 1, segmentWidthPx, lineHeightPx);
|
|
4676
|
+
}
|
|
4677
|
+
if (segment.font !== lastAppliedFont) {
|
|
4678
|
+
context.font = segment.font;
|
|
4679
|
+
lastAppliedFont = segment.font;
|
|
4680
|
+
}
|
|
4681
|
+
setCanvasFillStyle(
|
|
4682
|
+
context,
|
|
4683
|
+
segment.run.color,
|
|
4684
|
+
THUMBNAIL_DIRECT_DEFAULT_TEXT_COLOR
|
|
4685
|
+
);
|
|
4686
|
+
context.fillText(segment.text, cursorXPx, baselineYPx);
|
|
4687
|
+
cursorXPx += segmentWidthPx;
|
|
4688
|
+
});
|
|
4689
|
+
});
|
|
4690
|
+
context.restore();
|
|
4691
|
+
}
|
|
4692
|
+
function drawDirectThumbnailParagraph(context, paragraph) {
|
|
4693
|
+
const xPx = Math.round(paragraph.xPx);
|
|
4694
|
+
const yPx = Math.round(paragraph.yPx);
|
|
4695
|
+
const widthPx = Math.max(1, Math.round(paragraph.widthPx));
|
|
4696
|
+
const heightPx = Math.max(1, Math.round(paragraph.heightPx));
|
|
4697
|
+
if (paragraph.backgroundColor) {
|
|
4698
|
+
setCanvasFillStyle(context, paragraph.backgroundColor, "transparent");
|
|
4699
|
+
context.fillRect(xPx, yPx, widthPx, heightPx);
|
|
4700
|
+
}
|
|
4701
|
+
drawDirectThumbnailTextRuns({
|
|
4702
|
+
context,
|
|
4703
|
+
runs: paragraph.runs,
|
|
4704
|
+
xPx: xPx + 1,
|
|
4705
|
+
yPx,
|
|
4706
|
+
widthPx: Math.max(1, widthPx - 2),
|
|
4707
|
+
heightPx,
|
|
4708
|
+
align: paragraph.align,
|
|
4709
|
+
lineHeightPx: paragraph.lineHeightPx,
|
|
4710
|
+
startLineIndex: paragraph.startLineIndex
|
|
4711
|
+
});
|
|
4712
|
+
}
|
|
4713
|
+
function drawDirectThumbnailImagePlaceholder(context, image, hairlineSourcePx) {
|
|
4714
|
+
const xPx = Math.round(image.xPx);
|
|
4715
|
+
const yPx = Math.round(image.yPx);
|
|
4716
|
+
const widthPx = Math.max(1, Math.round(image.widthPx));
|
|
4717
|
+
const heightPx = Math.max(1, Math.round(image.heightPx));
|
|
4718
|
+
setCanvasFillStyle(
|
|
4719
|
+
context,
|
|
4720
|
+
image.backgroundColor,
|
|
4721
|
+
THUMBNAIL_DIRECT_IMAGE_BACKGROUND
|
|
4722
|
+
);
|
|
4723
|
+
context.fillRect(xPx, yPx, widthPx, heightPx);
|
|
4724
|
+
setCanvasStrokeStyle(
|
|
4725
|
+
context,
|
|
4726
|
+
image.borderColor,
|
|
4727
|
+
THUMBNAIL_DIRECT_TABLE_BORDER_COLOR
|
|
4728
|
+
);
|
|
4729
|
+
context.lineWidth = hairlineSourcePx;
|
|
4730
|
+
context.strokeRect(xPx, yPx, widthPx, heightPx);
|
|
4731
|
+
}
|
|
4732
|
+
function drawDirectThumbnailTable(context, table, hairlineSourcePx) {
|
|
4733
|
+
const tableXPx = Math.round(table.xPx);
|
|
4734
|
+
const tableYPx = Math.round(table.yPx);
|
|
4735
|
+
const tableWidthPx = Math.max(1, Math.round(table.widthPx));
|
|
4736
|
+
const tableHeightPx = Math.max(1, Math.round(table.heightPx));
|
|
4737
|
+
context.save();
|
|
4738
|
+
context.beginPath();
|
|
4739
|
+
context.rect(tableXPx, tableYPx, tableWidthPx, tableHeightPx);
|
|
4740
|
+
context.clip();
|
|
4741
|
+
setCanvasStrokeStyle(
|
|
4742
|
+
context,
|
|
4743
|
+
table.borderColor,
|
|
4744
|
+
THUMBNAIL_DIRECT_TABLE_BORDER_COLOR
|
|
4745
|
+
);
|
|
4746
|
+
context.lineWidth = hairlineSourcePx;
|
|
4747
|
+
table.cells.forEach((cell) => {
|
|
4748
|
+
const xPx = tableXPx + Math.round(cell.xPx);
|
|
4749
|
+
const yPx = tableYPx + Math.round(cell.yPx);
|
|
4750
|
+
const widthPx = Math.max(1, Math.round(cell.widthPx));
|
|
4751
|
+
const heightPx = Math.max(1, Math.round(cell.heightPx));
|
|
4752
|
+
if (cell.backgroundColor) {
|
|
4753
|
+
setCanvasFillStyle(context, cell.backgroundColor, "transparent");
|
|
4754
|
+
context.fillRect(xPx, yPx, widthPx, heightPx);
|
|
4755
|
+
}
|
|
4756
|
+
context.strokeRect(xPx, yPx, widthPx, heightPx);
|
|
4757
|
+
if (cell.runs?.length) {
|
|
4758
|
+
drawDirectThumbnailTextRuns({
|
|
4759
|
+
context,
|
|
4760
|
+
runs: cell.runs,
|
|
4761
|
+
xPx: xPx + 3,
|
|
4762
|
+
yPx: yPx + 2,
|
|
4763
|
+
widthPx: Math.max(1, widthPx - 6),
|
|
4764
|
+
heightPx: Math.max(1, heightPx - 4),
|
|
4765
|
+
lineHeightPx: 13
|
|
4766
|
+
});
|
|
4767
|
+
}
|
|
4768
|
+
});
|
|
4769
|
+
context.restore();
|
|
4770
|
+
}
|
|
4771
|
+
function renderDocxThumbnailSnapshotSurface(params) {
|
|
4772
|
+
if (typeof document === "undefined") {
|
|
4773
|
+
throw new Error("DOCX thumbnails require a browser environment.");
|
|
4774
|
+
}
|
|
4775
|
+
const sourceWidthPx = directThumbnailPositivePx(params.snapshot.sourceWidthPx);
|
|
4776
|
+
const sourceHeightPx = directThumbnailPositivePx(params.snapshot.sourceHeightPx);
|
|
4777
|
+
const pixelWidthPx = Math.max(1, Math.round(params.pixelWidthPx));
|
|
4778
|
+
const pixelHeightPx = Math.max(1, Math.round(params.pixelHeightPx));
|
|
4779
|
+
const surface = document.createElement("canvas");
|
|
4780
|
+
surface.width = pixelWidthPx;
|
|
4781
|
+
surface.height = pixelHeightPx;
|
|
4782
|
+
const context = surface.getContext("2d");
|
|
4783
|
+
if (!context) {
|
|
4784
|
+
throw new Error("2D canvas context is unavailable for DOCX thumbnails.");
|
|
4785
|
+
}
|
|
4786
|
+
const scaleX = pixelWidthPx / sourceWidthPx;
|
|
4787
|
+
const scaleY = pixelHeightPx / sourceHeightPx;
|
|
4788
|
+
const hairlineSourcePx = Math.max(0.75, 1 / Math.max(scaleX, scaleY));
|
|
4789
|
+
context.setTransform(scaleX, 0, 0, scaleY, 0, 0);
|
|
4790
|
+
context.imageSmoothingEnabled = true;
|
|
4791
|
+
context.imageSmoothingQuality = "high";
|
|
4792
|
+
setCanvasFillStyle(
|
|
4793
|
+
context,
|
|
4794
|
+
params.snapshot.pageBackgroundColor,
|
|
4795
|
+
"#ffffff"
|
|
4796
|
+
);
|
|
4797
|
+
context.fillRect(0, 0, sourceWidthPx, sourceHeightPx);
|
|
4798
|
+
params.snapshot.elements.slice(0, THUMBNAIL_DIRECT_MAX_ELEMENTS).forEach((element) => {
|
|
4799
|
+
switch (element.kind) {
|
|
4800
|
+
case "paragraph":
|
|
4801
|
+
drawDirectThumbnailParagraph(context, element);
|
|
4802
|
+
break;
|
|
4803
|
+
case "image-placeholder":
|
|
4804
|
+
drawDirectThumbnailImagePlaceholder(
|
|
4805
|
+
context,
|
|
4806
|
+
element,
|
|
4807
|
+
hairlineSourcePx
|
|
4808
|
+
);
|
|
4809
|
+
break;
|
|
4810
|
+
case "table":
|
|
4811
|
+
drawDirectThumbnailTable(context, element, hairlineSourcePx);
|
|
4812
|
+
break;
|
|
4813
|
+
}
|
|
4814
|
+
});
|
|
4815
|
+
return surface;
|
|
4816
|
+
}
|
|
4469
4817
|
async function buildDocxThumbnailSvgMarkup(params) {
|
|
4470
4818
|
const { pageElement, sourceWidthPx, sourceHeightPx, widthPx, heightPx } = params;
|
|
4471
4819
|
const clone = pageElement.cloneNode(true);
|
|
@@ -4527,16 +4875,33 @@ async function rasterizeDocxThumbnailSurface(params) {
|
|
|
4527
4875
|
return surface;
|
|
4528
4876
|
}
|
|
4529
4877
|
function blitDocxThumbnailSurface(surface, canvas, resolution) {
|
|
4530
|
-
|
|
4531
|
-
|
|
4532
|
-
|
|
4533
|
-
|
|
4878
|
+
const pixelWidth = Math.max(1, Math.round(resolution.pixelWidthPx));
|
|
4879
|
+
const pixelHeight = Math.max(1, Math.round(resolution.pixelHeightPx));
|
|
4880
|
+
const cssWidth = `${Math.max(1, Math.round(resolution.widthPx))}px`;
|
|
4881
|
+
const cssHeight = `${Math.max(1, Math.round(resolution.heightPx))}px`;
|
|
4882
|
+
let bufferResized = false;
|
|
4883
|
+
if (canvas.width !== pixelWidth) {
|
|
4884
|
+
canvas.width = pixelWidth;
|
|
4885
|
+
bufferResized = true;
|
|
4886
|
+
}
|
|
4887
|
+
if (canvas.height !== pixelHeight) {
|
|
4888
|
+
canvas.height = pixelHeight;
|
|
4889
|
+
bufferResized = true;
|
|
4890
|
+
}
|
|
4891
|
+
if (canvas.style.width !== cssWidth) {
|
|
4892
|
+
canvas.style.width = cssWidth;
|
|
4893
|
+
}
|
|
4894
|
+
if (canvas.style.height !== cssHeight) {
|
|
4895
|
+
canvas.style.height = cssHeight;
|
|
4896
|
+
}
|
|
4534
4897
|
const context = canvas.getContext("2d");
|
|
4535
4898
|
if (!context) {
|
|
4536
4899
|
throw new Error("2D canvas context is unavailable for DOCX thumbnails.");
|
|
4537
4900
|
}
|
|
4538
4901
|
context.setTransform(1, 0, 0, 1, 0, 0);
|
|
4539
|
-
|
|
4902
|
+
if (!bufferResized) {
|
|
4903
|
+
context.clearRect(0, 0, canvas.width, canvas.height);
|
|
4904
|
+
}
|
|
4540
4905
|
context.drawImage(surface, 0, 0, canvas.width, canvas.height);
|
|
4541
4906
|
}
|
|
4542
4907
|
var DocxThumbnailSurfaceCache = class {
|
|
@@ -4611,6 +4976,7 @@ var SerialIdleTaskQueue = class {
|
|
|
4611
4976
|
now;
|
|
4612
4977
|
pumpScheduled = false;
|
|
4613
4978
|
running = false;
|
|
4979
|
+
nextSequence = 0;
|
|
4614
4980
|
constructor(options) {
|
|
4615
4981
|
this.scheduleTask = options?.scheduleTask ?? defaultScheduleTask;
|
|
4616
4982
|
this.scheduleDelayed = options?.scheduleDelayed ?? defaultScheduleDelayed;
|
|
@@ -4620,14 +4986,23 @@ var SerialIdleTaskQueue = class {
|
|
|
4620
4986
|
get pendingCount() {
|
|
4621
4987
|
return this.pending.length;
|
|
4622
4988
|
}
|
|
4623
|
-
enqueue(key, run) {
|
|
4989
|
+
enqueue(key, run, options) {
|
|
4990
|
+
const priority = Number.isFinite(options?.priority) ? Number(options?.priority) : 0;
|
|
4624
4991
|
return new Promise((resolve) => {
|
|
4625
4992
|
const existing = this.pending.find((entry) => entry.key === key);
|
|
4626
4993
|
if (existing) {
|
|
4627
4994
|
existing.run = run;
|
|
4628
4995
|
existing.resolvers.push(resolve);
|
|
4996
|
+
existing.priority = Math.min(existing.priority, priority);
|
|
4629
4997
|
} else {
|
|
4630
|
-
this.pending.push({
|
|
4998
|
+
this.pending.push({
|
|
4999
|
+
key,
|
|
5000
|
+
run,
|
|
5001
|
+
resolvers: [resolve],
|
|
5002
|
+
priority,
|
|
5003
|
+
sequence: this.nextSequence
|
|
5004
|
+
});
|
|
5005
|
+
this.nextSequence += 1;
|
|
4631
5006
|
}
|
|
4632
5007
|
this.schedulePump();
|
|
4633
5008
|
});
|
|
@@ -4672,6 +5047,8 @@ var SerialIdleTaskQueue = class {
|
|
|
4672
5047
|
}
|
|
4673
5048
|
const now = this.now();
|
|
4674
5049
|
let earliestWaitMs;
|
|
5050
|
+
let bestIndex = -1;
|
|
5051
|
+
let bestEntry;
|
|
4675
5052
|
for (let index = 0; index < this.pending.length; index += 1) {
|
|
4676
5053
|
const candidate = this.pending[index];
|
|
4677
5054
|
if (!candidate) {
|
|
@@ -4680,11 +5057,18 @@ var SerialIdleTaskQueue = class {
|
|
|
4680
5057
|
const lastRunAt = this.lastRunAtByKey.get(candidate.key);
|
|
4681
5058
|
const waitMs = lastRunAt === void 0 ? 0 : lastRunAt + this.minTaskIntervalMs - now;
|
|
4682
5059
|
if (waitMs <= 0) {
|
|
4683
|
-
|
|
4684
|
-
|
|
5060
|
+
if (!bestEntry || candidate.priority < bestEntry.priority || candidate.priority === bestEntry.priority && candidate.sequence < bestEntry.sequence) {
|
|
5061
|
+
bestEntry = candidate;
|
|
5062
|
+
bestIndex = index;
|
|
5063
|
+
}
|
|
5064
|
+
continue;
|
|
4685
5065
|
}
|
|
4686
5066
|
earliestWaitMs = earliestWaitMs === void 0 ? waitMs : Math.min(earliestWaitMs, waitMs);
|
|
4687
5067
|
}
|
|
5068
|
+
if (bestEntry && bestIndex >= 0) {
|
|
5069
|
+
this.pending.splice(bestIndex, 1);
|
|
5070
|
+
return { entry: bestEntry };
|
|
5071
|
+
}
|
|
4688
5072
|
return earliestWaitMs === void 0 ? void 0 : { retryDelayMs: earliestWaitMs };
|
|
4689
5073
|
}
|
|
4690
5074
|
async runNext() {
|
|
@@ -23683,13 +24067,14 @@ function ensureDocxViewerPageSurfaceRegistry(editor) {
|
|
|
23683
24067
|
pageElements: /* @__PURE__ */ new Map(),
|
|
23684
24068
|
pageContentKeys: /* @__PURE__ */ new Map(),
|
|
23685
24069
|
pageSizes: /* @__PURE__ */ new Map(),
|
|
24070
|
+
pageThumbnailSnapshots: /* @__PURE__ */ new Map(),
|
|
23686
24071
|
listeners: /* @__PURE__ */ new Set()
|
|
23687
24072
|
};
|
|
23688
24073
|
docxViewerPageSurfaceRegistryByEditor.set(owner, registry);
|
|
23689
24074
|
}
|
|
23690
24075
|
return registry;
|
|
23691
24076
|
}
|
|
23692
|
-
function syncDocxViewerPageSurfaceContentKeys(editor, contentKeysByPage, pageSizesByPage = []) {
|
|
24077
|
+
function syncDocxViewerPageSurfaceContentKeys(editor, contentKeysByPage, pageSizesByPage = [], thumbnailSnapshotsByPage = []) {
|
|
23693
24078
|
const registry = ensureDocxViewerPageSurfaceRegistry(editor);
|
|
23694
24079
|
let changed = false;
|
|
23695
24080
|
contentKeysByPage.forEach((contentKey, pageIndex) => {
|
|
@@ -23707,6 +24092,20 @@ function syncDocxViewerPageSurfaceContentKeys(editor, contentKeysByPage, pageSiz
|
|
|
23707
24092
|
changed = true;
|
|
23708
24093
|
}
|
|
23709
24094
|
});
|
|
24095
|
+
thumbnailSnapshotsByPage.forEach((snapshot, pageIndex) => {
|
|
24096
|
+
const previous = registry.pageThumbnailSnapshots.get(pageIndex);
|
|
24097
|
+
if (!snapshot) {
|
|
24098
|
+
if (previous) {
|
|
24099
|
+
registry.pageThumbnailSnapshots.delete(pageIndex);
|
|
24100
|
+
changed = true;
|
|
24101
|
+
}
|
|
24102
|
+
return;
|
|
24103
|
+
}
|
|
24104
|
+
if (previous?.key !== snapshot.key) {
|
|
24105
|
+
registry.pageThumbnailSnapshots.set(pageIndex, snapshot);
|
|
24106
|
+
changed = true;
|
|
24107
|
+
}
|
|
24108
|
+
});
|
|
23710
24109
|
registry.pageContentKeys.forEach((_, pageIndex) => {
|
|
23711
24110
|
if (pageIndex >= contentKeysByPage.length) {
|
|
23712
24111
|
registry.pageContentKeys.delete(pageIndex);
|
|
@@ -23719,6 +24118,12 @@ function syncDocxViewerPageSurfaceContentKeys(editor, contentKeysByPage, pageSiz
|
|
|
23719
24118
|
changed = true;
|
|
23720
24119
|
}
|
|
23721
24120
|
});
|
|
24121
|
+
registry.pageThumbnailSnapshots.forEach((_, pageIndex) => {
|
|
24122
|
+
if (pageIndex >= thumbnailSnapshotsByPage.length) {
|
|
24123
|
+
registry.pageThumbnailSnapshots.delete(pageIndex);
|
|
24124
|
+
changed = true;
|
|
24125
|
+
}
|
|
24126
|
+
});
|
|
23722
24127
|
if (changed) {
|
|
23723
24128
|
notifyDocxViewerPageSurfaceSubscribers(registry);
|
|
23724
24129
|
}
|
|
@@ -23799,6 +24204,359 @@ function resolveDocxViewerPageSurfaceSize(element, fallbackWidthPx, fallbackHeig
|
|
|
23799
24204
|
heightPx: Math.max(1, Math.round(fallbackHeightPx))
|
|
23800
24205
|
};
|
|
23801
24206
|
}
|
|
24207
|
+
var DOCX_DIRECT_THUMBNAIL_MAX_ELEMENTS_PER_PAGE = 260;
|
|
24208
|
+
var DOCX_DIRECT_THUMBNAIL_MAX_TEXT_RUNS = 28;
|
|
24209
|
+
var DOCX_DIRECT_THUMBNAIL_MAX_TEXT_CHARS = 900;
|
|
24210
|
+
var DOCX_DIRECT_THUMBNAIL_MAX_TABLE_CELLS = 220;
|
|
24211
|
+
var DOCX_DIRECT_THUMBNAIL_MAX_CELL_TEXT_CHARS = 120;
|
|
24212
|
+
function docxThumbnailCssNumber(value) {
|
|
24213
|
+
if (typeof value === "number" && Number.isFinite(value)) {
|
|
24214
|
+
return value;
|
|
24215
|
+
}
|
|
24216
|
+
if (typeof value === "string") {
|
|
24217
|
+
const parsed = Number.parseFloat(value);
|
|
24218
|
+
return Number.isFinite(parsed) ? parsed : 0;
|
|
24219
|
+
}
|
|
24220
|
+
return 0;
|
|
24221
|
+
}
|
|
24222
|
+
function normalizeDocxThumbnailColor(value) {
|
|
24223
|
+
const normalized = value?.trim();
|
|
24224
|
+
if (!normalized || normalized.toLowerCase() === "auto") {
|
|
24225
|
+
return void 0;
|
|
24226
|
+
}
|
|
24227
|
+
if (/^[0-9a-fA-F]{6}$/.test(normalized)) {
|
|
24228
|
+
return `#${normalized}`;
|
|
24229
|
+
}
|
|
24230
|
+
return normalized;
|
|
24231
|
+
}
|
|
24232
|
+
function docxThumbnailTextRunStylesMatch(left, right) {
|
|
24233
|
+
return left.bold === right.bold && left.italic === right.italic && left.color === right.color && left.backgroundColor === right.backgroundColor && left.fontSizePx === right.fontSizePx && left.fontFamily === right.fontFamily;
|
|
24234
|
+
}
|
|
24235
|
+
function appendDocxThumbnailTextRun(runs, run, remaining) {
|
|
24236
|
+
if (remaining.chars <= 0 || runs.length >= DOCX_DIRECT_THUMBNAIL_MAX_TEXT_RUNS) {
|
|
24237
|
+
return;
|
|
24238
|
+
}
|
|
24239
|
+
const text = run.text.slice(0, remaining.chars);
|
|
24240
|
+
if (!text) {
|
|
24241
|
+
return;
|
|
24242
|
+
}
|
|
24243
|
+
remaining.chars -= text.length;
|
|
24244
|
+
const nextRun = { ...run, text };
|
|
24245
|
+
const previous = runs[runs.length - 1];
|
|
24246
|
+
if (previous && docxThumbnailTextRunStylesMatch(previous, nextRun)) {
|
|
24247
|
+
previous.text += nextRun.text;
|
|
24248
|
+
return;
|
|
24249
|
+
}
|
|
24250
|
+
runs.push(nextRun);
|
|
24251
|
+
}
|
|
24252
|
+
function docxThumbnailFallbackHeadingRunStyle(paragraph) {
|
|
24253
|
+
const headingLevel = paragraph.style?.headingLevel;
|
|
24254
|
+
return headingLevel && headingLevel >= 1 && headingLevel <= 6 ? DEFAULT_WORD_HEADING_RUN_STYLES[headingLevel] : void 0;
|
|
24255
|
+
}
|
|
24256
|
+
function docxThumbnailTextRunsFromParagraph(paragraph, documentTheme, maxChars = DOCX_DIRECT_THUMBNAIL_MAX_TEXT_CHARS) {
|
|
24257
|
+
const runs = [];
|
|
24258
|
+
const remaining = { chars: maxChars };
|
|
24259
|
+
const fallbackHeadingStyle = docxThumbnailFallbackHeadingRunStyle(paragraph);
|
|
24260
|
+
const fallbackFontFamily = cssFontFamily(fallbackHeadingStyle?.fontFamily) ?? cssFontFamily(paragraphDominantFontFamily(paragraph));
|
|
24261
|
+
paragraph.children.forEach((child) => {
|
|
24262
|
+
if (remaining.chars <= 0) {
|
|
24263
|
+
return;
|
|
24264
|
+
}
|
|
24265
|
+
const style = child.type === "text" || child.type === "form-field" ? child.style : void 0;
|
|
24266
|
+
const text = child.type === "text" ? child.text : child.type === "form-field" ? formFieldDisplayValue2(child) : child.type === "image" ? child.alt || "[image]" : "";
|
|
24267
|
+
appendDocxThumbnailTextRun(
|
|
24268
|
+
runs,
|
|
24269
|
+
{
|
|
24270
|
+
text,
|
|
24271
|
+
bold: style?.bold ?? fallbackHeadingStyle?.bold,
|
|
24272
|
+
italic: style?.italic ?? fallbackHeadingStyle?.italic,
|
|
24273
|
+
color: normalizeDocxThumbnailColor(
|
|
24274
|
+
themedRunColor(style?.color ?? fallbackHeadingStyle?.color, documentTheme)
|
|
24275
|
+
),
|
|
24276
|
+
backgroundColor: normalizeDocxThumbnailColor(
|
|
24277
|
+
style?.backgroundColor ?? resolveHighlightColor(style?.highlight)
|
|
24278
|
+
),
|
|
24279
|
+
fontSizePx: style?.fontSizePt && style.fontSizePt > 0 ? Math.max(6, style.fontSizePt * 96 / 72) : fallbackHeadingStyle?.fontSizePt ? Math.max(6, fallbackHeadingStyle.fontSizePt * 96 / 72) : paragraphBaseFontSizePx(paragraph),
|
|
24280
|
+
fontFamily: cssFontFamily(style?.fontFamily) ?? fallbackFontFamily
|
|
24281
|
+
},
|
|
24282
|
+
remaining
|
|
24283
|
+
);
|
|
24284
|
+
});
|
|
24285
|
+
return runs;
|
|
24286
|
+
}
|
|
24287
|
+
function docxThumbnailCellTextRuns(cell, documentTheme) {
|
|
24288
|
+
const paragraph = tableCellParagraphsRecursively(cell.nodes).find(
|
|
24289
|
+
(candidate) => paragraphText(candidate).trim().length > 0
|
|
24290
|
+
);
|
|
24291
|
+
if (!paragraph) {
|
|
24292
|
+
return void 0;
|
|
24293
|
+
}
|
|
24294
|
+
const runs = docxThumbnailTextRunsFromParagraph(
|
|
24295
|
+
paragraph,
|
|
24296
|
+
documentTheme,
|
|
24297
|
+
DOCX_DIRECT_THUMBNAIL_MAX_CELL_TEXT_CHARS
|
|
24298
|
+
);
|
|
24299
|
+
return runs.length > 0 ? runs : void 0;
|
|
24300
|
+
}
|
|
24301
|
+
function buildDocxThumbnailParagraphElements(params) {
|
|
24302
|
+
const {
|
|
24303
|
+
paragraph,
|
|
24304
|
+
segment,
|
|
24305
|
+
contentLeftPx,
|
|
24306
|
+
contentWidthPx,
|
|
24307
|
+
yPx,
|
|
24308
|
+
heightPx,
|
|
24309
|
+
numberingDefinitions,
|
|
24310
|
+
docGridLinePitchPx,
|
|
24311
|
+
documentTheme
|
|
24312
|
+
} = params;
|
|
24313
|
+
const blockStyle = paragraphBlockStyle(
|
|
24314
|
+
paragraph,
|
|
24315
|
+
numberingDefinitions,
|
|
24316
|
+
void 0,
|
|
24317
|
+
docGridLinePitchPx
|
|
24318
|
+
);
|
|
24319
|
+
const paragraphLineRange = segment.paragraphLineRange;
|
|
24320
|
+
const marginTopPx = paragraphLineRange && paragraphLineRange.startLineIndex > 0 ? 0 : Math.max(0, docxThumbnailCssNumber(blockStyle.marginTop));
|
|
24321
|
+
const marginBottomPx = paragraphLineRange && paragraphLineRange.endLineIndex < paragraphLineRange.totalLineCount ? 0 : Math.max(0, docxThumbnailCssNumber(blockStyle.marginBottom));
|
|
24322
|
+
const marginLeftPx = docxThumbnailCssNumber(blockStyle.marginLeft);
|
|
24323
|
+
const marginRightPx = Math.max(0, docxThumbnailCssNumber(blockStyle.marginRight));
|
|
24324
|
+
const xPx = contentLeftPx + marginLeftPx;
|
|
24325
|
+
const widthPx = Math.max(8, contentWidthPx - marginLeftPx - marginRightPx);
|
|
24326
|
+
const bodyYPx = yPx + marginTopPx;
|
|
24327
|
+
const bodyHeightPx = Math.max(1, heightPx - marginTopPx - marginBottomPx);
|
|
24328
|
+
const runs = docxThumbnailTextRunsFromParagraph(paragraph, documentTheme);
|
|
24329
|
+
const elements = [];
|
|
24330
|
+
elements.push({
|
|
24331
|
+
kind: "paragraph",
|
|
24332
|
+
xPx,
|
|
24333
|
+
yPx: bodyYPx,
|
|
24334
|
+
widthPx,
|
|
24335
|
+
heightPx: bodyHeightPx,
|
|
24336
|
+
align: paragraph.style?.align,
|
|
24337
|
+
backgroundColor: normalizeDocxThumbnailColor(
|
|
24338
|
+
paragraph.style?.backgroundColor
|
|
24339
|
+
),
|
|
24340
|
+
lineHeightPx: paragraphLineRange?.lineHeightPx ?? estimateParagraphLineHeightPx(paragraph, docGridLinePitchPx),
|
|
24341
|
+
startLineIndex: paragraphLineRange?.startLineIndex,
|
|
24342
|
+
runs
|
|
24343
|
+
});
|
|
24344
|
+
const hasVisibleText = runs.some((run) => run.text.trim().length > 0);
|
|
24345
|
+
if (!hasVisibleText) {
|
|
24346
|
+
const imageRuns = paragraph.children.filter(
|
|
24347
|
+
(child) => child.type === "image"
|
|
24348
|
+
);
|
|
24349
|
+
imageRuns.slice(0, 4).forEach((imageRun) => {
|
|
24350
|
+
const imageWidthPx = Math.max(18, imageRun.widthPx ?? widthPx * 0.5);
|
|
24351
|
+
const imageHeightPx = Math.max(18, imageRun.heightPx ?? bodyHeightPx * 0.5);
|
|
24352
|
+
const scale = Math.min(
|
|
24353
|
+
1,
|
|
24354
|
+
(widthPx - 4) / imageWidthPx,
|
|
24355
|
+
(bodyHeightPx - 4) / imageHeightPx
|
|
24356
|
+
);
|
|
24357
|
+
elements.push({
|
|
24358
|
+
kind: "image-placeholder",
|
|
24359
|
+
xPx: xPx + 2,
|
|
24360
|
+
yPx: bodyYPx + 2,
|
|
24361
|
+
widthPx: Math.max(12, imageWidthPx * scale),
|
|
24362
|
+
heightPx: Math.max(12, imageHeightPx * scale)
|
|
24363
|
+
});
|
|
24364
|
+
});
|
|
24365
|
+
}
|
|
24366
|
+
return elements;
|
|
24367
|
+
}
|
|
24368
|
+
function buildDocxThumbnailTableElement(params) {
|
|
24369
|
+
const {
|
|
24370
|
+
table,
|
|
24371
|
+
segment,
|
|
24372
|
+
contentLeftPx,
|
|
24373
|
+
contentWidthPx,
|
|
24374
|
+
contentHeightPx,
|
|
24375
|
+
yPx,
|
|
24376
|
+
heightPx,
|
|
24377
|
+
numberingDefinitions,
|
|
24378
|
+
docGridLinePitchPx,
|
|
24379
|
+
documentTheme
|
|
24380
|
+
} = params;
|
|
24381
|
+
const columnCount = tableColumnCount(table);
|
|
24382
|
+
const tableIndentPx = twipsToSignedPixels(table.style?.indentTwips) ?? 0;
|
|
24383
|
+
const tableWidthPx = twipsToPixels(table.style?.widthTwips);
|
|
24384
|
+
const definedWidthsTwips = columnWidthsFromTableDefinition(table, columnCount);
|
|
24385
|
+
const rawTableColumnWidthsPx = definedWidthsTwips && definedWidthsTwips.length > 0 ? normalizeColumnWidthsPx(
|
|
24386
|
+
definedWidthsTwips.map((widthTwips) => twipsToPixels(widthTwips) ?? 0),
|
|
24387
|
+
columnCount,
|
|
24388
|
+
tableWidthPx,
|
|
24389
|
+
1
|
|
24390
|
+
) : defaultColumnWidthsPx(columnCount, tableWidthPx);
|
|
24391
|
+
const rawResolvedTableWidthPx = tableWidthPx ?? rawTableColumnWidthsPx.reduce((sum, widthPx) => sum + widthPx, 0);
|
|
24392
|
+
const maxTableWidthPx = Math.max(
|
|
24393
|
+
24,
|
|
24394
|
+
contentWidthPx - tableIndentPx - resolveCollapsedTableHorizontalOuterBleedPx(table, columnCount)
|
|
24395
|
+
);
|
|
24396
|
+
const resolvedTableWidthPx = clampTableWidthPx(
|
|
24397
|
+
rawResolvedTableWidthPx,
|
|
24398
|
+
maxTableWidthPx
|
|
24399
|
+
);
|
|
24400
|
+
const { columnWidthsPx } = resolveFittedTableColumnWidths(
|
|
24401
|
+
table,
|
|
24402
|
+
rawTableColumnWidthsPx,
|
|
24403
|
+
resolvedTableWidthPx
|
|
24404
|
+
);
|
|
24405
|
+
const rowHeightsPx = estimateTableRowHeightsPx(
|
|
24406
|
+
table,
|
|
24407
|
+
contentWidthPx,
|
|
24408
|
+
numberingDefinitions,
|
|
24409
|
+
docGridLinePitchPx,
|
|
24410
|
+
contentHeightPx
|
|
24411
|
+
);
|
|
24412
|
+
const startRowIndex = Math.max(
|
|
24413
|
+
0,
|
|
24414
|
+
segment.tableRowSlice?.rowIndex ?? segment.tableRowRange?.startRowIndex ?? 0
|
|
24415
|
+
);
|
|
24416
|
+
const endRowIndex = Math.min(
|
|
24417
|
+
table.rows.length,
|
|
24418
|
+
segment.tableRowSlice ? startRowIndex + 1 : segment.tableRowRange?.endRowIndex ?? table.rows.length
|
|
24419
|
+
);
|
|
24420
|
+
const cells = [];
|
|
24421
|
+
let rowYPx = segment.tableRowSlice ? -Math.max(0, segment.tableRowSlice.startOffsetPx) : 0;
|
|
24422
|
+
for (let rowIndex = startRowIndex; rowIndex < endRowIndex && cells.length < DOCX_DIRECT_THUMBNAIL_MAX_TABLE_CELLS; rowIndex += 1) {
|
|
24423
|
+
const row = table.rows[rowIndex];
|
|
24424
|
+
if (!row) {
|
|
24425
|
+
continue;
|
|
24426
|
+
}
|
|
24427
|
+
const rowHeightPx = segment.tableRowSlice && rowIndex === segment.tableRowSlice.rowIndex ? Math.max(1, segment.tableRowSlice.totalRowHeightPx) : Math.max(1, rowHeightsPx[rowIndex] ?? MIN_PARAGRAPH_LINE_HEIGHT_PX);
|
|
24428
|
+
let columnCursor = 0;
|
|
24429
|
+
row.cells.forEach((cell) => {
|
|
24430
|
+
if (cells.length >= DOCX_DIRECT_THUMBNAIL_MAX_TABLE_CELLS) {
|
|
24431
|
+
return;
|
|
24432
|
+
}
|
|
24433
|
+
const span = cell.style?.gridSpan && cell.style.gridSpan > 1 ? cell.style.gridSpan : 1;
|
|
24434
|
+
const xPx = columnWidthsPx.slice(0, columnCursor).reduce((sum, widthPx2) => sum + widthPx2, 0);
|
|
24435
|
+
const widthPx = columnWidthsPx.slice(columnCursor, columnCursor + span).reduce((sum, widthPx2) => sum + widthPx2, 0);
|
|
24436
|
+
columnCursor += span;
|
|
24437
|
+
cells.push({
|
|
24438
|
+
xPx,
|
|
24439
|
+
yPx: rowYPx,
|
|
24440
|
+
widthPx: Math.max(1, widthPx),
|
|
24441
|
+
heightPx: rowHeightPx,
|
|
24442
|
+
backgroundColor: normalizeDocxThumbnailColor(
|
|
24443
|
+
cell.style?.backgroundColor ?? row.style?.backgroundColor
|
|
24444
|
+
),
|
|
24445
|
+
runs: docxThumbnailCellTextRuns(cell, documentTheme)
|
|
24446
|
+
});
|
|
24447
|
+
});
|
|
24448
|
+
rowYPx += rowHeightPx;
|
|
24449
|
+
}
|
|
24450
|
+
if (cells.length === 0) {
|
|
24451
|
+
return void 0;
|
|
24452
|
+
}
|
|
24453
|
+
return {
|
|
24454
|
+
kind: "table",
|
|
24455
|
+
xPx: contentLeftPx + tableIndentPx,
|
|
24456
|
+
yPx,
|
|
24457
|
+
widthPx: Math.max(1, resolvedTableWidthPx),
|
|
24458
|
+
heightPx: Math.max(1, heightPx),
|
|
24459
|
+
cells
|
|
24460
|
+
};
|
|
24461
|
+
}
|
|
24462
|
+
function buildDocxPageThumbnailRenderSnapshotEntries(params) {
|
|
24463
|
+
const {
|
|
24464
|
+
model,
|
|
24465
|
+
pageNodeSegmentsByPage,
|
|
24466
|
+
pageSectionInfoByIndex,
|
|
24467
|
+
contentKeysByPage,
|
|
24468
|
+
fallbackLayout,
|
|
24469
|
+
documentTheme,
|
|
24470
|
+
docGridLinePitchPxByNodeIndex,
|
|
24471
|
+
numberingDefinitions
|
|
24472
|
+
} = params;
|
|
24473
|
+
return pageNodeSegmentsByPage.map((pageSegments, pageIndex) => {
|
|
24474
|
+
const key = `${contentKeysByPage[pageIndex] ?? ""}|theme:${documentTheme}`;
|
|
24475
|
+
let cachedSnapshot;
|
|
24476
|
+
return {
|
|
24477
|
+
key,
|
|
24478
|
+
getSnapshot: () => {
|
|
24479
|
+
if (cachedSnapshot) {
|
|
24480
|
+
return cachedSnapshot;
|
|
24481
|
+
}
|
|
24482
|
+
const pageLayout = pageSectionInfoByIndex[pageIndex]?.layout ?? fallbackLayout;
|
|
24483
|
+
const contentLeftPx = pageLayout.marginsPx.left;
|
|
24484
|
+
const contentTopPx = pageLayout.marginsPx.top;
|
|
24485
|
+
const contentWidthPx = Math.max(
|
|
24486
|
+
1,
|
|
24487
|
+
pageLayout.pageWidthPx - pageLayout.marginsPx.left - pageLayout.marginsPx.right
|
|
24488
|
+
);
|
|
24489
|
+
const contentHeightPx = Math.max(
|
|
24490
|
+
1,
|
|
24491
|
+
pageLayout.pageHeightPx - pageLayout.marginsPx.top - pageLayout.marginsPx.bottom
|
|
24492
|
+
);
|
|
24493
|
+
const elements = [];
|
|
24494
|
+
let yPx = contentTopPx;
|
|
24495
|
+
for (const segment of pageSegments) {
|
|
24496
|
+
if (elements.length >= DOCX_DIRECT_THUMBNAIL_MAX_ELEMENTS_PER_PAGE) {
|
|
24497
|
+
break;
|
|
24498
|
+
}
|
|
24499
|
+
const node = model.nodes[segment.nodeIndex];
|
|
24500
|
+
if (!node) {
|
|
24501
|
+
continue;
|
|
24502
|
+
}
|
|
24503
|
+
const docGridLinePitchPx = docGridLinePitchPxByNodeIndex.get(
|
|
24504
|
+
segment.nodeIndex
|
|
24505
|
+
);
|
|
24506
|
+
const segmentHeightPx = estimateRenderedPageSegmentHeightPx(
|
|
24507
|
+
node,
|
|
24508
|
+
segment,
|
|
24509
|
+
model,
|
|
24510
|
+
contentWidthPx,
|
|
24511
|
+
numberingDefinitions,
|
|
24512
|
+
docGridLinePitchPx
|
|
24513
|
+
);
|
|
24514
|
+
if (node.type === "paragraph" && !segment.tableRowRange) {
|
|
24515
|
+
elements.push(
|
|
24516
|
+
...buildDocxThumbnailParagraphElements({
|
|
24517
|
+
paragraph: node,
|
|
24518
|
+
segment,
|
|
24519
|
+
contentLeftPx,
|
|
24520
|
+
contentTopPx,
|
|
24521
|
+
contentWidthPx,
|
|
24522
|
+
yPx,
|
|
24523
|
+
heightPx: segmentHeightPx,
|
|
24524
|
+
numberingDefinitions,
|
|
24525
|
+
docGridLinePitchPx,
|
|
24526
|
+
documentTheme
|
|
24527
|
+
})
|
|
24528
|
+
);
|
|
24529
|
+
} else if (node.type === "table") {
|
|
24530
|
+
const tableElement = buildDocxThumbnailTableElement({
|
|
24531
|
+
table: node,
|
|
24532
|
+
segment,
|
|
24533
|
+
contentLeftPx,
|
|
24534
|
+
contentWidthPx,
|
|
24535
|
+
contentHeightPx,
|
|
24536
|
+
yPx,
|
|
24537
|
+
heightPx: segmentHeightPx,
|
|
24538
|
+
numberingDefinitions,
|
|
24539
|
+
docGridLinePitchPx,
|
|
24540
|
+
documentTheme
|
|
24541
|
+
});
|
|
24542
|
+
if (tableElement) {
|
|
24543
|
+
elements.push(tableElement);
|
|
24544
|
+
}
|
|
24545
|
+
}
|
|
24546
|
+
yPx += Math.max(1, segmentHeightPx);
|
|
24547
|
+
}
|
|
24548
|
+
cachedSnapshot = {
|
|
24549
|
+
key,
|
|
24550
|
+
sourceWidthPx: pageLayout.pageWidthPx,
|
|
24551
|
+
sourceHeightPx: pageLayout.pageHeightPx,
|
|
24552
|
+
pageBackgroundColor: documentTheme === "dark" ? "#111827" : "#ffffff",
|
|
24553
|
+
elements
|
|
24554
|
+
};
|
|
24555
|
+
return cachedSnapshot;
|
|
24556
|
+
}
|
|
24557
|
+
};
|
|
24558
|
+
});
|
|
24559
|
+
}
|
|
23802
24560
|
function DocxDetachedThumbnailPageSurface({
|
|
23803
24561
|
editor,
|
|
23804
24562
|
pageIndex
|
|
@@ -23824,17 +24582,19 @@ var DocxDetachedThumbnailSurfaceRenderer = class {
|
|
|
23824
24582
|
host;
|
|
23825
24583
|
root;
|
|
23826
24584
|
activePageIndex;
|
|
24585
|
+
activeRenderKey;
|
|
23827
24586
|
async renderPageSurface(params) {
|
|
23828
24587
|
if (typeof document === "undefined" || typeof window === "undefined") {
|
|
23829
24588
|
return void 0;
|
|
23830
24589
|
}
|
|
23831
|
-
const { editor, registry, pageIndex } = params;
|
|
24590
|
+
const { editor, registry, pageIndex, renderKey } = params;
|
|
23832
24591
|
await this.ensureRoot();
|
|
23833
24592
|
if (!this.root) {
|
|
23834
24593
|
return void 0;
|
|
23835
24594
|
}
|
|
23836
|
-
if (this.activePageIndex !== pageIndex) {
|
|
24595
|
+
if (this.activePageIndex !== pageIndex || this.activeRenderKey !== renderKey) {
|
|
23837
24596
|
this.activePageIndex = pageIndex;
|
|
24597
|
+
this.activeRenderKey = renderKey;
|
|
23838
24598
|
this.root.render(
|
|
23839
24599
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
23840
24600
|
DocxDetachedThumbnailPageSurface,
|
|
@@ -23857,6 +24617,7 @@ var DocxDetachedThumbnailSurfaceRenderer = class {
|
|
|
23857
24617
|
}
|
|
23858
24618
|
this.host = void 0;
|
|
23859
24619
|
this.activePageIndex = void 0;
|
|
24620
|
+
this.activeRenderKey = void 0;
|
|
23860
24621
|
}
|
|
23861
24622
|
async ensureRoot() {
|
|
23862
24623
|
if (this.root) {
|
|
@@ -23897,8 +24658,42 @@ var DocxDetachedThumbnailSurfaceRenderer = class {
|
|
|
23897
24658
|
return pageElement?.isConnected ? pageElement : void 0;
|
|
23898
24659
|
}
|
|
23899
24660
|
};
|
|
23900
|
-
var DOCX_THUMBNAIL_SURFACE_CACHE_MAX_ENTRIES =
|
|
24661
|
+
var DOCX_THUMBNAIL_SURFACE_CACHE_MAX_ENTRIES = 64;
|
|
23901
24662
|
var DOCX_THUMBNAIL_MIN_RASTER_INTERVAL_MS = 200;
|
|
24663
|
+
var DOCX_THUMBNAIL_RENDER_PRIORITY_VISIBLE = 0;
|
|
24664
|
+
var DOCX_THUMBNAIL_RENDER_PRIORITY_ATTACHED = 1;
|
|
24665
|
+
var DOCX_THUMBNAIL_RENDER_PRIORITY_PREFETCH = 2;
|
|
24666
|
+
function normalizeDocxThumbnailMinRasterIntervalMs(value) {
|
|
24667
|
+
return Number.isFinite(value) && Number(value) >= 0 ? Number(value) : DOCX_THUMBNAIL_MIN_RASTER_INTERVAL_MS;
|
|
24668
|
+
}
|
|
24669
|
+
function normalizeDocxThumbnailPageIndexes(indexes, totalPages) {
|
|
24670
|
+
if (!indexes?.length || totalPages <= 0) {
|
|
24671
|
+
return [];
|
|
24672
|
+
}
|
|
24673
|
+
const seen = /* @__PURE__ */ new Set();
|
|
24674
|
+
const normalized = [];
|
|
24675
|
+
indexes.forEach((pageIndex) => {
|
|
24676
|
+
if (!Number.isFinite(pageIndex)) {
|
|
24677
|
+
return;
|
|
24678
|
+
}
|
|
24679
|
+
const roundedPageIndex = Math.trunc(pageIndex);
|
|
24680
|
+
if (roundedPageIndex < 0 || roundedPageIndex >= totalPages || seen.has(roundedPageIndex)) {
|
|
24681
|
+
return;
|
|
24682
|
+
}
|
|
24683
|
+
seen.add(roundedPageIndex);
|
|
24684
|
+
normalized.push(roundedPageIndex);
|
|
24685
|
+
});
|
|
24686
|
+
return normalized;
|
|
24687
|
+
}
|
|
24688
|
+
function docxThumbnailPageIndexesKey(pageIndexes) {
|
|
24689
|
+
return pageIndexes.join(",");
|
|
24690
|
+
}
|
|
24691
|
+
function docxThumbnailCanvasQueueKey(canvasId) {
|
|
24692
|
+
return `canvas:${canvasId}`;
|
|
24693
|
+
}
|
|
24694
|
+
function docxThumbnailPrefetchQueueKey(pageIndex) {
|
|
24695
|
+
return `prefetch:${pageIndex}`;
|
|
24696
|
+
}
|
|
23902
24697
|
function resolveDocxPageThumbnailResolution(options) {
|
|
23903
24698
|
const safeSourceWidthPx = Math.max(1, Math.round(options.sourceWidthPx));
|
|
23904
24699
|
const safeSourceHeightPx = Math.max(1, Math.round(options.sourceHeightPx));
|
|
@@ -23978,6 +24773,12 @@ function useDocxPageThumbnails(editor, options = {}) {
|
|
|
23978
24773
|
const lastPaintedThumbnailKeyByCanvasRef = React.useRef(
|
|
23979
24774
|
/* @__PURE__ */ new WeakMap()
|
|
23980
24775
|
);
|
|
24776
|
+
const thumbnailQueueKeyByCanvasRef = React.useRef(
|
|
24777
|
+
/* @__PURE__ */ new WeakMap()
|
|
24778
|
+
);
|
|
24779
|
+
const nextThumbnailCanvasQueueIdRef = React.useRef(0);
|
|
24780
|
+
const queuedPrefetchThumbnailKeysRef = React.useRef(/* @__PURE__ */ new Set());
|
|
24781
|
+
const thumbnailMinRasterIntervalMs = normalizeDocxThumbnailMinRasterIntervalMs(options.minRasterIntervalMs);
|
|
23981
24782
|
const ensureThumbnailSurfaceCache = React.useCallback(() => {
|
|
23982
24783
|
if (!thumbnailSurfaceCacheRef.current) {
|
|
23983
24784
|
thumbnailSurfaceCacheRef.current = new DocxThumbnailSurfaceCache(
|
|
@@ -23989,18 +24790,50 @@ function useDocxPageThumbnails(editor, options = {}) {
|
|
|
23989
24790
|
const ensureThumbnailRasterQueue = React.useCallback(() => {
|
|
23990
24791
|
if (!thumbnailRasterQueueRef.current) {
|
|
23991
24792
|
thumbnailRasterQueueRef.current = new SerialIdleTaskQueue({
|
|
23992
|
-
minTaskIntervalMs:
|
|
24793
|
+
minTaskIntervalMs: thumbnailMinRasterIntervalMs
|
|
23993
24794
|
});
|
|
23994
24795
|
}
|
|
23995
24796
|
return thumbnailRasterQueueRef.current;
|
|
23996
|
-
}, []);
|
|
24797
|
+
}, [thumbnailMinRasterIntervalMs]);
|
|
24798
|
+
const thumbnailQueueKeyForCanvas = React.useCallback(
|
|
24799
|
+
(canvas) => {
|
|
24800
|
+
const existing = thumbnailQueueKeyByCanvasRef.current.get(canvas);
|
|
24801
|
+
if (existing) {
|
|
24802
|
+
return existing;
|
|
24803
|
+
}
|
|
24804
|
+
const nextKey = docxThumbnailCanvasQueueKey(
|
|
24805
|
+
nextThumbnailCanvasQueueIdRef.current
|
|
24806
|
+
);
|
|
24807
|
+
nextThumbnailCanvasQueueIdRef.current += 1;
|
|
24808
|
+
thumbnailQueueKeyByCanvasRef.current.set(canvas, nextKey);
|
|
24809
|
+
return nextKey;
|
|
24810
|
+
},
|
|
24811
|
+
[]
|
|
24812
|
+
);
|
|
24813
|
+
React.useEffect(() => {
|
|
24814
|
+
thumbnailRasterQueueRef.current?.clear();
|
|
24815
|
+
thumbnailRasterQueueRef.current = void 0;
|
|
24816
|
+
queuedPrefetchThumbnailKeysRef.current.clear();
|
|
24817
|
+
}, [thumbnailMinRasterIntervalMs]);
|
|
23997
24818
|
React.useEffect(() => {
|
|
23998
24819
|
thumbnailSurfaceCacheRef.current?.clear();
|
|
23999
24820
|
thumbnailRasterQueueRef.current?.clear();
|
|
24000
24821
|
detachedThumbnailSurfaceRendererRef.current?.clear();
|
|
24001
24822
|
detachedThumbnailSurfaceRendererRef.current = void 0;
|
|
24002
24823
|
lastPaintedThumbnailKeyByCanvasRef.current = /* @__PURE__ */ new WeakMap();
|
|
24824
|
+
thumbnailQueueKeyByCanvasRef.current = /* @__PURE__ */ new WeakMap();
|
|
24825
|
+
nextThumbnailCanvasQueueIdRef.current = 0;
|
|
24826
|
+
queuedPrefetchThumbnailKeysRef.current.clear();
|
|
24003
24827
|
}, [editor.documentLoadNonce, pageSurfaceRegistryOwner]);
|
|
24828
|
+
React.useEffect(() => {
|
|
24829
|
+
detachedThumbnailSurfaceRendererRef.current?.clear();
|
|
24830
|
+
detachedThumbnailSurfaceRendererRef.current = void 0;
|
|
24831
|
+
}, [
|
|
24832
|
+
editor.documentTheme,
|
|
24833
|
+
editor.model,
|
|
24834
|
+
editor.showComments,
|
|
24835
|
+
editor.showTrackedChanges
|
|
24836
|
+
]);
|
|
24004
24837
|
React.useEffect(
|
|
24005
24838
|
() => () => {
|
|
24006
24839
|
detachedThumbnailSurfaceRendererRef.current?.clear();
|
|
@@ -24025,6 +24858,125 @@ function useDocxPageThumbnails(editor, options = {}) {
|
|
|
24025
24858
|
},
|
|
24026
24859
|
[editor.documentTheme, pageSurfaceRegistry, thumbnailResolutionOptionsKey]
|
|
24027
24860
|
);
|
|
24861
|
+
const totalThumbnailPages = Math.max(1, editor.totalPages);
|
|
24862
|
+
const visibleThumbnailPageIndexes = React.useMemo(
|
|
24863
|
+
() => normalizeDocxThumbnailPageIndexes(
|
|
24864
|
+
options.renderWindow?.visiblePageIndexes,
|
|
24865
|
+
totalThumbnailPages
|
|
24866
|
+
),
|
|
24867
|
+
[options.renderWindow?.visiblePageIndexes, totalThumbnailPages]
|
|
24868
|
+
);
|
|
24869
|
+
const prefetchThumbnailPageIndexes = React.useMemo(
|
|
24870
|
+
() => normalizeDocxThumbnailPageIndexes(
|
|
24871
|
+
options.renderWindow?.prefetchPageIndexes,
|
|
24872
|
+
totalThumbnailPages
|
|
24873
|
+
),
|
|
24874
|
+
[options.renderWindow?.prefetchPageIndexes, totalThumbnailPages]
|
|
24875
|
+
);
|
|
24876
|
+
const visibleThumbnailPageIndexesKey = docxThumbnailPageIndexesKey(
|
|
24877
|
+
visibleThumbnailPageIndexes
|
|
24878
|
+
);
|
|
24879
|
+
const prefetchThumbnailPageIndexesKey = docxThumbnailPageIndexesKey(
|
|
24880
|
+
prefetchThumbnailPageIndexes
|
|
24881
|
+
);
|
|
24882
|
+
const visibleThumbnailPageIndexSet = React.useMemo(
|
|
24883
|
+
() => new Set(visibleThumbnailPageIndexes),
|
|
24884
|
+
[visibleThumbnailPageIndexesKey]
|
|
24885
|
+
);
|
|
24886
|
+
const thumbnailRenderPriorityForPage = React.useCallback(
|
|
24887
|
+
(pageIndex, fallbackPriority = DOCX_THUMBNAIL_RENDER_PRIORITY_ATTACHED) => visibleThumbnailPageIndexSet.has(pageIndex) ? DOCX_THUMBNAIL_RENDER_PRIORITY_VISIBLE : fallbackPriority,
|
|
24888
|
+
[visibleThumbnailPageIndexSet]
|
|
24889
|
+
);
|
|
24890
|
+
const renderPageThumbnailSurface = React.useCallback(
|
|
24891
|
+
async (pageIndex, renderOptions) => {
|
|
24892
|
+
const force = renderOptions?.force === true;
|
|
24893
|
+
const runSkipKey = thumbnailSkipKeyForPage(pageIndex);
|
|
24894
|
+
const detachedRenderKey = [
|
|
24895
|
+
runSkipKey ?? `load:${editor.documentLoadNonce}`,
|
|
24896
|
+
editor.showComments ? "comments:1" : "comments:0",
|
|
24897
|
+
editor.showTrackedChanges ? "tracked:1" : "tracked:0"
|
|
24898
|
+
].join("|");
|
|
24899
|
+
const thumbnailSnapshotEntry = pageSurfaceRegistry.pageThumbnailSnapshots.get(pageIndex);
|
|
24900
|
+
const fallbackSourceSize = resolveDocxViewerRegisteredPageSurfaceSize(
|
|
24901
|
+
pageSurfaceRegistry,
|
|
24902
|
+
pageIndex,
|
|
24903
|
+
fallbackLayout.pageWidthPx,
|
|
24904
|
+
fallbackLayout.pageHeightPx
|
|
24905
|
+
);
|
|
24906
|
+
const resolution = resolveDocxPageThumbnailResolution({
|
|
24907
|
+
sourceWidthPx: fallbackSourceSize.widthPx,
|
|
24908
|
+
sourceHeightPx: fallbackSourceSize.heightPx,
|
|
24909
|
+
resolution: options.resolution,
|
|
24910
|
+
maxWidthPx: options.maxWidthPx,
|
|
24911
|
+
maxHeightPx: options.maxHeightPx,
|
|
24912
|
+
pixelRatio: options.pixelRatio
|
|
24913
|
+
});
|
|
24914
|
+
const surfaceKey = runSkipKey === void 0 ? void 0 : `${runSkipKey}|${fallbackSourceSize.widthPx}x${fallbackSourceSize.heightPx}|${resolution.pixelWidthPx}x${resolution.pixelHeightPx}`;
|
|
24915
|
+
const surfaceCache = ensureThumbnailSurfaceCache();
|
|
24916
|
+
let surface = !force && surfaceKey !== void 0 ? surfaceCache.get(surfaceKey) : void 0;
|
|
24917
|
+
if (!surface) {
|
|
24918
|
+
const thumbnailSnapshot = thumbnailSnapshotEntry?.getSnapshot();
|
|
24919
|
+
if (thumbnailSnapshot) {
|
|
24920
|
+
surface = renderDocxThumbnailSnapshotSurface({
|
|
24921
|
+
snapshot: thumbnailSnapshot,
|
|
24922
|
+
widthPx: resolution.widthPx,
|
|
24923
|
+
heightPx: resolution.heightPx,
|
|
24924
|
+
pixelWidthPx: resolution.pixelWidthPx,
|
|
24925
|
+
pixelHeightPx: resolution.pixelHeightPx
|
|
24926
|
+
});
|
|
24927
|
+
if (surfaceKey !== void 0) {
|
|
24928
|
+
surfaceCache.set(surfaceKey, surface);
|
|
24929
|
+
}
|
|
24930
|
+
return { surface, resolution, runSkipKey };
|
|
24931
|
+
}
|
|
24932
|
+
let livePageElement = pageSurfaceRegistry.pageElements.get(pageIndex);
|
|
24933
|
+
if (!livePageElement || !livePageElement.isConnected) {
|
|
24934
|
+
if (!detachedThumbnailSurfaceRendererRef.current) {
|
|
24935
|
+
detachedThumbnailSurfaceRendererRef.current = new DocxDetachedThumbnailSurfaceRenderer();
|
|
24936
|
+
}
|
|
24937
|
+
livePageElement = await detachedThumbnailSurfaceRendererRef.current.renderPageSurface({
|
|
24938
|
+
editor,
|
|
24939
|
+
registry: pageSurfaceRegistry,
|
|
24940
|
+
pageIndex,
|
|
24941
|
+
renderKey: detachedRenderKey
|
|
24942
|
+
});
|
|
24943
|
+
}
|
|
24944
|
+
if (!livePageElement || !livePageElement.isConnected) {
|
|
24945
|
+
return void 0;
|
|
24946
|
+
}
|
|
24947
|
+
const sourceSize = resolveDocxViewerPageSurfaceSize(
|
|
24948
|
+
livePageElement,
|
|
24949
|
+
fallbackSourceSize.widthPx,
|
|
24950
|
+
fallbackSourceSize.heightPx
|
|
24951
|
+
);
|
|
24952
|
+
surface = await rasterizeDocxThumbnailSurface({
|
|
24953
|
+
pageElement: livePageElement,
|
|
24954
|
+
sourceWidthPx: sourceSize.widthPx,
|
|
24955
|
+
sourceHeightPx: sourceSize.heightPx,
|
|
24956
|
+
widthPx: resolution.widthPx,
|
|
24957
|
+
heightPx: resolution.heightPx,
|
|
24958
|
+
pixelWidthPx: resolution.pixelWidthPx,
|
|
24959
|
+
pixelHeightPx: resolution.pixelHeightPx
|
|
24960
|
+
});
|
|
24961
|
+
if (surfaceKey !== void 0) {
|
|
24962
|
+
surfaceCache.set(surfaceKey, surface);
|
|
24963
|
+
}
|
|
24964
|
+
}
|
|
24965
|
+
return { surface, resolution, runSkipKey };
|
|
24966
|
+
},
|
|
24967
|
+
[
|
|
24968
|
+
ensureThumbnailSurfaceCache,
|
|
24969
|
+
fallbackLayout.pageHeightPx,
|
|
24970
|
+
fallbackLayout.pageWidthPx,
|
|
24971
|
+
options.resolution,
|
|
24972
|
+
options.maxHeightPx,
|
|
24973
|
+
options.maxWidthPx,
|
|
24974
|
+
options.pixelRatio,
|
|
24975
|
+
pageSurfaceRegistry,
|
|
24976
|
+
thumbnailSkipKeyForPage,
|
|
24977
|
+
editor
|
|
24978
|
+
]
|
|
24979
|
+
);
|
|
24028
24980
|
const renderPageThumbnailToCanvas = React.useCallback(
|
|
24029
24981
|
async (pageIndex, canvas, renderOptions) => {
|
|
24030
24982
|
if (options.disabled) {
|
|
@@ -24041,74 +24993,19 @@ function useDocxPageThumbnails(editor, options = {}) {
|
|
|
24041
24993
|
updatePageThumbnailState(pageIndex, "ready");
|
|
24042
24994
|
return;
|
|
24043
24995
|
}
|
|
24044
|
-
|
|
24045
|
-
await ensureThumbnailRasterQueue().enqueue(targetCanvas, async () => {
|
|
24996
|
+
const paintIntoTarget = async () => {
|
|
24046
24997
|
if (requiresAttachedTarget && attachedCanvasByPageRef.current.get(pageIndex) !== targetCanvas) {
|
|
24047
24998
|
return;
|
|
24048
24999
|
}
|
|
24049
|
-
const runSkipKey = thumbnailSkipKeyForPage(pageIndex);
|
|
24050
|
-
const fallbackSourceSize = resolveDocxViewerRegisteredPageSurfaceSize(
|
|
24051
|
-
pageSurfaceRegistry,
|
|
24052
|
-
pageIndex,
|
|
24053
|
-
fallbackLayout.pageWidthPx,
|
|
24054
|
-
fallbackLayout.pageHeightPx
|
|
24055
|
-
);
|
|
24056
|
-
const resolution = resolveDocxPageThumbnailResolution({
|
|
24057
|
-
sourceWidthPx: fallbackSourceSize.widthPx,
|
|
24058
|
-
sourceHeightPx: fallbackSourceSize.heightPx,
|
|
24059
|
-
resolution: options.resolution,
|
|
24060
|
-
maxWidthPx: options.maxWidthPx,
|
|
24061
|
-
maxHeightPx: options.maxHeightPx,
|
|
24062
|
-
pixelRatio: options.pixelRatio
|
|
24063
|
-
});
|
|
24064
|
-
const surfaceKey = runSkipKey === void 0 ? void 0 : `${runSkipKey}|${fallbackSourceSize.widthPx}x${fallbackSourceSize.heightPx}|${resolution.pixelWidthPx}x${resolution.pixelHeightPx}`;
|
|
24065
|
-
const surfaceCache = ensureThumbnailSurfaceCache();
|
|
24066
25000
|
try {
|
|
24067
|
-
|
|
24068
|
-
|
|
24069
|
-
|
|
24070
|
-
|
|
24071
|
-
|
|
24072
|
-
|
|
24073
|
-
if (!detachedThumbnailSurfaceRendererRef.current) {
|
|
24074
|
-
detachedThumbnailSurfaceRendererRef.current = new DocxDetachedThumbnailSurfaceRenderer();
|
|
24075
|
-
}
|
|
24076
|
-
livePageElement = await detachedThumbnailSurfaceRendererRef.current.renderPageSurface(
|
|
24077
|
-
{
|
|
24078
|
-
editor,
|
|
24079
|
-
registry: pageSurfaceRegistry,
|
|
24080
|
-
pageIndex
|
|
24081
|
-
}
|
|
24082
|
-
);
|
|
24083
|
-
renderedDetachedSurface = true;
|
|
24084
|
-
}
|
|
24085
|
-
if (!livePageElement || !livePageElement.isConnected) {
|
|
24086
|
-
updatePageThumbnailState(pageIndex, "unavailable");
|
|
24087
|
-
return;
|
|
24088
|
-
}
|
|
24089
|
-
const sourceSize = resolveDocxViewerPageSurfaceSize(
|
|
24090
|
-
livePageElement,
|
|
24091
|
-
fallbackSourceSize.widthPx,
|
|
24092
|
-
fallbackSourceSize.heightPx
|
|
24093
|
-
);
|
|
24094
|
-
surface = await rasterizeDocxThumbnailSurface({
|
|
24095
|
-
pageElement: livePageElement,
|
|
24096
|
-
sourceWidthPx: sourceSize.widthPx,
|
|
24097
|
-
sourceHeightPx: sourceSize.heightPx,
|
|
24098
|
-
widthPx: resolution.widthPx,
|
|
24099
|
-
heightPx: resolution.heightPx,
|
|
24100
|
-
pixelWidthPx: resolution.pixelWidthPx,
|
|
24101
|
-
pixelHeightPx: resolution.pixelHeightPx
|
|
24102
|
-
});
|
|
24103
|
-
if (surfaceKey !== void 0) {
|
|
24104
|
-
surfaceCache.set(surfaceKey, surface);
|
|
24105
|
-
}
|
|
24106
|
-
} finally {
|
|
24107
|
-
if (renderedDetachedSurface) {
|
|
24108
|
-
detachedThumbnailSurfaceRendererRef.current?.clear();
|
|
24109
|
-
}
|
|
24110
|
-
}
|
|
25001
|
+
const rendered = await renderPageThumbnailSurface(pageIndex, {
|
|
25002
|
+
force
|
|
25003
|
+
});
|
|
25004
|
+
if (!rendered) {
|
|
25005
|
+
updatePageThumbnailState(pageIndex, "unavailable");
|
|
25006
|
+
return;
|
|
24111
25007
|
}
|
|
25008
|
+
const { surface, resolution, runSkipKey } = rendered;
|
|
24112
25009
|
blitDocxThumbnailSurface(surface, targetCanvas, resolution);
|
|
24113
25010
|
if (runSkipKey !== void 0) {
|
|
24114
25011
|
lastPaintedThumbnailKeyByCanvasRef.current.set(
|
|
@@ -24124,33 +25021,116 @@ function useDocxPageThumbnails(editor, options = {}) {
|
|
|
24124
25021
|
error instanceof Error ? error : new Error("Failed to render DOCX page thumbnail.")
|
|
24125
25022
|
);
|
|
24126
25023
|
}
|
|
24127
|
-
}
|
|
25024
|
+
};
|
|
25025
|
+
updatePageThumbnailState(pageIndex, "rendering");
|
|
25026
|
+
if (pageSurfaceRegistry.pageThumbnailSnapshots.has(pageIndex)) {
|
|
25027
|
+
await paintIntoTarget();
|
|
25028
|
+
return;
|
|
25029
|
+
}
|
|
25030
|
+
await ensureThumbnailRasterQueue().enqueue(
|
|
25031
|
+
thumbnailQueueKeyForCanvas(targetCanvas),
|
|
25032
|
+
paintIntoTarget,
|
|
25033
|
+
{
|
|
25034
|
+
priority: renderOptions?.priority ?? thumbnailRenderPriorityForPage(
|
|
25035
|
+
pageIndex,
|
|
25036
|
+
DOCX_THUMBNAIL_RENDER_PRIORITY_ATTACHED
|
|
25037
|
+
)
|
|
25038
|
+
}
|
|
25039
|
+
);
|
|
24128
25040
|
},
|
|
24129
25041
|
[
|
|
24130
25042
|
ensureThumbnailRasterQueue,
|
|
24131
|
-
ensureThumbnailSurfaceCache,
|
|
24132
|
-
fallbackLayout.pageHeightPx,
|
|
24133
|
-
fallbackLayout.pageWidthPx,
|
|
24134
25043
|
options.disabled,
|
|
24135
|
-
options.resolution,
|
|
24136
|
-
options.maxHeightPx,
|
|
24137
|
-
options.maxWidthPx,
|
|
24138
|
-
options.pixelRatio,
|
|
24139
25044
|
pageSurfaceRegistry,
|
|
25045
|
+
renderPageThumbnailSurface,
|
|
24140
25046
|
thumbnailSkipKeyForPage,
|
|
24141
|
-
|
|
24142
|
-
|
|
25047
|
+
thumbnailQueueKeyForCanvas,
|
|
25048
|
+
thumbnailRenderPriorityForPage,
|
|
25049
|
+
updatePageThumbnailState
|
|
24143
25050
|
]
|
|
24144
25051
|
);
|
|
25052
|
+
const prefetchPageThumbnailSurface = React.useCallback(
|
|
25053
|
+
async (pageIndex) => {
|
|
25054
|
+
if (options.disabled) {
|
|
25055
|
+
return;
|
|
25056
|
+
}
|
|
25057
|
+
const queueKey = docxThumbnailPrefetchQueueKey(pageIndex);
|
|
25058
|
+
queuedPrefetchThumbnailKeysRef.current.add(queueKey);
|
|
25059
|
+
await ensureThumbnailRasterQueue().enqueue(
|
|
25060
|
+
queueKey,
|
|
25061
|
+
async () => {
|
|
25062
|
+
try {
|
|
25063
|
+
await renderPageThumbnailSurface(pageIndex);
|
|
25064
|
+
} catch {
|
|
25065
|
+
}
|
|
25066
|
+
},
|
|
25067
|
+
{ priority: DOCX_THUMBNAIL_RENDER_PRIORITY_PREFETCH }
|
|
25068
|
+
);
|
|
25069
|
+
queuedPrefetchThumbnailKeysRef.current.delete(queueKey);
|
|
25070
|
+
},
|
|
25071
|
+
[ensureThumbnailRasterQueue, options.disabled, renderPageThumbnailSurface]
|
|
25072
|
+
);
|
|
24145
25073
|
const requestAttachedThumbnailRenders = React.useCallback(
|
|
24146
25074
|
async (renderOptions) => {
|
|
24147
|
-
const
|
|
24148
|
-
(
|
|
25075
|
+
const attachedPageIndexes = [
|
|
25076
|
+
...attachedCanvasByPageRef.current.keys()
|
|
25077
|
+
].sort((leftPageIndex, rightPageIndex) => {
|
|
25078
|
+
const leftPriority = thumbnailRenderPriorityForPage(leftPageIndex);
|
|
25079
|
+
const rightPriority = thumbnailRenderPriorityForPage(rightPageIndex);
|
|
25080
|
+
return leftPriority - rightPriority || leftPageIndex - rightPageIndex;
|
|
25081
|
+
});
|
|
25082
|
+
const tasks = attachedPageIndexes.map(
|
|
25083
|
+
(pageIndex) => renderPageThumbnailToCanvas(pageIndex, void 0, {
|
|
25084
|
+
...renderOptions,
|
|
25085
|
+
priority: thumbnailRenderPriorityForPage(pageIndex)
|
|
25086
|
+
})
|
|
24149
25087
|
);
|
|
24150
25088
|
await Promise.all(tasks);
|
|
24151
25089
|
},
|
|
24152
|
-
[renderPageThumbnailToCanvas]
|
|
25090
|
+
[renderPageThumbnailToCanvas, thumbnailRenderPriorityForPage]
|
|
24153
25091
|
);
|
|
25092
|
+
const requestPrefetchThumbnailRenders = React.useCallback(async () => {
|
|
25093
|
+
if (options.disabled) {
|
|
25094
|
+
queuedPrefetchThumbnailKeysRef.current.forEach((queueKey) => {
|
|
25095
|
+
thumbnailRasterQueueRef.current?.cancel(queueKey);
|
|
25096
|
+
});
|
|
25097
|
+
queuedPrefetchThumbnailKeysRef.current.clear();
|
|
25098
|
+
return;
|
|
25099
|
+
}
|
|
25100
|
+
const requestedPrefetchKeys = new Set(
|
|
25101
|
+
prefetchThumbnailPageIndexes.map(
|
|
25102
|
+
(pageIndex) => docxThumbnailPrefetchQueueKey(pageIndex)
|
|
25103
|
+
)
|
|
25104
|
+
);
|
|
25105
|
+
queuedPrefetchThumbnailKeysRef.current.forEach((queueKey) => {
|
|
25106
|
+
if (!requestedPrefetchKeys.has(queueKey)) {
|
|
25107
|
+
thumbnailRasterQueueRef.current?.cancel(queueKey);
|
|
25108
|
+
queuedPrefetchThumbnailKeysRef.current.delete(queueKey);
|
|
25109
|
+
}
|
|
25110
|
+
});
|
|
25111
|
+
const tasks = prefetchThumbnailPageIndexes.map(
|
|
25112
|
+
(pageIndex) => prefetchPageThumbnailSurface(pageIndex)
|
|
25113
|
+
);
|
|
25114
|
+
await Promise.all(tasks);
|
|
25115
|
+
}, [
|
|
25116
|
+
options.disabled,
|
|
25117
|
+
prefetchPageThumbnailSurface,
|
|
25118
|
+
prefetchThumbnailPageIndexes
|
|
25119
|
+
]);
|
|
25120
|
+
React.useEffect(() => {
|
|
25121
|
+
void requestPrefetchThumbnailRenders();
|
|
25122
|
+
}, [
|
|
25123
|
+
editor.documentLoadNonce,
|
|
25124
|
+
editor.documentTheme,
|
|
25125
|
+
editor.model,
|
|
25126
|
+
mountedPageElements,
|
|
25127
|
+
options.maxHeightPx,
|
|
25128
|
+
options.maxWidthPx,
|
|
25129
|
+
options.pixelRatio,
|
|
25130
|
+
options.resolution,
|
|
25131
|
+
prefetchThumbnailPageIndexesKey,
|
|
25132
|
+
requestPrefetchThumbnailRenders
|
|
25133
|
+
]);
|
|
24154
25134
|
const rerenderAttachedThumbnails = React.useCallback(
|
|
24155
25135
|
async () => requestAttachedThumbnailRenders({ force: true }),
|
|
24156
25136
|
[requestAttachedThumbnailRenders]
|
|
@@ -24179,7 +25159,7 @@ function useDocxPageThumbnails(editor, options = {}) {
|
|
|
24179
25159
|
options.pixelRatio,
|
|
24180
25160
|
requestAttachedThumbnailRenders
|
|
24181
25161
|
]);
|
|
24182
|
-
const
|
|
25162
|
+
const thumbnailGeometryItems = React.useMemo(() => {
|
|
24183
25163
|
const totalPages = Math.max(1, editor.totalPages);
|
|
24184
25164
|
return Array.from({ length: totalPages }, (_, pageIndex) => {
|
|
24185
25165
|
const pageElement = mountedPageElements.get(pageIndex);
|
|
@@ -24201,15 +25181,14 @@ function useDocxPageThumbnails(editor, options = {}) {
|
|
|
24201
25181
|
maxHeightPx: options.maxHeightPx,
|
|
24202
25182
|
pixelRatio: options.pixelRatio
|
|
24203
25183
|
});
|
|
24204
|
-
const state = pageThumbnailStates.get(pageIndex);
|
|
24205
25184
|
return {
|
|
25185
|
+
// Internal: drives the default status below; stripped before exposure.
|
|
25186
|
+
hasElement: Boolean(pageElement),
|
|
24206
25187
|
pageIndex,
|
|
24207
25188
|
pageNumber: pageIndex + 1,
|
|
24208
25189
|
sourceWidthPx: sourceSize.widthPx,
|
|
24209
25190
|
sourceHeightPx: sourceSize.heightPx,
|
|
24210
25191
|
isMounted: Boolean(pageElement && pageElement.isConnected),
|
|
24211
|
-
status: state?.status ?? (pageElement ? "idle" : "unavailable"),
|
|
24212
|
-
error: state?.error,
|
|
24213
25192
|
paint: (canvas) => {
|
|
24214
25193
|
if (!canvas || options.disabled) {
|
|
24215
25194
|
return false;
|
|
@@ -24226,7 +25205,9 @@ function useDocxPageThumbnails(editor, options = {}) {
|
|
|
24226
25205
|
}
|
|
24227
25206
|
const previousCanvas = attachedCanvasByPageRef.current.get(pageIndex);
|
|
24228
25207
|
if (previousCanvas) {
|
|
24229
|
-
thumbnailRasterQueueRef.current?.cancel(
|
|
25208
|
+
thumbnailRasterQueueRef.current?.cancel(
|
|
25209
|
+
thumbnailQueueKeyForCanvas(previousCanvas)
|
|
25210
|
+
);
|
|
24230
25211
|
}
|
|
24231
25212
|
attachedCanvasByPageRef.current.delete(pageIndex);
|
|
24232
25213
|
};
|
|
@@ -24262,8 +25243,20 @@ function useDocxPageThumbnails(editor, options = {}) {
|
|
|
24262
25243
|
options.maxWidthPx,
|
|
24263
25244
|
options.pixelRatio,
|
|
24264
25245
|
pageSurfaceRegistry,
|
|
24265
|
-
|
|
25246
|
+
thumbnailQueueKeyForCanvas
|
|
24266
25247
|
]);
|
|
25248
|
+
const thumbnails = React.useMemo(
|
|
25249
|
+
() => thumbnailGeometryItems.map((geometryItem) => {
|
|
25250
|
+
const { hasElement, ...item } = geometryItem;
|
|
25251
|
+
const state = pageThumbnailStates.get(item.pageIndex);
|
|
25252
|
+
return {
|
|
25253
|
+
...item,
|
|
25254
|
+
status: state?.status ?? (hasElement ? "idle" : "unavailable"),
|
|
25255
|
+
error: state?.error
|
|
25256
|
+
};
|
|
25257
|
+
}),
|
|
25258
|
+
[thumbnailGeometryItems, pageThumbnailStates]
|
|
25259
|
+
);
|
|
24267
25260
|
const paintThumbnail = React.useCallback(
|
|
24268
25261
|
(pageIndex, canvas) => {
|
|
24269
25262
|
if (!canvas || options.disabled) {
|
|
@@ -26647,15 +27640,39 @@ function DocxEditorViewer({
|
|
|
26647
27640
|
}),
|
|
26648
27641
|
[documentLayout, pageNodeSegmentsByPage, pageSectionInfoByIndex]
|
|
26649
27642
|
);
|
|
27643
|
+
const pageThumbnailSnapshotEntriesByPage = React.useMemo(
|
|
27644
|
+
() => buildDocxPageThumbnailRenderSnapshotEntries({
|
|
27645
|
+
model: editor.model,
|
|
27646
|
+
pageNodeSegmentsByPage,
|
|
27647
|
+
pageSectionInfoByIndex,
|
|
27648
|
+
contentKeysByPage: pageThumbnailContentKeysByPage,
|
|
27649
|
+
fallbackLayout: documentLayout,
|
|
27650
|
+
documentTheme: editor.documentTheme,
|
|
27651
|
+
docGridLinePitchPxByNodeIndex,
|
|
27652
|
+
numberingDefinitions: editor.model.metadata.numberingDefinitions
|
|
27653
|
+
}),
|
|
27654
|
+
[
|
|
27655
|
+
docGridLinePitchPxByNodeIndex,
|
|
27656
|
+
documentLayout,
|
|
27657
|
+
editor.documentTheme,
|
|
27658
|
+
editor.model,
|
|
27659
|
+
editor.model.metadata.numberingDefinitions,
|
|
27660
|
+
pageNodeSegmentsByPage,
|
|
27661
|
+
pageSectionInfoByIndex,
|
|
27662
|
+
pageThumbnailContentKeysByPage
|
|
27663
|
+
]
|
|
27664
|
+
);
|
|
26650
27665
|
React.useEffect(() => {
|
|
26651
27666
|
syncDocxViewerPageSurfaceContentKeys(
|
|
26652
27667
|
editor,
|
|
26653
27668
|
pageThumbnailContentKeysByPage,
|
|
26654
|
-
pageThumbnailSurfaceSizesByPage
|
|
27669
|
+
pageThumbnailSurfaceSizesByPage,
|
|
27670
|
+
pageThumbnailSnapshotEntriesByPage
|
|
26655
27671
|
);
|
|
26656
27672
|
}, [
|
|
26657
27673
|
pageSurfaceRegistryOwner,
|
|
26658
27674
|
pageThumbnailContentKeysByPage,
|
|
27675
|
+
pageThumbnailSnapshotEntriesByPage,
|
|
26659
27676
|
pageThumbnailSurfaceSizesByPage
|
|
26660
27677
|
]);
|
|
26661
27678
|
const resolveStyleRefFieldValueForPage = React.useMemo(() => {
|