@file-viewer/renderer-pdf 2.2.5 → 2.2.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/dist/pdf.js +160 -4
  2. package/package.json +2 -2
package/dist/pdf.js CHANGED
@@ -341,6 +341,10 @@ export default async function renderPdf(buffer, target, context) {
341
341
  let suppressScrollEventUntil = 0;
342
342
  let userScrollIntentUntil = 0;
343
343
  let scrollStateFrame = 0;
344
+ let rotationOperationVersion = 0;
345
+ let pendingUserRotationAnchor = null;
346
+ let zoomOperationVersion = 0;
347
+ let pendingUserZoomAnchor = null;
344
348
  let pdfSearchState = createPdfSearchState();
345
349
  let pdfMatchesCount = { current: 0, total: 0 };
346
350
  let pdfSearchOptions;
@@ -457,9 +461,22 @@ export default async function renderPdf(buffer, target, context) {
457
461
  visit(outlineItems, 0);
458
462
  return result;
459
463
  };
464
+ const navScrollTopByMode = {
465
+ pages: 0,
466
+ outline: 0,
467
+ };
460
468
  const renderNavList = () => {
469
+ if (navList.classList.contains('pdf-page-list')) {
470
+ navScrollTopByMode.pages = navList.scrollTop;
471
+ }
472
+ else if (navList.classList.contains('pdf-outline-list')) {
473
+ navScrollTopByMode.outline = navList.scrollTop;
474
+ }
461
475
  navList.replaceChildren();
462
476
  navList.className = navMode === 'pages' ? 'pdf-page-list' : 'pdf-outline-list';
477
+ const restoreNavScrollTop = () => {
478
+ navList.scrollTop = navScrollTopByMode[navMode];
479
+ };
463
480
  if (navMode === 'pages') {
464
481
  thumbnailObserver === null || thumbnailObserver === void 0 ? void 0 : thumbnailObserver.disconnect();
465
482
  for (let page = 1; page <= pageCount; page += 1) {
@@ -479,6 +496,7 @@ export default async function renderPdf(buffer, target, context) {
479
496
  button.addEventListener('click', () => goToPage(page, 'page-click', 'user'));
480
497
  navList.append(button);
481
498
  }
499
+ restoreNavScrollTop();
482
500
  return;
483
501
  }
484
502
  const entries = flattenedOutlineItems();
@@ -501,6 +519,7 @@ export default async function renderPdf(buffer, target, context) {
501
519
  if (!entries.length) {
502
520
  navList.append(createElement(documentRef, 'div', 'pdf-outline-empty', t('pdf.nav.outlineEmpty')));
503
521
  }
522
+ restoreNavScrollTop();
504
523
  };
505
524
  const paintPdfThumbnail = (pageNumber, thumb) => {
506
525
  const imageUrl = pdfThumbnails.get(pageNumber);
@@ -1020,7 +1039,93 @@ export default async function renderPdf(buffer, target, context) {
1020
1039
  autoFitWidth = false;
1021
1040
  activeFitRequest = null;
1022
1041
  };
1042
+ const getPdfPageElement = (pageNumber) => {
1043
+ var _a;
1044
+ const pageView = (_a = pdfContext.viewer) === null || _a === void 0 ? void 0 : _a.getPageView(pageNumber - 1);
1045
+ return (pageView === null || pageView === void 0 ? void 0 : pageView.div) ||
1046
+ pdfViewerRoot.querySelector(`.page[data-page-number="${pageNumber}"]`);
1047
+ };
1048
+ const captureCurrentPdfPageAnchor = () => {
1049
+ const pageElement = getPdfPageElement(currentPage);
1050
+ if (!pageElement) {
1051
+ return null;
1052
+ }
1053
+ const containerRect = container.getBoundingClientRect();
1054
+ const pageRect = pageElement.getBoundingClientRect();
1055
+ const pageHeight = pageElement.offsetHeight || pageRect.height;
1056
+ if (pageHeight <= 0) {
1057
+ return null;
1058
+ }
1059
+ const pageTop = pageRect.top - containerRect.top + container.scrollTop;
1060
+ const inPageRatio = (container.scrollTop - pageTop) / pageHeight;
1061
+ return {
1062
+ page: currentPage,
1063
+ inPageRatio: Math.max(0, Math.min(1, inPageRatio)),
1064
+ };
1065
+ };
1066
+ const cancelPendingUserRotationRestore = () => {
1067
+ if (!pendingUserRotationAnchor) {
1068
+ return;
1069
+ }
1070
+ pendingUserRotationAnchor = null;
1071
+ rotationOperationVersion += 1;
1072
+ };
1073
+ const cancelPendingUserZoomRestore = () => {
1074
+ if (!pendingUserZoomAnchor) {
1075
+ return;
1076
+ }
1077
+ pendingUserZoomAnchor = null;
1078
+ zoomOperationVersion += 1;
1079
+ };
1080
+ const restorePdfPageAnchor = (anchor, isActive, release) => {
1081
+ const apply = () => {
1082
+ if (destroyed || !isActive()) {
1083
+ return false;
1084
+ }
1085
+ const pageElement = getPdfPageElement(anchor.page);
1086
+ if (!pageElement) {
1087
+ return false;
1088
+ }
1089
+ const containerRect = container.getBoundingClientRect();
1090
+ const pageRect = pageElement.getBoundingClientRect();
1091
+ const pageHeight = pageElement.offsetHeight || pageRect.height;
1092
+ if (pageHeight <= 0) {
1093
+ return false;
1094
+ }
1095
+ const pageTop = pageRect.top - containerRect.top + container.scrollTop;
1096
+ const maxTop = Math.max(0, container.scrollHeight - container.clientHeight);
1097
+ suppressProgrammaticScrollEvents();
1098
+ container.scrollTop = Math.max(0, Math.min(maxTop, pageTop + anchor.inPageRatio * pageHeight));
1099
+ currentPage = anchor.page;
1100
+ syncUi();
1101
+ return true;
1102
+ };
1103
+ apply();
1104
+ void waitForPaint(targetWindow)
1105
+ .then(() => {
1106
+ apply();
1107
+ return waitForPaint(targetWindow);
1108
+ })
1109
+ .then(apply);
1110
+ targetWindow.requestAnimationFrame(() => {
1111
+ apply();
1112
+ targetWindow.requestAnimationFrame(apply);
1113
+ });
1114
+ targetWindow.setTimeout(() => {
1115
+ apply();
1116
+ if (isActive())
1117
+ release();
1118
+ }, 180);
1119
+ };
1120
+ const restoreUserRotationAnchor = (anchor, operationVersion) => restorePdfPageAnchor(anchor, () => operationVersion === rotationOperationVersion && pendingUserRotationAnchor === anchor, () => {
1121
+ pendingUserRotationAnchor = null;
1122
+ });
1123
+ const restoreUserZoomAnchor = (anchor, operationVersion) => restorePdfPageAnchor(anchor, () => operationVersion === zoomOperationVersion && pendingUserZoomAnchor === anchor, () => {
1124
+ pendingUserZoomAnchor = null;
1125
+ });
1023
1126
  const recordUserScrollIntent = () => {
1127
+ cancelPendingUserRotationRestore();
1128
+ cancelPendingUserZoomRestore();
1024
1129
  userScrollIntentUntil = Date.now() + 750;
1025
1130
  suppressScrollEventUntil = 0;
1026
1131
  markFitInteraction('user');
@@ -1190,6 +1295,7 @@ export default async function renderPdf(buffer, target, context) {
1190
1295
  if (!pdfContext.viewer) {
1191
1296
  return;
1192
1297
  }
1298
+ cancelPendingUserZoomRestore();
1193
1299
  userScrollIntentUntil = 0;
1194
1300
  suppressProgrammaticScrollEvents();
1195
1301
  autoFitWidth = true;
@@ -1201,6 +1307,7 @@ export default async function renderPdf(buffer, target, context) {
1201
1307
  };
1202
1308
  const applyPdfFit = async (request) => {
1203
1309
  var _a, _b, _c;
1310
+ cancelPendingUserZoomRestore();
1204
1311
  userScrollIntentUntil = 0;
1205
1312
  activeFitRequest = { ...request };
1206
1313
  if (!pdfContext.viewer || loadStatus !== 'ready') {
@@ -1288,13 +1395,30 @@ export default async function renderPdf(buffer, target, context) {
1288
1395
  fitToWidth('viewer');
1289
1396
  });
1290
1397
  };
1398
+ const setAnchoredScale = (scale, action, source) => {
1399
+ if (!pdfContext.viewer) {
1400
+ return;
1401
+ }
1402
+ cancelPendingUserRotationRestore();
1403
+ pendingUserZoomAnchor || (pendingUserZoomAnchor = captureCurrentPdfPageAnchor());
1404
+ const pageAnchor = pendingUserZoomAnchor;
1405
+ const operationVersion = ++zoomOperationVersion;
1406
+ if (pageAnchor) {
1407
+ suppressProgrammaticScrollEvents();
1408
+ currentPage = pageAnchor.page;
1409
+ }
1410
+ setScale(scale, action, source);
1411
+ if (pageAnchor) {
1412
+ restoreUserZoomAnchor(pageAnchor, operationVersion);
1413
+ }
1414
+ };
1291
1415
  const zoomIn = (source = 'user') => {
1292
1416
  markFitInteraction(source);
1293
- setScale(currentScale + SCALE_STEP, 'zoom-in', source);
1417
+ setAnchoredScale(currentScale + SCALE_STEP, 'zoom-in', source);
1294
1418
  };
1295
1419
  const zoomOut = (source = 'user') => {
1296
1420
  markFitInteraction(source);
1297
- setScale(currentScale - SCALE_STEP, 'zoom-out', source);
1421
+ setAnchoredScale(currentScale - SCALE_STEP, 'zoom-out', source);
1298
1422
  };
1299
1423
  const reapplyFitAfterLayout = (source, notifyViewState = true) => {
1300
1424
  if (activeFitRequest) {
@@ -1313,6 +1437,19 @@ export default async function renderPdf(buffer, target, context) {
1313
1437
  const applyRotation = (rotation, action = 'rotation-change', source = 'viewer', notifyViewState = true) => {
1314
1438
  markFitInteraction(source);
1315
1439
  const normalized = normalizeRotation(rotation);
1440
+ if (source === 'user' && pdfContext.viewer) {
1441
+ cancelPendingUserZoomRestore();
1442
+ pendingUserRotationAnchor || (pendingUserRotationAnchor = captureCurrentPdfPageAnchor());
1443
+ }
1444
+ else {
1445
+ cancelPendingUserRotationRestore();
1446
+ }
1447
+ const pageAnchor = source === 'user' ? pendingUserRotationAnchor : null;
1448
+ const operationVersion = ++rotationOperationVersion;
1449
+ if (pageAnchor) {
1450
+ suppressProgrammaticScrollEvents();
1451
+ currentPage = pageAnchor.page;
1452
+ }
1316
1453
  currentRotation = normalized;
1317
1454
  pdfThumbnails.clear();
1318
1455
  pendingPdfThumbnails.clear();
@@ -1323,6 +1460,9 @@ export default async function renderPdf(buffer, target, context) {
1323
1460
  pdfContext.viewer.pagesRotation = normalized;
1324
1461
  void waitForPaint(targetWindow).then(() => {
1325
1462
  var _a;
1463
+ if (operationVersion !== rotationOperationVersion) {
1464
+ return;
1465
+ }
1326
1466
  const refocusBoundingBoxes = () => {
1327
1467
  if (pdfBoundingBoxController.hasBoxes()) {
1328
1468
  void waitForPaint(targetWindow)
@@ -1331,6 +1471,9 @@ export default async function renderPdf(buffer, target, context) {
1331
1471
  }
1332
1472
  };
1333
1473
  if (reapplyFitAfterLayout(source, notifyViewState)) {
1474
+ if (pageAnchor) {
1475
+ restoreUserRotationAnchor(pageAnchor, operationVersion);
1476
+ }
1334
1477
  if (notifyViewState) {
1335
1478
  emitViewStateChange(action, source);
1336
1479
  }
@@ -1340,6 +1483,9 @@ export default async function renderPdf(buffer, target, context) {
1340
1483
  (_a = pdfContext.viewer) === null || _a === void 0 ? void 0 : _a.update();
1341
1484
  scheduleLegacyPageDimensionPatch();
1342
1485
  syncUi();
1486
+ if (pageAnchor) {
1487
+ restoreUserRotationAnchor(pageAnchor, operationVersion);
1488
+ }
1343
1489
  if (notifyViewState) {
1344
1490
  emitViewStateChange(action, source);
1345
1491
  }
@@ -1358,6 +1504,10 @@ export default async function renderPdf(buffer, target, context) {
1358
1504
  if (!pdfContext.viewer || !pageCount) {
1359
1505
  return;
1360
1506
  }
1507
+ if (source === 'user') {
1508
+ cancelPendingUserRotationRestore();
1509
+ cancelPendingUserZoomRestore();
1510
+ }
1361
1511
  markFitInteraction(source);
1362
1512
  const nextPage = Math.min(pageCount, Math.max(1, pageNumber));
1363
1513
  runWithStableHorizontalScroll(() => {
@@ -1604,6 +1754,12 @@ export default async function renderPdf(buffer, target, context) {
1604
1754
  }
1605
1755
  });
1606
1756
  eventBus.on('pagechanging', ({ pageNumber }) => {
1757
+ const pendingPageAnchor = pendingUserRotationAnchor || pendingUserZoomAnchor;
1758
+ if (pendingPageAnchor && pageNumber !== pendingPageAnchor.page) {
1759
+ currentPage = pendingPageAnchor.page;
1760
+ syncUi();
1761
+ return;
1762
+ }
1607
1763
  const previousPage = currentPage;
1608
1764
  currentPage = pageNumber;
1609
1765
  syncUi();
@@ -1823,12 +1979,12 @@ export default async function renderPdf(buffer, target, context) {
1823
1979
  },
1824
1980
  resetZoom: () => {
1825
1981
  markFitInteraction('api');
1826
- setScale(1, 'zoom-reset', 'api');
1982
+ setAnchoredScale(1, 'zoom-reset', 'api');
1827
1983
  return getPdfZoomState();
1828
1984
  },
1829
1985
  setZoom: scale => {
1830
1986
  markFitInteraction('api');
1831
- setScale(scale, 'zoom-change', 'api');
1987
+ setAnchoredScale(scale, 'zoom-change', 'api');
1832
1988
  return getPdfZoomState();
1833
1989
  },
1834
1990
  fit: applyPdfFit,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@file-viewer/renderer-pdf",
3
- "version": "2.2.5",
3
+ "version": "2.2.7",
4
4
  "private": false,
5
5
  "type": "module",
6
6
  "description": "Standalone PDF renderer plugin for File Viewer powered by PDF.js.",
@@ -54,7 +54,7 @@
54
54
  "LICENSE"
55
55
  ],
56
56
  "dependencies": {
57
- "@file-viewer/core": "2.2.5",
57
+ "@file-viewer/core": "2.2.7",
58
58
  "@fontsource-variable/noto-sans-sc": "5.2.10",
59
59
  "pdf-lib": "1.17.1",
60
60
  "pdfjs-dist": "5.4.624"