make new pdf viewer the default for all users

remove old pdf viewer
This commit is contained in:
Brian Gough 2015-03-20 11:28:28 +00:00
parent ff55e4c5ed
commit b0a32b1ef8
11 changed files with 16 additions and 1508 deletions

View file

@ -23,9 +23,6 @@ for path in [
"#{jsPath}main.js", "#{jsPath}main.js",
"#{jsPath}libs.js", "#{jsPath}libs.js",
"#{jsPath}ace/ace.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.js",
"#{jsPath}libs/pdfjs-1.0.712/pdf.worker.js", "#{jsPath}libs/pdfjs-1.0.712/pdf.worker.js",
"#{jsPath}libs/pdfjs-1.0.712/compatibility.js", "#{jsPath}libs/pdfjs-1.0.712/compatibility.js",

View file

@ -89,14 +89,14 @@ block content
window.requirejs = { window.requirejs = {
"paths" : { "paths" : {
"mathjax": "/js/libs/mathjax/MathJax.js?config=TeX-AMS_HTML", "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')}", "urlArgs" : "fingerprint=#{fingerprint(jsPath + 'ide.js')}-#{fingerprint(jsPath + 'libs.js')}",
"waitSeconds": 0, "waitSeconds": 0,
"shim": { "shim": {
"libs/pdfListView/PdfListView": {
deps: ["libs/pdf"]
},
"libs/pdf": { "libs/pdf": {
deps: ["libs/compatibility"] deps: ["libs/compatibility"]
}, },
@ -117,21 +117,12 @@ block content
- locals.suppressDefaultJs = true - locals.suppressDefaultJs = true
- // user.featureSwitches = {} // override - var pdfPath = 'libs/pdfjs-1.0.712/pdf.worker.js'
- 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 fingerprintedPath = fingerprint(jsPath+pdfPath) - var fingerprintedPath = fingerprint(jsPath+pdfPath)
- var pdfJsWorkerPath = jsPath+pdfPath+'?fingerprint='+fingerprintedPath - var pdfJsWorkerPath = jsPath+pdfPath+'?fingerprint='+fingerprintedPath
script(type='text/javascript'). script(type='text/javascript').
window.pdfJsWorkerPath = "#{pdfJsWorkerPath}"; 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( script(
data-main=jsPath+"ide.js", data-main=jsPath+"ide.js",
baseurl=jsPath, baseurl=jsPath,

View file

@ -58,30 +58,16 @@ div.full-size.pdf(ng-controller="PdfController")
i.split-screen i.split-screen
.pdf-viewer(ng-show="pdf.url && pdf.view == 'pdf' && !pdf.failure && !pdf.timeout && !pdf.error") .pdf-viewer(ng-show="pdf.url && pdf.view == 'pdf' && !pdf.failure && !pdf.timeout && !pdf.error")
- // user.featureSwitches = {} // override div(
- var usePdfNG = user.featureSwitches && user.featureSwitches.pdfng; pdfng
if !usePdfNG ng-if="settings.pdfViewer == 'pdfjs'"
div( pdf-src="pdf.url"
pdfjs key="{{ project_id }}"
ng-if="settings.pdfViewer == 'pdfjs'" resize-on="layout:main:resize,layout:pdf:resize"
pdf-src="pdf.url" highlights="pdf.highlights"
key="{{ project_id }}" position="pdf.position"
resize-on="layout:main:resize,layout:pdf:resize" dbl-click-callback="syncToCode"
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"
)
iframe( iframe(
ng-src="{{ pdf.url }}" ng-src="{{ pdf.url }}"

View file

@ -1,8 +1,7 @@
define [ define [
"ide/pdf/controllers/PdfController" "ide/pdf/controllers/PdfController"
"ide/pdf/controllers/PdfViewToggleController" "ide/pdf/controllers/PdfViewToggleController"
"ide/pdf/directives/pdfJs" "ide/pdfng/directives/pdfJs"
"ide/pdfng/directives/pdfJs" # alternative incremental viewer
], () -> ], () ->
class PdfManager class PdfManager
constructor: (@ide, @$scope) -> constructor: (@ide, @$scope) ->

View file

@ -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>
"""
}
]

View file

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

View file

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

View file

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

View file

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