Horizontal Display Mode

Hello,

I would like to implement an horizontal continuous display mode. I found some traces from 2017 about an old sample that demonstrated how to do that using a custom Display Mode. But that sample does not appear to be available anymore.

https://groups.google.com/g/pdfnet-webviewer/c/-_KzNV0Ynmk/m/H0Jk1aRRBQAJ

https://groups.google.com/g/pdfnet-webviewer/c/s8NSYQPJjBk/m/-wHgann0BAAJ

I tried to implement the custom DisplayMode myself but I don’t know where to start.

Can you please help?

Vincent Robert

Hi Vincent,

Would you be able to share the code for DisplayMode you implemented so far? Here is one more example of custom display mode https://groups.google.com/g/pdfnet-webviewer/c/9gAnAU4ecVk/m/UkWtJtTIAwAJ

Sardor Isakov
Software Developer
PDFTron Systems, Inc.

I do not have code yet as I was not able to implement the Custom Functions at all since they are not documented.

Thank you for the link but this code appears to be for an older PDFTron version and I had a hard time trying to convert it to work with v6, without success.

Hi Vincent,

There’re a few changes that we need to do besides converting the code in the above link to work in WebViewer v6.

  1. Add the custom display mode to https://github.com/PDFTron/webviewer-ui/blob/7.0/src/constants/displayModeObjects.js
    This is to make sure that the ViewControlsOverlay component can render properly.

  2. Add the custom display mode to this file and set the value to be 0.
    This is to make sure that the page number doesn’t change when users scroll up or down

  3. Use the following code in the config file

