diff --git a/pdf.js b/pdf.js
index 30337b898..378c17580 100644
--- a/pdf.js
+++ b/pdf.js
@@ -3607,8 +3607,8 @@ var Page = (function pagePage() {
gfx.execute(this.code, xref, resources);
gfx.endDrawing();
},
- rotatePoint: function pageRotatePoint(x, y) {
- var rotate = this.rotate;
+ rotatePoint: function pageRotatePoint(x, y, reverse) {
+ var rotate = reverse ? (360 - this.rotate) : this.rotate;
switch (rotate) {
case 180:
return {x: this.width - x, y: y};
@@ -3616,6 +3616,7 @@ var Page = (function pagePage() {
return {x: this.width - y, y: this.height - x};
case 270:
return {x: y, y: x};
+ case 360:
case 0:
default:
return {x: x, y: this.height - y};
diff --git a/web/images/bookmark.svg b/web/images/bookmark.svg
new file mode 100644
index 000000000..2c1fa130d
--- /dev/null
+++ b/web/images/bookmark.svg
@@ -0,0 +1,661 @@
+
+
+
+
diff --git a/web/viewer.css b/web/viewer.css
index e72bdc286..6c7b551e8 100644
--- a/web/viewer.css
+++ b/web/viewer.css
@@ -34,6 +34,10 @@ body {
margin: 4px;
}
+#controls > a > img {
+ margin: 2px;
+}
+
#controls > button {
line-height: 32px;
}
diff --git a/web/viewer.html b/web/viewer.html
index 5da063b91..f761c94fe 100644
--- a/web/viewer.html
+++ b/web/viewer.html
@@ -70,6 +70,10 @@
+
+
+
+
--
diff --git a/web/viewer.js b/web/viewer.js
index 91562baf4..0808fbce9 100644
--- a/web/viewer.js
+++ b/web/viewer.js
@@ -89,16 +89,23 @@ var PDFView = {
var pages = this.pages;
var input = document.getElementById('pageNumber');
if (!(0 < val && val <= pages.length)) {
- input.value = this.page;
+ var event = document.createEvent('UIEvents');
+ event.initUIEvent('pagechange', false, false, window, 0);
+ event.pageNumber = this.page;
+ window.dispatchEvent(event);
return;
}
currentPageNumber = val;
- document.getElementById('previous').disabled = (val == 1);
- document.getElementById('next').disabled = (val == pages.length);
- if (input.value != val) {
- input.value = val;
- }
+ var event = document.createEvent('UIEvents');
+ event.initUIEvent('pagechange', false, false, window, 0);
+ event.pageNumber = val;
+ window.dispatchEvent(event);
+
+ // checking if the this.page was called from the updateViewarea function:
+ // avoiding the creation of two "set page" method (internal and public)
+ if (updateViewarea.inProgress)
+ return;
pages[val - 1].scrollIntoView();
},
@@ -147,12 +154,20 @@ var PDFView = {
if (typeof dest === 'string')
return '#' + escape(dest);
if (dest instanceof Array) {
- var destRef = dest[0]; // see nevigateTo method for dest format
+ var destRef = dest[0]; // see navigateTo method for dest format
var pageNumber = destRef instanceof Object ?
this.pagesRefMap[destRef.num + ' ' + destRef.gen + ' R'] :
(destRef + 1);
if (pageNumber) {
- return '#page=' + pageNumber + '&dest=' + dest.slice(1).join(',');
+ var pdfOpenParams = '#page=' + pageNumber;
+ if (isName(dest[1], 'XYZ')) {
+ var scale = (dest[4] || this.currentScale);
+ pdfOpenParams += '&zoom=' + (scale * 100);
+ if (dest[2] || dest[3]) {
+ pdfOpenParams += ',' + (dest[2] || 0) + ',' + (dest[3] || 0);
+ }
+ }
+ return pdfOpenParams;
}
}
return '';
@@ -228,10 +243,32 @@ var PDFView = {
return;
if (hash.indexOf('=') >= 0) {
- // TODO more complex hashes, for now catching page=XX only
- var m = /\bpage=(\d+)/.exec(hash);
- if (m && m[1] > 0)
- this.page = m[1];
+ // parsing query string
+ var paramsPairs = hash.split('&');
+ var params = {};
+ for (var i = 0; i < paramsPairs.length; ++i) {
+ var paramPair = paramsPairs[i].split('=');
+ params[paramPair[0]] = paramPair[1];
+ }
+ // borrowing syntax from "Parameters for Opening PDF Files"
+ if ('nameddest' in params) {
+ PDFView.navigateTo(params.nameddest);
+ return;
+ }
+ if ('page' in params) {
+ var pageNumber = (params.page | 0) || 1;
+ this.page = pageNumber;
+ if ('zoom' in params) {
+ var zoomArgs = params.zoom.split(','); // scale,left,top
+ // building destination array
+ var dest = [null, new Name('XYZ'), (zoomArgs[1] | 0),
+ (zoomArgs[2] | 0), (zoomArgs[0] | 0) / 100];
+ var currentPage = this.pages[pageNumber - 1];
+ currentPage.scrollIntoView(dest);
+ } else
+ this.page = page; // simple page
+ return;
+ }
} else if (/^\d+$/.test(hash)) // page number
this.page = hash;
else // named destination
@@ -341,6 +378,11 @@ var PageView = function pageView(container, content, id, pageWidth, pageHeight,
}
}
+ this.getPagePoint = function pageViewGetPagePoint(x, y) {
+ var scale = PDFView.currentScale;
+ return this.content.rotatePoint(x / scale, y / scale);
+ };
+
this.scrollIntoView = function pageViewScrollIntoView(dest) {
if (!dest) {
div.scrollIntoView(true);
@@ -390,7 +432,7 @@ var PageView = function pageView(container, content, id, pageWidth, pageHeight,
this.content.rotatePoint(x + width, y + height)
];
- if (scale)
+ if (scale && scale !== PDFView.currentScale)
PDFView.setScale(scale, true);
setTimeout(function pageViewScrollIntoViewRelayout() {
@@ -578,13 +620,21 @@ function updateViewarea() {
if (!visiblePages.length)
return;
+ updateViewarea.inProgress = true; // used in "set page"
var currentId = PDFView.page;
var firstPage = visiblePages[0];
- var lastPage = visiblePages[visiblePages.length - 1];
- if (currentId > lastPage.id && lastPage.y > window.pageYOffset)
- PDFView.page = lastPage.id;
- else if (currentId < firstPage.id)
- PDFView.page = firstPage.id;
+ PDFView.page = firstPage.id;
+ updateViewarea.inProgress = false;
+
+ var kViewerTopMargin = 52;
+ var pageNumber = firstPage.id;
+ var pdfOpenParams = '#page=' + pageNumber;
+ pdfOpenParams += '&zoom=' + Math.round(PDFView.currentScale * 100);
+ var currentPage = PDFView.pages[pageNumber - 1];
+ var topLeft = currentPage.getPagePoint(window.pageXOffset,
+ window.pageYOffset - firstPage.y - kViewerTopMargin);
+ pdfOpenParams += ',' + Math.round(topLeft.x) + ',' + Math.round(topLeft.y);
+ document.getElementById('viewBookmark').href = pdfOpenParams;
}
window.addEventListener('scroll', function webViewerScroll(evt) {
@@ -625,6 +675,9 @@ window.addEventListener('change', function webViewerChange(evt) {
fileReader.readAsBinaryString(file);
document.title = file.name;
+
+ // URL does not reflect proper document location - hiding bookmark icon.
+ document.getElementById('viewBookmark').style.display = 'none';
}, true);
window.addEventListener('transitionend', function webViewerTransitionend(evt) {
@@ -675,10 +728,11 @@ window.addEventListener('scalechange', function scalechange(evt) {
}, true);
window.addEventListener('pagechange', function pagechange(evt) {
- var page = evt.detail;
- document.getElementById('pageNumber').value = page;
- document.getElementById('previous').disabled = (page == 1);
- document.getElementById('next').disabled = (page == PDFView.pages.length);
+ var page = evt.pageNumber;
+ if (document.getElementById('pageNumber').value != page)
+ document.getElementById('pageNumber').value = page;
+ document.getElementById('previous').disabled = (page <= 1);
+ document.getElementById('next').disabled = (page >= PDFView.pages.length);
}, true);
window.addEventListener('keydown', function keydown(evt) {