1
0
Fork 0
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:
Tim van der Meij 2021-10-15 21:40:51 +02:00 committed by GitHub
commit e504e81cda
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
15 changed files with 303 additions and 270 deletions

View file

@ -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

View file

@ -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

View file

@ -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, {

View file

@ -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.

View 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

View file

@ -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);

View file

@ -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;

View file

@ -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 };

View file

@ -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");

View file

@ -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 };

View file

@ -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

View file

@ -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,

View file

@ -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);

View file

@ -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>

View file

@ -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"),