window.addEventListener(‘viewerLoaded’, function() {
const readerControl = window.readerControl;
const docViewer = readerControl.docViewer;
const displayMode = new window.CoreControls.DisplayMode(docViewer, ‘horizontal’, true);

displayMode.setCustomFunctions({
windowToPage(windowPt, pageIndex) {
const zoom = docViewer.getZoom();
const pc = document.getElementById(‘pageContainer’ + pageIndex);
const scrollViewContainer = docViewer.getScrollViewElement();
const offset = getOffset(pc);

const scaledPt = {
x: windowPt.x - offset.left - scrollViewContainer.scrollLeft,
y: windowPt.y - offset.top - scrollViewContainer.scrollTop,
};

return {
pageIndex,
x: scaledPt.x / zoom,
y: scaledPt.y / zoom,
};
},

pageToWindow(pagePt, pageIndex) {
const zoom = docViewer.getZoom();
const scaledPt = {
x: pagePt.x * zoom,
y: pagePt.y * zoom,
};

const pc = document.getElementById(‘pageContainer’ + pageIndex);
const offset = getOffset(pc);
const scrollViewContainer = docViewer.getScrollViewElement();

return {
x: scaledPt.x + offset.left + scrollViewContainer.scrollLeft,
y: scaledPt.y + offset.top + scrollViewContainer.scrollTop,
};
},

getSelectedPages(mousePt1, mousePt2) {
let firstPageIndex = null;
let lastPageIndex = null;

const doc = docViewer.getDocument();
const pageCount = docViewer.getPageCount();
for (let pageIndex = 1; pageIndex < pageCount; pageIndex++) {
if (firstPageIndex !== null && lastPageIndex !== null) {
break;
}

const page = doc.getPageInfo(pageIndex);
const pc = document.getElementById(‘pageContainer’ + pageIndex);
const offset = getOffset(pc);
const scrollViewContainer = docViewer.getScrollViewElement();

const pageRect = {
x1: offset.left + scrollViewContainer.scrollLeft,
y1: offset.top + scrollViewContainer.scrollTop,
x2: offset.left + page.width * docViewer.getZoom() + scrollViewContainer.scrollLeft,
y2: offset.top + page.height * docViewer.getZoom() + scrollViewContainer.scrollTop,
};

if (
firstPageIndex === null &&
mousePt1.x <= pageRect.x2 &&
mousePt1.x >= pageRect.x1 &&
mousePt1.y <= pageRect.y2 &&
mousePt1.y >= pageRect.y1
) {
firstPageIndex = pageIndex;
}

if (
lastPageIndex === null &&
mousePt2.x <= pageRect.x2 &&
mousePt2.x >= pageRect.x1 &&
mousePt2.y <= pageRect.y2 &&
mousePt2.y >= pageRect.y1
) {
lastPageIndex = pageIndex;
}
}

if (firstPageIndex > lastPageIndex) {
[firstPageIndex, lastPageIndex] = [lastPageIndex, firstPageIndex];
}

return {
first: firstPageIndex,
last: lastPageIndex,
};
},

getVisiblePages() {
const pageIndexes = [];

const scrollContainer = docViewer.getScrollViewElement();
const offset = getOffset(scrollContainer);
const boundingRect = scrollContainer.getBoundingClientRect();
const viewportTop = scrollContainer.scrollTop + offset.top;
const viewportBottom = viewportTop + boundingRect.height;
const viewportLeft = scrollContainer.scrollLeft + offset.left;
const viewportRight = viewportLeft + boundingRect.width;

const doc = docViewer.getDocument();
let page;

for (let pageIndex = 0; pageIndex < docViewer.getPageCount(); pageIndex++) {
page = doc.getPageInfo(pageIndex);

const pt1 = this.pageToWindow(
{
x: 0,
y: 0,
},
pageIndex
);

const pt2 = this.pageToWindow(
{
x: page.width,
y: page.height,
},
pageIndex
);

if (
(pt1.x < pt2.x ? pt1.x : pt2.x) <= viewportRight &&
(pt1.x < pt2.x ? pt2.x : pt1.x) >= viewportLeft &&
(pt1.y < pt2.y ? pt1.y : pt2.y) <= viewportBottom &&
(pt1.y < pt2.y ? pt2.y : pt1.y) >= viewportTop
) {
pageIndexes.push(pageIndex);
}
}

return pageIndexes;
},

getPageTransform(pageIndex) {
const page = docViewer.getDocument().getPageInfo(pageIndex);

return {
x: 0,
y: 0,
width: page.width,
height: page.height,
};
},

createPageSections() {
const doc = docViewer.getDocument();
let totalWidth = 0;

const pageCount = docViewer.getPageCount();

for (let pageIndex = 0; pageIndex < pageCount; pageIndex++) {
const page = doc.getPageInfo(pageIndex);
totalWidth += page.width;
createPageSection(pageIndex, page.height, page.width);
}

// update the view with the new total width that we’ve calculated
const viewElement = docViewer.getViewerElement();
viewElement.style.width = ${totalWidth * docViewer.getZoom() + pageCount * docViewer.getMargin() * 2}px;

docViewer.updateVisiblePages();
},
});

docViewer.setViewportRenderMode(false);
docViewer.getDisplayModeManager().setDisplayMode(displayMode);
docViewer.defaults.DisplayMode = displayMode;

function createPageSection(pageIndex, pageHeight, pageWidth) {
const pageSection = document.createElement(‘div’);
pageSection.id = pageSection${pageIndex};
pageSection.style.width = ${Math.floor(pageWidth * docViewer.getZoom())}px;
pageSection.style.height = ${Math.floor(pageHeight * docViewer.getZoom())}px;
pageSection.style.margin = ${docViewer.getMargin()}px;
pageSection.style.float = ‘left’;
pageSection.style.position = ‘relative’;

const pageContainer = document.createElement(‘div’);
pageContainer.id = pageContainer${pageIndex};
pageContainer.classList.add(‘pageContainer’);
pageContainer.style.zIndex = 1;
pageContainer.style.width = ${Math.floor(pageWidth * docViewer.getZoom())}px;
pageContainer.style.height = ${Math.floor(pageHeight * docViewer.getZoom())}px;

pageSection.appendChild(pageContainer);

const viewerElement = docViewer.getViewerElement();
viewerElement.append(pageSection);
}

function getOffset(ele) {
const rect = ele.getBoundingClientRect();

return {
top: rect.top + window.pageYOffset,
left: rect.left + window.pageXOffset,
};
}
});

window.addEventListener(‘documentLoaded’, function() {
const readerControl = window.readerControl;
const docViewer = readerControl.docViewer;

const doc = docViewer.getDocument();
const scrollViewContainer = docViewer.getScrollViewElement();
const boundingRect = scrollViewContainer.getBoundingClientRect();

var fitZoom = (boundingRect.height - getScrollbarSize() - docViewer.getMargin() * 2) / doc.getPageInfo(1).height;
readerControl.setZoomLevel(fitZoom);
readerControl.docViewer.setCurrentPage(1);
});

