Add a new Page scrolling mode (issue 2638, 8952, 10907)

This implements a new Page scrolling mode, essentially bringing (and extending) the functionality from `PDFSinglePageViewer` into the regular `PDFViewer`-class. Compared to `PDFSinglePageViewer`, which as its name suggests will only display one page at a time, in the `PDFViewer`-implementation this new Page scrolling mode also support spreadModes properly (somewhat similar to e.g. Adobe Reader).

Given the size and scope of these changes, I've tried to focus on implementing the basic functionality. Hence there's room for further clean-up and/or improvements, including e.g. simplifying the CSS/JS related to PresentationMode and implementing easier page-switching with the mouse-wheel/arrow-keys.
This commit is contained in:
Jonas Jenwald 2021-10-07 14:04:41 +02:00
parent 3945965605
commit 511458fbbc
14 changed files with 296 additions and 244 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

@ -13,118 +13,25 @@
* limitations under the License.
*/
import { ScrollMode, SpreadMode } from "./ui_utils.js";
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;
this._scrollMode = ScrollMode.PAGE;
this._spreadMode = SpreadMode.NONE;
}
_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);
}
// eslint-disable-next-line accessor-pairs
set scrollMode(mode) {}
_updateScrollMode() {}
_updateSpreadMode() {}
// eslint-disable-next-line accessor-pairs
set spreadMode(mode) {}
_getPageAdvance() {
return 1;
}
_updateSpreadMode() {}
}
export { PDFSinglePageViewer };

View File

@ -13,65 +13,8 @@
* limitations under the License.
*/
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);
}
_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 });
}
_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();
}
_updateHelper(visiblePages) {
if (this.isInPresentationMode) {
return;
}
let currentId = this._currentPageNumber;
let stillFullyVisible = false;
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);
}
}
class PDFViewer extends BaseViewer {}
export { PDFViewer };

View File

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