Merge pull request #12870 from Snuffleupagus/page-advance
Add previous/next-page functionality that takes scroll/spread-modes into account (issue 11946)
This commit is contained in:
commit
d4c4f5d4e5
@ -650,11 +650,21 @@ describe("ui_utils", function () {
|
|||||||
const hiddenWidth =
|
const hiddenWidth =
|
||||||
Math.max(0, scrollLeft - viewLeft) +
|
Math.max(0, scrollLeft - viewLeft) +
|
||||||
Math.max(0, viewRight - scrollRight);
|
Math.max(0, viewRight - scrollRight);
|
||||||
const visibleArea =
|
|
||||||
(div.clientHeight - hiddenHeight) * (div.clientWidth - hiddenWidth);
|
const fractionHeight =
|
||||||
const percent =
|
(div.clientHeight - hiddenHeight) / div.clientHeight;
|
||||||
((visibleArea * 100) / div.clientHeight / div.clientWidth) | 0;
|
const fractionWidth =
|
||||||
views.push({ id: view.id, x: viewLeft, y: viewTop, view, percent });
|
(div.clientWidth - hiddenWidth) / div.clientWidth;
|
||||||
|
const percent = (fractionHeight * fractionWidth * 100) | 0;
|
||||||
|
|
||||||
|
views.push({
|
||||||
|
id: view.id,
|
||||||
|
x: viewLeft,
|
||||||
|
y: viewTop,
|
||||||
|
view,
|
||||||
|
percent,
|
||||||
|
widthPercent: (fractionWidth * 100) | 0,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return { first: views[0], last: views[views.length - 1], views };
|
return { first: views[0], last: views[views.length - 1], views };
|
||||||
|
12
web/app.js
12
web/app.js
@ -2829,10 +2829,10 @@ function webViewerLastPage() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
function webViewerNextPage() {
|
function webViewerNextPage() {
|
||||||
PDFViewerApplication.page++;
|
PDFViewerApplication.pdfViewer.nextPage();
|
||||||
}
|
}
|
||||||
function webViewerPreviousPage() {
|
function webViewerPreviousPage() {
|
||||||
PDFViewerApplication.page--;
|
PDFViewerApplication.pdfViewer.previousPage();
|
||||||
}
|
}
|
||||||
function webViewerZoomIn() {
|
function webViewerZoomIn() {
|
||||||
PDFViewerApplication.zoomIn();
|
PDFViewerApplication.zoomIn();
|
||||||
@ -3351,13 +3351,9 @@ function webViewerKeyDown(evt) {
|
|||||||
(!turnOnlyIfPageFit || pdfViewer.currentScaleValue === "page-fit")
|
(!turnOnlyIfPageFit || pdfViewer.currentScaleValue === "page-fit")
|
||||||
) {
|
) {
|
||||||
if (turnPage > 0) {
|
if (turnPage > 0) {
|
||||||
if (PDFViewerApplication.page < PDFViewerApplication.pagesCount) {
|
pdfViewer.nextPage();
|
||||||
PDFViewerApplication.page++;
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
if (PDFViewerApplication.page > 1) {
|
pdfViewer.previousPage();
|
||||||
PDFViewerApplication.page--;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
handled = true;
|
handled = true;
|
||||||
}
|
}
|
||||||
|
@ -1507,6 +1507,140 @@ class BaseViewer {
|
|||||||
this.update();
|
this.update();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
_getPageAdvance(currentPageNumber, previous = false) {
|
||||||
|
if (this.isInPresentationMode) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
switch (this._scrollMode) {
|
||||||
|
case ScrollMode.WRAPPED: {
|
||||||
|
const { views } = this._getVisiblePages(),
|
||||||
|
pageLayout = new Map();
|
||||||
|
|
||||||
|
// Determine the current (visible) page layout.
|
||||||
|
for (const { id, y, percent, widthPercent } of views) {
|
||||||
|
if (percent === 0 || widthPercent < 100) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
let yArray = pageLayout.get(y);
|
||||||
|
if (!yArray) {
|
||||||
|
pageLayout.set(y, (yArray ||= []));
|
||||||
|
}
|
||||||
|
yArray.push(id);
|
||||||
|
}
|
||||||
|
// Find the row of the current page.
|
||||||
|
for (const yArray of pageLayout.values()) {
|
||||||
|
const currentIndex = yArray.indexOf(currentPageNumber);
|
||||||
|
if (currentIndex === -1) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
const numPages = yArray.length;
|
||||||
|
if (numPages === 1) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
// Handle documents with varying page sizes.
|
||||||
|
if (previous) {
|
||||||
|
for (let i = currentIndex - 1, ii = 0; i >= ii; i--) {
|
||||||
|
const currentId = yArray[i],
|
||||||
|
expectedId = yArray[i + 1] - 1;
|
||||||
|
if (currentId < expectedId) {
|
||||||
|
return currentPageNumber - expectedId;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
for (let i = currentIndex + 1, ii = numPages; i < ii; i++) {
|
||||||
|
const currentId = yArray[i],
|
||||||
|
expectedId = yArray[i - 1] + 1;
|
||||||
|
if (currentId > expectedId) {
|
||||||
|
return expectedId - currentPageNumber;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// The current row is "complete", advance to the previous/next one.
|
||||||
|
if (previous) {
|
||||||
|
const firstId = yArray[0];
|
||||||
|
if (firstId < currentPageNumber) {
|
||||||
|
return currentPageNumber - firstId + 1;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
const lastId = yArray[numPages - 1];
|
||||||
|
if (lastId > currentPageNumber) {
|
||||||
|
return lastId - currentPageNumber + 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case ScrollMode.HORIZONTAL: {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case ScrollMode.VERTICAL: {
|
||||||
|
if (this._spreadMode === SpreadMode.NONE) {
|
||||||
|
break; // Normal vertical scrolling.
|
||||||
|
}
|
||||||
|
const parity = this._spreadMode - 1;
|
||||||
|
|
||||||
|
if (previous && currentPageNumber % 2 !== parity) {
|
||||||
|
break; // Left-hand side page.
|
||||||
|
} else if (!previous && currentPageNumber % 2 === parity) {
|
||||||
|
break; // Right-hand side page.
|
||||||
|
}
|
||||||
|
const { views } = this._getVisiblePages(),
|
||||||
|
expectedId = previous ? currentPageNumber - 1 : currentPageNumber + 1;
|
||||||
|
|
||||||
|
for (const { id, percent, widthPercent } of views) {
|
||||||
|
if (id !== expectedId) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (percent > 0 && widthPercent === 100) {
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Go to the next page, taking scroll/spread-modes into account.
|
||||||
|
* @returns {boolean} Whether navigation occured.
|
||||||
|
*/
|
||||||
|
nextPage() {
|
||||||
|
const currentPageNumber = this._currentPageNumber,
|
||||||
|
pagesCount = this.pagesCount;
|
||||||
|
|
||||||
|
if (currentPageNumber >= pagesCount) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
const advance =
|
||||||
|
this._getPageAdvance(currentPageNumber, /* previous = */ false) || 1;
|
||||||
|
|
||||||
|
this.currentPageNumber = Math.min(currentPageNumber + advance, pagesCount);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Go to the previous page, taking scroll/spread-modes into account.
|
||||||
|
* @returns {boolean} Whether navigation occured.
|
||||||
|
*/
|
||||||
|
previousPage() {
|
||||||
|
const currentPageNumber = this._currentPageNumber;
|
||||||
|
|
||||||
|
if (currentPageNumber <= 1) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
const advance =
|
||||||
|
this._getPageAdvance(currentPageNumber, /* previous = */ true) || 1;
|
||||||
|
|
||||||
|
this.currentPageNumber = Math.max(currentPageNumber - advance, 1);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
initializeScriptingEvents() {
|
initializeScriptingEvents() {
|
||||||
if (!this.enableScripting || this._pageOpenPendingSet) {
|
if (!this.enableScripting || this._pageOpenPendingSet) {
|
||||||
return;
|
return;
|
||||||
|
@ -401,15 +401,11 @@ class PDFLinkService {
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case "NextPage":
|
case "NextPage":
|
||||||
if (this.page < this.pagesCount) {
|
this.pdfViewer.nextPage();
|
||||||
this.page++;
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case "PrevPage":
|
case "PrevPage":
|
||||||
if (this.page > 1) {
|
this.pdfViewer.previousPage();
|
||||||
this.page--;
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case "LastPage":
|
case "LastPage":
|
||||||
|
@ -138,7 +138,9 @@ class PDFPresentationMode {
|
|||||||
const totalDelta = this.mouseScrollDelta;
|
const totalDelta = this.mouseScrollDelta;
|
||||||
this._resetMouseScrollState();
|
this._resetMouseScrollState();
|
||||||
const success =
|
const success =
|
||||||
totalDelta > 0 ? this._goToPreviousPage() : this._goToNextPage();
|
totalDelta > 0
|
||||||
|
? this.pdfViewer.previousPage()
|
||||||
|
: this.pdfViewer.nextPage();
|
||||||
if (success) {
|
if (success) {
|
||||||
this.mouseScrollTimeStamp = currentTime;
|
this.mouseScrollTimeStamp = currentTime;
|
||||||
}
|
}
|
||||||
@ -153,32 +155,6 @@ class PDFPresentationMode {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @private
|
|
||||||
*/
|
|
||||||
_goToPreviousPage() {
|
|
||||||
const page = this.pdfViewer.currentPageNumber;
|
|
||||||
// If we're at the first page, we don't need to do anything.
|
|
||||||
if (page <= 1) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
this.pdfViewer.currentPageNumber = page - 1;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @private
|
|
||||||
*/
|
|
||||||
_goToNextPage() {
|
|
||||||
const page = this.pdfViewer.currentPageNumber;
|
|
||||||
// If we're at the last page, we don't need to do anything.
|
|
||||||
if (page >= this.pdfViewer.pagesCount) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
this.pdfViewer.currentPageNumber = page + 1;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @private
|
* @private
|
||||||
*/
|
*/
|
||||||
@ -315,9 +291,9 @@ class PDFPresentationMode {
|
|||||||
evt.preventDefault();
|
evt.preventDefault();
|
||||||
|
|
||||||
if (evt.shiftKey) {
|
if (evt.shiftKey) {
|
||||||
this._goToPreviousPage();
|
this.pdfViewer.previousPage();
|
||||||
} else {
|
} else {
|
||||||
this._goToNextPage();
|
this.pdfViewer.nextPage();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -422,9 +398,9 @@ class PDFPresentationMode {
|
|||||||
delta = dy;
|
delta = dy;
|
||||||
}
|
}
|
||||||
if (delta > 0) {
|
if (delta > 0) {
|
||||||
this._goToPreviousPage();
|
this.pdfViewer.previousPage();
|
||||||
} else if (delta < 0) {
|
} else if (delta < 0) {
|
||||||
this._goToNextPage();
|
this.pdfViewer.nextPage();
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -121,6 +121,10 @@ class PDFSinglePageViewer extends BaseViewer {
|
|||||||
_updateScrollMode() {}
|
_updateScrollMode() {}
|
||||||
|
|
||||||
_updateSpreadMode() {}
|
_updateSpreadMode() {}
|
||||||
|
|
||||||
|
_getPageAdvance() {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export { PDFSinglePageViewer };
|
export { PDFSinglePageViewer };
|
||||||
|
@ -13,6 +13,7 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import { ScrollMode, SpreadMode } from "./ui_utils.js";
|
||||||
import { BaseViewer } from "./base_viewer.js";
|
import { BaseViewer } from "./base_viewer.js";
|
||||||
import { shadow } from "pdfjs-lib";
|
import { shadow } from "pdfjs-lib";
|
||||||
|
|
||||||
@ -57,7 +58,11 @@ class PDFViewer extends BaseViewer {
|
|||||||
if (page.percent < 100) {
|
if (page.percent < 100) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (page.id === currentId) {
|
if (
|
||||||
|
page.id === currentId &&
|
||||||
|
this._scrollMode === ScrollMode.VERTICAL &&
|
||||||
|
this._spreadMode === SpreadMode.NONE
|
||||||
|
) {
|
||||||
stillFullyVisible = true;
|
stillFullyVisible = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -563,17 +563,18 @@ function getVisibleElements({
|
|||||||
Math.max(0, top - currentHeight) + Math.max(0, viewBottom - bottom);
|
Math.max(0, top - currentHeight) + Math.max(0, viewBottom - bottom);
|
||||||
const hiddenWidth =
|
const hiddenWidth =
|
||||||
Math.max(0, left - currentWidth) + Math.max(0, viewRight - right);
|
Math.max(0, left - currentWidth) + Math.max(0, viewRight - right);
|
||||||
const percent =
|
|
||||||
(((viewHeight - hiddenHeight) * (viewWidth - hiddenWidth) * 100) /
|
const fractionHeight = (viewHeight - hiddenHeight) / viewHeight,
|
||||||
viewHeight /
|
fractionWidth = (viewWidth - hiddenWidth) / viewWidth;
|
||||||
viewWidth) |
|
const percent = (fractionHeight * fractionWidth * 100) | 0;
|
||||||
0;
|
|
||||||
visible.push({
|
visible.push({
|
||||||
id: view.id,
|
id: view.id,
|
||||||
x: currentWidth,
|
x: currentWidth,
|
||||||
y: currentHeight,
|
y: currentHeight,
|
||||||
view,
|
view,
|
||||||
percent,
|
percent,
|
||||||
|
widthPercent: (fractionWidth * 100) | 0,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user