mirror of
https://github.com/overleaf/overleaf.git
synced 2024-11-21 20:47:08 -05:00
make new pdf viewer the default for all users
remove old pdf viewer
This commit is contained in:
parent
ff55e4c5ed
commit
b0a32b1ef8
11 changed files with 16 additions and 1508 deletions
|
@ -23,9 +23,6 @@ for path in [
|
|||
"#{jsPath}main.js",
|
||||
"#{jsPath}libs.js",
|
||||
"#{jsPath}ace/ace.js",
|
||||
"#{jsPath}libs/pdf.js",
|
||||
"#{jsPath}libs/pdf.worker.js",
|
||||
"#{jsPath}libs/compatibility.js",
|
||||
"#{jsPath}libs/pdfjs-1.0.712/pdf.js",
|
||||
"#{jsPath}libs/pdfjs-1.0.712/pdf.worker.js",
|
||||
"#{jsPath}libs/pdfjs-1.0.712/compatibility.js",
|
||||
|
|
|
@ -89,14 +89,14 @@ block content
|
|||
window.requirejs = {
|
||||
"paths" : {
|
||||
"mathjax": "/js/libs/mathjax/MathJax.js?config=TeX-AMS_HTML",
|
||||
"moment": "libs/moment-2.7.0"
|
||||
"moment": "libs/moment-2.7.0",
|
||||
"libs/pdf": "libs/pdfjs-1.0.712/pdf",
|
||||
"libs/compatibility": "libs/pdfjs-1.0.712/compatibility"
|
||||
|
||||
},
|
||||
"urlArgs" : "fingerprint=#{fingerprint(jsPath + 'ide.js')}-#{fingerprint(jsPath + 'libs.js')}",
|
||||
"waitSeconds": 0,
|
||||
"shim": {
|
||||
"libs/pdfListView/PdfListView": {
|
||||
deps: ["libs/pdf"]
|
||||
},
|
||||
"libs/pdf": {
|
||||
deps: ["libs/compatibility"]
|
||||
},
|
||||
|
@ -117,21 +117,12 @@ block content
|
|||
|
||||
- locals.suppressDefaultJs = true
|
||||
|
||||
- // user.featureSwitches = {} // override
|
||||
- var usePdfNG = user.featureSwitches && user.featureSwitches.pdfng;
|
||||
- var pdfPath = 'libs/pdf.worker.js';
|
||||
- if (usePdfNG) { pdfPath = 'libs/pdfjs-1.0.712/pdf.worker.js'; }
|
||||
- var pdfPath = 'libs/pdfjs-1.0.712/pdf.worker.js'
|
||||
- var fingerprintedPath = fingerprint(jsPath+pdfPath)
|
||||
- var pdfJsWorkerPath = jsPath+pdfPath+'?fingerprint='+fingerprintedPath
|
||||
script(type='text/javascript').
|
||||
window.pdfJsWorkerPath = "#{pdfJsWorkerPath}";
|
||||
|
||||
// when using pdfng we need a different pdfjs version
|
||||
if usePdfNG
|
||||
script(type='text/javascript').
|
||||
window.requirejs.paths['libs/pdf'] = 'libs/pdfjs-1.0.712/pdf'
|
||||
window.requirejs.paths['libs/compatibility'] = 'libs/pdfjs-1.0.712/compatibility'
|
||||
|
||||
script(
|
||||
data-main=jsPath+"ide.js",
|
||||
baseurl=jsPath,
|
||||
|
|
|
@ -58,30 +58,16 @@ div.full-size.pdf(ng-controller="PdfController")
|
|||
i.split-screen
|
||||
|
||||
.pdf-viewer(ng-show="pdf.url && pdf.view == 'pdf' && !pdf.failure && !pdf.timeout && !pdf.error")
|
||||
- // user.featureSwitches = {} // override
|
||||
- var usePdfNG = user.featureSwitches && user.featureSwitches.pdfng;
|
||||
if !usePdfNG
|
||||
div(
|
||||
pdfjs
|
||||
ng-if="settings.pdfViewer == 'pdfjs'"
|
||||
pdf-src="pdf.url"
|
||||
key="{{ project_id }}"
|
||||
resize-on="layout:main:resize,layout:pdf:resize"
|
||||
highlights="pdf.highlights"
|
||||
position="pdf.position"
|
||||
dbl-click-callback="syncToCode"
|
||||
)
|
||||
else
|
||||
div(
|
||||
pdfng
|
||||
ng-if="settings.pdfViewer == 'pdfjs'"
|
||||
pdf-src="pdf.url"
|
||||
key="{{ project_id }}"
|
||||
resize-on="layout:main:resize,layout:pdf:resize"
|
||||
highlights="pdf.highlights"
|
||||
position="pdf.position"
|
||||
dbl-click-callback="syncToCode"
|
||||
)
|
||||
div(
|
||||
pdfng
|
||||
ng-if="settings.pdfViewer == 'pdfjs'"
|
||||
pdf-src="pdf.url"
|
||||
key="{{ project_id }}"
|
||||
resize-on="layout:main:resize,layout:pdf:resize"
|
||||
highlights="pdf.highlights"
|
||||
position="pdf.position"
|
||||
dbl-click-callback="syncToCode"
|
||||
)
|
||||
|
||||
iframe(
|
||||
ng-src="{{ pdf.url }}"
|
||||
|
|
|
@ -1,8 +1,7 @@
|
|||
define [
|
||||
"ide/pdf/controllers/PdfController"
|
||||
"ide/pdf/controllers/PdfViewToggleController"
|
||||
"ide/pdf/directives/pdfJs"
|
||||
"ide/pdfng/directives/pdfJs" # alternative incremental viewer
|
||||
"ide/pdfng/directives/pdfJs"
|
||||
], () ->
|
||||
class PdfManager
|
||||
constructor: (@ide, @$scope) ->
|
||||
|
|
|
@ -1,193 +0,0 @@
|
|||
define [
|
||||
"base"
|
||||
"libs/pdfListView/PdfListView"
|
||||
"libs/pdfListView/TextLayerBuilder"
|
||||
"libs/pdfListView/AnnotationsLayerBuilder"
|
||||
"libs/pdfListView/HighlightsLayerBuilder"
|
||||
"text!libs/pdfListView/TextLayer.css"
|
||||
"text!libs/pdfListView/AnnotationsLayer.css"
|
||||
"text!libs/pdfListView/HighlightsLayer.css"
|
||||
], (
|
||||
App
|
||||
PDFListView
|
||||
TextLayerBuilder
|
||||
AnnotationsLayerBuilder
|
||||
HighlightsLayerBuilder
|
||||
textLayerCss
|
||||
annotationsLayerCss
|
||||
highlightsLayerCss
|
||||
) ->
|
||||
if PDFJS?
|
||||
PDFJS.workerSrc = window.pdfJsWorkerPath
|
||||
|
||||
style = $("<style/>")
|
||||
style.text(textLayerCss + "\n" + annotationsLayerCss + "\n" + highlightsLayerCss)
|
||||
$("body").append(style)
|
||||
|
||||
App.directive "pdfjs", ["$timeout", "localStorage", ($timeout, localStorage) ->
|
||||
return {
|
||||
scope: {
|
||||
"pdfSrc": "="
|
||||
"highlights": "="
|
||||
"position": "="
|
||||
"dblClickCallback": "="
|
||||
}
|
||||
link: (scope, element, attrs) ->
|
||||
pdfListView = new PDFListView element.find(".pdfjs-viewer")[0],
|
||||
textLayerBuilder: TextLayerBuilder
|
||||
annotationsLayerBuilder: AnnotationsLayerBuilder
|
||||
highlightsLayerBuilder: HighlightsLayerBuilder
|
||||
ondblclick: (e) -> onDoubleClick(e)
|
||||
# logLevel: PDFListView.Logger.DEBUG
|
||||
pdfListView.listView.pageWidthOffset = 20
|
||||
pdfListView.listView.pageHeightOffset = 20
|
||||
|
||||
scope.loading = false
|
||||
|
||||
onProgress = (progress) ->
|
||||
scope.$apply () ->
|
||||
scope.progress = Math.floor(progress.loaded/progress.total*100)
|
||||
|
||||
initializedPosition = false
|
||||
initializePosition = () ->
|
||||
return if initializedPosition
|
||||
initializedPosition = true
|
||||
|
||||
if (scale = localStorage("pdf.scale"))?
|
||||
pdfListView.setScaleMode(scale.scaleMode, scale.scale)
|
||||
else
|
||||
pdfListView.setToFitWidth()
|
||||
|
||||
if (position = localStorage("pdf.position.#{attrs.key}"))
|
||||
pdfListView.setPdfPosition(position)
|
||||
|
||||
scope.position = pdfListView.getPdfPosition(true)
|
||||
|
||||
$(window).unload () =>
|
||||
localStorage "pdf.scale", {
|
||||
scaleMode: pdfListView.getScaleMode()
|
||||
scale: pdfListView.getScale()
|
||||
}
|
||||
localStorage "pdf.position.#{attrs.key}", pdfListView.getPdfPosition()
|
||||
|
||||
flashControls = () ->
|
||||
scope.flashControls = true
|
||||
$timeout () ->
|
||||
scope.flashControls = false
|
||||
, 1000
|
||||
|
||||
element.find(".pdfjs-viewer").scroll () ->
|
||||
scope.position = pdfListView.getPdfPosition(true)
|
||||
|
||||
onDoubleClick = (e) ->
|
||||
scope.dblClickCallback?(page: e.page, offset: { top: e.y, left: e.x })
|
||||
|
||||
scope.$watch "pdfSrc", (url) ->
|
||||
if url
|
||||
scope.loading = true
|
||||
scope.progress = 0
|
||||
|
||||
pdfListView
|
||||
.loadPdf(url, onProgress)
|
||||
.then () ->
|
||||
scope.$apply () ->
|
||||
scope.loading = false
|
||||
delete scope.progress
|
||||
initializePosition()
|
||||
flashControls()
|
||||
|
||||
scope.$watch "highlights", (areas) ->
|
||||
return if !areas?
|
||||
highlights = for area in areas or []
|
||||
{
|
||||
page: area.page - 1
|
||||
highlight:
|
||||
left: area.h
|
||||
top: area.v
|
||||
height: area.height
|
||||
width: area.width
|
||||
}
|
||||
|
||||
if highlights.length > 0
|
||||
first = highlights[0]
|
||||
pdfListView.setPdfPosition({
|
||||
page: first.page
|
||||
offset:
|
||||
left: first.highlight.left
|
||||
top: first.highlight.top - 80
|
||||
}, true)
|
||||
|
||||
pdfListView.clearHighlights()
|
||||
pdfListView.setHighlights(highlights, true)
|
||||
|
||||
setTimeout () =>
|
||||
pdfListView.clearHighlights()
|
||||
, 1000
|
||||
|
||||
scope.fitToHeight = () ->
|
||||
pdfListView.setToFitHeight()
|
||||
|
||||
scope.fitToWidth = () ->
|
||||
pdfListView.setToFitWidth()
|
||||
|
||||
scope.zoomIn = () ->
|
||||
scale = pdfListView.getScale()
|
||||
pdfListView.setScale(scale * 1.2)
|
||||
|
||||
scope.zoomOut = () ->
|
||||
scale = pdfListView.getScale()
|
||||
pdfListView.setScale(scale / 1.2)
|
||||
|
||||
if attrs.resizeOn?
|
||||
for event in attrs.resizeOn.split(",")
|
||||
scope.$on event, () ->
|
||||
pdfListView.onResize()
|
||||
|
||||
template: """
|
||||
<div class="pdfjs-viewer"></div>
|
||||
<div class="pdfjs-controls" ng-class="{'flash': flashControls }">
|
||||
<div class="btn-group">
|
||||
<a href
|
||||
class="btn btn-info btn-lg"
|
||||
ng-click="fitToWidth()"
|
||||
tooltip="Fit to Width"
|
||||
tooltip-append-to-body="true"
|
||||
tooltip-placement="bottom"
|
||||
>
|
||||
<i class="fa fa-fw fa-arrows-h"></i>
|
||||
</a>
|
||||
<a href
|
||||
class="btn btn-info btn-lg"
|
||||
ng-click="fitToHeight()"
|
||||
tooltip="Fit to Height"
|
||||
tooltip-append-to-body="true"
|
||||
tooltip-placement="bottom"
|
||||
>
|
||||
<i class="fa fa-fw fa-arrows-v"></i>
|
||||
</a>
|
||||
<a href
|
||||
class="btn btn-info btn-lg"
|
||||
ng-click="zoomIn()"
|
||||
tooltip="Zoom In"
|
||||
tooltip-append-to-body="true"
|
||||
tooltip-placement="bottom"
|
||||
>
|
||||
<i class="fa fa-fw fa-search-plus"></i>
|
||||
</a>
|
||||
<a href
|
||||
class="btn btn-info btn-lg"
|
||||
ng-click="zoomOut()"
|
||||
tooltip="Zoom Out"
|
||||
tooltip-append-to-body="true"
|
||||
tooltip-placement="bottom"
|
||||
>
|
||||
<i class="fa fa-fw fa-search-minus"></i>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="progress-thin" ng-show="loading">
|
||||
<div class="progress-bar" ng-style="{ 'width': progress + '%' }"></div>
|
||||
</div>
|
||||
"""
|
||||
}
|
||||
]
|
Binary file not shown.
Binary file not shown.
|
@ -1,64 +0,0 @@
|
|||
(function (name, context, definition) {
|
||||
if (typeof module != 'undefined' && module.exports) module.exports = definition()
|
||||
else if (typeof define == 'function' && define.amd) define(definition)
|
||||
else context[name] = definition()
|
||||
})('AnnotationsLayerBuilder', this, function (name, context) {
|
||||
|
||||
function AnnotationsLayerBuilder(pageView, annotationsLayerDiv) {
|
||||
this.annotationsLayerDiv = annotationsLayerDiv;
|
||||
this.pageView = pageView;
|
||||
};
|
||||
|
||||
AnnotationsLayerBuilder.EXTERNAL_LINK_TARGET = "_blank";
|
||||
|
||||
AnnotationsLayerBuilder.prototype = {
|
||||
setAnnotations: function(annotations) {
|
||||
for (var i = 0; i < annotations.length; i++) {
|
||||
var annotation = annotations[i];
|
||||
switch (annotation.subtype) {
|
||||
case 'Link':
|
||||
this.addLink(annotation);
|
||||
break;
|
||||
case 'Text':
|
||||
// TODO
|
||||
break;
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
addLink: function(link) {
|
||||
var element = this.buildLinkElementFromRect(link.rect);
|
||||
this.setLinkTarget(element, link);
|
||||
this.annotationsLayerDiv.appendChild(element);
|
||||
},
|
||||
|
||||
buildLinkElementFromRect: function(rect) {
|
||||
rect = this.pageView.viewport.convertToViewportRectangle(rect);
|
||||
rect = PDFJS.Util.normalizeRect(rect);
|
||||
var element = document.createElement("a");
|
||||
element.style.left = Math.floor(rect[0]) + 'px';
|
||||
element.style.top = Math.floor(rect[1]) + 'px';
|
||||
element.style.width = Math.ceil(rect[2] - rect[0]) + 'px';
|
||||
element.style.height = Math.ceil(rect[3] - rect[1]) + 'px';
|
||||
return element;
|
||||
},
|
||||
|
||||
setLinkTarget: function(element, link) {
|
||||
if (link.url) {
|
||||
element.href = link.url;
|
||||
element.target = this.EXTERNAL_LINK_TARGET;
|
||||
} else if (link.dest) {
|
||||
element.href = "#" + link.dest;
|
||||
var listView = this.pageView.listView;
|
||||
element.onclick = function(e) {
|
||||
e.preventDefault()
|
||||
listView.navigateTo(link.dest);
|
||||
}
|
||||
// TODO
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return AnnotationsLayerBuilder;
|
||||
|
||||
});
|
|
@ -1,39 +0,0 @@
|
|||
(function (name, context, definition) {
|
||||
if (typeof module != 'undefined' && module.exports) module.exports = definition()
|
||||
else if (typeof define == 'function' && define.amd) define(definition)
|
||||
else context[name] = definition()
|
||||
})('HighlightsLayerBuilder', this, function (name, context) {
|
||||
|
||||
function HighlightsLayerBuilder(pageView, highlightsLayerDiv) {
|
||||
this.highlightsLayerDiv = highlightsLayerDiv;
|
||||
this.pageView = pageView;
|
||||
this.highlightElements = [];
|
||||
};
|
||||
|
||||
HighlightsLayerBuilder.EXTERNAL_LINK_TARGET = "_blank";
|
||||
|
||||
HighlightsLayerBuilder.prototype = {
|
||||
addHighlight: function(left, top, width, height) {
|
||||
rect = this.pageView.viewport.convertToViewportRectangle([left, top, left + width, top + height]);
|
||||
rect = PDFJS.Util.normalizeRect(rect);
|
||||
var element = document.createElement("div");
|
||||
element.style.left = Math.floor(rect[0]) + 'px';
|
||||
element.style.top = Math.floor(rect[1]) + 'px';
|
||||
element.style.width = Math.ceil(rect[2] - rect[0]) + 'px';
|
||||
element.style.height = Math.ceil(rect[3] - rect[1]) + 'px';
|
||||
this.highlightElements.push(element);
|
||||
this.highlightsLayerDiv.appendChild(element);
|
||||
return element;
|
||||
},
|
||||
|
||||
clearHighlights: function() {
|
||||
for (var i = 0; i < this.highlightElements.length; i++) {
|
||||
this.highlightElements[i].remove();
|
||||
}
|
||||
this.highlightElements = [];
|
||||
}
|
||||
}
|
||||
|
||||
return HighlightsLayerBuilder;
|
||||
|
||||
});
|
|
@ -1,972 +0,0 @@
|
|||
(function (name, context, definition) {
|
||||
if (typeof module != 'undefined' && module.exports) module.exports = definition()
|
||||
else if (typeof define == 'function' && define.amd) define(definition)
|
||||
else context[name] = definition()
|
||||
})('PDFListView', this, function (name, context) {
|
||||
|
||||
//#expand __BUNDLE__
|
||||
|
||||
function Logger() {
|
||||
this.logLevel = Logger.INFO;
|
||||
var self = this;
|
||||
if (typeof(console) == "object" && typeof(console.log) == "function") {
|
||||
this.debug = function() {
|
||||
if (self.logLevel <= Logger.DEBUG) {
|
||||
console.log.apply(console, arguments);
|
||||
}
|
||||
};
|
||||
this.info = function() {
|
||||
if (self.logLevel <= Logger.INFO) {
|
||||
console.log.apply(console, arguments);
|
||||
}
|
||||
};
|
||||
this.error = function() {
|
||||
if (self.logLevel <= Logger.ERROR) {
|
||||
console.log.apply(console, arguments);
|
||||
}
|
||||
};
|
||||
} else {
|
||||
this.debug = this.info = this.error = function nop() {};
|
||||
}
|
||||
}
|
||||
|
||||
Logger.DEBUG = 0;
|
||||
Logger.INFO = 1;
|
||||
Logger.ERROR = 2;
|
||||
|
||||
var logger = new Logger();
|
||||
|
||||
function _flat(arr) {
|
||||
var res = arr.reduce(function(a, b) {
|
||||
return a.concat(b);
|
||||
});
|
||||
return res;
|
||||
}
|
||||
|
||||
function failDumper(err) {
|
||||
logger.error(err);
|
||||
}
|
||||
|
||||
if (typeof(PDFJS) === "undefined") {
|
||||
logger.error("PDF.js is not yet loaded.");
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Wrapper around the raw PDF.JS document.
|
||||
*/
|
||||
function Document(url, password, onProgress) {
|
||||
this.pdfDocument = null;
|
||||
this.pages = null;
|
||||
|
||||
var parameters = {password: password};
|
||||
if (typeof url === 'string') { // URL
|
||||
parameters.url = url;
|
||||
} else if (url && 'byteLength' in url) { // ArrayBuffer
|
||||
parameters.data = url;
|
||||
}
|
||||
|
||||
this.initialized = new PDFJS.LegacyPromise();
|
||||
PDFJS.getDocument(parameters, null, null, onProgress).then(this.loadPages.bind(this), failDumper);
|
||||
}
|
||||
|
||||
Document.prototype.loadPages = function(pdfDocument) {
|
||||
this.pdfDocument = pdfDocument;
|
||||
var pagesCount = this.pagesCount = pdfDocument.numPages;
|
||||
|
||||
var pagePromises = [];
|
||||
for (var i = 1; i <= pagesCount; i++) {
|
||||
pagePromises.push(pdfDocument.getPage(i));
|
||||
}
|
||||
|
||||
this.pageRefMap = pageRefMap = {};
|
||||
var pagesPromise = Promise.all(pagePromises);
|
||||
var doc = this
|
||||
pagesPromise.then(function(promisedPages) {
|
||||
doc.pages = promisedPages.map(function(pdfPage, i) {
|
||||
var pageRef = pdfPage.ref
|
||||
pageRefMap[pageRef.num + ' ' + pageRef.gen] = i;
|
||||
return new Page(pdfPage);
|
||||
});
|
||||
});
|
||||
|
||||
var destinationsPromise = pdfDocument.getDestinations();
|
||||
destinationsPromise.then(function(destinations) {
|
||||
doc.destinations = destinations;
|
||||
});
|
||||
|
||||
Promise.all([pagesPromise, destinationsPromise]).then(function() {
|
||||
doc.initialized.resolve();
|
||||
}, failDumper);
|
||||
};
|
||||
|
||||
/**
|
||||
* Handles the rendering. Multiple ListViews can be bound to a RenderController.
|
||||
* In that case, the RenderController figures out what's page has the highest
|
||||
* priority to render
|
||||
*/
|
||||
function RenderController() {
|
||||
this.listViews = [];
|
||||
this.renderList = [];
|
||||
}
|
||||
|
||||
RenderController.prototype = {
|
||||
addListView: function(listView) {
|
||||
// TODO: assert listView not already in list of this.listView
|
||||
this.listViews.push(listView);
|
||||
},
|
||||
|
||||
updateRenderList: function() {
|
||||
this.renderList = _flat(this.listViews.map(function(listView) {
|
||||
return listView.getPagesToRender();
|
||||
}));
|
||||
|
||||
// TODO: Some "highest-priority" sorting algorithm on the renderList.
|
||||
|
||||
this.doRender();
|
||||
},
|
||||
|
||||
pageToRender: function() {
|
||||
if (this.renderList.length === 0) return null;
|
||||
|
||||
return this.renderList[0];
|
||||
},
|
||||
|
||||
doRender: function() {
|
||||
var pageToRender = this.pageToRender();
|
||||
|
||||
if (!pageToRender) return;
|
||||
|
||||
pageToRender.render(this);
|
||||
},
|
||||
|
||||
finishedRendering: function(pageView) {
|
||||
var idx = this.renderList.indexOf(pageView);
|
||||
|
||||
pageView.callAfterRenderedCallbacks();
|
||||
|
||||
// If the finished pageView is in the list of pages to render,
|
||||
// then remove it from the list and render start rendering the
|
||||
// next page.
|
||||
if (idx !== -1) {
|
||||
this.renderList.splice(idx, 1);
|
||||
this.doRender();
|
||||
}
|
||||
},
|
||||
|
||||
onResize: function() {
|
||||
var renderAgain = false
|
||||
this.listViews.map(function(listView) {
|
||||
if (listView.calculateScale()) {
|
||||
listView.layout();
|
||||
renderAgain = true;
|
||||
}
|
||||
});
|
||||
if (renderAgain) {
|
||||
this.updateRenderList();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
var LAYOUT_SINGLE = 'layout_single';
|
||||
var SCALE_MODE_AUTO = 'scale_mode_auto';
|
||||
var SCALE_MODE_VALUE = 'scale_mode_value';
|
||||
var SCALE_MODE_FIT_WIDTH = 'scale_mode_fit_width';
|
||||
var SCALE_MODE_FIT_HEIGHT = 'scale_mode_fit_height';
|
||||
|
||||
/**
|
||||
* Main view that holds the single pageContainer/pageViews of the pdfDoc.
|
||||
*/
|
||||
function ListView(dom, options) {
|
||||
this.dom = dom;
|
||||
this.options = options;
|
||||
|
||||
this.pageLayout = LAYOUT_SINGLE;
|
||||
this.scaleMode = SCALE_MODE_VALUE;
|
||||
this.scale = 1.0;
|
||||
|
||||
this.pageWidthOffset = 0;
|
||||
this.pageHeightOffset = 0;
|
||||
|
||||
this.pageViews = [];
|
||||
this.containerViews = [];
|
||||
}
|
||||
|
||||
ListView.prototype = {
|
||||
setDocument: function(pdfDoc) {
|
||||
this.clearPages();
|
||||
|
||||
this.pdfDoc = pdfDoc;
|
||||
|
||||
this.assignPagesToContainer();
|
||||
this.layout();
|
||||
},
|
||||
|
||||
clearPages: function() {
|
||||
var self = this;
|
||||
this.containerViews.map(function(container) {
|
||||
self.dom.removeChild(container.dom);
|
||||
});
|
||||
this.pageViews = [];
|
||||
this.containerViews = [];
|
||||
},
|
||||
|
||||
assignPagesToContainer: function() {
|
||||
// TODO: Handle multiple layout types here. For now, assume to have one page
|
||||
// per pageContainer.
|
||||
this.pdfDoc.pages.map(function(page) {
|
||||
var pageView = new PageView(page, this);
|
||||
|
||||
// TODO: Switch over to a proper event handler
|
||||
var that = this;
|
||||
var index = that.pageViews.length;
|
||||
pageView.ondblclick = function(e) {
|
||||
e.page = index;
|
||||
if (that.ondblclick) {
|
||||
that.ondblclick.call(that, e);
|
||||
}
|
||||
}
|
||||
this.pageViews.push(pageView);
|
||||
|
||||
var container = new PageContainerView(this);
|
||||
container.setPageView(pageView, 0);
|
||||
this.containerViews.push(container);
|
||||
|
||||
this.dom.appendChild(container.dom);
|
||||
}, this);
|
||||
},
|
||||
|
||||
layout: function() {
|
||||
this.savePdfPosition();
|
||||
this.containerViews.forEach(function(containerView) {
|
||||
containerView.layout();
|
||||
});
|
||||
this.restorePdfPosition();
|
||||
},
|
||||
|
||||
getScale: function() {
|
||||
return this.scale;
|
||||
},
|
||||
|
||||
getScaleMode: function() {
|
||||
return this.scaleMode;
|
||||
},
|
||||
|
||||
setScaleMode: function(scaleMode, scale) {
|
||||
this.scaleMode = scaleMode;
|
||||
if (scaleMode == SCALE_MODE_VALUE) {
|
||||
this.scale = scale;
|
||||
}
|
||||
this.calculateScale();
|
||||
this.layout();
|
||||
},
|
||||
|
||||
setScale: function(scale) {
|
||||
this.setScaleMode(SCALE_MODE_VALUE, scale);
|
||||
},
|
||||
|
||||
setToAutoScale: function() {
|
||||
this.setScaleMode(SCALE_MODE_AUTO);
|
||||
},
|
||||
|
||||
setToFitWidth: function() {
|
||||
this.setScaleMode(SCALE_MODE_FIT_WIDTH);
|
||||
},
|
||||
|
||||
setToFitHeight: function() {
|
||||
this.setScaleMode(SCALE_MODE_FIT_HEIGHT);
|
||||
},
|
||||
|
||||
// Calculates the new scale. Returns `true` if the scale changed.
|
||||
calculateScale: function() {
|
||||
var newScale = this.scale;
|
||||
var oldScale = newScale;
|
||||
var scaleMode = this.scaleMode;
|
||||
if (scaleMode === SCALE_MODE_FIT_WIDTH || scaleMode === SCALE_MODE_AUTO) {
|
||||
var clientWidth = this.dom.clientWidth;
|
||||
if (clientWidth == 0) {
|
||||
logger.debug("LIST VIEW NOT VISIBLE")
|
||||
return false;
|
||||
}
|
||||
var maxNormalWidth = 0;
|
||||
this.containerViews.forEach(function(containerView) {
|
||||
maxNormalWidth = Math.max(maxNormalWidth, containerView.normalWidth);
|
||||
});
|
||||
var scale = (clientWidth - this.pageWidthOffset)/maxNormalWidth;
|
||||
if (scaleMode === SCALE_MODE_AUTO) {
|
||||
scale = Math.min(1.0, scale);
|
||||
}
|
||||
newScale = scale;
|
||||
} else if (scaleMode === SCALE_MODE_FIT_HEIGHT) {
|
||||
var clientHeight = this.dom.clientHeight;
|
||||
if (clientHeight == 0) {
|
||||
logger.debug("LIST VIEW NOT VISIBLE")
|
||||
return false;
|
||||
}
|
||||
var maxNormalHeight = 0;
|
||||
this.containerViews.forEach(function(containerView) {
|
||||
maxNormalHeight = Math.max(maxNormalHeight, containerView.normalHeight);
|
||||
});
|
||||
newScale = (clientHeight - this.pageHeightOffset)/maxNormalHeight;
|
||||
}
|
||||
this.scale = newScale;
|
||||
return newScale !== oldScale;
|
||||
},
|
||||
|
||||
getPagesToRender: function() {
|
||||
// Cache these results to avoid dom access.
|
||||
this.scrollTop = this.dom.scrollTop;
|
||||
this.scrollBottom = this.scrollTop + this.dom.clientHeight;
|
||||
|
||||
// TODO: For now, this only returns the visible pages and not
|
||||
// +1/-1 one to render in advance.
|
||||
return this.pageViews.filter(function(pageView) {
|
||||
var isVisible = pageView.isVisible();
|
||||
|
||||
if (isVisible && !pageView.isRendered) {
|
||||
return true;
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
navigateTo: function(destRef) {
|
||||
var destination = this.pdfDoc.destinations[destRef]
|
||||
if (typeof destination !== "object") {
|
||||
return;
|
||||
}
|
||||
logger.debug("NAVIGATING TO", destination);
|
||||
|
||||
var pageRef = destination[0];
|
||||
var pageNumber = this.pdfDoc.pageRefMap[pageRef.num + ' ' + pageRef.gen];
|
||||
if (typeof pageNumber !== "number") {
|
||||
return;
|
||||
}
|
||||
logger.debug("PAGE NUMBER", pageNumber);
|
||||
|
||||
var pageView = this.pageViews[pageNumber];
|
||||
|
||||
var destinationType = destination[1].name;
|
||||
switch(destinationType) {
|
||||
case "XYZ":
|
||||
var x = destination[2];
|
||||
var y = destination[3];
|
||||
break;
|
||||
default:
|
||||
// TODO
|
||||
return;
|
||||
}
|
||||
var position = pageView.getPdfPositionInViewer(x,y);
|
||||
this.dom.scrollTop = position.top;
|
||||
this.dom.scrollLeft = position.left;
|
||||
},
|
||||
|
||||
savePdfPosition: function() {
|
||||
this.pdfPosition = this.getPdfPosition();
|
||||
logger.debug("SAVED PDF POSITION", this.pdfPosition);
|
||||
},
|
||||
|
||||
restorePdfPosition: function() {
|
||||
logger.debug("RESTORING PDF POSITION", this.pdfPosition);
|
||||
this.setPdfPosition(this.pdfPosition);
|
||||
},
|
||||
|
||||
getPdfPosition: function(fromTop) {
|
||||
var pdfPosition = null;
|
||||
for (var i = 0; i < this.pageViews.length; i++) {
|
||||
var pageView = this.pageViews[i];
|
||||
var pdfOffset = pageView.getUppermostVisiblePdfOffset();
|
||||
if (pdfOffset !== null) {
|
||||
if (fromTop) {
|
||||
pdfOffset = pageView.normalHeight - pdfOffset;
|
||||
}
|
||||
pdfPosition = {
|
||||
page: i,
|
||||
offset: {
|
||||
top: pdfOffset,
|
||||
left: 0 // TODO
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
return pdfPosition;
|
||||
},
|
||||
|
||||
setPdfPosition: function(pdfPosition, fromTop) {
|
||||
if (typeof pdfPosition !== "undefined" && pdfPosition != null) {
|
||||
var offset = pdfPosition.offset;
|
||||
var page_index = pdfPosition.page;
|
||||
var pageView = this.pageViews[page_index];
|
||||
if (fromTop) {
|
||||
offset.top = pageView.normalHeight - offset.top;
|
||||
}
|
||||
var position = pageView.getPdfPositionInViewer(offset.left, offset.top);
|
||||
this.dom.scrollTop = position.top;
|
||||
}
|
||||
},
|
||||
|
||||
setHighlights: function(highlights, fromTop) {
|
||||
for (i = 0; i < highlights.length; i++) {
|
||||
var pageIndex = highlights[i].page;
|
||||
var pageView = this.pageViews[pageIndex];
|
||||
(function(pageView, highlight) {
|
||||
pageView.doWhenRendered(function() {
|
||||
pageView.addHighlight(highlight.highlight, fromTop);
|
||||
});
|
||||
})(pageView, highlights[i]);
|
||||
}
|
||||
},
|
||||
|
||||
clearHighlights: function() {
|
||||
for (var i = 0; i < this.pageViews.length; i++) {
|
||||
var pageView = this.pageViews[i].clearHighlights();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/*
|
||||
* A PageContainerView holds multiple PageViews. E.g. in a two-page layout,
|
||||
* every pageContainerView holds two PageViews and is responsible to layout
|
||||
* them.
|
||||
*/
|
||||
function PageContainerView(listView) {
|
||||
this.listView = listView;
|
||||
|
||||
var dom = this.dom = document.createElement('div');
|
||||
dom.className = 'plv-page-container page-container';
|
||||
this.pages = [];
|
||||
}
|
||||
|
||||
PageContainerView.prototype = {
|
||||
setPageView: function(pageView, idx) {
|
||||
// TODO: handle case if there is already a page here
|
||||
this.pages[idx] = pageView;
|
||||
|
||||
// TODO: handle page idx properly
|
||||
this.dom.appendChild(pageView.dom);
|
||||
},
|
||||
|
||||
removePageView: function(idx) {
|
||||
// TODO: check if idx is set on page[]
|
||||
this.dom.removeChild(this.pages[idx].dom);
|
||||
},
|
||||
|
||||
layout: function() {
|
||||
var scale = this.listView.scale;
|
||||
|
||||
var normalWidth = 0;
|
||||
var normalHeight = 0;
|
||||
|
||||
this.pages.forEach(function(pageView) {
|
||||
pageView.layout();
|
||||
normalWidth += pageView.normalWidth;
|
||||
normalHeight = Math.max(pageView.normalHeight, normalHeight);
|
||||
});
|
||||
|
||||
this.normalWidth = normalWidth;
|
||||
this.normalHeight = normalHeight;
|
||||
|
||||
this.dom.style.width = (normalWidth * scale) + 'px';
|
||||
this.dom.style.height = (normalHeight * scale) + 'px';
|
||||
}
|
||||
};
|
||||
|
||||
var RenderingStates = {
|
||||
INITIAL: 0,
|
||||
RUNNING: 1,
|
||||
PAUSED: 2,
|
||||
FINISHED: 3
|
||||
};
|
||||
|
||||
var idCounter = 0;
|
||||
|
||||
|
||||
/**
|
||||
* The view for a single page.
|
||||
*/
|
||||
function PageView(page, listView) {
|
||||
this.page = page;
|
||||
this.listView = listView;
|
||||
this.id = idCounter++;
|
||||
this.number = this.page.number;
|
||||
|
||||
this.rotation = 0;
|
||||
|
||||
this.isRendered = false;
|
||||
this.renderState = RenderingStates.INITIAL;
|
||||
this.afterRenderCallbacks = [];
|
||||
|
||||
var dom = this.dom = document.createElement('div');
|
||||
dom.className = "plv-page-view page-view";
|
||||
var that = this;
|
||||
dom.ondblclick = function(e) {
|
||||
var layerX = e.layerX;
|
||||
var layerY = e.layerY;
|
||||
var element = e.target;
|
||||
while (element.offsetParent && element.offsetParent !== dom) {
|
||||
layerX = layerX + element.offsetLeft;
|
||||
layerY = layerY + element.offsetTop;
|
||||
element = element.offsetParent;
|
||||
}
|
||||
|
||||
var pdfPoint = that.viewport.convertToPdfPoint(layerX, layerY);
|
||||
var event = {
|
||||
x: pdfPoint[0],
|
||||
y: that.normalHeight - pdfPoint[1]
|
||||
};
|
||||
|
||||
if (that.ondblclick) {
|
||||
that.ondblclick.call(that.listView, event)
|
||||
}
|
||||
}
|
||||
this.createNewCanvas();
|
||||
}
|
||||
|
||||
PageView.prototype = {
|
||||
layout: function() {
|
||||
var scale = this.listView.scale;
|
||||
|
||||
var viewport = this.viewport =
|
||||
this.page.pdfPage.getViewport(scale, this.rotation);
|
||||
|
||||
this.normalWidth = viewport.width / scale;
|
||||
this.normalHeight = viewport.height / scale;
|
||||
|
||||
// Only change the width/height property of the canvas if it really
|
||||
// changed. Every assignment to the width/height property clears the
|
||||
// content of the canvas.
|
||||
|
||||
var outputScale = this.getOutputScale();
|
||||
|
||||
var scaledWidth = (Math.floor(viewport.width) * outputScale.sx) | 0;
|
||||
var scaledHeight = (Math.floor(viewport.height) * outputScale.sy) | 0;
|
||||
|
||||
var newWidth = Math.floor(viewport.width);
|
||||
var newHeight = Math.floor(viewport.height);
|
||||
|
||||
if (this.canvas.width !== newWidth) {
|
||||
this.canvas.width = scaledWidth;
|
||||
this.canvas.style.width = newWidth + 'px';
|
||||
this.resetRenderState();
|
||||
}
|
||||
if (this.canvas.height !== newHeight) {
|
||||
this.canvas.height = scaledHeight;
|
||||
this.canvas.style.height = newHeight + 'px';
|
||||
this.resetRenderState();
|
||||
}
|
||||
|
||||
if(outputScale.scaled){
|
||||
var ctx = this.getCanvasContext()
|
||||
ctx.scale(outputScale.sx, outputScale.sy)
|
||||
}
|
||||
|
||||
this.width = viewport.width;
|
||||
this.height = viewport.height;
|
||||
},
|
||||
|
||||
isVisible: function() {
|
||||
var listView = this.listView;
|
||||
var dom = this.dom;
|
||||
var offsetTop = dom.offsetTop;
|
||||
var offsetBottom = offsetTop + this.height;
|
||||
|
||||
return offsetBottom >= listView.scrollTop &&
|
||||
offsetTop <= listView.scrollBottom;
|
||||
},
|
||||
|
||||
resetRenderState: function() {
|
||||
this.renderState = RenderingStates.INITIAL;
|
||||
this.isRendered = false;
|
||||
if (this.textLayerDiv) {
|
||||
this.dom.removeChild(this.textLayerDiv);
|
||||
delete this.textLayerDiv;
|
||||
}
|
||||
if (this.annotationsLayerDiv) {
|
||||
this.dom.removeChild(this.annotationsLayerDiv);
|
||||
delete this.annotationsLayerDiv;
|
||||
}
|
||||
},
|
||||
|
||||
render: function(renderController) {
|
||||
return this.page.render(this, renderController);
|
||||
},
|
||||
|
||||
getCanvasContext: function() {
|
||||
return this.canvas.getContext('2d');
|
||||
},
|
||||
|
||||
/**
|
||||
* Returns scale factor for the canvas. It makes sense for the HiDPI displays.
|
||||
* @return {Object} The object with horizontal (sx) and vertical (sy)
|
||||
scales. The scaled property is set to false if scaling is
|
||||
not required, true otherwise.
|
||||
*/
|
||||
|
||||
getOutputScale: function(){
|
||||
var ctx = this.getCanvasContext()
|
||||
var devicePixelRatio = window.devicePixelRatio || 1;
|
||||
var backingStoreRatio = ctx.webkitBackingStorePixelRatio ||
|
||||
ctx.mozBackingStorePixelRatio ||
|
||||
ctx.msBackingStorePixelRatio ||
|
||||
ctx.oBackingStorePixelRatio ||
|
||||
ctx.backingStorePixelRatio || 1;
|
||||
var pixelRatio = devicePixelRatio / backingStoreRatio;
|
||||
return {
|
||||
sx: pixelRatio,
|
||||
sy: pixelRatio,
|
||||
scaled: pixelRatio != 1
|
||||
};
|
||||
},
|
||||
|
||||
createNewCanvas: function() {
|
||||
if (this.canvas) {
|
||||
this.dom.removeChild(this.canvas);
|
||||
}
|
||||
var canvas = this.canvas = document.createElement('canvas');
|
||||
this.dom.appendChild(canvas);
|
||||
this.layout();
|
||||
},
|
||||
|
||||
pdfPositionToPixels: function(x, y) {
|
||||
return this.viewport.convertToViewportPoint(x, y);
|
||||
},
|
||||
|
||||
getCanvasPositionInViewer: function() {
|
||||
return {
|
||||
left: this.canvas.offsetLeft + this.dom.offsetLeft,
|
||||
top: this.canvas.offsetTop + this.dom.offsetTop
|
||||
}
|
||||
},
|
||||
|
||||
getPdfPositionInViewer: function(x, y) {
|
||||
pageOffset = this.pdfPositionToPixels(x, y);
|
||||
canvasOffset = this.getCanvasPositionInViewer();
|
||||
return {
|
||||
left: canvasOffset.left + pageOffset[0],
|
||||
top: canvasOffset.top + pageOffset[1]
|
||||
}
|
||||
},
|
||||
|
||||
getUppermostVisibleCanvasOffset: function() {
|
||||
var pagePosition = this.getCanvasPositionInViewer();
|
||||
var pageHeight = this.canvas.height;
|
||||
var viewportTop = this.listView.dom.scrollTop;
|
||||
var viewportHeight = this.listView.dom.clientHeight;
|
||||
// Check if the top of the page is showing, i.e:
|
||||
// _______________
|
||||
// | |
|
||||
// | ........ |
|
||||
// | . . |
|
||||
// ----.------.---
|
||||
// . .
|
||||
// ........
|
||||
var topVisible = (pagePosition.top > viewportTop && pagePosition.top < viewportTop + viewportHeight);
|
||||
// Check if at least some of the page is showing, i.e:
|
||||
// ........ ........
|
||||
// ____.______.___ ---.------.---
|
||||
// | . . | or | . . |
|
||||
// | . . | | . . |
|
||||
// | ........ | | . . |
|
||||
// --------------- ---.------.---
|
||||
// ........
|
||||
var someContentVisible = (pagePosition.top < viewportTop && pagePosition.top + pageHeight > viewportTop);
|
||||
if (topVisible) {
|
||||
return 0;
|
||||
} else if (someContentVisible) {
|
||||
return viewportTop - pagePosition.top;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
},
|
||||
|
||||
getUppermostVisiblePdfOffset: function() {
|
||||
var canvasOffset = this.getUppermostVisibleCanvasOffset();
|
||||
if (canvasOffset === null) {
|
||||
return null;
|
||||
}
|
||||
var pdfOffset = this.viewport.convertToPdfPoint(0, canvasOffset);
|
||||
return pdfOffset[1];
|
||||
},
|
||||
|
||||
clearHighlights: function() {
|
||||
if (this.highlightsLayer) {
|
||||
this.highlightsLayer.clearHighlights();
|
||||
}
|
||||
},
|
||||
|
||||
addHighlight: function(highlight, fromTop) {
|
||||
if (this.highlightsLayer) {
|
||||
var top = highlight.top;
|
||||
var left = highlight.left;
|
||||
var width = highlight.width;
|
||||
var height = highlight.height;
|
||||
if (fromTop) {
|
||||
top = this.normalHeight - top;
|
||||
}
|
||||
this.highlightsLayer.addHighlight(left, top, width, height);
|
||||
}
|
||||
},
|
||||
|
||||
doWhenRendered: function(callback) {
|
||||
if (this.isRendered) {
|
||||
callback()
|
||||
} else {
|
||||
this.afterRenderCallbacks.push(callback);
|
||||
}
|
||||
},
|
||||
|
||||
callAfterRenderedCallbacks: function () {
|
||||
var callbacks = this.afterRenderCallbacks;
|
||||
for (var i = 0; i < callbacks.length; i++) {
|
||||
callbacks[i]();
|
||||
}
|
||||
this.afterRenderCallbacks = [];
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* An abstraction around the raw page object of PDF.JS, that also handles the
|
||||
* rendering logic of (maybe multiple) pageView(s) that are based on this page.
|
||||
*/
|
||||
function Page(pdfPage, number) {
|
||||
this.number = number;
|
||||
this.pdfPage = pdfPage;
|
||||
|
||||
this.renderContextList = {};
|
||||
}
|
||||
|
||||
Page.prototype = {
|
||||
render: function(pageView, renderController) {
|
||||
var renderContext;
|
||||
|
||||
// FEATURE: If the page was rendered already once, then use the old
|
||||
// version as a placeholder until the new version is rendered at the
|
||||
// expected quality.
|
||||
|
||||
// FEATURE: If the page can be rendered at low quality (thumbnail) and
|
||||
// there is already a higher resolution rendering, then use this one
|
||||
// instead of rerendering from scratch again.
|
||||
|
||||
// PageView is not layouted.
|
||||
if (!pageView.viewport) return;
|
||||
|
||||
// Nothing todo.
|
||||
if (pageView.isRendered) return;
|
||||
|
||||
// Not most important page to render ATM.
|
||||
if (renderController.pageToRender() !== pageView) return;
|
||||
|
||||
var self = this;
|
||||
var viewport;
|
||||
if (renderContext = this.renderContextList[pageView.id]) {
|
||||
viewport = renderContext.viewport;
|
||||
|
||||
// TODO: handle rotation
|
||||
if (viewport.height !== pageView.viewport.height ||
|
||||
viewport.height !== pageView.viewport.height)
|
||||
{
|
||||
// The viewport changed -> need to rerender.
|
||||
renderContext.abandon = true;
|
||||
delete self.renderContextList[pageView.id];
|
||||
pageView.createNewCanvas();
|
||||
self.render(pageView, renderController);
|
||||
} else if (renderContext.state === RenderingStates.PAUSED) {
|
||||
// There is already a not finished renderState ->
|
||||
logger.debug('RESUME', pageView.id);
|
||||
renderContext.resume();
|
||||
}
|
||||
}
|
||||
|
||||
if (!renderContext) {
|
||||
viewport = pageView.viewport;
|
||||
// No rendering data yet -> create a new renderContext and start
|
||||
// the rendering process.
|
||||
|
||||
var textLayer;
|
||||
var textLayerBuilder = pageView.listView.options.textLayerBuilder;
|
||||
if (textLayerBuilder) {
|
||||
var textLayerDiv = pageView.textLayerDiv = document.createElement("div");
|
||||
textLayerDiv.className = 'plv-text-layer text-layer';
|
||||
pageView.dom.appendChild(textLayerDiv);
|
||||
textLayer = new textLayerBuilder(textLayerDiv);
|
||||
this.pdfPage.getTextContent().then(
|
||||
function(textContent) {
|
||||
textLayer.setTextContent(textContent);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
var annotationsLayerBuilder = pageView.listView.options.annotationsLayerBuilder;
|
||||
if (annotationsLayerBuilder) {
|
||||
var annotationsLayerDiv = pageView.annotationsLayerDiv = document.createElement("div");
|
||||
annotationsLayerDiv.className = 'plv-annotations-layer annotations-layer';
|
||||
pageView.dom.appendChild(annotationsLayerDiv);
|
||||
var annotationsLayer = new annotationsLayerBuilder(pageView, annotationsLayerDiv);
|
||||
this.pdfPage.getAnnotations().then(
|
||||
function(annotations) {
|
||||
annotationsLayer.setAnnotations(annotations)
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
var highlightsLayerBuilder = pageView.listView.options.highlightsLayerBuilder;
|
||||
if (highlightsLayerBuilder) {
|
||||
var highlightsLayerDiv = pageView.highlightsLayerDiv = document.createElement("div");
|
||||
highlightsLayerDiv.className = 'plv-highlights-layer highlights-layer';
|
||||
pageView.dom.appendChild(highlightsLayerDiv);
|
||||
pageView.highlightsLayer = new highlightsLayerBuilder(pageView, highlightsLayerDiv);
|
||||
}
|
||||
|
||||
renderContext = {
|
||||
canvasContext: pageView.getCanvasContext(),
|
||||
viewport: viewport,
|
||||
textLayer: textLayer,
|
||||
continueCallback: function pdfViewContinueCallback(cont) {
|
||||
if (renderContext.abandon) {
|
||||
logger.debug("ABANDON", pageView.id);
|
||||
return;
|
||||
}
|
||||
|
||||
if (renderController.pageToRender() !== pageView) {
|
||||
logger.debug('PAUSE', pageView.id);
|
||||
renderContext.state = RenderingStates.PAUSED;
|
||||
renderContext.resume = function resumeCallback() {
|
||||
renderContext.state = RenderingStates.RUNNING;
|
||||
cont();
|
||||
};
|
||||
return;
|
||||
}
|
||||
logger.debug('CONT', pageView.id);
|
||||
cont();
|
||||
}
|
||||
};
|
||||
this.renderContextList[pageView.id] = renderContext;
|
||||
|
||||
logger.debug("BEGIN", pageView.id);
|
||||
renderContext.renderPromise = this.pdfPage.render(renderContext).promise;
|
||||
renderContext.renderPromise.then(
|
||||
function pdfPageRenderCallback() {
|
||||
logger.debug('DONE', pageView.id);
|
||||
pageView.isRendered = true;
|
||||
renderController.finishedRendering(pageView);
|
||||
},
|
||||
failDumper
|
||||
);
|
||||
}
|
||||
|
||||
return renderContext.renderPromise;
|
||||
}
|
||||
};
|
||||
|
||||
function PDFListView(mainDiv, options) {
|
||||
if (typeof(options) != "object") {
|
||||
options = {};
|
||||
}
|
||||
if (typeof(options.logLevel) != "number") {
|
||||
options.logLevel = Logger.INFO;
|
||||
}
|
||||
logger.logLevel = options.logLevel;
|
||||
|
||||
var self = this;
|
||||
|
||||
this.listView = new ListView(mainDiv, options);
|
||||
this.listView.ondblclick = function(e) {
|
||||
if (options.ondblclick) {
|
||||
options.ondblclick.call(self, e);
|
||||
}
|
||||
}
|
||||
|
||||
this.renderController = new RenderController();
|
||||
this.renderController.addListView(this.listView);
|
||||
this.renderController.updateRenderList();
|
||||
|
||||
|
||||
mainDiv.addEventListener('scroll', function() {
|
||||
// This will update the list AND start rendering if needed.
|
||||
self.renderController.updateRenderList();
|
||||
});
|
||||
|
||||
window.addEventListener('resize', function() {
|
||||
// Check if the scale changed due to the resizing.
|
||||
if (self.listView.calculateScale()) {
|
||||
// Update the layout and start rendering. Changing the layout
|
||||
// of the PageView makes it rendering stop.
|
||||
self.listView.layout();
|
||||
self.renderController.updateRenderList();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
PDFListView.prototype = {
|
||||
loadPdf: function(url, onProgress) {
|
||||
this.doc = new Document(url, null, onProgress);
|
||||
var self = this;
|
||||
var promise = this.doc.initialized;
|
||||
promise.then(function() {
|
||||
logger.debug('LOADED');
|
||||
self.listView.setDocument(self.doc);
|
||||
self.renderController.updateRenderList();
|
||||
}, failDumper);
|
||||
return promise;
|
||||
},
|
||||
|
||||
getScale: function() {
|
||||
return this.listView.getScale();
|
||||
},
|
||||
|
||||
getScaleMode: function() {
|
||||
return this.listView.getScaleMode()
|
||||
},
|
||||
|
||||
setScaleMode: function(scaleMode, scale) {
|
||||
this.listView.setScaleMode(scaleMode, scale);
|
||||
this.renderController.updateRenderList();
|
||||
},
|
||||
|
||||
setScale: function(scale) {
|
||||
this.listView.setScale(scale);
|
||||
this.renderController.updateRenderList();
|
||||
},
|
||||
|
||||
setToAutoScale: function() {
|
||||
this.listView.setToAutoScale();
|
||||
this.renderController.updateRenderList();
|
||||
},
|
||||
|
||||
setToFitWidth: function() {
|
||||
this.listView.setToFitWidth();
|
||||
this.renderController.updateRenderList();
|
||||
},
|
||||
|
||||
setToFitHeight: function() {
|
||||
this.listView.setToFitHeight();
|
||||
this.renderController.updateRenderList();
|
||||
},
|
||||
|
||||
onResize: function() {
|
||||
this.renderController.onResize();
|
||||
},
|
||||
|
||||
getPdfPosition: function(fromTop) {
|
||||
return this.listView.getPdfPosition(fromTop);
|
||||
},
|
||||
|
||||
setPdfPosition: function(pdfPosition, fromTop) {
|
||||
this.listView.setPdfPosition(pdfPosition, fromTop);
|
||||
},
|
||||
|
||||
setHighlights: function(highlights, fromTop) {
|
||||
this.listView.setHighlights(highlights, fromTop);
|
||||
},
|
||||
|
||||
clearHighlights: function() {
|
||||
this.listView.clearHighlights();
|
||||
}
|
||||
};
|
||||
PDFListView.Logger = Logger;
|
||||
|
||||
return PDFListView;
|
||||
|
||||
});
|
||||
|
|
@ -1,197 +0,0 @@
|
|||
(function (name, context, definition) {
|
||||
if (typeof module != 'undefined' && module.exports) module.exports = definition()
|
||||
else if (typeof define == 'function' && define.amd) define(definition)
|
||||
else context[name] = definition()
|
||||
})('TextLayerBuilder', this, function (name, context) {
|
||||
|
||||
// optimised CSS custom property getter/setter
|
||||
var CustomStyle = (function CustomStyleClosure() {
|
||||
|
||||
// As noted on: http://www.zachstronaut.com/posts/2009/02/17/
|
||||
// animate-css-transforms-firefox-webkit.html
|
||||
// in some versions of IE9 it is critical that ms appear in this list
|
||||
// before Moz
|
||||
var prefixes = ['ms', 'Moz', 'Webkit', 'O'];
|
||||
var _cache = { };
|
||||
|
||||
function CustomStyle() {
|
||||
}
|
||||
|
||||
CustomStyle.getProp = function get(propName, element) {
|
||||
// check cache only when no element is given
|
||||
if (arguments.length == 1 && typeof _cache[propName] == 'string') {
|
||||
return _cache[propName];
|
||||
}
|
||||
|
||||
element = element || document.documentElement;
|
||||
var style = element.style, prefixed, uPropName;
|
||||
|
||||
// test standard property first
|
||||
if (typeof style[propName] == 'string') {
|
||||
return (_cache[propName] = propName);
|
||||
}
|
||||
|
||||
// capitalize
|
||||
uPropName = propName.charAt(0).toUpperCase() + propName.slice(1);
|
||||
|
||||
// test vendor specific properties
|
||||
for (var i = 0, l = prefixes.length; i < l; i++) {
|
||||
prefixed = prefixes[i] + uPropName;
|
||||
if (typeof style[prefixed] == 'string') {
|
||||
return (_cache[propName] = prefixed);
|
||||
}
|
||||
}
|
||||
|
||||
//if all fails then set to undefined
|
||||
return (_cache[propName] = 'undefined');
|
||||
};
|
||||
|
||||
CustomStyle.setProp = function set(propName, element, str) {
|
||||
var prop = this.getProp(propName);
|
||||
if (prop != 'undefined')
|
||||
element.style[prop] = str;
|
||||
};
|
||||
|
||||
return CustomStyle;
|
||||
})();
|
||||
|
||||
function TextLayerBuilder(textLayerDiv) {
|
||||
this.textLayerDiv = textLayerDiv;
|
||||
};
|
||||
|
||||
TextLayerBuilder.prototype = {
|
||||
beginLayout: function() {
|
||||
this.textDivs = [];
|
||||
this.textLayerQueue = [];
|
||||
this.renderingDone = false;
|
||||
},
|
||||
|
||||
endLayout: function() {
|
||||
this.layoutDone = true;
|
||||
this.insertDivContent();
|
||||
},
|
||||
|
||||
appendText: function(geom) {
|
||||
var textDiv = document.createElement('div');
|
||||
|
||||
// vScale and hScale already contain the scaling to pixel units
|
||||
var fontHeight = geom.fontSize * Math.abs(geom.vScale);
|
||||
textDiv.dataset.canvasWidth = geom.canvasWidth * geom.hScale;
|
||||
textDiv.dataset.fontName = geom.fontName;
|
||||
|
||||
textDiv.style.fontSize = fontHeight + 'px';
|
||||
textDiv.style.fontFamily = geom.fontFamily;
|
||||
textDiv.style.left = geom.x + 'px';
|
||||
textDiv.style.top = (geom.y - fontHeight) + 'px';
|
||||
|
||||
textDiv.ondblclick = function(e) {
|
||||
if (window.getSelection)
|
||||
window.getSelection().removeAllRanges();
|
||||
else if (document.selection)
|
||||
document.selection.empty();
|
||||
}
|
||||
|
||||
// The content of the div is set in the `setTextContent` function.
|
||||
|
||||
this.textDivs.push(textDiv);
|
||||
},
|
||||
|
||||
setTextContent: function(textContent) {
|
||||
this.textContent = textContent;
|
||||
this.insertDivContent();
|
||||
},
|
||||
|
||||
insertDivContent: function() {
|
||||
// Only set the content of the divs once layout has finished, the content
|
||||
// for the divs is available and content is not yet set on the divs.
|
||||
if (!this.layoutDone || this.divContentDone || !this.textContent)
|
||||
return;
|
||||
|
||||
this.divContentDone = true;
|
||||
|
||||
var textDivs = this.textDivs;
|
||||
var bidiTexts = this.textContent.bidiTexts;
|
||||
|
||||
for (var i = 0; i < bidiTexts.length; i++) {
|
||||
var bidiText = bidiTexts[i];
|
||||
var textDiv = textDivs[i];
|
||||
if (!/\S/.test(bidiText.str)) {
|
||||
textDiv.dataset.isWhitespace = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
textDiv.textContent = bidiText.str;
|
||||
// bidiText.dir may be 'ttb' for vertical texts.
|
||||
textDiv.dir = bidiText.dir === 'rtl' ? 'rtl' : 'ltr';
|
||||
}
|
||||
|
||||
this.renderLayer();
|
||||
},
|
||||
|
||||
renderLayer: function() {
|
||||
var self = this;
|
||||
var textDivs = this.textDivs;
|
||||
var bidiTexts = this.textContent.bidiTexts;
|
||||
var textLayerDiv = this.textLayerDiv;
|
||||
var canvas = document.createElement('canvas');
|
||||
var ctx = canvas.getContext('2d');
|
||||
var textLayerFrag = document.createDocumentFragment();
|
||||
|
||||
// No point in rendering so many divs as it'd make the browser unusable
|
||||
// even after the divs are rendered
|
||||
var MAX_TEXT_DIVS_TO_RENDER = 100000;
|
||||
if (textDivs.length > MAX_TEXT_DIVS_TO_RENDER)
|
||||
return;
|
||||
|
||||
for (var i = 0, ii = textDivs.length; i < ii; i++) {
|
||||
var textDiv = textDivs[i];
|
||||
if ('isWhitespace' in textDiv.dataset) {
|
||||
continue;
|
||||
}
|
||||
textLayerFrag.appendChild(textDiv);
|
||||
|
||||
ctx.font = textDiv.style.fontSize + ' ' + textDiv.style.fontFamily;
|
||||
var width = ctx.measureText(textDiv.textContent).width;
|
||||
|
||||
if (width > 0) {
|
||||
var textScale = textDiv.dataset.canvasWidth / width;
|
||||
|
||||
var transform = 'scale(' + textScale + ', 1)';
|
||||
if (bidiTexts[i].dir === 'ttb') {
|
||||
transform = 'rotate(90deg) ' + transform;
|
||||
}
|
||||
CustomStyle.setProp('transform' , textDiv, transform);
|
||||
CustomStyle.setProp('transformOrigin' , textDiv, '0% 0%');
|
||||
|
||||
textLayerDiv.appendChild(textDiv);
|
||||
}
|
||||
}
|
||||
|
||||
this.renderingDone = true;
|
||||
//this.updateMatches();
|
||||
|
||||
textLayerDiv.appendChild(textLayerFrag);
|
||||
},
|
||||
|
||||
/*setupRenderLayoutTimer: function() {
|
||||
// Schedule renderLayout() if user has been scrolling, otherwise
|
||||
// run it right away
|
||||
var RENDER_DELAY = 200; // in ms
|
||||
var self = this;
|
||||
if (Date.now() - PDFView.lastScroll > RENDER_DELAY) {
|
||||
// Render right away
|
||||
this.renderLayer();
|
||||
} else {
|
||||
// Schedule
|
||||
if (this.renderTimer)
|
||||
clearTimeout(this.renderTimer);
|
||||
this.renderTimer = setTimeout(function() {
|
||||
self.setupRenderLayoutTimer();
|
||||
}, RENDER_DELAY);
|
||||
}
|
||||
}*/
|
||||
};
|
||||
|
||||
return TextLayerBuilder;
|
||||
|
||||
});
|
Loading…
Reference in a new issue