function getScrollbarSize() {
const wrapperDiv = document.createElement(‘div’);
wrapperDiv.style.width = ‘50px’;
wrapperDiv.style.height = ‘50px’;
wrapperDiv.style.overflow = ‘hidden’;
wrapperDiv.style.position = ‘absolute’;
wrapperDiv.style.top = ‘-200px’;
wrapperDiv.style.left = ‘-200px’;

const innerDiv = document.createElement(‘div’);
innerDiv.style.height = ‘100px’;

wrapperDiv.appendChild(innerDiv);
document.body.appendChild(wrapperDiv);

const w1 = wrapperDiv.clientWidth;
wrapperDiv.style[‘overflow-y’] = ‘auto’;
const w2 = wrapperDiv.clientWidth;

document.body.removeChild(wrapperDiv);

return w1 - w2;
}

Best Regards,
Zhijie Zhang
Software Developer
PDFTron Systems Inc.

For anyone who’s looking for the code for WebViewer 7.0.

window.addEventListener('viewerLoaded', function() {
const readerControl = window.readerControl;
const docViewer = readerControl.docViewer;
const displayMode = new window.CoreControls.DisplayMode(docViewer, 'horizontal', true);

displayMode.setCustomFunctions({
windowToPage: function(windowPt, pageNumber) {
const zoom = docViewer.getZoom();
const pc = document.getElementById('pageContainer' + pageNumber);
const scrollViewContainer = docViewer.getScrollViewElement();
const offset = getOffset(pc);

const scaledPt = {
x: windowPt.x - offset.left - scrollViewContainer.scrollLeft,
y: windowPt.y - offset.top - scrollViewContainer.scrollTop,
};

return {
pageNumber: pageNumber,
x: scaledPt.x / zoom,
y: scaledPt.y / zoom,
};
},

pageToWindow: function(pagePt, pageNumber) {
const zoom = docViewer.getZoom();
const scaledPt = {
x: pagePt.x * zoom,
y: pagePt.y * zoom,
};

const pc = document.getElementById('pageContainer' + pageNumber);
const offset = getOffset(pc);
const scrollViewContainer = docViewer.getScrollViewElement();

return {
x: scaledPt.x + offset.left + scrollViewContainer.scrollLeft,
y: scaledPt.y + offset.top + scrollViewContainer.scrollTop,
};
},

getSelectedPages: function(mousePt1, mousePt2) {
let firstPageNum = null;
let lastPageNum = null;

const doc = docViewer.getDocument();
const pageCount = docViewer.getPageCount();
for (let pageNum = 1; pageNum <= pageCount; pageNum++) {
if (firstPageNum !== null && lastPageNum !== null) {
break;
}

const page = doc.getPageInfo(pageNum);
const pc = document.getElementById('pageContainer' + pageNum);
const offset = getOffset(pc);
const scrollViewContainer = docViewer.getScrollViewElement();

const pageRect = {
x1: offset.left + scrollViewContainer.scrollLeft,
y1: offset.top + scrollViewContainer.scrollTop,
x2: offset.left + page.width * docViewer.getZoom() + scrollViewContainer.scrollLeft,
y2: offset.top + page.height * docViewer.getZoom() + scrollViewContainer.scrollTop,
};

if (
firstPageNum === null &&
mousePt1.x <= pageRect.x2 &&
mousePt1.x >= pageRect.x1 &&
mousePt1.y <= pageRect.y2 &&
mousePt1.y >= pageRect.y1
) {
firstPageNum = pageNum;
}

if (
lastPageNum === null &&
mousePt2.x <= pageRect.x2 &&
mousePt2.x >= pageRect.x1 &&
mousePt2.y <= pageRect.y2 &&
mousePt2.y >= pageRect.y1
) {
lastPageNum = pageNum;
}
}

if (firstPageNum > lastPageNum) {
[firstPageNum, lastPageNum] = [lastPageNum, firstPageNum];
}

return {
first: firstPageNum,
last: lastPageNum,
};
},

getVisiblePages: function() {
const pageNumbers = [];

const scrollContainer = docViewer.getScrollViewElement();
const offset = getOffset(scrollContainer);
const boundingRect = scrollContainer.getBoundingClientRect();
const viewportTop = scrollContainer.scrollTop + offset.top;
const viewportBottom = viewportTop + boundingRect.height;
const viewportLeft = scrollContainer.scrollLeft + offset.left;
const viewportRight = viewportLeft + boundingRect.width;

const doc = docViewer.getDocument();
let page;

for (let pageNum = 1; pageNum <= docViewer.getPageCount(); pageNum++) {
page = doc.getPageInfo(pageNum);

const pt1 = this.pageToWindow(
{
x: 0,
y: 0,
},
pageNum
);

const pt2 = this.pageToWindow(
{
x: page.width,
y: page.height,
},
pageNum
);

if (
(pt1.x < pt2.x ? pt1.x : pt2.x) <= viewportRight &&
(pt1.x < pt2.x ? pt2.x : pt1.x) >= viewportLeft &&
(pt1.y < pt2.y ? pt1.y : pt2.y) <= viewportBottom &&
(pt1.y < pt2.y ? pt2.y : pt1.y) >= viewportTop
) {
pageNumbers.push(pageNum);
}
}

return pageNumbers;
},

getPageTransform: function(pageNumber) {
const page = docViewer.getDocument().getPageInfo(pageNumber);

return {
x: 0,
y: 0,
width: page.width,
height: page.height,
};
},

createPageSections: function() {
const doc = docViewer.getDocument();
let totalWidth = 0;

const pageCount = docViewer.getPageCount();

for (let pageNum = 1; pageNum <= pageCount; pageNum++) {
const page = doc.getPageInfo(pageNum);
totalWidth += page.width;
createPageSection(pageNum, page.height, page.width);
}

// update the view with the new total width that we've calculated
const viewElement = docViewer.getViewerElement();
viewElement.style.width = `${totalWidth * docViewer.getZoom() +
pageCount * docViewer.getMargin() * 2}px`;

docViewer.updateVisiblePages();
},
});

docViewer.setViewportRenderMode(false);
docViewer.getDisplayModeManager().setDisplayMode(displayMode);
docViewer.defaults.DisplayMode = displayMode;

function createPageSection(pageNumber, pageHeight, pageWidth) {
const pageSection = document.createElement('div');
pageSection.id = `pageSection${pageNumber}`;
pageSection.style.width = `${Math.floor(pageWidth * docViewer.getZoom())}px`;
pageSection.style.height = `${Math.floor(pageHeight * docViewer.getZoom())}px`;
pageSection.style.margin = `${docViewer.getMargin()}px`;
pageSection.style.float = 'left';
pageSection.style.position = 'relative';

const pageContainer = document.createElement('div');
pageContainer.id = `pageContainer${pageNumber}`;
pageContainer.classList.add('pageContainer');
pageContainer.style.zIndex = 1;
pageContainer.style.width = `${Math.floor(pageWidth * docViewer.getZoom())}px`;
pageContainer.style.height = `${Math.floor(pageHeight * docViewer.getZoom())}px`;

pageSection.appendChild(pageContainer);

const viewerElement = docViewer.getViewerElement();
viewerElement.append(pageSection);
}

function getOffset(ele) {
const rect = ele.getBoundingClientRect();

return {
top: rect.top + window.pageYOffset,
left: rect.left + window.pageXOffset,
};
}
});

