mirror of
https://github.com/mozilla/pdf.js.git
synced 2025-04-19 22:58:07 +02:00
Merge pull request #14112 from Snuffleupagus/ScrollMode-PAGE
Add a new Page scrolling mode (issue 2638, 8952, 10907)
This commit is contained in:
commit
e504e81cda
15 changed files with 303 additions and 270 deletions
|
@ -61,6 +61,8 @@ cursor_text_select_tool_label=Text Selection Tool
|
|||
cursor_hand_tool.title=Enable Hand Tool
|
||||
cursor_hand_tool_label=Hand Tool
|
||||
|
||||
scroll_page.title=Use Page Scrolling
|
||||
scroll_page_label=Page Scrolling
|
||||
scroll_vertical.title=Use Vertical Scrolling
|
||||
scroll_vertical_label=Vertical Scrolling
|
||||
scroll_horizontal.title=Use Horizontal Scrolling
|
||||
|
|
|
@ -61,6 +61,8 @@ cursor_text_select_tool_label=Textmarkeringsverktyg
|
|||
cursor_hand_tool.title=Aktivera handverktyg
|
||||
cursor_hand_tool_label=Handverktyg
|
||||
|
||||
scroll_page.title=Använd sidrullning
|
||||
scroll_page_label=Sidrullning
|
||||
scroll_vertical.title=Använd vertikal rullning
|
||||
scroll_vertical_label=Vertikal rullning
|
||||
scroll_horizontal.title=Använd horisontell rullning
|
||||
|
|
14
web/app.js
14
web/app.js
|
@ -16,7 +16,7 @@
|
|||
|
||||
import {
|
||||
animationStarted,
|
||||
apiPageLayoutToSpreadMode,
|
||||
apiPageLayoutToViewerModes,
|
||||
apiPageModeToSidebarView,
|
||||
AutomationEventBus,
|
||||
AutoPrintRegExp,
|
||||
|
@ -1300,8 +1300,16 @@ const PDFViewerApplication = {
|
|||
if (pageMode && sidebarView === SidebarView.UNKNOWN) {
|
||||
sidebarView = apiPageModeToSidebarView(pageMode);
|
||||
}
|
||||
if (pageLayout && spreadMode === SpreadMode.UNKNOWN) {
|
||||
spreadMode = apiPageLayoutToSpreadMode(pageLayout);
|
||||
if (
|
||||
pageLayout &&
|
||||
scrollMode === ScrollMode.UNKNOWN &&
|
||||
spreadMode === SpreadMode.UNKNOWN
|
||||
) {
|
||||
const modes = apiPageLayoutToViewerModes(pageLayout);
|
||||
// TODO: Try to improve page-switching when using the mouse-wheel
|
||||
// and/or arrow-keys before allowing the document to control this.
|
||||
// scrollMode = modes.scrollMode;
|
||||
spreadMode = modes.spreadMode;
|
||||
}
|
||||
|
||||
this.setInitialView(hash, {
|
||||
|
|
|
@ -445,14 +445,6 @@ class BaseViewer {
|
|||
return this.pdfDocument ? this._pagesCapability.promise : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
get _viewerElement() {
|
||||
// In most viewers, e.g. `PDFViewer`, this should return `this.viewer`.
|
||||
throw new Error("Not implemented: _viewerElement");
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
|
@ -538,6 +530,10 @@ class BaseViewer {
|
|||
this._firstPageCapability.resolve(firstPdfPage);
|
||||
this._optionalContentConfigPromise = optionalContentConfigPromise;
|
||||
|
||||
const viewerElement =
|
||||
this._scrollMode === ScrollMode.PAGE
|
||||
? this._scrollModePageState.shadowViewer
|
||||
: this.viewer;
|
||||
const scale = this.currentScale;
|
||||
const viewport = firstPdfPage.getViewport({
|
||||
scale: scale * PixelsPerInch.PDF_TO_CSS_UNITS,
|
||||
|
@ -552,7 +548,7 @@ class BaseViewer {
|
|||
|
||||
for (let pageNum = 1; pageNum <= pagesCount; ++pageNum) {
|
||||
const pageView = new PDFPageView({
|
||||
container: this._viewerElement,
|
||||
container: viewerElement,
|
||||
eventBus: this.eventBus,
|
||||
id: pageNum,
|
||||
scale,
|
||||
|
@ -582,7 +578,12 @@ class BaseViewer {
|
|||
firstPageView.setPdfPage(firstPdfPage);
|
||||
this.linkService.cachePageRef(1, firstPdfPage.ref);
|
||||
}
|
||||
if (this._spreadMode !== SpreadMode.NONE) {
|
||||
|
||||
if (this._scrollMode === ScrollMode.PAGE) {
|
||||
// Since the pages are placed in a `DocumentFragment`, ensure that
|
||||
// the current page becomes visible upon loading of the document.
|
||||
this._ensurePageViewVisible();
|
||||
} else if (this._spreadMode !== SpreadMode.NONE) {
|
||||
this._updateSpreadMode();
|
||||
}
|
||||
|
||||
|
@ -684,8 +685,16 @@ class BaseViewer {
|
|||
this._onePageRenderedCapability = createPromiseCapability();
|
||||
this._pagesCapability = createPromiseCapability();
|
||||
this._scrollMode = ScrollMode.VERTICAL;
|
||||
this._previousScrollMode = ScrollMode.UNKNOWN;
|
||||
this._spreadMode = SpreadMode.NONE;
|
||||
|
||||
this._scrollModePageState = {
|
||||
shadowViewer: document.createDocumentFragment(),
|
||||
previousPageNumber: 1,
|
||||
scrollDown: true,
|
||||
pages: [],
|
||||
};
|
||||
|
||||
if (this._onBeforeDraw) {
|
||||
this.eventBus._off("pagerender", this._onBeforeDraw);
|
||||
this._onBeforeDraw = null;
|
||||
|
@ -700,6 +709,71 @@ class BaseViewer {
|
|||
this._updateScrollMode();
|
||||
}
|
||||
|
||||
_ensurePageViewVisible() {
|
||||
if (this._scrollMode !== ScrollMode.PAGE) {
|
||||
throw new Error("_ensurePageViewVisible: Invalid scrollMode value.");
|
||||
}
|
||||
const pageNumber = this._currentPageNumber,
|
||||
state = this._scrollModePageState,
|
||||
viewer = this.viewer;
|
||||
|
||||
// Remove the currently active pages, if any, from the viewer.
|
||||
if (viewer.hasChildNodes()) {
|
||||
// Temporarily remove all the pages from the DOM.
|
||||
viewer.textContent = "";
|
||||
|
||||
for (const pageView of this._pages) {
|
||||
state.shadowViewer.appendChild(pageView.div);
|
||||
}
|
||||
}
|
||||
state.pages.length = 0;
|
||||
|
||||
if (this._spreadMode === SpreadMode.NONE) {
|
||||
// Finally, append the new page to the viewer.
|
||||
const pageView = this._pages[pageNumber - 1];
|
||||
viewer.appendChild(pageView.div);
|
||||
|
||||
state.pages.push(pageView);
|
||||
} else {
|
||||
const pageIndexSet = new Set(),
|
||||
parity = this._spreadMode - 1;
|
||||
|
||||
// Determine the pageIndices in the new spread.
|
||||
if (pageNumber % 2 !== parity) {
|
||||
// Left-hand side page.
|
||||
pageIndexSet.add(pageNumber - 1);
|
||||
pageIndexSet.add(pageNumber);
|
||||
} else {
|
||||
// Right-hand side page.
|
||||
pageIndexSet.add(pageNumber - 2);
|
||||
pageIndexSet.add(pageNumber - 1);
|
||||
}
|
||||
|
||||
// Finally, append the new pages to the viewer and apply the spreadMode.
|
||||
let spread = null;
|
||||
for (let i = 0, ii = this._pages.length; i < ii; ++i) {
|
||||
if (!pageIndexSet.has(i)) {
|
||||
continue;
|
||||
}
|
||||
if (spread === null) {
|
||||
spread = document.createElement("div");
|
||||
spread.className = "spread";
|
||||
viewer.appendChild(spread);
|
||||
} else if (i % 2 === parity) {
|
||||
spread = spread.cloneNode(false);
|
||||
viewer.appendChild(spread);
|
||||
}
|
||||
const pageView = this._pages[i];
|
||||
spread.appendChild(pageView.div);
|
||||
|
||||
state.pages.push(pageView);
|
||||
}
|
||||
}
|
||||
|
||||
state.scrollDown = pageNumber >= state.previousPageNumber;
|
||||
state.previousPageNumber = pageNumber;
|
||||
}
|
||||
|
||||
_scrollUpdate() {
|
||||
if (this.pagesCount === 0) {
|
||||
return;
|
||||
|
@ -708,6 +782,29 @@ class BaseViewer {
|
|||
}
|
||||
|
||||
_scrollIntoView({ pageDiv, pageSpot = null, pageNumber = null }) {
|
||||
if (this._scrollMode === ScrollMode.PAGE) {
|
||||
if (pageNumber) {
|
||||
// Ensure that `this._currentPageNumber` is correct.
|
||||
this._setCurrentPageNumber(pageNumber);
|
||||
}
|
||||
this._ensurePageViewVisible();
|
||||
// Ensure that rendering always occurs, to avoid showing a blank page,
|
||||
// even if the current position doesn't change when the page is scrolled.
|
||||
this.update();
|
||||
}
|
||||
|
||||
if (!pageSpot && !this.isInPresentationMode) {
|
||||
const left = pageDiv.offsetLeft + pageDiv.clientLeft;
|
||||
const right = left + pageDiv.clientWidth;
|
||||
const { scrollLeft, clientWidth } = this.container;
|
||||
if (
|
||||
this._scrollMode === ScrollMode.HORIZONTAL ||
|
||||
left < scrollLeft ||
|
||||
right > scrollLeft + clientWidth
|
||||
) {
|
||||
pageSpot = { left: 0, top: 0 };
|
||||
}
|
||||
}
|
||||
scrollIntoView(pageDiv, pageSpot);
|
||||
}
|
||||
|
||||
|
@ -772,8 +869,7 @@ class BaseViewer {
|
|||
get _pageWidthScaleFactor() {
|
||||
if (
|
||||
this._spreadMode !== SpreadMode.NONE &&
|
||||
this._scrollMode !== ScrollMode.HORIZONTAL &&
|
||||
!this.isInPresentationMode
|
||||
this._scrollMode !== ScrollMode.HORIZONTAL
|
||||
) {
|
||||
return 2;
|
||||
}
|
||||
|
@ -794,7 +890,7 @@ class BaseViewer {
|
|||
let hPadding = noPadding ? 0 : SCROLLBAR_PADDING;
|
||||
let vPadding = noPadding ? 0 : VERTICAL_PADDING;
|
||||
|
||||
if (!noPadding && this._isScrollModeHorizontal) {
|
||||
if (!noPadding && this._scrollMode === ScrollMode.HORIZONTAL) {
|
||||
[hPadding, vPadding] = [vPadding, hPadding]; // Swap the padding values.
|
||||
}
|
||||
const pageWidthScale =
|
||||
|
@ -1047,10 +1143,6 @@ class BaseViewer {
|
|||
};
|
||||
}
|
||||
|
||||
_updateHelper(visiblePages) {
|
||||
throw new Error("Not implemented: _updateHelper");
|
||||
}
|
||||
|
||||
update() {
|
||||
const visible = this._getVisiblePages();
|
||||
const visiblePages = visible.views,
|
||||
|
@ -1064,7 +1156,28 @@ class BaseViewer {
|
|||
|
||||
this.renderingQueue.renderHighestPriority(visible);
|
||||
|
||||
this._updateHelper(visiblePages); // Run any class-specific update code.
|
||||
if (!this.isInPresentationMode) {
|
||||
const isSimpleLayout =
|
||||
this._spreadMode === SpreadMode.NONE &&
|
||||
(this._scrollMode === ScrollMode.PAGE ||
|
||||
this._scrollMode === ScrollMode.VERTICAL);
|
||||
let currentId = this._currentPageNumber;
|
||||
let stillFullyVisible = false;
|
||||
|
||||
for (const page of visiblePages) {
|
||||
if (page.percent < 100) {
|
||||
break;
|
||||
}
|
||||
if (page.id === currentId && isSimpleLayout) {
|
||||
stillFullyVisible = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!stillFullyVisible) {
|
||||
currentId = visiblePages[0].id;
|
||||
}
|
||||
this._setCurrentPageNumber(currentId);
|
||||
}
|
||||
|
||||
this._updateLocation(visible.first);
|
||||
this.eventBus.dispatch("updateviewarea", {
|
||||
|
@ -1081,14 +1194,6 @@ class BaseViewer {
|
|||
this.container.focus();
|
||||
}
|
||||
|
||||
get _isScrollModeHorizontal() {
|
||||
// Used to ensure that pre-rendering of the next/previous page works
|
||||
// correctly, since Scroll/Spread modes are ignored in Presentation Mode.
|
||||
return this.isInPresentationMode
|
||||
? false
|
||||
: this._scrollMode === ScrollMode.HORIZONTAL;
|
||||
}
|
||||
|
||||
get _isContainerRtl() {
|
||||
return getComputedStyle(this.container).direction === "rtl";
|
||||
}
|
||||
|
@ -1115,9 +1220,8 @@ class BaseViewer {
|
|||
|
||||
/**
|
||||
* Helper method for `this._getVisiblePages`. Should only ever be used when
|
||||
* the viewer can only display a single page at a time, for example in:
|
||||
* - `PDFSinglePageViewer`.
|
||||
* - `PDFViewer` with Presentation Mode active.
|
||||
* the viewer can only display a single page at a time, for example:
|
||||
* - When PresentationMode is active.
|
||||
*/
|
||||
_getCurrentVisiblePage() {
|
||||
if (!this.pagesCount) {
|
||||
|
@ -1138,12 +1242,24 @@ class BaseViewer {
|
|||
}
|
||||
|
||||
_getVisiblePages() {
|
||||
if (this.isInPresentationMode) {
|
||||
// The algorithm in `getVisibleElements` doesn't work in all browsers and
|
||||
// configurations (e.g. Chrome) when PresentationMode is active.
|
||||
return this._getCurrentVisiblePage();
|
||||
}
|
||||
const views =
|
||||
this._scrollMode === ScrollMode.PAGE
|
||||
? this._scrollModePageState.pages
|
||||
: this._pages,
|
||||
horizontal = this._scrollMode === ScrollMode.HORIZONTAL,
|
||||
rtl = horizontal && this._isContainerRtl;
|
||||
|
||||
return getVisibleElements({
|
||||
scrollEl: this.container,
|
||||
views: this._pages,
|
||||
views,
|
||||
sortByVisibility: true,
|
||||
horizontal: this._isScrollModeHorizontal,
|
||||
rtl: this._isScrollModeHorizontal && this._isContainerRtl,
|
||||
horizontal,
|
||||
rtl,
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -1245,15 +1361,25 @@ class BaseViewer {
|
|||
return promise;
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
get _scrollAhead() {
|
||||
switch (this._scrollMode) {
|
||||
case ScrollMode.PAGE:
|
||||
return this._scrollModePageState.scrollDown;
|
||||
case ScrollMode.HORIZONTAL:
|
||||
return this.scroll.right;
|
||||
}
|
||||
return this.scroll.down;
|
||||
}
|
||||
|
||||
forceRendering(currentlyVisiblePages) {
|
||||
const visiblePages = currentlyVisiblePages || this._getVisiblePages();
|
||||
const scrollAhead = this._isScrollModeHorizontal
|
||||
? this.scroll.right
|
||||
: this.scroll.down;
|
||||
const scrollAhead = this._scrollAhead;
|
||||
const preRenderExtra =
|
||||
this._scrollMode === ScrollMode.VERTICAL &&
|
||||
this._spreadMode !== SpreadMode.NONE &&
|
||||
!this.isInPresentationMode;
|
||||
this._scrollMode !== ScrollMode.HORIZONTAL;
|
||||
|
||||
const pageView = this.renderingQueue.getHighestPriority(
|
||||
visiblePages,
|
||||
|
@ -1492,6 +1618,8 @@ class BaseViewer {
|
|||
if (!isValidScrollMode(mode)) {
|
||||
throw new Error(`Invalid scroll mode: ${mode}`);
|
||||
}
|
||||
this._previousScrollMode = this._scrollMode;
|
||||
|
||||
this._scrollMode = mode;
|
||||
this.eventBus.dispatch("scrollmodechanged", { source: this, mode });
|
||||
|
||||
|
@ -1511,6 +1639,14 @@ class BaseViewer {
|
|||
if (!this.pdfDocument || !pageNumber) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (scrollMode === ScrollMode.PAGE) {
|
||||
this._ensurePageViewVisible();
|
||||
} else if (this._previousScrollMode === ScrollMode.PAGE) {
|
||||
// Ensure that the current spreadMode is still applied correctly when
|
||||
// the *previous* scrollMode was `ScrollMode.PAGE`.
|
||||
this._updateSpreadMode();
|
||||
}
|
||||
// Non-numeric scale values can be sensitive to the scroll orientation.
|
||||
// Call this before re-scrolling to the current page, to ensure that any
|
||||
// changes in scale don't move the current page.
|
||||
|
@ -1552,32 +1688,40 @@ class BaseViewer {
|
|||
}
|
||||
const viewer = this.viewer,
|
||||
pages = this._pages;
|
||||
// Temporarily remove all the pages from the DOM.
|
||||
viewer.textContent = "";
|
||||
|
||||
if (this._spreadMode === SpreadMode.NONE) {
|
||||
for (let i = 0, iMax = pages.length; i < iMax; ++i) {
|
||||
viewer.appendChild(pages[i].div);
|
||||
}
|
||||
if (this._scrollMode === ScrollMode.PAGE) {
|
||||
this._ensurePageViewVisible();
|
||||
} else {
|
||||
const parity = this._spreadMode - 1;
|
||||
let spread = null;
|
||||
for (let i = 0, iMax = pages.length; i < iMax; ++i) {
|
||||
if (spread === null) {
|
||||
spread = document.createElement("div");
|
||||
spread.className = "spread";
|
||||
viewer.appendChild(spread);
|
||||
} else if (i % 2 === parity) {
|
||||
spread = spread.cloneNode(false);
|
||||
viewer.appendChild(spread);
|
||||
// Temporarily remove all the pages from the DOM.
|
||||
viewer.textContent = "";
|
||||
|
||||
if (this._spreadMode === SpreadMode.NONE) {
|
||||
for (let i = 0, ii = pages.length; i < ii; ++i) {
|
||||
viewer.appendChild(pages[i].div);
|
||||
}
|
||||
} else {
|
||||
const parity = this._spreadMode - 1;
|
||||
let spread = null;
|
||||
for (let i = 0, ii = pages.length; i < ii; ++i) {
|
||||
if (spread === null) {
|
||||
spread = document.createElement("div");
|
||||
spread.className = "spread";
|
||||
viewer.appendChild(spread);
|
||||
} else if (i % 2 === parity) {
|
||||
spread = spread.cloneNode(false);
|
||||
viewer.appendChild(spread);
|
||||
}
|
||||
spread.appendChild(pages[i].div);
|
||||
}
|
||||
spread.appendChild(pages[i].div);
|
||||
}
|
||||
}
|
||||
|
||||
if (!pageNumber) {
|
||||
return;
|
||||
}
|
||||
// Non-numeric scale values can be sensitive to the scroll orientation.
|
||||
// Call this before re-scrolling to the current page, to ensure that any
|
||||
// changes in scale don't move the current page.
|
||||
if (this._currentScaleValue && isNaN(this._currentScaleValue)) {
|
||||
this._setScale(this._currentScaleValue, true);
|
||||
}
|
||||
|
@ -1589,9 +1733,6 @@ class BaseViewer {
|
|||
* @private
|
||||
*/
|
||||
_getPageAdvance(currentPageNumber, previous = false) {
|
||||
if (this.isInPresentationMode) {
|
||||
return 1;
|
||||
}
|
||||
switch (this._scrollMode) {
|
||||
case ScrollMode.WRAPPED: {
|
||||
const { views } = this._getVisiblePages(),
|
||||
|
@ -1655,6 +1796,7 @@ class BaseViewer {
|
|||
case ScrollMode.HORIZONTAL: {
|
||||
break;
|
||||
}
|
||||
case ScrollMode.PAGE:
|
||||
case ScrollMode.VERTICAL: {
|
||||
if (this._spreadMode === SpreadMode.NONE) {
|
||||
break; // Normal vertical scrolling.
|
||||
|
|
1
web/images/secondaryToolbarButton-scrollPage.svg
Normal file
1
web/images/secondaryToolbarButton-scrollPage.svg
Normal file
|
@ -0,0 +1 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" height="16" width="16"><path d="M9.5 4c1 0 1.5.5 1.5 1.5v5c0 1-.5 1.5-1.5 1.5h-3c-1 0-1.5-.5-1.5-1.5v-5C5 4.5 5.5 4 6.5 4z"/></svg>
|
After Width: | Height: | Size: 171 B |
|
@ -13,7 +13,12 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { normalizeWheelEventDelta, PresentationModeState } from "./ui_utils.js";
|
||||
import {
|
||||
normalizeWheelEventDelta,
|
||||
PresentationModeState,
|
||||
ScrollMode,
|
||||
SpreadMode,
|
||||
} from "./ui_utils.js";
|
||||
|
||||
const DELAY_BEFORE_RESETTING_SWITCH_IN_PROGRESS = 1500; // in ms
|
||||
const DELAY_BEFORE_HIDING_CONTROLS = 3000; // in ms
|
||||
|
@ -84,8 +89,10 @@ class PDFPresentationMode {
|
|||
}
|
||||
|
||||
this.args = {
|
||||
page: this.pdfViewer.currentPageNumber,
|
||||
previousScale: this.pdfViewer.currentScaleValue,
|
||||
pageNumber: this.pdfViewer.currentPageNumber,
|
||||
scaleValue: this.pdfViewer.currentScaleValue,
|
||||
scrollMode: this.pdfViewer.scrollMode,
|
||||
spreadMode: this.pdfViewer.spreadMode,
|
||||
};
|
||||
|
||||
return true;
|
||||
|
@ -203,7 +210,9 @@ class PDFPresentationMode {
|
|||
// Ensure that the correct page is scrolled into view when entering
|
||||
// Presentation Mode, by waiting until fullscreen mode in enabled.
|
||||
setTimeout(() => {
|
||||
this.pdfViewer.currentPageNumber = this.args.page;
|
||||
this.pdfViewer.scrollMode = ScrollMode.PAGE;
|
||||
this.pdfViewer.spreadMode = SpreadMode.NONE;
|
||||
this.pdfViewer.currentPageNumber = this.args.pageNumber;
|
||||
this.pdfViewer.currentScaleValue = "page-fit";
|
||||
}, 0);
|
||||
|
||||
|
@ -221,7 +230,7 @@ class PDFPresentationMode {
|
|||
* @private
|
||||
*/
|
||||
_exit() {
|
||||
const page = this.pdfViewer.currentPageNumber;
|
||||
const pageNumber = this.pdfViewer.currentPageNumber;
|
||||
this.container.classList.remove(ACTIVE_SELECTOR);
|
||||
|
||||
// Ensure that the correct page is scrolled into view when exiting
|
||||
|
@ -231,8 +240,10 @@ class PDFPresentationMode {
|
|||
this._removeFullscreenChangeListeners();
|
||||
this._notifyStateChange();
|
||||
|
||||
this.pdfViewer.currentScaleValue = this.args.previousScale;
|
||||
this.pdfViewer.currentPageNumber = page;
|
||||
this.pdfViewer.scrollMode = this.args.scrollMode;
|
||||
this.pdfViewer.spreadMode = this.args.spreadMode;
|
||||
this.pdfViewer.currentScaleValue = this.args.scaleValue;
|
||||
this.pdfViewer.currentPageNumber = pageNumber;
|
||||
this.args = null;
|
||||
}, 0);
|
||||
|
||||
|
|
|
@ -14,7 +14,7 @@
|
|||
*/
|
||||
|
||||
import { createPromiseCapability, shadow } from "pdfjs-lib";
|
||||
import { apiPageLayoutToSpreadMode } from "./ui_utils.js";
|
||||
import { apiPageLayoutToViewerModes } from "./ui_utils.js";
|
||||
import { RenderingStates } from "./pdf_rendering_queue.js";
|
||||
|
||||
/**
|
||||
|
@ -287,7 +287,11 @@ class PDFScriptingManager {
|
|||
console.error(value);
|
||||
break;
|
||||
case "layout":
|
||||
this._pdfViewer.spreadMode = apiPageLayoutToSpreadMode(value);
|
||||
if (isInPresentationMode) {
|
||||
return;
|
||||
}
|
||||
const modes = apiPageLayoutToViewerModes(value);
|
||||
this._pdfViewer.spreadMode = modes.spreadMode;
|
||||
break;
|
||||
case "page-num":
|
||||
this._pdfViewer.currentPageNumber = value + 1;
|
||||
|
|
|
@ -1,130 +0,0 @@
|
|||
/* Copyright 2017 Mozilla Foundation
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { BaseViewer } from "./base_viewer.js";
|
||||
import { shadow } from "pdfjs-lib";
|
||||
|
||||
class PDFSinglePageViewer extends BaseViewer {
|
||||
constructor(options) {
|
||||
super(options);
|
||||
|
||||
this.eventBus._on("pagesinit", evt => {
|
||||
// Since the pages are placed in a `DocumentFragment`, make sure that
|
||||
// the current page becomes visible upon loading of the document.
|
||||
this._ensurePageViewVisible();
|
||||
});
|
||||
}
|
||||
|
||||
get _viewerElement() {
|
||||
// Since we only want to display *one* page at a time when using the
|
||||
// `PDFSinglePageViewer`, we cannot append them to the `viewer` DOM element.
|
||||
// Instead, they are placed in a `DocumentFragment`, and only the current
|
||||
// page is displayed in the viewer (refer to `this._ensurePageViewVisible`).
|
||||
return shadow(this, "_viewerElement", this._shadowViewer);
|
||||
}
|
||||
|
||||
get _pageWidthScaleFactor() {
|
||||
return 1;
|
||||
}
|
||||
|
||||
_resetView() {
|
||||
super._resetView();
|
||||
this._previousPageNumber = 1;
|
||||
this._shadowViewer = document.createDocumentFragment();
|
||||
this._updateScrollDown = null;
|
||||
}
|
||||
|
||||
_ensurePageViewVisible() {
|
||||
const pageView = this._pages[this._currentPageNumber - 1];
|
||||
const previousPageView = this._pages[this._previousPageNumber - 1];
|
||||
|
||||
const viewerNodes = this.viewer.childNodes;
|
||||
switch (viewerNodes.length) {
|
||||
case 0: // Should *only* occur on initial loading.
|
||||
this.viewer.appendChild(pageView.div);
|
||||
break;
|
||||
case 1: // The normal page-switching case.
|
||||
if (viewerNodes[0] !== previousPageView.div) {
|
||||
throw new Error(
|
||||
"_ensurePageViewVisible: Unexpected previously visible page."
|
||||
);
|
||||
}
|
||||
if (pageView === previousPageView) {
|
||||
break; // The correct page is already visible.
|
||||
}
|
||||
// Switch visible pages, and reset the viewerContainer scroll position.
|
||||
this._shadowViewer.appendChild(previousPageView.div);
|
||||
this.viewer.appendChild(pageView.div);
|
||||
|
||||
this.container.scrollTop = 0;
|
||||
break;
|
||||
default:
|
||||
throw new Error(
|
||||
"_ensurePageViewVisible: Only one page should be visible at a time."
|
||||
);
|
||||
}
|
||||
this._previousPageNumber = this._currentPageNumber;
|
||||
}
|
||||
|
||||
_scrollUpdate() {
|
||||
if (this._updateScrollDown) {
|
||||
this._updateScrollDown();
|
||||
}
|
||||
super._scrollUpdate();
|
||||
}
|
||||
|
||||
_scrollIntoView({ pageDiv, pageSpot = null, pageNumber = null }) {
|
||||
if (pageNumber) {
|
||||
// Ensure that `this._currentPageNumber` is correct.
|
||||
this._setCurrentPageNumber(pageNumber);
|
||||
}
|
||||
const scrolledDown = this._currentPageNumber >= this._previousPageNumber;
|
||||
|
||||
this._ensurePageViewVisible();
|
||||
// Ensure that rendering always occurs, to avoid showing a blank page,
|
||||
// even if the current position doesn't change when the page is scrolled.
|
||||
this.update();
|
||||
|
||||
super._scrollIntoView({ pageDiv, pageSpot, pageNumber });
|
||||
|
||||
// Since scrolling is tracked using `requestAnimationFrame`, update the
|
||||
// scroll direction during the next `this._scrollUpdate` invocation.
|
||||
this._updateScrollDown = () => {
|
||||
this.scroll.down = scrolledDown;
|
||||
this._updateScrollDown = null;
|
||||
};
|
||||
}
|
||||
|
||||
_getVisiblePages() {
|
||||
return this._getCurrentVisiblePage();
|
||||
}
|
||||
|
||||
_updateHelper(visiblePages) {}
|
||||
|
||||
get _isScrollModeHorizontal() {
|
||||
// The Scroll/Spread modes are never used in `PDFSinglePageViewer`.
|
||||
return shadow(this, "_isScrollModeHorizontal", false);
|
||||
}
|
||||
|
||||
_updateScrollMode() {}
|
||||
|
||||
_updateSpreadMode() {}
|
||||
|
||||
_getPageAdvance() {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
export { PDFSinglePageViewer };
|
|
@ -31,6 +31,7 @@ import {
|
|||
} from "./xfa_layer_builder.js";
|
||||
import { EventBus, ProgressBar } from "./ui_utils.js";
|
||||
import { PDFLinkService, SimpleLinkService } from "./pdf_link_service.js";
|
||||
import { PDFSinglePageViewer, PDFViewer } from "./pdf_viewer.js";
|
||||
import { DownloadManager } from "./download_manager.js";
|
||||
import { GenericL10n } from "./genericl10n.js";
|
||||
import { NullL10n } from "./l10n_utils.js";
|
||||
|
@ -38,8 +39,6 @@ import { PDFFindController } from "./pdf_find_controller.js";
|
|||
import { PDFHistory } from "./pdf_history.js";
|
||||
import { PDFPageView } from "./pdf_page_view.js";
|
||||
import { PDFScriptingManager } from "./pdf_scripting_manager.js";
|
||||
import { PDFSinglePageViewer } from "./pdf_single_page_viewer.js";
|
||||
import { PDFViewer } from "./pdf_viewer.js";
|
||||
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
const pdfjsVersion = PDFJSDev.eval("BUNDLE_VERSION");
|
||||
|
|
|
@ -15,63 +15,25 @@
|
|||
|
||||
import { ScrollMode, SpreadMode } from "./ui_utils.js";
|
||||
import { BaseViewer } from "./base_viewer.js";
|
||||
import { shadow } from "pdfjs-lib";
|
||||
|
||||
class PDFViewer extends BaseViewer {
|
||||
get _viewerElement() {
|
||||
return shadow(this, "_viewerElement", this.viewer);
|
||||
class PDFViewer extends BaseViewer {}
|
||||
|
||||
class PDFSinglePageViewer extends BaseViewer {
|
||||
_resetView() {
|
||||
super._resetView();
|
||||
this._scrollMode = ScrollMode.PAGE;
|
||||
this._spreadMode = SpreadMode.NONE;
|
||||
}
|
||||
|
||||
_scrollIntoView({ pageDiv, pageSpot = null, pageNumber = null }) {
|
||||
if (!pageSpot && !this.isInPresentationMode) {
|
||||
const left = pageDiv.offsetLeft + pageDiv.clientLeft;
|
||||
const right = left + pageDiv.clientWidth;
|
||||
const { scrollLeft, clientWidth } = this.container;
|
||||
if (
|
||||
this._isScrollModeHorizontal ||
|
||||
left < scrollLeft ||
|
||||
right > scrollLeft + clientWidth
|
||||
) {
|
||||
pageSpot = { left: 0, top: 0 };
|
||||
}
|
||||
}
|
||||
super._scrollIntoView({ pageDiv, pageSpot, pageNumber });
|
||||
}
|
||||
// eslint-disable-next-line accessor-pairs
|
||||
set scrollMode(mode) {}
|
||||
|
||||
_getVisiblePages() {
|
||||
if (this.isInPresentationMode) {
|
||||
// The algorithm in `getVisibleElements` doesn't work in all browsers and
|
||||
// configurations (e.g. Chrome) when Presentation Mode is active.
|
||||
return this._getCurrentVisiblePage();
|
||||
}
|
||||
return super._getVisiblePages();
|
||||
}
|
||||
_updateScrollMode() {}
|
||||
|
||||
_updateHelper(visiblePages) {
|
||||
if (this.isInPresentationMode) {
|
||||
return;
|
||||
}
|
||||
let currentId = this._currentPageNumber;
|
||||
let stillFullyVisible = false;
|
||||
// eslint-disable-next-line accessor-pairs
|
||||
set spreadMode(mode) {}
|
||||
|
||||
for (const page of visiblePages) {
|
||||
if (page.percent < 100) {
|
||||
break;
|
||||
}
|
||||
if (
|
||||
page.id === currentId &&
|
||||
this._scrollMode === ScrollMode.VERTICAL &&
|
||||
this._spreadMode === SpreadMode.NONE
|
||||
) {
|
||||
stillFullyVisible = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!stillFullyVisible) {
|
||||
currentId = visiblePages[0].id;
|
||||
}
|
||||
this._setCurrentPageNumber(currentId);
|
||||
}
|
||||
_updateSpreadMode() {}
|
||||
}
|
||||
|
||||
export { PDFViewer };
|
||||
export { PDFSinglePageViewer, PDFViewer };
|
||||
|
|
|
@ -15,7 +15,7 @@
|
|||
|
||||
import { SCROLLBAR_PADDING, ScrollMode, SpreadMode } from "./ui_utils.js";
|
||||
import { CursorTool } from "./pdf_cursor_tools.js";
|
||||
import { PDFSinglePageViewer } from "./pdf_single_page_viewer.js";
|
||||
import { PDFSinglePageViewer } from "./pdf_viewer.js";
|
||||
|
||||
/**
|
||||
* @typedef {Object} SecondaryToolbarOptions
|
||||
|
@ -93,6 +93,12 @@ class SecondaryToolbar {
|
|||
eventDetails: { tool: CursorTool.HAND },
|
||||
close: true,
|
||||
},
|
||||
{
|
||||
element: options.scrollPageButton,
|
||||
eventName: "switchscrollmode",
|
||||
eventDetails: { mode: ScrollMode.PAGE },
|
||||
close: true,
|
||||
},
|
||||
{
|
||||
element: options.scrollVerticalButton,
|
||||
eventName: "switchscrollmode",
|
||||
|
@ -247,6 +253,10 @@ class SecondaryToolbar {
|
|||
|
||||
_bindScrollModeListener(buttons) {
|
||||
function scrollModeChanged({ mode }) {
|
||||
buttons.scrollPageButton.classList.toggle(
|
||||
"toggled",
|
||||
mode === ScrollMode.PAGE
|
||||
);
|
||||
buttons.scrollVerticalButton.classList.toggle(
|
||||
"toggled",
|
||||
mode === ScrollMode.VERTICAL
|
||||
|
|
|
@ -57,6 +57,7 @@ const ScrollMode = {
|
|||
VERTICAL: 0, // Default value.
|
||||
HORIZONTAL: 1,
|
||||
WRAPPED: 2,
|
||||
PAGE: 3,
|
||||
};
|
||||
|
||||
const SpreadMode = {
|
||||
|
@ -952,21 +953,32 @@ function getActiveOrFocusedElement() {
|
|||
* necessary Scroll/Spread modes (since SinglePage, TwoPageLeft,
|
||||
* and TwoPageRight all suggests using non-continuous scrolling).
|
||||
* @param {string} mode - The API PageLayout value.
|
||||
* @returns {number} A value from {SpreadMode}.
|
||||
* @returns {Object}
|
||||
*/
|
||||
function apiPageLayoutToSpreadMode(layout) {
|
||||
function apiPageLayoutToViewerModes(layout) {
|
||||
let scrollMode = ScrollMode.VERTICAL,
|
||||
spreadMode = SpreadMode.NONE;
|
||||
|
||||
switch (layout) {
|
||||
case "SinglePage":
|
||||
scrollMode = ScrollMode.PAGE;
|
||||
break;
|
||||
case "OneColumn":
|
||||
return SpreadMode.NONE;
|
||||
case "TwoColumnLeft":
|
||||
break;
|
||||
case "TwoPageLeft":
|
||||
return SpreadMode.ODD;
|
||||
case "TwoColumnRight":
|
||||
scrollMode = ScrollMode.PAGE;
|
||||
/* falls through */
|
||||
case "TwoColumnLeft":
|
||||
spreadMode = SpreadMode.ODD;
|
||||
break;
|
||||
case "TwoPageRight":
|
||||
return SpreadMode.EVEN;
|
||||
scrollMode = ScrollMode.PAGE;
|
||||
/* falls through */
|
||||
case "TwoColumnRight":
|
||||
spreadMode = SpreadMode.EVEN;
|
||||
break;
|
||||
}
|
||||
return SpreadMode.NONE; // Default value.
|
||||
return { scrollMode, spreadMode };
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -995,7 +1007,7 @@ function apiPageModeToSidebarView(mode) {
|
|||
|
||||
export {
|
||||
animationStarted,
|
||||
apiPageLayoutToSpreadMode,
|
||||
apiPageLayoutToViewerModes,
|
||||
apiPageModeToSidebarView,
|
||||
approximateFraction,
|
||||
AutomationEventBus,
|
||||
|
|
|
@ -94,6 +94,7 @@
|
|||
--secondaryToolbarButton-rotateCw-icon: url(images/secondaryToolbarButton-rotateCw.svg);
|
||||
--secondaryToolbarButton-selectTool-icon: url(images/secondaryToolbarButton-selectTool.svg);
|
||||
--secondaryToolbarButton-handTool-icon: url(images/secondaryToolbarButton-handTool.svg);
|
||||
--secondaryToolbarButton-scrollPage-icon: url(images/secondaryToolbarButton-scrollPage.svg);
|
||||
--secondaryToolbarButton-scrollVertical-icon: url(images/secondaryToolbarButton-scrollVertical.svg);
|
||||
--secondaryToolbarButton-scrollHorizontal-icon: url(images/secondaryToolbarButton-scrollHorizontal.svg);
|
||||
--secondaryToolbarButton-scrollWrapped-icon: url(images/secondaryToolbarButton-scrollWrapped.svg);
|
||||
|
@ -1196,6 +1197,11 @@ html[dir="rtl"] .secondaryToolbarButton > span {
|
|||
mask-image: var(--secondaryToolbarButton-handTool-icon);
|
||||
}
|
||||
|
||||
.secondaryToolbarButton.scrollPage::before {
|
||||
-webkit-mask-image: var(--secondaryToolbarButton-scrollPage-icon);
|
||||
mask-image: var(--secondaryToolbarButton-scrollPage-icon);
|
||||
}
|
||||
|
||||
.secondaryToolbarButton.scrollVertical::before {
|
||||
-webkit-mask-image: var(--secondaryToolbarButton-scrollVertical-icon);
|
||||
mask-image: var(--secondaryToolbarButton-scrollVertical-icon);
|
||||
|
|
|
@ -196,31 +196,34 @@ See https://github.com/adobe-type-tools/cmap-resources
|
|||
|
||||
<div class="horizontalToolbarSeparator"></div>
|
||||
|
||||
<button id="scrollVertical" class="secondaryToolbarButton scrollModeButtons scrollVertical toggled" title="Use Vertical Scrolling" tabindex="62" data-l10n-id="scroll_vertical">
|
||||
<button id="scrollPage" class="secondaryToolbarButton scrollModeButtons scrollPage" title="Use Page Scrolling" tabindex="62" data-l10n-id="scroll_page">
|
||||
<span data-l10n-id="scroll_page_label">Page Scrolling</span>
|
||||
</button>
|
||||
<button id="scrollVertical" class="secondaryToolbarButton scrollModeButtons scrollVertical toggled" title="Use Vertical Scrolling" tabindex="63" data-l10n-id="scroll_vertical">
|
||||
<span data-l10n-id="scroll_vertical_label">Vertical Scrolling</span>
|
||||
</button>
|
||||
<button id="scrollHorizontal" class="secondaryToolbarButton scrollModeButtons scrollHorizontal" title="Use Horizontal Scrolling" tabindex="63" data-l10n-id="scroll_horizontal">
|
||||
<button id="scrollHorizontal" class="secondaryToolbarButton scrollModeButtons scrollHorizontal" title="Use Horizontal Scrolling" tabindex="64" data-l10n-id="scroll_horizontal">
|
||||
<span data-l10n-id="scroll_horizontal_label">Horizontal Scrolling</span>
|
||||
</button>
|
||||
<button id="scrollWrapped" class="secondaryToolbarButton scrollModeButtons scrollWrapped" title="Use Wrapped Scrolling" tabindex="64" data-l10n-id="scroll_wrapped">
|
||||
<button id="scrollWrapped" class="secondaryToolbarButton scrollModeButtons scrollWrapped" title="Use Wrapped Scrolling" tabindex="65" data-l10n-id="scroll_wrapped">
|
||||
<span data-l10n-id="scroll_wrapped_label">Wrapped Scrolling</span>
|
||||
</button>
|
||||
|
||||
<div class="horizontalToolbarSeparator scrollModeButtons"></div>
|
||||
|
||||
<button id="spreadNone" class="secondaryToolbarButton spreadModeButtons spreadNone toggled" title="Do not join page spreads" tabindex="65" data-l10n-id="spread_none">
|
||||
<button id="spreadNone" class="secondaryToolbarButton spreadModeButtons spreadNone toggled" title="Do not join page spreads" tabindex="66" data-l10n-id="spread_none">
|
||||
<span data-l10n-id="spread_none_label">No Spreads</span>
|
||||
</button>
|
||||
<button id="spreadOdd" class="secondaryToolbarButton spreadModeButtons spreadOdd" title="Join page spreads starting with odd-numbered pages" tabindex="66" data-l10n-id="spread_odd">
|
||||
<button id="spreadOdd" class="secondaryToolbarButton spreadModeButtons spreadOdd" title="Join page spreads starting with odd-numbered pages" tabindex="67" data-l10n-id="spread_odd">
|
||||
<span data-l10n-id="spread_odd_label">Odd Spreads</span>
|
||||
</button>
|
||||
<button id="spreadEven" class="secondaryToolbarButton spreadModeButtons spreadEven" title="Join page spreads starting with even-numbered pages" tabindex="67" data-l10n-id="spread_even">
|
||||
<button id="spreadEven" class="secondaryToolbarButton spreadModeButtons spreadEven" title="Join page spreads starting with even-numbered pages" tabindex="68" data-l10n-id="spread_even">
|
||||
<span data-l10n-id="spread_even_label">Even Spreads</span>
|
||||
</button>
|
||||
|
||||
<div class="horizontalToolbarSeparator spreadModeButtons"></div>
|
||||
|
||||
<button id="documentProperties" class="secondaryToolbarButton documentProperties" title="Document Properties…" tabindex="68" data-l10n-id="document_properties">
|
||||
<button id="documentProperties" class="secondaryToolbarButton documentProperties" title="Document Properties…" tabindex="69" data-l10n-id="document_properties">
|
||||
<span data-l10n-id="document_properties_label">Document Properties…</span>
|
||||
</button>
|
||||
</div>
|
||||
|
|
|
@ -114,6 +114,7 @@ function getViewerConfiguration() {
|
|||
pageRotateCcwButton: document.getElementById("pageRotateCcw"),
|
||||
cursorSelectToolButton: document.getElementById("cursorSelectTool"),
|
||||
cursorHandToolButton: document.getElementById("cursorHandTool"),
|
||||
scrollPageButton: document.getElementById("scrollPage"),
|
||||
scrollVerticalButton: document.getElementById("scrollVertical"),
|
||||
scrollHorizontalButton: document.getElementById("scrollHorizontal"),
|
||||
scrollWrappedButton: document.getElementById("scrollWrapped"),
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue