@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.js
CHANGED
|
@@ -3160,6 +3160,14 @@ var THUMBNAIL_EXCLUDED_CLONE_SELECTOR = [
|
|
|
3160
3160
|
var THUMBNAIL_IMAGE_DOWNSCALE_MIN_DATA_URI_LENGTH = 32768;
|
|
3161
3161
|
var THUMBNAIL_IMAGE_DOWNSCALE_MAX_DIMENSION_PX = 512;
|
|
3162
3162
|
var THUMBNAIL_IMAGE_JPEG_QUALITY = 0.78;
|
|
3163
|
+
var THUMBNAIL_DIRECT_DEFAULT_FONT_FAMILY = "Calibri, Arial, sans-serif";
|
|
3164
|
+
var THUMBNAIL_DIRECT_DEFAULT_TEXT_COLOR = "#111827";
|
|
3165
|
+
var THUMBNAIL_DIRECT_TABLE_BORDER_COLOR = "#d1d5db";
|
|
3166
|
+
var THUMBNAIL_DIRECT_IMAGE_BACKGROUND = "#f3f4f6";
|
|
3167
|
+
var THUMBNAIL_DIRECT_MAX_ELEMENTS = 320;
|
|
3168
|
+
var THUMBNAIL_DIRECT_MAX_TEXT_CHARS = 640;
|
|
3169
|
+
var THUMBNAIL_DIRECT_MAX_LINES = 14;
|
|
3170
|
+
var THUMBNAIL_DIRECT_MAX_LAYOUT_LINES = 80;
|
|
3163
3171
|
function thumbnailSvgDataUri(svg) {
|
|
3164
3172
|
return `data:image/svg+xml;charset=utf-8,${encodeURIComponent(svg)}`;
|
|
3165
3173
|
}
|
|
@@ -3223,6 +3231,346 @@ function getDownscaledThumbnailImageDataUri(src) {
|
|
|
3223
3231
|
downscaledThumbnailImageCache.set(src, pending);
|
|
3224
3232
|
return pending;
|
|
3225
3233
|
}
|
|
3234
|
+
function directThumbnailPositivePx(value, fallback = 1) {
|
|
3235
|
+
return Number.isFinite(value) && value > 0 ? Math.max(1, Number(value)) : fallback;
|
|
3236
|
+
}
|
|
3237
|
+
function setCanvasFillStyle(context, color, fallback) {
|
|
3238
|
+
try {
|
|
3239
|
+
context.fillStyle = color || fallback;
|
|
3240
|
+
} catch {
|
|
3241
|
+
context.fillStyle = fallback;
|
|
3242
|
+
}
|
|
3243
|
+
}
|
|
3244
|
+
function setCanvasStrokeStyle(context, color, fallback) {
|
|
3245
|
+
try {
|
|
3246
|
+
context.strokeStyle = color || fallback;
|
|
3247
|
+
} catch {
|
|
3248
|
+
context.strokeStyle = fallback;
|
|
3249
|
+
}
|
|
3250
|
+
}
|
|
3251
|
+
function directThumbnailFont(run, fallbackFontSizePx) {
|
|
3252
|
+
const fontSizePx = Math.max(
|
|
3253
|
+
6,
|
|
3254
|
+
Math.min(
|
|
3255
|
+
36,
|
|
3256
|
+
Math.round(
|
|
3257
|
+
directThumbnailPositivePx(run?.fontSizePx, fallbackFontSizePx)
|
|
3258
|
+
)
|
|
3259
|
+
)
|
|
3260
|
+
);
|
|
3261
|
+
const fontStyle = run?.italic ? "italic " : "";
|
|
3262
|
+
const fontWeight = run?.bold ? "700 " : "";
|
|
3263
|
+
return `${fontStyle}${fontWeight}${fontSizePx}px ${run?.fontFamily || THUMBNAIL_DIRECT_DEFAULT_FONT_FAMILY}`;
|
|
3264
|
+
}
|
|
3265
|
+
var THUMBNAIL_DIRECT_TOKEN_REGEX = /(\r\n|\n|\t|[^\S\r\n\t]+|[^\s\r\n\t]+)/g;
|
|
3266
|
+
var THUMBNAIL_DIRECT_LEADING_WHITESPACE_REGEX = /^\s/;
|
|
3267
|
+
var THUMBNAIL_DIRECT_TEXT_MEASURE_CACHE_MAX_ENTRIES = 4096;
|
|
3268
|
+
var directThumbnailTextMeasureCache = /* @__PURE__ */ new Map();
|
|
3269
|
+
function measureDirectThumbnailToken(context, font, text) {
|
|
3270
|
+
if (!text) {
|
|
3271
|
+
return 0;
|
|
3272
|
+
}
|
|
3273
|
+
const cacheKey = `${font}\0${text}`;
|
|
3274
|
+
const cached = directThumbnailTextMeasureCache.get(cacheKey);
|
|
3275
|
+
if (cached !== void 0) {
|
|
3276
|
+
return cached;
|
|
3277
|
+
}
|
|
3278
|
+
const width = context.measureText(text).width;
|
|
3279
|
+
if (directThumbnailTextMeasureCache.size >= THUMBNAIL_DIRECT_TEXT_MEASURE_CACHE_MAX_ENTRIES) {
|
|
3280
|
+
const oldestKey = directThumbnailTextMeasureCache.keys().next().value;
|
|
3281
|
+
if (oldestKey !== void 0) {
|
|
3282
|
+
directThumbnailTextMeasureCache.delete(oldestKey);
|
|
3283
|
+
}
|
|
3284
|
+
}
|
|
3285
|
+
directThumbnailTextMeasureCache.set(cacheKey, width);
|
|
3286
|
+
return width;
|
|
3287
|
+
}
|
|
3288
|
+
function appendDirectThumbnailTextLine(lines, currentSegments, currentWidthPx) {
|
|
3289
|
+
const line = {
|
|
3290
|
+
segments: currentSegments,
|
|
3291
|
+
widthPx: currentWidthPx
|
|
3292
|
+
};
|
|
3293
|
+
lines.push(line);
|
|
3294
|
+
return line;
|
|
3295
|
+
}
|
|
3296
|
+
function layoutDirectThumbnailTextRuns(params) {
|
|
3297
|
+
const { context, runs, fallbackFontSizePx } = params;
|
|
3298
|
+
const widthPx = Math.max(1, params.widthPx);
|
|
3299
|
+
const maxLineCount = Math.max(
|
|
3300
|
+
1,
|
|
3301
|
+
Math.min(THUMBNAIL_DIRECT_MAX_LAYOUT_LINES, params.maxLineCount)
|
|
3302
|
+
);
|
|
3303
|
+
const lines = [];
|
|
3304
|
+
let currentSegments = [];
|
|
3305
|
+
let currentWidthPx = 0;
|
|
3306
|
+
let remainingChars = THUMBNAIL_DIRECT_MAX_TEXT_CHARS;
|
|
3307
|
+
const flushLine = () => {
|
|
3308
|
+
appendDirectThumbnailTextLine(lines, currentSegments, currentWidthPx);
|
|
3309
|
+
currentSegments = [];
|
|
3310
|
+
currentWidthPx = 0;
|
|
3311
|
+
};
|
|
3312
|
+
for (const run of runs) {
|
|
3313
|
+
if (lines.length >= maxLineCount || remainingChars <= 0) {
|
|
3314
|
+
break;
|
|
3315
|
+
}
|
|
3316
|
+
const text = run.text.slice(0, remainingChars);
|
|
3317
|
+
remainingChars -= text.length;
|
|
3318
|
+
const runFont = directThumbnailFont(run, fallbackFontSizePx);
|
|
3319
|
+
context.font = runFont;
|
|
3320
|
+
const tokens = text.match(THUMBNAIL_DIRECT_TOKEN_REGEX) ?? [];
|
|
3321
|
+
for (const token of tokens) {
|
|
3322
|
+
if (lines.length >= maxLineCount) {
|
|
3323
|
+
break;
|
|
3324
|
+
}
|
|
3325
|
+
if (token === "\n" || token === "\r\n") {
|
|
3326
|
+
flushLine();
|
|
3327
|
+
continue;
|
|
3328
|
+
}
|
|
3329
|
+
const drawableToken = token === " " ? " " : token;
|
|
3330
|
+
const tokenWidthPx = measureDirectThumbnailToken(
|
|
3331
|
+
context,
|
|
3332
|
+
runFont,
|
|
3333
|
+
drawableToken
|
|
3334
|
+
);
|
|
3335
|
+
const tokenIsWhitespace = THUMBNAIL_DIRECT_LEADING_WHITESPACE_REGEX.test(drawableToken);
|
|
3336
|
+
if (currentSegments.length > 0 && currentWidthPx + tokenWidthPx > widthPx && !tokenIsWhitespace) {
|
|
3337
|
+
flushLine();
|
|
3338
|
+
}
|
|
3339
|
+
if (currentSegments.length === 0 && tokenIsWhitespace) {
|
|
3340
|
+
continue;
|
|
3341
|
+
}
|
|
3342
|
+
currentSegments.push({
|
|
3343
|
+
run,
|
|
3344
|
+
text: drawableToken,
|
|
3345
|
+
widthPx: tokenWidthPx,
|
|
3346
|
+
font: runFont
|
|
3347
|
+
});
|
|
3348
|
+
currentWidthPx += tokenWidthPx;
|
|
3349
|
+
}
|
|
3350
|
+
}
|
|
3351
|
+
if (currentSegments.length > 0 || lines.length === 0) {
|
|
3352
|
+
flushLine();
|
|
3353
|
+
}
|
|
3354
|
+
return lines;
|
|
3355
|
+
}
|
|
3356
|
+
function directThumbnailAlignedX(params) {
|
|
3357
|
+
const { xPx, widthPx, lineWidthPx, align } = params;
|
|
3358
|
+
if (align === "center") {
|
|
3359
|
+
return xPx + Math.max(0, (widthPx - lineWidthPx) / 2);
|
|
3360
|
+
}
|
|
3361
|
+
if (align === "right") {
|
|
3362
|
+
return xPx + Math.max(0, widthPx - lineWidthPx);
|
|
3363
|
+
}
|
|
3364
|
+
return xPx;
|
|
3365
|
+
}
|
|
3366
|
+
function drawDirectThumbnailTextRuns(params) {
|
|
3367
|
+
const {
|
|
3368
|
+
context,
|
|
3369
|
+
runs,
|
|
3370
|
+
xPx,
|
|
3371
|
+
yPx,
|
|
3372
|
+
widthPx,
|
|
3373
|
+
heightPx,
|
|
3374
|
+
align,
|
|
3375
|
+
startLineIndex
|
|
3376
|
+
} = params;
|
|
3377
|
+
const safeWidthPx = Math.max(1, widthPx);
|
|
3378
|
+
const safeHeightPx = Math.max(1, heightPx);
|
|
3379
|
+
const fallbackFontSizePx = Math.max(
|
|
3380
|
+
7,
|
|
3381
|
+
Math.min(
|
|
3382
|
+
20,
|
|
3383
|
+
Math.round(
|
|
3384
|
+
runs.find((run) => Number.isFinite(run.fontSizePx))?.fontSizePx ?? 12
|
|
3385
|
+
)
|
|
3386
|
+
)
|
|
3387
|
+
);
|
|
3388
|
+
const lineHeightPx = Math.max(
|
|
3389
|
+
fallbackFontSizePx + 1,
|
|
3390
|
+
Math.round(params.lineHeightPx ?? fallbackFontSizePx * 1.25)
|
|
3391
|
+
);
|
|
3392
|
+
const skippedLineCount = Math.max(
|
|
3393
|
+
0,
|
|
3394
|
+
Math.min(THUMBNAIL_DIRECT_MAX_LAYOUT_LINES - 1, Math.trunc(startLineIndex ?? 0))
|
|
3395
|
+
);
|
|
3396
|
+
const visibleLineCount = Math.max(
|
|
3397
|
+
1,
|
|
3398
|
+
Math.min(
|
|
3399
|
+
THUMBNAIL_DIRECT_MAX_LINES,
|
|
3400
|
+
Math.ceil(safeHeightPx / Math.max(1, lineHeightPx)) + 1
|
|
3401
|
+
)
|
|
3402
|
+
);
|
|
3403
|
+
const lines = layoutDirectThumbnailTextRuns({
|
|
3404
|
+
context,
|
|
3405
|
+
runs,
|
|
3406
|
+
widthPx: safeWidthPx,
|
|
3407
|
+
fallbackFontSizePx,
|
|
3408
|
+
maxLineCount: skippedLineCount + visibleLineCount
|
|
3409
|
+
}).slice(skippedLineCount, skippedLineCount + visibleLineCount);
|
|
3410
|
+
context.save();
|
|
3411
|
+
context.beginPath();
|
|
3412
|
+
context.rect(xPx, yPx, safeWidthPx, safeHeightPx);
|
|
3413
|
+
context.clip();
|
|
3414
|
+
context.textBaseline = "alphabetic";
|
|
3415
|
+
let lastAppliedFont;
|
|
3416
|
+
lines.forEach((line, lineIndex) => {
|
|
3417
|
+
const lineTopPx = yPx + lineIndex * lineHeightPx;
|
|
3418
|
+
if (lineTopPx > yPx + safeHeightPx) {
|
|
3419
|
+
return;
|
|
3420
|
+
}
|
|
3421
|
+
let cursorXPx = directThumbnailAlignedX({
|
|
3422
|
+
xPx,
|
|
3423
|
+
widthPx: safeWidthPx,
|
|
3424
|
+
lineWidthPx: line.widthPx,
|
|
3425
|
+
align
|
|
3426
|
+
});
|
|
3427
|
+
const baselineYPx = lineTopPx + Math.max(1, Math.round(lineHeightPx * 0.78));
|
|
3428
|
+
line.segments.forEach((segment) => {
|
|
3429
|
+
const segmentWidthPx = segment.widthPx;
|
|
3430
|
+
if (segment.run.backgroundColor) {
|
|
3431
|
+
setCanvasFillStyle(context, segment.run.backgroundColor, "transparent");
|
|
3432
|
+
context.fillRect(cursorXPx, lineTopPx + 1, segmentWidthPx, lineHeightPx);
|
|
3433
|
+
}
|
|
3434
|
+
if (segment.font !== lastAppliedFont) {
|
|
3435
|
+
context.font = segment.font;
|
|
3436
|
+
lastAppliedFont = segment.font;
|
|
3437
|
+
}
|
|
3438
|
+
setCanvasFillStyle(
|
|
3439
|
+
context,
|
|
3440
|
+
segment.run.color,
|
|
3441
|
+
THUMBNAIL_DIRECT_DEFAULT_TEXT_COLOR
|
|
3442
|
+
);
|
|
3443
|
+
context.fillText(segment.text, cursorXPx, baselineYPx);
|
|
3444
|
+
cursorXPx += segmentWidthPx;
|
|
3445
|
+
});
|
|
3446
|
+
});
|
|
3447
|
+
context.restore();
|
|
3448
|
+
}
|
|
3449
|
+
function drawDirectThumbnailParagraph(context, paragraph) {
|
|
3450
|
+
const xPx = Math.round(paragraph.xPx);
|
|
3451
|
+
const yPx = Math.round(paragraph.yPx);
|
|
3452
|
+
const widthPx = Math.max(1, Math.round(paragraph.widthPx));
|
|
3453
|
+
const heightPx = Math.max(1, Math.round(paragraph.heightPx));
|
|
3454
|
+
if (paragraph.backgroundColor) {
|
|
3455
|
+
setCanvasFillStyle(context, paragraph.backgroundColor, "transparent");
|
|
3456
|
+
context.fillRect(xPx, yPx, widthPx, heightPx);
|
|
3457
|
+
}
|
|
3458
|
+
drawDirectThumbnailTextRuns({
|
|
3459
|
+
context,
|
|
3460
|
+
runs: paragraph.runs,
|
|
3461
|
+
xPx: xPx + 1,
|
|
3462
|
+
yPx,
|
|
3463
|
+
widthPx: Math.max(1, widthPx - 2),
|
|
3464
|
+
heightPx,
|
|
3465
|
+
align: paragraph.align,
|
|
3466
|
+
lineHeightPx: paragraph.lineHeightPx,
|
|
3467
|
+
startLineIndex: paragraph.startLineIndex
|
|
3468
|
+
});
|
|
3469
|
+
}
|
|
3470
|
+
function drawDirectThumbnailImagePlaceholder(context, image, hairlineSourcePx) {
|
|
3471
|
+
const xPx = Math.round(image.xPx);
|
|
3472
|
+
const yPx = Math.round(image.yPx);
|
|
3473
|
+
const widthPx = Math.max(1, Math.round(image.widthPx));
|
|
3474
|
+
const heightPx = Math.max(1, Math.round(image.heightPx));
|
|
3475
|
+
setCanvasFillStyle(
|
|
3476
|
+
context,
|
|
3477
|
+
image.backgroundColor,
|
|
3478
|
+
THUMBNAIL_DIRECT_IMAGE_BACKGROUND
|
|
3479
|
+
);
|
|
3480
|
+
context.fillRect(xPx, yPx, widthPx, heightPx);
|
|
3481
|
+
setCanvasStrokeStyle(
|
|
3482
|
+
context,
|
|
3483
|
+
image.borderColor,
|
|
3484
|
+
THUMBNAIL_DIRECT_TABLE_BORDER_COLOR
|
|
3485
|
+
);
|
|
3486
|
+
context.lineWidth = hairlineSourcePx;
|
|
3487
|
+
context.strokeRect(xPx, yPx, widthPx, heightPx);
|
|
3488
|
+
}
|
|
3489
|
+
function drawDirectThumbnailTable(context, table, hairlineSourcePx) {
|
|
3490
|
+
const tableXPx = Math.round(table.xPx);
|
|
3491
|
+
const tableYPx = Math.round(table.yPx);
|
|
3492
|
+
const tableWidthPx = Math.max(1, Math.round(table.widthPx));
|
|
3493
|
+
const tableHeightPx = Math.max(1, Math.round(table.heightPx));
|
|
3494
|
+
context.save();
|
|
3495
|
+
context.beginPath();
|
|
3496
|
+
context.rect(tableXPx, tableYPx, tableWidthPx, tableHeightPx);
|
|
3497
|
+
context.clip();
|
|
3498
|
+
setCanvasStrokeStyle(
|
|
3499
|
+
context,
|
|
3500
|
+
table.borderColor,
|
|
3501
|
+
THUMBNAIL_DIRECT_TABLE_BORDER_COLOR
|
|
3502
|
+
);
|
|
3503
|
+
context.lineWidth = hairlineSourcePx;
|
|
3504
|
+
table.cells.forEach((cell) => {
|
|
3505
|
+
const xPx = tableXPx + Math.round(cell.xPx);
|
|
3506
|
+
const yPx = tableYPx + Math.round(cell.yPx);
|
|
3507
|
+
const widthPx = Math.max(1, Math.round(cell.widthPx));
|
|
3508
|
+
const heightPx = Math.max(1, Math.round(cell.heightPx));
|
|
3509
|
+
if (cell.backgroundColor) {
|
|
3510
|
+
setCanvasFillStyle(context, cell.backgroundColor, "transparent");
|
|
3511
|
+
context.fillRect(xPx, yPx, widthPx, heightPx);
|
|
3512
|
+
}
|
|
3513
|
+
context.strokeRect(xPx, yPx, widthPx, heightPx);
|
|
3514
|
+
if (cell.runs?.length) {
|
|
3515
|
+
drawDirectThumbnailTextRuns({
|
|
3516
|
+
context,
|
|
3517
|
+
runs: cell.runs,
|
|
3518
|
+
xPx: xPx + 3,
|
|
3519
|
+
yPx: yPx + 2,
|
|
3520
|
+
widthPx: Math.max(1, widthPx - 6),
|
|
3521
|
+
heightPx: Math.max(1, heightPx - 4),
|
|
3522
|
+
lineHeightPx: 13
|
|
3523
|
+
});
|
|
3524
|
+
}
|
|
3525
|
+
});
|
|
3526
|
+
context.restore();
|
|
3527
|
+
}
|
|
3528
|
+
function renderDocxThumbnailSnapshotSurface(params) {
|
|
3529
|
+
if (typeof document === "undefined") {
|
|
3530
|
+
throw new Error("DOCX thumbnails require a browser environment.");
|
|
3531
|
+
}
|
|
3532
|
+
const sourceWidthPx = directThumbnailPositivePx(params.snapshot.sourceWidthPx);
|
|
3533
|
+
const sourceHeightPx = directThumbnailPositivePx(params.snapshot.sourceHeightPx);
|
|
3534
|
+
const pixelWidthPx = Math.max(1, Math.round(params.pixelWidthPx));
|
|
3535
|
+
const pixelHeightPx = Math.max(1, Math.round(params.pixelHeightPx));
|
|
3536
|
+
const surface = document.createElement("canvas");
|
|
3537
|
+
surface.width = pixelWidthPx;
|
|
3538
|
+
surface.height = pixelHeightPx;
|
|
3539
|
+
const context = surface.getContext("2d");
|
|
3540
|
+
if (!context) {
|
|
3541
|
+
throw new Error("2D canvas context is unavailable for DOCX thumbnails.");
|
|
3542
|
+
}
|
|
3543
|
+
const scaleX = pixelWidthPx / sourceWidthPx;
|
|
3544
|
+
const scaleY = pixelHeightPx / sourceHeightPx;
|
|
3545
|
+
const hairlineSourcePx = Math.max(0.75, 1 / Math.max(scaleX, scaleY));
|
|
3546
|
+
context.setTransform(scaleX, 0, 0, scaleY, 0, 0);
|
|
3547
|
+
context.imageSmoothingEnabled = true;
|
|
3548
|
+
context.imageSmoothingQuality = "high";
|
|
3549
|
+
setCanvasFillStyle(
|
|
3550
|
+
context,
|
|
3551
|
+
params.snapshot.pageBackgroundColor,
|
|
3552
|
+
"#ffffff"
|
|
3553
|
+
);
|
|
3554
|
+
context.fillRect(0, 0, sourceWidthPx, sourceHeightPx);
|
|
3555
|
+
params.snapshot.elements.slice(0, THUMBNAIL_DIRECT_MAX_ELEMENTS).forEach((element) => {
|
|
3556
|
+
switch (element.kind) {
|
|
3557
|
+
case "paragraph":
|
|
3558
|
+
drawDirectThumbnailParagraph(context, element);
|
|
3559
|
+
break;
|
|
3560
|
+
case "image-placeholder":
|
|
3561
|
+
drawDirectThumbnailImagePlaceholder(
|
|
3562
|
+
context,
|
|
3563
|
+
element,
|
|
3564
|
+
hairlineSourcePx
|
|
3565
|
+
);
|
|
3566
|
+
break;
|
|
3567
|
+
case "table":
|
|
3568
|
+
drawDirectThumbnailTable(context, element, hairlineSourcePx);
|
|
3569
|
+
break;
|
|
3570
|
+
}
|
|
3571
|
+
});
|
|
3572
|
+
return surface;
|
|
3573
|
+
}
|
|
3226
3574
|
async function buildDocxThumbnailSvgMarkup(params) {
|
|
3227
3575
|
const { pageElement, sourceWidthPx, sourceHeightPx, widthPx, heightPx } = params;
|
|
3228
3576
|
const clone = pageElement.cloneNode(true);
|
|
@@ -3284,16 +3632,33 @@ async function rasterizeDocxThumbnailSurface(params) {
|
|
|
3284
3632
|
return surface;
|
|
3285
3633
|
}
|
|
3286
3634
|
function blitDocxThumbnailSurface(surface, canvas, resolution) {
|
|
3287
|
-
|
|
3288
|
-
|
|
3289
|
-
|
|
3290
|
-
|
|
3635
|
+
const pixelWidth = Math.max(1, Math.round(resolution.pixelWidthPx));
|
|
3636
|
+
const pixelHeight = Math.max(1, Math.round(resolution.pixelHeightPx));
|
|
3637
|
+
const cssWidth = `${Math.max(1, Math.round(resolution.widthPx))}px`;
|
|
3638
|
+
const cssHeight = `${Math.max(1, Math.round(resolution.heightPx))}px`;
|
|
3639
|
+
let bufferResized = false;
|
|
3640
|
+
if (canvas.width !== pixelWidth) {
|
|
3641
|
+
canvas.width = pixelWidth;
|
|
3642
|
+
bufferResized = true;
|
|
3643
|
+
}
|
|
3644
|
+
if (canvas.height !== pixelHeight) {
|
|
3645
|
+
canvas.height = pixelHeight;
|
|
3646
|
+
bufferResized = true;
|
|
3647
|
+
}
|
|
3648
|
+
if (canvas.style.width !== cssWidth) {
|
|
3649
|
+
canvas.style.width = cssWidth;
|
|
3650
|
+
}
|
|
3651
|
+
if (canvas.style.height !== cssHeight) {
|
|
3652
|
+
canvas.style.height = cssHeight;
|
|
3653
|
+
}
|
|
3291
3654
|
const context = canvas.getContext("2d");
|
|
3292
3655
|
if (!context) {
|
|
3293
3656
|
throw new Error("2D canvas context is unavailable for DOCX thumbnails.");
|
|
3294
3657
|
}
|
|
3295
3658
|
context.setTransform(1, 0, 0, 1, 0, 0);
|
|
3296
|
-
|
|
3659
|
+
if (!bufferResized) {
|
|
3660
|
+
context.clearRect(0, 0, canvas.width, canvas.height);
|
|
3661
|
+
}
|
|
3297
3662
|
context.drawImage(surface, 0, 0, canvas.width, canvas.height);
|
|
3298
3663
|
}
|
|
3299
3664
|
var DocxThumbnailSurfaceCache = class {
|
|
@@ -3368,6 +3733,7 @@ var SerialIdleTaskQueue = class {
|
|
|
3368
3733
|
now;
|
|
3369
3734
|
pumpScheduled = false;
|
|
3370
3735
|
running = false;
|
|
3736
|
+
nextSequence = 0;
|
|
3371
3737
|
constructor(options) {
|
|
3372
3738
|
this.scheduleTask = options?.scheduleTask ?? defaultScheduleTask;
|
|
3373
3739
|
this.scheduleDelayed = options?.scheduleDelayed ?? defaultScheduleDelayed;
|
|
@@ -3377,14 +3743,23 @@ var SerialIdleTaskQueue = class {
|
|
|
3377
3743
|
get pendingCount() {
|
|
3378
3744
|
return this.pending.length;
|
|
3379
3745
|
}
|
|
3380
|
-
enqueue(key, run) {
|
|
3746
|
+
enqueue(key, run, options) {
|
|
3747
|
+
const priority = Number.isFinite(options?.priority) ? Number(options?.priority) : 0;
|
|
3381
3748
|
return new Promise((resolve) => {
|
|
3382
3749
|
const existing = this.pending.find((entry) => entry.key === key);
|
|
3383
3750
|
if (existing) {
|
|
3384
3751
|
existing.run = run;
|
|
3385
3752
|
existing.resolvers.push(resolve);
|
|
3753
|
+
existing.priority = Math.min(existing.priority, priority);
|
|
3386
3754
|
} else {
|
|
3387
|
-
this.pending.push({
|
|
3755
|
+
this.pending.push({
|
|
3756
|
+
key,
|
|
3757
|
+
run,
|
|
3758
|
+
resolvers: [resolve],
|
|
3759
|
+
priority,
|
|
3760
|
+
sequence: this.nextSequence
|
|
3761
|
+
});
|
|
3762
|
+
this.nextSequence += 1;
|
|
3388
3763
|
}
|
|
3389
3764
|
this.schedulePump();
|
|
3390
3765
|
});
|
|
@@ -3429,6 +3804,8 @@ var SerialIdleTaskQueue = class {
|
|
|
3429
3804
|
}
|
|
3430
3805
|
const now = this.now();
|
|
3431
3806
|
let earliestWaitMs;
|
|
3807
|
+
let bestIndex = -1;
|
|
3808
|
+
let bestEntry;
|
|
3432
3809
|
for (let index = 0; index < this.pending.length; index += 1) {
|
|
3433
3810
|
const candidate = this.pending[index];
|
|
3434
3811
|
if (!candidate) {
|
|
@@ -3437,11 +3814,18 @@ var SerialIdleTaskQueue = class {
|
|
|
3437
3814
|
const lastRunAt = this.lastRunAtByKey.get(candidate.key);
|
|
3438
3815
|
const waitMs = lastRunAt === void 0 ? 0 : lastRunAt + this.minTaskIntervalMs - now;
|
|
3439
3816
|
if (waitMs <= 0) {
|
|
3440
|
-
|
|
3441
|
-
|
|
3817
|
+
if (!bestEntry || candidate.priority < bestEntry.priority || candidate.priority === bestEntry.priority && candidate.sequence < bestEntry.sequence) {
|
|
3818
|
+
bestEntry = candidate;
|
|
3819
|
+
bestIndex = index;
|
|
3820
|
+
}
|
|
3821
|
+
continue;
|
|
3442
3822
|
}
|
|
3443
3823
|
earliestWaitMs = earliestWaitMs === void 0 ? waitMs : Math.min(earliestWaitMs, waitMs);
|
|
3444
3824
|
}
|
|
3825
|
+
if (bestEntry && bestIndex >= 0) {
|
|
3826
|
+
this.pending.splice(bestIndex, 1);
|
|
3827
|
+
return { entry: bestEntry };
|
|
3828
|
+
}
|
|
3445
3829
|
return earliestWaitMs === void 0 ? void 0 : { retryDelayMs: earliestWaitMs };
|
|
3446
3830
|
}
|
|
3447
3831
|
async runNext() {
|
|
@@ -22440,13 +22824,14 @@ function ensureDocxViewerPageSurfaceRegistry(editor) {
|
|
|
22440
22824
|
pageElements: /* @__PURE__ */ new Map(),
|
|
22441
22825
|
pageContentKeys: /* @__PURE__ */ new Map(),
|
|
22442
22826
|
pageSizes: /* @__PURE__ */ new Map(),
|
|
22827
|
+
pageThumbnailSnapshots: /* @__PURE__ */ new Map(),
|
|
22443
22828
|
listeners: /* @__PURE__ */ new Set()
|
|
22444
22829
|
};
|
|
22445
22830
|
docxViewerPageSurfaceRegistryByEditor.set(owner, registry);
|
|
22446
22831
|
}
|
|
22447
22832
|
return registry;
|
|
22448
22833
|
}
|
|
22449
|
-
function syncDocxViewerPageSurfaceContentKeys(editor, contentKeysByPage, pageSizesByPage = []) {
|
|
22834
|
+
function syncDocxViewerPageSurfaceContentKeys(editor, contentKeysByPage, pageSizesByPage = [], thumbnailSnapshotsByPage = []) {
|
|
22450
22835
|
const registry = ensureDocxViewerPageSurfaceRegistry(editor);
|
|
22451
22836
|
let changed = false;
|
|
22452
22837
|
contentKeysByPage.forEach((contentKey, pageIndex) => {
|
|
@@ -22464,6 +22849,20 @@ function syncDocxViewerPageSurfaceContentKeys(editor, contentKeysByPage, pageSiz
|
|
|
22464
22849
|
changed = true;
|
|
22465
22850
|
}
|
|
22466
22851
|
});
|
|
22852
|
+
thumbnailSnapshotsByPage.forEach((snapshot, pageIndex) => {
|
|
22853
|
+
const previous = registry.pageThumbnailSnapshots.get(pageIndex);
|
|
22854
|
+
if (!snapshot) {
|
|
22855
|
+
if (previous) {
|
|
22856
|
+
registry.pageThumbnailSnapshots.delete(pageIndex);
|
|
22857
|
+
changed = true;
|
|
22858
|
+
}
|
|
22859
|
+
return;
|
|
22860
|
+
}
|
|
22861
|
+
if (previous?.key !== snapshot.key) {
|
|
22862
|
+
registry.pageThumbnailSnapshots.set(pageIndex, snapshot);
|
|
22863
|
+
changed = true;
|
|
22864
|
+
}
|
|
22865
|
+
});
|
|
22467
22866
|
registry.pageContentKeys.forEach((_, pageIndex) => {
|
|
22468
22867
|
if (pageIndex >= contentKeysByPage.length) {
|
|
22469
22868
|
registry.pageContentKeys.delete(pageIndex);
|
|
@@ -22476,6 +22875,12 @@ function syncDocxViewerPageSurfaceContentKeys(editor, contentKeysByPage, pageSiz
|
|
|
22476
22875
|
changed = true;
|
|
22477
22876
|
}
|
|
22478
22877
|
});
|
|
22878
|
+
registry.pageThumbnailSnapshots.forEach((_, pageIndex) => {
|
|
22879
|
+
if (pageIndex >= thumbnailSnapshotsByPage.length) {
|
|
22880
|
+
registry.pageThumbnailSnapshots.delete(pageIndex);
|
|
22881
|
+
changed = true;
|
|
22882
|
+
}
|
|
22883
|
+
});
|
|
22479
22884
|
if (changed) {
|
|
22480
22885
|
notifyDocxViewerPageSurfaceSubscribers(registry);
|
|
22481
22886
|
}
|
|
@@ -22556,6 +22961,359 @@ function resolveDocxViewerPageSurfaceSize(element, fallbackWidthPx, fallbackHeig
|
|
|
22556
22961
|
heightPx: Math.max(1, Math.round(fallbackHeightPx))
|
|
22557
22962
|
};
|
|
22558
22963
|
}
|
|
22964
|
+
var DOCX_DIRECT_THUMBNAIL_MAX_ELEMENTS_PER_PAGE = 260;
|
|
22965
|
+
var DOCX_DIRECT_THUMBNAIL_MAX_TEXT_RUNS = 28;
|
|
22966
|
+
var DOCX_DIRECT_THUMBNAIL_MAX_TEXT_CHARS = 900;
|
|
22967
|
+
var DOCX_DIRECT_THUMBNAIL_MAX_TABLE_CELLS = 220;
|
|
22968
|
+
var DOCX_DIRECT_THUMBNAIL_MAX_CELL_TEXT_CHARS = 120;
|
|
22969
|
+
function docxThumbnailCssNumber(value) {
|
|
22970
|
+
if (typeof value === "number" && Number.isFinite(value)) {
|
|
22971
|
+
return value;
|
|
22972
|
+
}
|
|
22973
|
+
if (typeof value === "string") {
|
|
22974
|
+
const parsed = Number.parseFloat(value);
|
|
22975
|
+
return Number.isFinite(parsed) ? parsed : 0;
|
|
22976
|
+
}
|
|
22977
|
+
return 0;
|
|
22978
|
+
}
|
|
22979
|
+
function normalizeDocxThumbnailColor(value) {
|
|
22980
|
+
const normalized = value?.trim();
|
|
22981
|
+
if (!normalized || normalized.toLowerCase() === "auto") {
|
|
22982
|
+
return void 0;
|
|
22983
|
+
}
|
|
22984
|
+
if (/^[0-9a-fA-F]{6}$/.test(normalized)) {
|
|
22985
|
+
return `#${normalized}`;
|
|
22986
|
+
}
|
|
22987
|
+
return normalized;
|
|
22988
|
+
}
|
|
22989
|
+
function docxThumbnailTextRunStylesMatch(left, right) {
|
|
22990
|
+
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;
|
|
22991
|
+
}
|
|
22992
|
+
function appendDocxThumbnailTextRun(runs, run, remaining) {
|
|
22993
|
+
if (remaining.chars <= 0 || runs.length >= DOCX_DIRECT_THUMBNAIL_MAX_TEXT_RUNS) {
|
|
22994
|
+
return;
|
|
22995
|
+
}
|
|
22996
|
+
const text = run.text.slice(0, remaining.chars);
|
|
22997
|
+
if (!text) {
|
|
22998
|
+
return;
|
|
22999
|
+
}
|
|
23000
|
+
remaining.chars -= text.length;
|
|
23001
|
+
const nextRun = { ...run, text };
|
|
23002
|
+
const previous = runs[runs.length - 1];
|
|
23003
|
+
if (previous && docxThumbnailTextRunStylesMatch(previous, nextRun)) {
|
|
23004
|
+
previous.text += nextRun.text;
|
|
23005
|
+
return;
|
|
23006
|
+
}
|
|
23007
|
+
runs.push(nextRun);
|
|
23008
|
+
}
|
|
23009
|
+
function docxThumbnailFallbackHeadingRunStyle(paragraph) {
|
|
23010
|
+
const headingLevel = paragraph.style?.headingLevel;
|
|
23011
|
+
return headingLevel && headingLevel >= 1 && headingLevel <= 6 ? DEFAULT_WORD_HEADING_RUN_STYLES[headingLevel] : void 0;
|
|
23012
|
+
}
|
|
23013
|
+
function docxThumbnailTextRunsFromParagraph(paragraph, documentTheme, maxChars = DOCX_DIRECT_THUMBNAIL_MAX_TEXT_CHARS) {
|
|
23014
|
+
const runs = [];
|
|
23015
|
+
const remaining = { chars: maxChars };
|
|
23016
|
+
const fallbackHeadingStyle = docxThumbnailFallbackHeadingRunStyle(paragraph);
|
|
23017
|
+
const fallbackFontFamily = cssFontFamily(fallbackHeadingStyle?.fontFamily) ?? cssFontFamily(paragraphDominantFontFamily(paragraph));
|
|
23018
|
+
paragraph.children.forEach((child) => {
|
|
23019
|
+
if (remaining.chars <= 0) {
|
|
23020
|
+
return;
|
|
23021
|
+
}
|
|
23022
|
+
const style = child.type === "text" || child.type === "form-field" ? child.style : void 0;
|
|
23023
|
+
const text = child.type === "text" ? child.text : child.type === "form-field" ? formFieldDisplayValue2(child) : child.type === "image" ? child.alt || "[image]" : "";
|
|
23024
|
+
appendDocxThumbnailTextRun(
|
|
23025
|
+
runs,
|
|
23026
|
+
{
|
|
23027
|
+
text,
|
|
23028
|
+
bold: style?.bold ?? fallbackHeadingStyle?.bold,
|
|
23029
|
+
italic: style?.italic ?? fallbackHeadingStyle?.italic,
|
|
23030
|
+
color: normalizeDocxThumbnailColor(
|
|
23031
|
+
themedRunColor(style?.color ?? fallbackHeadingStyle?.color, documentTheme)
|
|
23032
|
+
),
|
|
23033
|
+
backgroundColor: normalizeDocxThumbnailColor(
|
|
23034
|
+
style?.backgroundColor ?? resolveHighlightColor(style?.highlight)
|
|
23035
|
+
),
|
|
23036
|
+
fontSizePx: style?.fontSizePt && style.fontSizePt > 0 ? Math.max(6, style.fontSizePt * 96 / 72) : fallbackHeadingStyle?.fontSizePt ? Math.max(6, fallbackHeadingStyle.fontSizePt * 96 / 72) : paragraphBaseFontSizePx(paragraph),
|
|
23037
|
+
fontFamily: cssFontFamily(style?.fontFamily) ?? fallbackFontFamily
|
|
23038
|
+
},
|
|
23039
|
+
remaining
|
|
23040
|
+
);
|
|
23041
|
+
});
|
|
23042
|
+
return runs;
|
|
23043
|
+
}
|
|
23044
|
+
function docxThumbnailCellTextRuns(cell, documentTheme) {
|
|
23045
|
+
const paragraph = tableCellParagraphsRecursively(cell.nodes).find(
|
|
23046
|
+
(candidate) => paragraphText(candidate).trim().length > 0
|
|
23047
|
+
);
|
|
23048
|
+
if (!paragraph) {
|
|
23049
|
+
return void 0;
|
|
23050
|
+
}
|
|
23051
|
+
const runs = docxThumbnailTextRunsFromParagraph(
|
|
23052
|
+
paragraph,
|
|
23053
|
+
documentTheme,
|
|
23054
|
+
DOCX_DIRECT_THUMBNAIL_MAX_CELL_TEXT_CHARS
|
|
23055
|
+
);
|
|
23056
|
+
return runs.length > 0 ? runs : void 0;
|
|
23057
|
+
}
|
|
23058
|
+
function buildDocxThumbnailParagraphElements(params) {
|
|
23059
|
+
const {
|
|
23060
|
+
paragraph,
|
|
23061
|
+
segment,
|
|
23062
|
+
contentLeftPx,
|
|
23063
|
+
contentWidthPx,
|
|
23064
|
+
yPx,
|
|
23065
|
+
heightPx,
|
|
23066
|
+
numberingDefinitions,
|
|
23067
|
+
docGridLinePitchPx,
|
|
23068
|
+
documentTheme
|
|
23069
|
+
} = params;
|
|
23070
|
+
const blockStyle = paragraphBlockStyle(
|
|
23071
|
+
paragraph,
|
|
23072
|
+
numberingDefinitions,
|
|
23073
|
+
void 0,
|
|
23074
|
+
docGridLinePitchPx
|
|
23075
|
+
);
|
|
23076
|
+
const paragraphLineRange = segment.paragraphLineRange;
|
|
23077
|
+
const marginTopPx = paragraphLineRange && paragraphLineRange.startLineIndex > 0 ? 0 : Math.max(0, docxThumbnailCssNumber(blockStyle.marginTop));
|
|
23078
|
+
const marginBottomPx = paragraphLineRange && paragraphLineRange.endLineIndex < paragraphLineRange.totalLineCount ? 0 : Math.max(0, docxThumbnailCssNumber(blockStyle.marginBottom));
|
|
23079
|
+
const marginLeftPx = docxThumbnailCssNumber(blockStyle.marginLeft);
|
|
23080
|
+
const marginRightPx = Math.max(0, docxThumbnailCssNumber(blockStyle.marginRight));
|
|
23081
|
+
const xPx = contentLeftPx + marginLeftPx;
|
|
23082
|
+
const widthPx = Math.max(8, contentWidthPx - marginLeftPx - marginRightPx);
|
|
23083
|
+
const bodyYPx = yPx + marginTopPx;
|
|
23084
|
+
const bodyHeightPx = Math.max(1, heightPx - marginTopPx - marginBottomPx);
|
|
23085
|
+
const runs = docxThumbnailTextRunsFromParagraph(paragraph, documentTheme);
|
|
23086
|
+
const elements = [];
|
|
23087
|
+
elements.push({
|
|
23088
|
+
kind: "paragraph",
|
|
23089
|
+
xPx,
|
|
23090
|
+
yPx: bodyYPx,
|
|
23091
|
+
widthPx,
|
|
23092
|
+
heightPx: bodyHeightPx,
|
|
23093
|
+
align: paragraph.style?.align,
|
|
23094
|
+
backgroundColor: normalizeDocxThumbnailColor(
|
|
23095
|
+
paragraph.style?.backgroundColor
|
|
23096
|
+
),
|
|
23097
|
+
lineHeightPx: paragraphLineRange?.lineHeightPx ?? estimateParagraphLineHeightPx(paragraph, docGridLinePitchPx),
|
|
23098
|
+
startLineIndex: paragraphLineRange?.startLineIndex,
|
|
23099
|
+
runs
|
|
23100
|
+
});
|
|
23101
|
+
const hasVisibleText = runs.some((run) => run.text.trim().length > 0);
|
|
23102
|
+
if (!hasVisibleText) {
|
|
23103
|
+
const imageRuns = paragraph.children.filter(
|
|
23104
|
+
(child) => child.type === "image"
|
|
23105
|
+
);
|
|
23106
|
+
imageRuns.slice(0, 4).forEach((imageRun) => {
|
|
23107
|
+
const imageWidthPx = Math.max(18, imageRun.widthPx ?? widthPx * 0.5);
|
|
23108
|
+
const imageHeightPx = Math.max(18, imageRun.heightPx ?? bodyHeightPx * 0.5);
|
|
23109
|
+
const scale = Math.min(
|
|
23110
|
+
1,
|
|
23111
|
+
(widthPx - 4) / imageWidthPx,
|
|
23112
|
+
(bodyHeightPx - 4) / imageHeightPx
|
|
23113
|
+
);
|
|
23114
|
+
elements.push({
|
|
23115
|
+
kind: "image-placeholder",
|
|
23116
|
+
xPx: xPx + 2,
|
|
23117
|
+
yPx: bodyYPx + 2,
|
|
23118
|
+
widthPx: Math.max(12, imageWidthPx * scale),
|
|
23119
|
+
heightPx: Math.max(12, imageHeightPx * scale)
|
|
23120
|
+
});
|
|
23121
|
+
});
|
|
23122
|
+
}
|
|
23123
|
+
return elements;
|
|
23124
|
+
}
|
|
23125
|
+
function buildDocxThumbnailTableElement(params) {
|
|
23126
|
+
const {
|
|
23127
|
+
table,
|
|
23128
|
+
segment,
|
|
23129
|
+
contentLeftPx,
|
|
23130
|
+
contentWidthPx,
|
|
23131
|
+
contentHeightPx,
|
|
23132
|
+
yPx,
|
|
23133
|
+
heightPx,
|
|
23134
|
+
numberingDefinitions,
|
|
23135
|
+
docGridLinePitchPx,
|
|
23136
|
+
documentTheme
|
|
23137
|
+
} = params;
|
|
23138
|
+
const columnCount = tableColumnCount(table);
|
|
23139
|
+
const tableIndentPx = twipsToSignedPixels(table.style?.indentTwips) ?? 0;
|
|
23140
|
+
const tableWidthPx = twipsToPixels(table.style?.widthTwips);
|
|
23141
|
+
const definedWidthsTwips = columnWidthsFromTableDefinition(table, columnCount);
|
|
23142
|
+
const rawTableColumnWidthsPx = definedWidthsTwips && definedWidthsTwips.length > 0 ? normalizeColumnWidthsPx(
|
|
23143
|
+
definedWidthsTwips.map((widthTwips) => twipsToPixels(widthTwips) ?? 0),
|
|
23144
|
+
columnCount,
|
|
23145
|
+
tableWidthPx,
|
|
23146
|
+
1
|
|
23147
|
+
) : defaultColumnWidthsPx(columnCount, tableWidthPx);
|
|
23148
|
+
const rawResolvedTableWidthPx = tableWidthPx ?? rawTableColumnWidthsPx.reduce((sum, widthPx) => sum + widthPx, 0);
|
|
23149
|
+
const maxTableWidthPx = Math.max(
|
|
23150
|
+
24,
|
|
23151
|
+
contentWidthPx - tableIndentPx - resolveCollapsedTableHorizontalOuterBleedPx(table, columnCount)
|
|
23152
|
+
);
|
|
23153
|
+
const resolvedTableWidthPx = clampTableWidthPx(
|
|
23154
|
+
rawResolvedTableWidthPx,
|
|
23155
|
+
maxTableWidthPx
|
|
23156
|
+
);
|
|
23157
|
+
const { columnWidthsPx } = resolveFittedTableColumnWidths(
|
|
23158
|
+
table,
|
|
23159
|
+
rawTableColumnWidthsPx,
|
|
23160
|
+
resolvedTableWidthPx
|
|
23161
|
+
);
|
|
23162
|
+
const rowHeightsPx = estimateTableRowHeightsPx(
|
|
23163
|
+
table,
|
|
23164
|
+
contentWidthPx,
|
|
23165
|
+
numberingDefinitions,
|
|
23166
|
+
docGridLinePitchPx,
|
|
23167
|
+
contentHeightPx
|
|
23168
|
+
);
|
|
23169
|
+
const startRowIndex = Math.max(
|
|
23170
|
+
0,
|
|
23171
|
+
segment.tableRowSlice?.rowIndex ?? segment.tableRowRange?.startRowIndex ?? 0
|
|
23172
|
+
);
|
|
23173
|
+
const endRowIndex = Math.min(
|
|
23174
|
+
table.rows.length,
|
|
23175
|
+
segment.tableRowSlice ? startRowIndex + 1 : segment.tableRowRange?.endRowIndex ?? table.rows.length
|
|
23176
|
+
);
|
|
23177
|
+
const cells = [];
|
|
23178
|
+
let rowYPx = segment.tableRowSlice ? -Math.max(0, segment.tableRowSlice.startOffsetPx) : 0;
|
|
23179
|
+
for (let rowIndex = startRowIndex; rowIndex < endRowIndex && cells.length < DOCX_DIRECT_THUMBNAIL_MAX_TABLE_CELLS; rowIndex += 1) {
|
|
23180
|
+
const row = table.rows[rowIndex];
|
|
23181
|
+
if (!row) {
|
|
23182
|
+
continue;
|
|
23183
|
+
}
|
|
23184
|
+
const rowHeightPx = segment.tableRowSlice && rowIndex === segment.tableRowSlice.rowIndex ? Math.max(1, segment.tableRowSlice.totalRowHeightPx) : Math.max(1, rowHeightsPx[rowIndex] ?? MIN_PARAGRAPH_LINE_HEIGHT_PX);
|
|
23185
|
+
let columnCursor = 0;
|
|
23186
|
+
row.cells.forEach((cell) => {
|
|
23187
|
+
if (cells.length >= DOCX_DIRECT_THUMBNAIL_MAX_TABLE_CELLS) {
|
|
23188
|
+
return;
|
|
23189
|
+
}
|
|
23190
|
+
const span = cell.style?.gridSpan && cell.style.gridSpan > 1 ? cell.style.gridSpan : 1;
|
|
23191
|
+
const xPx = columnWidthsPx.slice(0, columnCursor).reduce((sum, widthPx2) => sum + widthPx2, 0);
|
|
23192
|
+
const widthPx = columnWidthsPx.slice(columnCursor, columnCursor + span).reduce((sum, widthPx2) => sum + widthPx2, 0);
|
|
23193
|
+
columnCursor += span;
|
|
23194
|
+
cells.push({
|
|
23195
|
+
xPx,
|
|
23196
|
+
yPx: rowYPx,
|
|
23197
|
+
widthPx: Math.max(1, widthPx),
|
|
23198
|
+
heightPx: rowHeightPx,
|
|
23199
|
+
backgroundColor: normalizeDocxThumbnailColor(
|
|
23200
|
+
cell.style?.backgroundColor ?? row.style?.backgroundColor
|
|
23201
|
+
),
|
|
23202
|
+
runs: docxThumbnailCellTextRuns(cell, documentTheme)
|
|
23203
|
+
});
|
|
23204
|
+
});
|
|
23205
|
+
rowYPx += rowHeightPx;
|
|
23206
|
+
}
|
|
23207
|
+
if (cells.length === 0) {
|
|
23208
|
+
return void 0;
|
|
23209
|
+
}
|
|
23210
|
+
return {
|
|
23211
|
+
kind: "table",
|
|
23212
|
+
xPx: contentLeftPx + tableIndentPx,
|
|
23213
|
+
yPx,
|
|
23214
|
+
widthPx: Math.max(1, resolvedTableWidthPx),
|
|
23215
|
+
heightPx: Math.max(1, heightPx),
|
|
23216
|
+
cells
|
|
23217
|
+
};
|
|
23218
|
+
}
|
|
23219
|
+
function buildDocxPageThumbnailRenderSnapshotEntries(params) {
|
|
23220
|
+
const {
|
|
23221
|
+
model,
|
|
23222
|
+
pageNodeSegmentsByPage,
|
|
23223
|
+
pageSectionInfoByIndex,
|
|
23224
|
+
contentKeysByPage,
|
|
23225
|
+
fallbackLayout,
|
|
23226
|
+
documentTheme,
|
|
23227
|
+
docGridLinePitchPxByNodeIndex,
|
|
23228
|
+
numberingDefinitions
|
|
23229
|
+
} = params;
|
|
23230
|
+
return pageNodeSegmentsByPage.map((pageSegments, pageIndex) => {
|
|
23231
|
+
const key = `${contentKeysByPage[pageIndex] ?? ""}|theme:${documentTheme}`;
|
|
23232
|
+
let cachedSnapshot;
|
|
23233
|
+
return {
|
|
23234
|
+
key,
|
|
23235
|
+
getSnapshot: () => {
|
|
23236
|
+
if (cachedSnapshot) {
|
|
23237
|
+
return cachedSnapshot;
|
|
23238
|
+
}
|
|
23239
|
+
const pageLayout = pageSectionInfoByIndex[pageIndex]?.layout ?? fallbackLayout;
|
|
23240
|
+
const contentLeftPx = pageLayout.marginsPx.left;
|
|
23241
|
+
const contentTopPx = pageLayout.marginsPx.top;
|
|
23242
|
+
const contentWidthPx = Math.max(
|
|
23243
|
+
1,
|
|
23244
|
+
pageLayout.pageWidthPx - pageLayout.marginsPx.left - pageLayout.marginsPx.right
|
|
23245
|
+
);
|
|
23246
|
+
const contentHeightPx = Math.max(
|
|
23247
|
+
1,
|
|
23248
|
+
pageLayout.pageHeightPx - pageLayout.marginsPx.top - pageLayout.marginsPx.bottom
|
|
23249
|
+
);
|
|
23250
|
+
const elements = [];
|
|
23251
|
+
let yPx = contentTopPx;
|
|
23252
|
+
for (const segment of pageSegments) {
|
|
23253
|
+
if (elements.length >= DOCX_DIRECT_THUMBNAIL_MAX_ELEMENTS_PER_PAGE) {
|
|
23254
|
+
break;
|
|
23255
|
+
}
|
|
23256
|
+
const node = model.nodes[segment.nodeIndex];
|
|
23257
|
+
if (!node) {
|
|
23258
|
+
continue;
|
|
23259
|
+
}
|
|
23260
|
+
const docGridLinePitchPx = docGridLinePitchPxByNodeIndex.get(
|
|
23261
|
+
segment.nodeIndex
|
|
23262
|
+
);
|
|
23263
|
+
const segmentHeightPx = estimateRenderedPageSegmentHeightPx(
|
|
23264
|
+
node,
|
|
23265
|
+
segment,
|
|
23266
|
+
model,
|
|
23267
|
+
contentWidthPx,
|
|
23268
|
+
numberingDefinitions,
|
|
23269
|
+
docGridLinePitchPx
|
|
23270
|
+
);
|
|
23271
|
+
if (node.type === "paragraph" && !segment.tableRowRange) {
|
|
23272
|
+
elements.push(
|
|
23273
|
+
...buildDocxThumbnailParagraphElements({
|
|
23274
|
+
paragraph: node,
|
|
23275
|
+
segment,
|
|
23276
|
+
contentLeftPx,
|
|
23277
|
+
contentTopPx,
|
|
23278
|
+
contentWidthPx,
|
|
23279
|
+
yPx,
|
|
23280
|
+
heightPx: segmentHeightPx,
|
|
23281
|
+
numberingDefinitions,
|
|
23282
|
+
docGridLinePitchPx,
|
|
23283
|
+
documentTheme
|
|
23284
|
+
})
|
|
23285
|
+
);
|
|
23286
|
+
} else if (node.type === "table") {
|
|
23287
|
+
const tableElement = buildDocxThumbnailTableElement({
|
|
23288
|
+
table: node,
|
|
23289
|
+
segment,
|
|
23290
|
+
contentLeftPx,
|
|
23291
|
+
contentWidthPx,
|
|
23292
|
+
contentHeightPx,
|
|
23293
|
+
yPx,
|
|
23294
|
+
heightPx: segmentHeightPx,
|
|
23295
|
+
numberingDefinitions,
|
|
23296
|
+
docGridLinePitchPx,
|
|
23297
|
+
documentTheme
|
|
23298
|
+
});
|
|
23299
|
+
if (tableElement) {
|
|
23300
|
+
elements.push(tableElement);
|
|
23301
|
+
}
|
|
23302
|
+
}
|
|
23303
|
+
yPx += Math.max(1, segmentHeightPx);
|
|
23304
|
+
}
|
|
23305
|
+
cachedSnapshot = {
|
|
23306
|
+
key,
|
|
23307
|
+
sourceWidthPx: pageLayout.pageWidthPx,
|
|
23308
|
+
sourceHeightPx: pageLayout.pageHeightPx,
|
|
23309
|
+
pageBackgroundColor: documentTheme === "dark" ? "#111827" : "#ffffff",
|
|
23310
|
+
elements
|
|
23311
|
+
};
|
|
23312
|
+
return cachedSnapshot;
|
|
23313
|
+
}
|
|
23314
|
+
};
|
|
23315
|
+
});
|
|
23316
|
+
}
|
|
22559
23317
|
function DocxDetachedThumbnailPageSurface({
|
|
22560
23318
|
editor,
|
|
22561
23319
|
pageIndex
|
|
@@ -22581,17 +23339,19 @@ var DocxDetachedThumbnailSurfaceRenderer = class {
|
|
|
22581
23339
|
host;
|
|
22582
23340
|
root;
|
|
22583
23341
|
activePageIndex;
|
|
23342
|
+
activeRenderKey;
|
|
22584
23343
|
async renderPageSurface(params) {
|
|
22585
23344
|
if (typeof document === "undefined" || typeof window === "undefined") {
|
|
22586
23345
|
return void 0;
|
|
22587
23346
|
}
|
|
22588
|
-
const { editor, registry, pageIndex } = params;
|
|
23347
|
+
const { editor, registry, pageIndex, renderKey } = params;
|
|
22589
23348
|
await this.ensureRoot();
|
|
22590
23349
|
if (!this.root) {
|
|
22591
23350
|
return void 0;
|
|
22592
23351
|
}
|
|
22593
|
-
if (this.activePageIndex !== pageIndex) {
|
|
23352
|
+
if (this.activePageIndex !== pageIndex || this.activeRenderKey !== renderKey) {
|
|
22594
23353
|
this.activePageIndex = pageIndex;
|
|
23354
|
+
this.activeRenderKey = renderKey;
|
|
22595
23355
|
this.root.render(
|
|
22596
23356
|
/* @__PURE__ */ jsx(
|
|
22597
23357
|
DocxDetachedThumbnailPageSurface,
|
|
@@ -22614,6 +23374,7 @@ var DocxDetachedThumbnailSurfaceRenderer = class {
|
|
|
22614
23374
|
}
|
|
22615
23375
|
this.host = void 0;
|
|
22616
23376
|
this.activePageIndex = void 0;
|
|
23377
|
+
this.activeRenderKey = void 0;
|
|
22617
23378
|
}
|
|
22618
23379
|
async ensureRoot() {
|
|
22619
23380
|
if (this.root) {
|
|
@@ -22654,8 +23415,42 @@ var DocxDetachedThumbnailSurfaceRenderer = class {
|
|
|
22654
23415
|
return pageElement?.isConnected ? pageElement : void 0;
|
|
22655
23416
|
}
|
|
22656
23417
|
};
|
|
22657
|
-
var DOCX_THUMBNAIL_SURFACE_CACHE_MAX_ENTRIES =
|
|
23418
|
+
var DOCX_THUMBNAIL_SURFACE_CACHE_MAX_ENTRIES = 64;
|
|
22658
23419
|
var DOCX_THUMBNAIL_MIN_RASTER_INTERVAL_MS = 200;
|
|
23420
|
+
var DOCX_THUMBNAIL_RENDER_PRIORITY_VISIBLE = 0;
|
|
23421
|
+
var DOCX_THUMBNAIL_RENDER_PRIORITY_ATTACHED = 1;
|
|
23422
|
+
var DOCX_THUMBNAIL_RENDER_PRIORITY_PREFETCH = 2;
|
|
23423
|
+
function normalizeDocxThumbnailMinRasterIntervalMs(value) {
|
|
23424
|
+
return Number.isFinite(value) && Number(value) >= 0 ? Number(value) : DOCX_THUMBNAIL_MIN_RASTER_INTERVAL_MS;
|
|
23425
|
+
}
|
|
23426
|
+
function normalizeDocxThumbnailPageIndexes(indexes, totalPages) {
|
|
23427
|
+
if (!indexes?.length || totalPages <= 0) {
|
|
23428
|
+
return [];
|
|
23429
|
+
}
|
|
23430
|
+
const seen = /* @__PURE__ */ new Set();
|
|
23431
|
+
const normalized = [];
|
|
23432
|
+
indexes.forEach((pageIndex) => {
|
|
23433
|
+
if (!Number.isFinite(pageIndex)) {
|
|
23434
|
+
return;
|
|
23435
|
+
}
|
|
23436
|
+
const roundedPageIndex = Math.trunc(pageIndex);
|
|
23437
|
+
if (roundedPageIndex < 0 || roundedPageIndex >= totalPages || seen.has(roundedPageIndex)) {
|
|
23438
|
+
return;
|
|
23439
|
+
}
|
|
23440
|
+
seen.add(roundedPageIndex);
|
|
23441
|
+
normalized.push(roundedPageIndex);
|
|
23442
|
+
});
|
|
23443
|
+
return normalized;
|
|
23444
|
+
}
|
|
23445
|
+
function docxThumbnailPageIndexesKey(pageIndexes) {
|
|
23446
|
+
return pageIndexes.join(",");
|
|
23447
|
+
}
|
|
23448
|
+
function docxThumbnailCanvasQueueKey(canvasId) {
|
|
23449
|
+
return `canvas:${canvasId}`;
|
|
23450
|
+
}
|
|
23451
|
+
function docxThumbnailPrefetchQueueKey(pageIndex) {
|
|
23452
|
+
return `prefetch:${pageIndex}`;
|
|
23453
|
+
}
|
|
22659
23454
|
function resolveDocxPageThumbnailResolution(options) {
|
|
22660
23455
|
const safeSourceWidthPx = Math.max(1, Math.round(options.sourceWidthPx));
|
|
22661
23456
|
const safeSourceHeightPx = Math.max(1, Math.round(options.sourceHeightPx));
|
|
@@ -22735,6 +23530,12 @@ function useDocxPageThumbnails(editor, options = {}) {
|
|
|
22735
23530
|
const lastPaintedThumbnailKeyByCanvasRef = React.useRef(
|
|
22736
23531
|
/* @__PURE__ */ new WeakMap()
|
|
22737
23532
|
);
|
|
23533
|
+
const thumbnailQueueKeyByCanvasRef = React.useRef(
|
|
23534
|
+
/* @__PURE__ */ new WeakMap()
|
|
23535
|
+
);
|
|
23536
|
+
const nextThumbnailCanvasQueueIdRef = React.useRef(0);
|
|
23537
|
+
const queuedPrefetchThumbnailKeysRef = React.useRef(/* @__PURE__ */ new Set());
|
|
23538
|
+
const thumbnailMinRasterIntervalMs = normalizeDocxThumbnailMinRasterIntervalMs(options.minRasterIntervalMs);
|
|
22738
23539
|
const ensureThumbnailSurfaceCache = React.useCallback(() => {
|
|
22739
23540
|
if (!thumbnailSurfaceCacheRef.current) {
|
|
22740
23541
|
thumbnailSurfaceCacheRef.current = new DocxThumbnailSurfaceCache(
|
|
@@ -22746,18 +23547,50 @@ function useDocxPageThumbnails(editor, options = {}) {
|
|
|
22746
23547
|
const ensureThumbnailRasterQueue = React.useCallback(() => {
|
|
22747
23548
|
if (!thumbnailRasterQueueRef.current) {
|
|
22748
23549
|
thumbnailRasterQueueRef.current = new SerialIdleTaskQueue({
|
|
22749
|
-
minTaskIntervalMs:
|
|
23550
|
+
minTaskIntervalMs: thumbnailMinRasterIntervalMs
|
|
22750
23551
|
});
|
|
22751
23552
|
}
|
|
22752
23553
|
return thumbnailRasterQueueRef.current;
|
|
22753
|
-
}, []);
|
|
23554
|
+
}, [thumbnailMinRasterIntervalMs]);
|
|
23555
|
+
const thumbnailQueueKeyForCanvas = React.useCallback(
|
|
23556
|
+
(canvas) => {
|
|
23557
|
+
const existing = thumbnailQueueKeyByCanvasRef.current.get(canvas);
|
|
23558
|
+
if (existing) {
|
|
23559
|
+
return existing;
|
|
23560
|
+
}
|
|
23561
|
+
const nextKey = docxThumbnailCanvasQueueKey(
|
|
23562
|
+
nextThumbnailCanvasQueueIdRef.current
|
|
23563
|
+
);
|
|
23564
|
+
nextThumbnailCanvasQueueIdRef.current += 1;
|
|
23565
|
+
thumbnailQueueKeyByCanvasRef.current.set(canvas, nextKey);
|
|
23566
|
+
return nextKey;
|
|
23567
|
+
},
|
|
23568
|
+
[]
|
|
23569
|
+
);
|
|
23570
|
+
React.useEffect(() => {
|
|
23571
|
+
thumbnailRasterQueueRef.current?.clear();
|
|
23572
|
+
thumbnailRasterQueueRef.current = void 0;
|
|
23573
|
+
queuedPrefetchThumbnailKeysRef.current.clear();
|
|
23574
|
+
}, [thumbnailMinRasterIntervalMs]);
|
|
22754
23575
|
React.useEffect(() => {
|
|
22755
23576
|
thumbnailSurfaceCacheRef.current?.clear();
|
|
22756
23577
|
thumbnailRasterQueueRef.current?.clear();
|
|
22757
23578
|
detachedThumbnailSurfaceRendererRef.current?.clear();
|
|
22758
23579
|
detachedThumbnailSurfaceRendererRef.current = void 0;
|
|
22759
23580
|
lastPaintedThumbnailKeyByCanvasRef.current = /* @__PURE__ */ new WeakMap();
|
|
23581
|
+
thumbnailQueueKeyByCanvasRef.current = /* @__PURE__ */ new WeakMap();
|
|
23582
|
+
nextThumbnailCanvasQueueIdRef.current = 0;
|
|
23583
|
+
queuedPrefetchThumbnailKeysRef.current.clear();
|
|
22760
23584
|
}, [editor.documentLoadNonce, pageSurfaceRegistryOwner]);
|
|
23585
|
+
React.useEffect(() => {
|
|
23586
|
+
detachedThumbnailSurfaceRendererRef.current?.clear();
|
|
23587
|
+
detachedThumbnailSurfaceRendererRef.current = void 0;
|
|
23588
|
+
}, [
|
|
23589
|
+
editor.documentTheme,
|
|
23590
|
+
editor.model,
|
|
23591
|
+
editor.showComments,
|
|
23592
|
+
editor.showTrackedChanges
|
|
23593
|
+
]);
|
|
22761
23594
|
React.useEffect(
|
|
22762
23595
|
() => () => {
|
|
22763
23596
|
detachedThumbnailSurfaceRendererRef.current?.clear();
|
|
@@ -22782,6 +23615,125 @@ function useDocxPageThumbnails(editor, options = {}) {
|
|
|
22782
23615
|
},
|
|
22783
23616
|
[editor.documentTheme, pageSurfaceRegistry, thumbnailResolutionOptionsKey]
|
|
22784
23617
|
);
|
|
23618
|
+
const totalThumbnailPages = Math.max(1, editor.totalPages);
|
|
23619
|
+
const visibleThumbnailPageIndexes = React.useMemo(
|
|
23620
|
+
() => normalizeDocxThumbnailPageIndexes(
|
|
23621
|
+
options.renderWindow?.visiblePageIndexes,
|
|
23622
|
+
totalThumbnailPages
|
|
23623
|
+
),
|
|
23624
|
+
[options.renderWindow?.visiblePageIndexes, totalThumbnailPages]
|
|
23625
|
+
);
|
|
23626
|
+
const prefetchThumbnailPageIndexes = React.useMemo(
|
|
23627
|
+
() => normalizeDocxThumbnailPageIndexes(
|
|
23628
|
+
options.renderWindow?.prefetchPageIndexes,
|
|
23629
|
+
totalThumbnailPages
|
|
23630
|
+
),
|
|
23631
|
+
[options.renderWindow?.prefetchPageIndexes, totalThumbnailPages]
|
|
23632
|
+
);
|
|
23633
|
+
const visibleThumbnailPageIndexesKey = docxThumbnailPageIndexesKey(
|
|
23634
|
+
visibleThumbnailPageIndexes
|
|
23635
|
+
);
|
|
23636
|
+
const prefetchThumbnailPageIndexesKey = docxThumbnailPageIndexesKey(
|
|
23637
|
+
prefetchThumbnailPageIndexes
|
|
23638
|
+
);
|
|
23639
|
+
const visibleThumbnailPageIndexSet = React.useMemo(
|
|
23640
|
+
() => new Set(visibleThumbnailPageIndexes),
|
|
23641
|
+
[visibleThumbnailPageIndexesKey]
|
|
23642
|
+
);
|
|
23643
|
+
const thumbnailRenderPriorityForPage = React.useCallback(
|
|
23644
|
+
(pageIndex, fallbackPriority = DOCX_THUMBNAIL_RENDER_PRIORITY_ATTACHED) => visibleThumbnailPageIndexSet.has(pageIndex) ? DOCX_THUMBNAIL_RENDER_PRIORITY_VISIBLE : fallbackPriority,
|
|
23645
|
+
[visibleThumbnailPageIndexSet]
|
|
23646
|
+
);
|
|
23647
|
+
const renderPageThumbnailSurface = React.useCallback(
|
|
23648
|
+
async (pageIndex, renderOptions) => {
|
|
23649
|
+
const force = renderOptions?.force === true;
|
|
23650
|
+
const runSkipKey = thumbnailSkipKeyForPage(pageIndex);
|
|
23651
|
+
const detachedRenderKey = [
|
|
23652
|
+
runSkipKey ?? `load:${editor.documentLoadNonce}`,
|
|
23653
|
+
editor.showComments ? "comments:1" : "comments:0",
|
|
23654
|
+
editor.showTrackedChanges ? "tracked:1" : "tracked:0"
|
|
23655
|
+
].join("|");
|
|
23656
|
+
const thumbnailSnapshotEntry = pageSurfaceRegistry.pageThumbnailSnapshots.get(pageIndex);
|
|
23657
|
+
const fallbackSourceSize = resolveDocxViewerRegisteredPageSurfaceSize(
|
|
23658
|
+
pageSurfaceRegistry,
|
|
23659
|
+
pageIndex,
|
|
23660
|
+
fallbackLayout.pageWidthPx,
|
|
23661
|
+
fallbackLayout.pageHeightPx
|
|
23662
|
+
);
|
|
23663
|
+
const resolution = resolveDocxPageThumbnailResolution({
|
|
23664
|
+
sourceWidthPx: fallbackSourceSize.widthPx,
|
|
23665
|
+
sourceHeightPx: fallbackSourceSize.heightPx,
|
|
23666
|
+
resolution: options.resolution,
|
|
23667
|
+
maxWidthPx: options.maxWidthPx,
|
|
23668
|
+
maxHeightPx: options.maxHeightPx,
|
|
23669
|
+
pixelRatio: options.pixelRatio
|
|
23670
|
+
});
|
|
23671
|
+
const surfaceKey = runSkipKey === void 0 ? void 0 : `${runSkipKey}|${fallbackSourceSize.widthPx}x${fallbackSourceSize.heightPx}|${resolution.pixelWidthPx}x${resolution.pixelHeightPx}`;
|
|
23672
|
+
const surfaceCache = ensureThumbnailSurfaceCache();
|
|
23673
|
+
let surface = !force && surfaceKey !== void 0 ? surfaceCache.get(surfaceKey) : void 0;
|
|
23674
|
+
if (!surface) {
|
|
23675
|
+
const thumbnailSnapshot = thumbnailSnapshotEntry?.getSnapshot();
|
|
23676
|
+
if (thumbnailSnapshot) {
|
|
23677
|
+
surface = renderDocxThumbnailSnapshotSurface({
|
|
23678
|
+
snapshot: thumbnailSnapshot,
|
|
23679
|
+
widthPx: resolution.widthPx,
|
|
23680
|
+
heightPx: resolution.heightPx,
|
|
23681
|
+
pixelWidthPx: resolution.pixelWidthPx,
|
|
23682
|
+
pixelHeightPx: resolution.pixelHeightPx
|
|
23683
|
+
});
|
|
23684
|
+
if (surfaceKey !== void 0) {
|
|
23685
|
+
surfaceCache.set(surfaceKey, surface);
|
|
23686
|
+
}
|
|
23687
|
+
return { surface, resolution, runSkipKey };
|
|
23688
|
+
}
|
|
23689
|
+
let livePageElement = pageSurfaceRegistry.pageElements.get(pageIndex);
|
|
23690
|
+
if (!livePageElement || !livePageElement.isConnected) {
|
|
23691
|
+
if (!detachedThumbnailSurfaceRendererRef.current) {
|
|
23692
|
+
detachedThumbnailSurfaceRendererRef.current = new DocxDetachedThumbnailSurfaceRenderer();
|
|
23693
|
+
}
|
|
23694
|
+
livePageElement = await detachedThumbnailSurfaceRendererRef.current.renderPageSurface({
|
|
23695
|
+
editor,
|
|
23696
|
+
registry: pageSurfaceRegistry,
|
|
23697
|
+
pageIndex,
|
|
23698
|
+
renderKey: detachedRenderKey
|
|
23699
|
+
});
|
|
23700
|
+
}
|
|
23701
|
+
if (!livePageElement || !livePageElement.isConnected) {
|
|
23702
|
+
return void 0;
|
|
23703
|
+
}
|
|
23704
|
+
const sourceSize = resolveDocxViewerPageSurfaceSize(
|
|
23705
|
+
livePageElement,
|
|
23706
|
+
fallbackSourceSize.widthPx,
|
|
23707
|
+
fallbackSourceSize.heightPx
|
|
23708
|
+
);
|
|
23709
|
+
surface = await rasterizeDocxThumbnailSurface({
|
|
23710
|
+
pageElement: livePageElement,
|
|
23711
|
+
sourceWidthPx: sourceSize.widthPx,
|
|
23712
|
+
sourceHeightPx: sourceSize.heightPx,
|
|
23713
|
+
widthPx: resolution.widthPx,
|
|
23714
|
+
heightPx: resolution.heightPx,
|
|
23715
|
+
pixelWidthPx: resolution.pixelWidthPx,
|
|
23716
|
+
pixelHeightPx: resolution.pixelHeightPx
|
|
23717
|
+
});
|
|
23718
|
+
if (surfaceKey !== void 0) {
|
|
23719
|
+
surfaceCache.set(surfaceKey, surface);
|
|
23720
|
+
}
|
|
23721
|
+
}
|
|
23722
|
+
return { surface, resolution, runSkipKey };
|
|
23723
|
+
},
|
|
23724
|
+
[
|
|
23725
|
+
ensureThumbnailSurfaceCache,
|
|
23726
|
+
fallbackLayout.pageHeightPx,
|
|
23727
|
+
fallbackLayout.pageWidthPx,
|
|
23728
|
+
options.resolution,
|
|
23729
|
+
options.maxHeightPx,
|
|
23730
|
+
options.maxWidthPx,
|
|
23731
|
+
options.pixelRatio,
|
|
23732
|
+
pageSurfaceRegistry,
|
|
23733
|
+
thumbnailSkipKeyForPage,
|
|
23734
|
+
editor
|
|
23735
|
+
]
|
|
23736
|
+
);
|
|
22785
23737
|
const renderPageThumbnailToCanvas = React.useCallback(
|
|
22786
23738
|
async (pageIndex, canvas, renderOptions) => {
|
|
22787
23739
|
if (options.disabled) {
|
|
@@ -22798,74 +23750,19 @@ function useDocxPageThumbnails(editor, options = {}) {
|
|
|
22798
23750
|
updatePageThumbnailState(pageIndex, "ready");
|
|
22799
23751
|
return;
|
|
22800
23752
|
}
|
|
22801
|
-
|
|
22802
|
-
await ensureThumbnailRasterQueue().enqueue(targetCanvas, async () => {
|
|
23753
|
+
const paintIntoTarget = async () => {
|
|
22803
23754
|
if (requiresAttachedTarget && attachedCanvasByPageRef.current.get(pageIndex) !== targetCanvas) {
|
|
22804
23755
|
return;
|
|
22805
23756
|
}
|
|
22806
|
-
const runSkipKey = thumbnailSkipKeyForPage(pageIndex);
|
|
22807
|
-
const fallbackSourceSize = resolveDocxViewerRegisteredPageSurfaceSize(
|
|
22808
|
-
pageSurfaceRegistry,
|
|
22809
|
-
pageIndex,
|
|
22810
|
-
fallbackLayout.pageWidthPx,
|
|
22811
|
-
fallbackLayout.pageHeightPx
|
|
22812
|
-
);
|
|
22813
|
-
const resolution = resolveDocxPageThumbnailResolution({
|
|
22814
|
-
sourceWidthPx: fallbackSourceSize.widthPx,
|
|
22815
|
-
sourceHeightPx: fallbackSourceSize.heightPx,
|
|
22816
|
-
resolution: options.resolution,
|
|
22817
|
-
maxWidthPx: options.maxWidthPx,
|
|
22818
|
-
maxHeightPx: options.maxHeightPx,
|
|
22819
|
-
pixelRatio: options.pixelRatio
|
|
22820
|
-
});
|
|
22821
|
-
const surfaceKey = runSkipKey === void 0 ? void 0 : `${runSkipKey}|${fallbackSourceSize.widthPx}x${fallbackSourceSize.heightPx}|${resolution.pixelWidthPx}x${resolution.pixelHeightPx}`;
|
|
22822
|
-
const surfaceCache = ensureThumbnailSurfaceCache();
|
|
22823
23757
|
try {
|
|
22824
|
-
|
|
22825
|
-
|
|
22826
|
-
|
|
22827
|
-
|
|
22828
|
-
|
|
22829
|
-
|
|
22830
|
-
if (!detachedThumbnailSurfaceRendererRef.current) {
|
|
22831
|
-
detachedThumbnailSurfaceRendererRef.current = new DocxDetachedThumbnailSurfaceRenderer();
|
|
22832
|
-
}
|
|
22833
|
-
livePageElement = await detachedThumbnailSurfaceRendererRef.current.renderPageSurface(
|
|
22834
|
-
{
|
|
22835
|
-
editor,
|
|
22836
|
-
registry: pageSurfaceRegistry,
|
|
22837
|
-
pageIndex
|
|
22838
|
-
}
|
|
22839
|
-
);
|
|
22840
|
-
renderedDetachedSurface = true;
|
|
22841
|
-
}
|
|
22842
|
-
if (!livePageElement || !livePageElement.isConnected) {
|
|
22843
|
-
updatePageThumbnailState(pageIndex, "unavailable");
|
|
22844
|
-
return;
|
|
22845
|
-
}
|
|
22846
|
-
const sourceSize = resolveDocxViewerPageSurfaceSize(
|
|
22847
|
-
livePageElement,
|
|
22848
|
-
fallbackSourceSize.widthPx,
|
|
22849
|
-
fallbackSourceSize.heightPx
|
|
22850
|
-
);
|
|
22851
|
-
surface = await rasterizeDocxThumbnailSurface({
|
|
22852
|
-
pageElement: livePageElement,
|
|
22853
|
-
sourceWidthPx: sourceSize.widthPx,
|
|
22854
|
-
sourceHeightPx: sourceSize.heightPx,
|
|
22855
|
-
widthPx: resolution.widthPx,
|
|
22856
|
-
heightPx: resolution.heightPx,
|
|
22857
|
-
pixelWidthPx: resolution.pixelWidthPx,
|
|
22858
|
-
pixelHeightPx: resolution.pixelHeightPx
|
|
22859
|
-
});
|
|
22860
|
-
if (surfaceKey !== void 0) {
|
|
22861
|
-
surfaceCache.set(surfaceKey, surface);
|
|
22862
|
-
}
|
|
22863
|
-
} finally {
|
|
22864
|
-
if (renderedDetachedSurface) {
|
|
22865
|
-
detachedThumbnailSurfaceRendererRef.current?.clear();
|
|
22866
|
-
}
|
|
22867
|
-
}
|
|
23758
|
+
const rendered = await renderPageThumbnailSurface(pageIndex, {
|
|
23759
|
+
force
|
|
23760
|
+
});
|
|
23761
|
+
if (!rendered) {
|
|
23762
|
+
updatePageThumbnailState(pageIndex, "unavailable");
|
|
23763
|
+
return;
|
|
22868
23764
|
}
|
|
23765
|
+
const { surface, resolution, runSkipKey } = rendered;
|
|
22869
23766
|
blitDocxThumbnailSurface(surface, targetCanvas, resolution);
|
|
22870
23767
|
if (runSkipKey !== void 0) {
|
|
22871
23768
|
lastPaintedThumbnailKeyByCanvasRef.current.set(
|
|
@@ -22881,33 +23778,116 @@ function useDocxPageThumbnails(editor, options = {}) {
|
|
|
22881
23778
|
error instanceof Error ? error : new Error("Failed to render DOCX page thumbnail.")
|
|
22882
23779
|
);
|
|
22883
23780
|
}
|
|
22884
|
-
}
|
|
23781
|
+
};
|
|
23782
|
+
updatePageThumbnailState(pageIndex, "rendering");
|
|
23783
|
+
if (pageSurfaceRegistry.pageThumbnailSnapshots.has(pageIndex)) {
|
|
23784
|
+
await paintIntoTarget();
|
|
23785
|
+
return;
|
|
23786
|
+
}
|
|
23787
|
+
await ensureThumbnailRasterQueue().enqueue(
|
|
23788
|
+
thumbnailQueueKeyForCanvas(targetCanvas),
|
|
23789
|
+
paintIntoTarget,
|
|
23790
|
+
{
|
|
23791
|
+
priority: renderOptions?.priority ?? thumbnailRenderPriorityForPage(
|
|
23792
|
+
pageIndex,
|
|
23793
|
+
DOCX_THUMBNAIL_RENDER_PRIORITY_ATTACHED
|
|
23794
|
+
)
|
|
23795
|
+
}
|
|
23796
|
+
);
|
|
22885
23797
|
},
|
|
22886
23798
|
[
|
|
22887
23799
|
ensureThumbnailRasterQueue,
|
|
22888
|
-
ensureThumbnailSurfaceCache,
|
|
22889
|
-
fallbackLayout.pageHeightPx,
|
|
22890
|
-
fallbackLayout.pageWidthPx,
|
|
22891
23800
|
options.disabled,
|
|
22892
|
-
options.resolution,
|
|
22893
|
-
options.maxHeightPx,
|
|
22894
|
-
options.maxWidthPx,
|
|
22895
|
-
options.pixelRatio,
|
|
22896
23801
|
pageSurfaceRegistry,
|
|
23802
|
+
renderPageThumbnailSurface,
|
|
22897
23803
|
thumbnailSkipKeyForPage,
|
|
22898
|
-
|
|
22899
|
-
|
|
23804
|
+
thumbnailQueueKeyForCanvas,
|
|
23805
|
+
thumbnailRenderPriorityForPage,
|
|
23806
|
+
updatePageThumbnailState
|
|
22900
23807
|
]
|
|
22901
23808
|
);
|
|
23809
|
+
const prefetchPageThumbnailSurface = React.useCallback(
|
|
23810
|
+
async (pageIndex) => {
|
|
23811
|
+
if (options.disabled) {
|
|
23812
|
+
return;
|
|
23813
|
+
}
|
|
23814
|
+
const queueKey = docxThumbnailPrefetchQueueKey(pageIndex);
|
|
23815
|
+
queuedPrefetchThumbnailKeysRef.current.add(queueKey);
|
|
23816
|
+
await ensureThumbnailRasterQueue().enqueue(
|
|
23817
|
+
queueKey,
|
|
23818
|
+
async () => {
|
|
23819
|
+
try {
|
|
23820
|
+
await renderPageThumbnailSurface(pageIndex);
|
|
23821
|
+
} catch {
|
|
23822
|
+
}
|
|
23823
|
+
},
|
|
23824
|
+
{ priority: DOCX_THUMBNAIL_RENDER_PRIORITY_PREFETCH }
|
|
23825
|
+
);
|
|
23826
|
+
queuedPrefetchThumbnailKeysRef.current.delete(queueKey);
|
|
23827
|
+
},
|
|
23828
|
+
[ensureThumbnailRasterQueue, options.disabled, renderPageThumbnailSurface]
|
|
23829
|
+
);
|
|
22902
23830
|
const requestAttachedThumbnailRenders = React.useCallback(
|
|
22903
23831
|
async (renderOptions) => {
|
|
22904
|
-
const
|
|
22905
|
-
(
|
|
23832
|
+
const attachedPageIndexes = [
|
|
23833
|
+
...attachedCanvasByPageRef.current.keys()
|
|
23834
|
+
].sort((leftPageIndex, rightPageIndex) => {
|
|
23835
|
+
const leftPriority = thumbnailRenderPriorityForPage(leftPageIndex);
|
|
23836
|
+
const rightPriority = thumbnailRenderPriorityForPage(rightPageIndex);
|
|
23837
|
+
return leftPriority - rightPriority || leftPageIndex - rightPageIndex;
|
|
23838
|
+
});
|
|
23839
|
+
const tasks = attachedPageIndexes.map(
|
|
23840
|
+
(pageIndex) => renderPageThumbnailToCanvas(pageIndex, void 0, {
|
|
23841
|
+
...renderOptions,
|
|
23842
|
+
priority: thumbnailRenderPriorityForPage(pageIndex)
|
|
23843
|
+
})
|
|
22906
23844
|
);
|
|
22907
23845
|
await Promise.all(tasks);
|
|
22908
23846
|
},
|
|
22909
|
-
[renderPageThumbnailToCanvas]
|
|
23847
|
+
[renderPageThumbnailToCanvas, thumbnailRenderPriorityForPage]
|
|
22910
23848
|
);
|
|
23849
|
+
const requestPrefetchThumbnailRenders = React.useCallback(async () => {
|
|
23850
|
+
if (options.disabled) {
|
|
23851
|
+
queuedPrefetchThumbnailKeysRef.current.forEach((queueKey) => {
|
|
23852
|
+
thumbnailRasterQueueRef.current?.cancel(queueKey);
|
|
23853
|
+
});
|
|
23854
|
+
queuedPrefetchThumbnailKeysRef.current.clear();
|
|
23855
|
+
return;
|
|
23856
|
+
}
|
|
23857
|
+
const requestedPrefetchKeys = new Set(
|
|
23858
|
+
prefetchThumbnailPageIndexes.map(
|
|
23859
|
+
(pageIndex) => docxThumbnailPrefetchQueueKey(pageIndex)
|
|
23860
|
+
)
|
|
23861
|
+
);
|
|
23862
|
+
queuedPrefetchThumbnailKeysRef.current.forEach((queueKey) => {
|
|
23863
|
+
if (!requestedPrefetchKeys.has(queueKey)) {
|
|
23864
|
+
thumbnailRasterQueueRef.current?.cancel(queueKey);
|
|
23865
|
+
queuedPrefetchThumbnailKeysRef.current.delete(queueKey);
|
|
23866
|
+
}
|
|
23867
|
+
});
|
|
23868
|
+
const tasks = prefetchThumbnailPageIndexes.map(
|
|
23869
|
+
(pageIndex) => prefetchPageThumbnailSurface(pageIndex)
|
|
23870
|
+
);
|
|
23871
|
+
await Promise.all(tasks);
|
|
23872
|
+
}, [
|
|
23873
|
+
options.disabled,
|
|
23874
|
+
prefetchPageThumbnailSurface,
|
|
23875
|
+
prefetchThumbnailPageIndexes
|
|
23876
|
+
]);
|
|
23877
|
+
React.useEffect(() => {
|
|
23878
|
+
void requestPrefetchThumbnailRenders();
|
|
23879
|
+
}, [
|
|
23880
|
+
editor.documentLoadNonce,
|
|
23881
|
+
editor.documentTheme,
|
|
23882
|
+
editor.model,
|
|
23883
|
+
mountedPageElements,
|
|
23884
|
+
options.maxHeightPx,
|
|
23885
|
+
options.maxWidthPx,
|
|
23886
|
+
options.pixelRatio,
|
|
23887
|
+
options.resolution,
|
|
23888
|
+
prefetchThumbnailPageIndexesKey,
|
|
23889
|
+
requestPrefetchThumbnailRenders
|
|
23890
|
+
]);
|
|
22911
23891
|
const rerenderAttachedThumbnails = React.useCallback(
|
|
22912
23892
|
async () => requestAttachedThumbnailRenders({ force: true }),
|
|
22913
23893
|
[requestAttachedThumbnailRenders]
|
|
@@ -22936,7 +23916,7 @@ function useDocxPageThumbnails(editor, options = {}) {
|
|
|
22936
23916
|
options.pixelRatio,
|
|
22937
23917
|
requestAttachedThumbnailRenders
|
|
22938
23918
|
]);
|
|
22939
|
-
const
|
|
23919
|
+
const thumbnailGeometryItems = React.useMemo(() => {
|
|
22940
23920
|
const totalPages = Math.max(1, editor.totalPages);
|
|
22941
23921
|
return Array.from({ length: totalPages }, (_, pageIndex) => {
|
|
22942
23922
|
const pageElement = mountedPageElements.get(pageIndex);
|
|
@@ -22958,15 +23938,14 @@ function useDocxPageThumbnails(editor, options = {}) {
|
|
|
22958
23938
|
maxHeightPx: options.maxHeightPx,
|
|
22959
23939
|
pixelRatio: options.pixelRatio
|
|
22960
23940
|
});
|
|
22961
|
-
const state = pageThumbnailStates.get(pageIndex);
|
|
22962
23941
|
return {
|
|
23942
|
+
// Internal: drives the default status below; stripped before exposure.
|
|
23943
|
+
hasElement: Boolean(pageElement),
|
|
22963
23944
|
pageIndex,
|
|
22964
23945
|
pageNumber: pageIndex + 1,
|
|
22965
23946
|
sourceWidthPx: sourceSize.widthPx,
|
|
22966
23947
|
sourceHeightPx: sourceSize.heightPx,
|
|
22967
23948
|
isMounted: Boolean(pageElement && pageElement.isConnected),
|
|
22968
|
-
status: state?.status ?? (pageElement ? "idle" : "unavailable"),
|
|
22969
|
-
error: state?.error,
|
|
22970
23949
|
paint: (canvas) => {
|
|
22971
23950
|
if (!canvas || options.disabled) {
|
|
22972
23951
|
return false;
|
|
@@ -22983,7 +23962,9 @@ function useDocxPageThumbnails(editor, options = {}) {
|
|
|
22983
23962
|
}
|
|
22984
23963
|
const previousCanvas = attachedCanvasByPageRef.current.get(pageIndex);
|
|
22985
23964
|
if (previousCanvas) {
|
|
22986
|
-
thumbnailRasterQueueRef.current?.cancel(
|
|
23965
|
+
thumbnailRasterQueueRef.current?.cancel(
|
|
23966
|
+
thumbnailQueueKeyForCanvas(previousCanvas)
|
|
23967
|
+
);
|
|
22987
23968
|
}
|
|
22988
23969
|
attachedCanvasByPageRef.current.delete(pageIndex);
|
|
22989
23970
|
};
|
|
@@ -23019,8 +24000,20 @@ function useDocxPageThumbnails(editor, options = {}) {
|
|
|
23019
24000
|
options.maxWidthPx,
|
|
23020
24001
|
options.pixelRatio,
|
|
23021
24002
|
pageSurfaceRegistry,
|
|
23022
|
-
|
|
24003
|
+
thumbnailQueueKeyForCanvas
|
|
23023
24004
|
]);
|
|
24005
|
+
const thumbnails = React.useMemo(
|
|
24006
|
+
() => thumbnailGeometryItems.map((geometryItem) => {
|
|
24007
|
+
const { hasElement, ...item } = geometryItem;
|
|
24008
|
+
const state = pageThumbnailStates.get(item.pageIndex);
|
|
24009
|
+
return {
|
|
24010
|
+
...item,
|
|
24011
|
+
status: state?.status ?? (hasElement ? "idle" : "unavailable"),
|
|
24012
|
+
error: state?.error
|
|
24013
|
+
};
|
|
24014
|
+
}),
|
|
24015
|
+
[thumbnailGeometryItems, pageThumbnailStates]
|
|
24016
|
+
);
|
|
23024
24017
|
const paintThumbnail = React.useCallback(
|
|
23025
24018
|
(pageIndex, canvas) => {
|
|
23026
24019
|
if (!canvas || options.disabled) {
|
|
@@ -25404,15 +26397,39 @@ function DocxEditorViewer({
|
|
|
25404
26397
|
}),
|
|
25405
26398
|
[documentLayout, pageNodeSegmentsByPage, pageSectionInfoByIndex]
|
|
25406
26399
|
);
|
|
26400
|
+
const pageThumbnailSnapshotEntriesByPage = React.useMemo(
|
|
26401
|
+
() => buildDocxPageThumbnailRenderSnapshotEntries({
|
|
26402
|
+
model: editor.model,
|
|
26403
|
+
pageNodeSegmentsByPage,
|
|
26404
|
+
pageSectionInfoByIndex,
|
|
26405
|
+
contentKeysByPage: pageThumbnailContentKeysByPage,
|
|
26406
|
+
fallbackLayout: documentLayout,
|
|
26407
|
+
documentTheme: editor.documentTheme,
|
|
26408
|
+
docGridLinePitchPxByNodeIndex,
|
|
26409
|
+
numberingDefinitions: editor.model.metadata.numberingDefinitions
|
|
26410
|
+
}),
|
|
26411
|
+
[
|
|
26412
|
+
docGridLinePitchPxByNodeIndex,
|
|
26413
|
+
documentLayout,
|
|
26414
|
+
editor.documentTheme,
|
|
26415
|
+
editor.model,
|
|
26416
|
+
editor.model.metadata.numberingDefinitions,
|
|
26417
|
+
pageNodeSegmentsByPage,
|
|
26418
|
+
pageSectionInfoByIndex,
|
|
26419
|
+
pageThumbnailContentKeysByPage
|
|
26420
|
+
]
|
|
26421
|
+
);
|
|
25407
26422
|
React.useEffect(() => {
|
|
25408
26423
|
syncDocxViewerPageSurfaceContentKeys(
|
|
25409
26424
|
editor,
|
|
25410
26425
|
pageThumbnailContentKeysByPage,
|
|
25411
|
-
pageThumbnailSurfaceSizesByPage
|
|
26426
|
+
pageThumbnailSurfaceSizesByPage,
|
|
26427
|
+
pageThumbnailSnapshotEntriesByPage
|
|
25412
26428
|
);
|
|
25413
26429
|
}, [
|
|
25414
26430
|
pageSurfaceRegistryOwner,
|
|
25415
26431
|
pageThumbnailContentKeysByPage,
|
|
26432
|
+
pageThumbnailSnapshotEntriesByPage,
|
|
25416
26433
|
pageThumbnailSurfaceSizesByPage
|
|
25417
26434
|
]);
|
|
25418
26435
|
const resolveStyleRefFieldValueForPage = React.useMemo(() => {
|