window.addEventListener('documentLoaded', function() {
const readerControl = window.readerControl;
const docViewer = readerControl.docViewer;

const doc = docViewer.getDocument();
const scrollViewContainer = docViewer.getScrollViewElement();
const boundingRect = scrollViewContainer.getBoundingClientRect();

var fitZoom = (boundingRect.height - getScrollbarSize() - docViewer.getMargin() * 2) / doc.getPageInfo(1).height;
readerControl.setZoomLevel(fitZoom);
readerControl.docViewer.setCurrentPage(1);
});

function getScrollbarSize() {
const wrapperDiv = document.createElement('div');
wrapperDiv.style.width = '50px';
wrapperDiv.style.height = '50px';
wrapperDiv.style.overflow = 'hidden';
wrapperDiv.style.position = 'absolute';
wrapperDiv.style.top = '-200px';
wrapperDiv.style.left = '-200px';

const innerDiv = document.createElement('div');
innerDiv.style.height = '100px';

wrapperDiv.appendChild(innerDiv);
document.body.appendChild(wrapperDiv);

const w1 = wrapperDiv.clientWidth;
wrapperDiv.style['overflow-y'] = 'auto';
const w2 = wrapperDiv.clientWidth;

document.body.removeChild(wrapperDiv);

return w1 - w2;
}

The main change is that the code uses page numbers instead of page indexes.

Zhijie Zhang
Software Developer
PDFTron System Inc.