mirror of
https://github.com/Yetangitu/owncloud-apps.git
synced 2025-10-02 14:49:17 +02:00
files_reader: PDF support, basic functions work (i.e. is usable as reader)
This commit is contained in:
parent
0340300ba0
commit
5da12f92e5
18 changed files with 1730 additions and 138 deletions
|
@ -1,5 +1,9 @@
|
|||
## UNRELEASED
|
||||
###
|
||||
### Added
|
||||
- Reader now supports PDF
|
||||
- PDF should work more or less like EPUB, ie. double page spreads are supported
|
||||
- optional double-buffering for faster rendering, can be disabled for low-memory devices
|
||||
- optional selectable text layer, can be disabled for low-memory devices
|
||||
|
||||
## 1.0.0 - 2017-03-15
|
||||
### Added
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
- add hook to delete book info when books are deleted
|
||||
- do this for files_opds as well
|
||||
- for maximize page area, disable two-column when in portrait mode
|
||||
- maybe always disable two-column in portrait mode
|
||||
- swipe gestures for open/close sidebar, page turn
|
||||
- disable wide page turn when placing markers
|
||||
- index
|
||||
- search
|
||||
- bookmarks
|
||||
- annotations
|
||||
- settings
|
||||
|
|
|
@ -121,7 +121,7 @@ The same Android device showing a zoomed-in part of a page|![The same Android de
|
|||
[SS18]: https://github.com/Yetangitu/owncloud-apps/blob/master/screenshots/photo_2017-03-15_18-28-56.jpg?raw=true "The same Android device showing a zoomed-in part of a page"
|
||||
]]>
|
||||
</description>
|
||||
<version>1.0.0</version>
|
||||
<version>1.0.1</version>
|
||||
<licence>AGPL</licence>
|
||||
<author>Frank de Lange</author>
|
||||
<documentation>
|
||||
|
|
|
@ -46,6 +46,8 @@
|
|||
|
||||
hideControls: function() {
|
||||
$('#app-content #controls').hide();
|
||||
// and, for NC12...
|
||||
$('#app-navigation').css("display", "none");
|
||||
},
|
||||
|
||||
hide: function() {
|
||||
|
@ -54,6 +56,8 @@
|
|||
}
|
||||
$("#controls").show();
|
||||
$('#app-content #controls').removeClass('hidden');
|
||||
// NC12...
|
||||
$('#app-navigation').css("display", "");
|
||||
if ($('#isPublic').val()) {
|
||||
$('#imgframe').show();
|
||||
$('footer').show();
|
||||
|
|
Binary file not shown.
|
@ -12,7 +12,7 @@
|
|||
$preferences = $_['preferences'];
|
||||
$metadata = $_['metadata'];
|
||||
$annotations = $_['annotations'];
|
||||
$title = htmlentities(basename($dllink));
|
||||
$title = htmlentities(basename($downloadLink));
|
||||
$revision = '0071';
|
||||
$version = \OCP\App::getAppVersion('files_reader') . '.' . $revision;
|
||||
|
||||
|
@ -41,12 +41,14 @@
|
|||
<link rel="stylesheet" href="<?php p($urlGenerator->linkTo('files_reader', 'vendor/pdfjs/css/main.css')) ?>?v=<?php p($version) ?>">
|
||||
<link rel="stylesheet" href="<?php p($urlGenerator->linkTo('files_reader', 'vendor/pdfjs/css/sidebar.css')) ?>?v=<?php p($version) ?>">
|
||||
<link rel="stylesheet" href="<?php p($urlGenerator->linkTo('files_reader', 'vendor/pdfjs/css/popup.css')) ?>?v=<?php p($version) ?>">
|
||||
<link rel="stylesheet" href="<?php p($urlGenerator->linkTo('files_reader', 'vendor/pdfjs/css/text_layer_builder.css')) ?>?v=<?php p($version) ?>">
|
||||
<script type="text/javascript" nonce="<?php p($nonce) ?>" src="<?php p($urlGenerator->linkTo('files_reader', 'vendor/epubjs/libs/jquery.min.js')) ?>?v=<?php p($version) ?>"> </script>
|
||||
<script type="text/javascript" nonce="<?php p($nonce) ?>" src="<?php p($urlGenerator->linkTo('files_reader', 'vendor/bartaz/jquery.highlight.js')) ?>?v=<?php p($version) ?>"> </script>
|
||||
<script type="text/javascript" nonce="<?php p($nonce) ?>" src="<?php p($urlGenerator->linkTo('files_reader', 'vendor/jquery/put-delete.js')) ?>?v=<?php p($version) ?>"> </script>
|
||||
<script type="text/javascript" nonce="<?php p($nonce) ?>" src="<?php p($urlGenerator->linkTo('files_reader', 'vendor/sindresorhus/screenfull.js')) ?>?v=<?php p($version) ?>"> </script>
|
||||
<script type="text/javascript" nonce="<?php p($nonce) ?>" src="<?php p($urlGenerator->linkTo('files_reader', 'vendor/pdfjs/lib/pdf.js')) ?>?v=<?php p($version) ?>"> </script>
|
||||
<script type="text/javascript" nonce="<?php p($nonce) ?>" src="<?php p($urlGenerator->linkTo('files_reader', 'vendor/pdfjs/pdf.reader.js')) ?>?v=<?php p($version) ?>"> </script>
|
||||
<script type="text/javascript" nonce="<?php p($nonce) ?>" src="<?php p($urlGenerator->linkTo('files_reader', 'vendor/pdfjs/controllers/textlayer_controller.js')) ?>?v=<?php p($version) ?>"> </script>
|
||||
<script type="text/javascript" nonce="<?php p($nonce) ?>" src="<?php p($urlGenerator->linkTo('files_reader', 'vendor/pdfjs/controllers/reader_controller.js')) ?>?v=<?php p($version) ?>"> </script>
|
||||
<script type="text/javascript" nonce="<?php p($nonce) ?>" src="<?php p($urlGenerator->linkTo('files_reader', 'vendor/pdfjs/controllers/sidebar_controller.js')) ?>?v=<?php p($version) ?>"> </script>
|
||||
<script type="text/javascript" nonce="<?php p($nonce) ?>" src="<?php p($urlGenerator->linkTo('files_reader', 'vendor/pdfjs/controllers/settings_controller.js')) ?>?v=<?php p($version) ?>"> </script>
|
||||
|
@ -249,6 +251,40 @@
|
|||
</span>
|
||||
</div>
|
||||
<div id="title-controls">
|
||||
<!-- select works fine, except for the fact that - as usual - apple mobile does not support icons...
|
||||
<label for="zoomlevel">zoom: </label>
|
||||
<select id="zoomlevel">
|
||||
<option value="spread" data-icon="" data-text="2-page"></option>
|
||||
<option value="fit_page" data-icon="" data-text="fit page"></option>
|
||||
<option value="fit_width" data-icon="" data-text="fit width"></option>
|
||||
<option value="0.25" class="text">25%</option>
|
||||
<option value="0.5" class="text">50%</option>
|
||||
<option value="0.75" class="text">75%</option>
|
||||
<option value="1" class="text">100%</option>
|
||||
<option value="1.25" class="text">125%</option>
|
||||
<option value="1.5" class="text">150%</option>
|
||||
<option value="2" class="text">200%</option>
|
||||
<option value="3" class="text">300%</option>
|
||||
<option value="4" class="text">400%</option>
|
||||
</select>
|
||||
-->
|
||||
<div id="zoom_options" class="hide">
|
||||
<div class="zoom_option icon-double_page_mode" data-value="spread" data-class="icon-double_page_mode" data-text=""></div>
|
||||
<div class="zoom_option icon-single_page_mode" data-value="fit_page" data-class="icon-single_page_mode" data-text=""></div>
|
||||
<div class="zoom_option icon-icon-fit-width" data-value="fit_width" data-class="icon-icon-fit-width" data-text=""></div>
|
||||
<div class="zoom_option" data-value="0.25" data-text="25%">25%</div>
|
||||
<div class="zoom_option" data-value="0.5" data-text="50%">50%</div>
|
||||
<div class="zoom_option" data-value="0.75" data-text="75%">75%</div>
|
||||
<div class="zoom_option" data-value="1" data-text="100%">100%</div>
|
||||
<div class="zoom_option" data-value="1.25" data-text="125%">125%</div>
|
||||
<div class="zoom_option" data-value="1.5" data-text="150%">150%</div>
|
||||
<div class="zoom_option" data-value="2" data-text="200%">200%</div>
|
||||
<div class="zoom_option" data-value="3" data-text="300%">300%</div>
|
||||
<div class="zoom_option" data-value="4" data-text="400%">400%</div>
|
||||
</div>
|
||||
<span id="zoom_icon"></span>
|
||||
<span class="controls-separator"> </span>
|
||||
<a></a>
|
||||
<a id="note" class="icon-comment">
|
||||
</a>
|
||||
<a id="bookmark" class="icon-turned_in_not">
|
||||
|
@ -276,9 +312,9 @@
|
|||
‹
|
||||
</div>
|
||||
</div>
|
||||
<div ID="viewer">
|
||||
<canvas id="left" class="viewer"></canvas>
|
||||
<canvas id="right" class="viewer"></canvas>
|
||||
<div id="viewer" class="flex">
|
||||
<canvas id="left" class="viewer"></canvas><div id="text_left" class="textLayer"></div>
|
||||
<canvas id="right" class="viewer"></canvas><div id="text_right" class="textLayer"></div>
|
||||
</div>
|
||||
<div id="next" class="arrow">
|
||||
<div class="translucent">
|
||||
|
|
BIN
files_reader/vendor/pdfjs/.pdf.reader.js.swp
vendored
Normal file
BIN
files_reader/vendor/pdfjs/.pdf.reader.js.swp
vendored
Normal file
Binary file not shown.
BIN
files_reader/vendor/pdfjs/controllers/.controls_controller.js.swp
vendored
Normal file
BIN
files_reader/vendor/pdfjs/controllers/.controls_controller.js.swp
vendored
Normal file
Binary file not shown.
BIN
files_reader/vendor/pdfjs/controllers/.reader_controller.js.swp
vendored
Normal file
BIN
files_reader/vendor/pdfjs/controllers/.reader_controller.js.swp
vendored
Normal file
Binary file not shown.
|
@ -1,27 +1,45 @@
|
|||
PDFJS.reader.ControlsController = function(book) {
|
||||
var reader = this;
|
||||
var reader = this,
|
||||
settings = reader.settings;
|
||||
|
||||
var $store = $("#store"),
|
||||
$viewer = $("#viewer"),
|
||||
$fullscreen = $("#fullscreen"),
|
||||
$fullscreenicon = $("#fullscreenicon"),
|
||||
$cancelfullscreenicon = $("#cancelfullscreenicon"),
|
||||
$slider = $("#slider"),
|
||||
$main = $("#main"),
|
||||
$sidebar = $("#sidebar"),
|
||||
$titlebar = $("#titlebar"),
|
||||
$settings = $("#setting"),
|
||||
$bookmark = $("#bookmark"),
|
||||
$note = $("#note");
|
||||
$note = $("#note"),
|
||||
$togglelayout = $("#toggle-layout"),
|
||||
$zoom_icon = $("#zoom_icon"),
|
||||
$zoom_options = $("#zoom_options"),
|
||||
$zoom_option = $(".zoom_option"),
|
||||
$page_num = $("#page_num");
|
||||
|
||||
var goOnline = function() {
|
||||
reader.offline = false;
|
||||
// $store.attr("src", $icon.data("save"));
|
||||
if (reader.isMobile() === true) {
|
||||
$titlebar.addClass("background_visible");
|
||||
};
|
||||
|
||||
var goOffline = function() {
|
||||
reader.offline = true;
|
||||
// $store.attr("src", $icon.data("saved"));
|
||||
var show = function () {
|
||||
$titlebar.removeClass("hide");
|
||||
};
|
||||
|
||||
var hide = function () {
|
||||
$titlebar.addClass("hide");
|
||||
};
|
||||
|
||||
var toggle = function () {
|
||||
$titlebar.toggleClass("hide");
|
||||
};
|
||||
|
||||
$viewer.on("touchstart", function(e) {
|
||||
reader.ControlsController.toggle();
|
||||
});
|
||||
|
||||
var fullscreen = false;
|
||||
|
||||
$slider.on("click", function () {
|
||||
|
@ -81,6 +99,94 @@ PDFJS.reader.ControlsController = function(book) {
|
|||
|
||||
});
|
||||
|
||||
/* select works fine on most browsers, but - of course - apple mobile has 'special needs'
|
||||
* in that it does not support any styling in the drop-down list, and as such can not display
|
||||
* icons there. Due to the unfortunate fact that many still buy these apple-encumbered devices
|
||||
* a custom select is needed...
|
||||
*
|
||||
$zoomlevel.val(settings.zoomLevel);
|
||||
|
||||
$zoomlevel.on("change", function () {
|
||||
reader.setZoom($(this).val());
|
||||
var $option = $zoomlevel.find(":selected");
|
||||
if ($option.data("icon") !== undefined)
|
||||
$("#zoom_icon")[0].textContent = $option.data("icon");
|
||||
});
|
||||
|
||||
*
|
||||
*/
|
||||
|
||||
/* custom select, supports icons in drop-down list */
|
||||
$zoom_icon.on("click", function () {
|
||||
var offset = $(this).offset();
|
||||
console.log(offset);
|
||||
$zoom_options.css({
|
||||
'left': parseInt(offset.left) + "px",
|
||||
'top' : parseInt(parseInt(offset.top) + parseInt($zoom_icon.height())) + "px"
|
||||
});
|
||||
|
||||
$zoom_options.toggleClass("hide");
|
||||
});
|
||||
|
||||
$zoom_icon[0].className="";
|
||||
var $current_zoom_option = $zoom_options.find("[data-value='" + settings.zoomLevel + "']");
|
||||
if ($current_zoom_option.data("class")) {
|
||||
$zoom_icon.addClass($current_zoom_option.data("class"));
|
||||
} else {
|
||||
$zoom_icon[0].textContent = $current_zoom_option.data("text");
|
||||
}
|
||||
|
||||
$zoom_option.on("click", function () {
|
||||
var $this = $(this);
|
||||
reader.setZoom($this.data("value"));
|
||||
$zoom_icon[0].className="";
|
||||
$zoom_icon[0].textContent = "";
|
||||
if ($this.data("class")) {
|
||||
$zoom_icon.addClass($this.data("class"));
|
||||
} else {
|
||||
$zoom_icon[0].textContent = $this.data("text");
|
||||
}
|
||||
$zoom_options.addClass("hide");
|
||||
});
|
||||
/* end custom select */
|
||||
|
||||
var enterPageNum = function(e) {
|
||||
var text = e.target,
|
||||
page;
|
||||
|
||||
switch (e.keyCode) {
|
||||
case 27: // escape - cancel, discard changes
|
||||
$page_num[0].removeEventListener("keydown", enterPageNum, false);
|
||||
$page_num.removeClass("editable");
|
||||
$page_num.prop("contenteditable",false);
|
||||
$page_num.text($page_num.data("content"));
|
||||
break;
|
||||
case 13: // enter - accept changes
|
||||
$page_num[0].removeEventListener("keydown", enterPageNum, false);
|
||||
$page_num.removeClass("editable");
|
||||
$page_num.attr("contenteditable", false);
|
||||
page = parseInt($page_num.text());
|
||||
if (page > 0 && page <= reader.settings.numPages) {
|
||||
reader.queuePage(page);
|
||||
} else {
|
||||
$page_num.text($page_num.data("content"));
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
e.stopPropagation;
|
||||
};
|
||||
|
||||
|
||||
$page_num.on("click", function() {
|
||||
$page_num.data("content", $page_num.text());
|
||||
$page_num.text("");
|
||||
$page_num.prop("contenteditable", true);
|
||||
$page_num.addClass("editable");
|
||||
$page_num[0].addEventListener("keydown", enterPageNum, false);
|
||||
});
|
||||
|
||||
|
||||
/*
|
||||
book.on('renderer:locationChanged', function(cfi){
|
||||
var cfiFragment = "#" + cfi;
|
||||
|
@ -113,6 +219,8 @@ PDFJS.reader.ControlsController = function(book) {
|
|||
*/
|
||||
|
||||
return {
|
||||
|
||||
"show": show,
|
||||
"hide": hide,
|
||||
"toggle": toggle
|
||||
};
|
||||
};
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
PDFJS.reader.ReaderController = function(book) {
|
||||
var $main = $("#main"),
|
||||
$viewer = $("#viewer"),
|
||||
$divider = $("#divider"),
|
||||
$loader = $("#loader"),
|
||||
$next = $("#next"),
|
||||
|
@ -84,7 +85,7 @@ PDFJS.reader.ReaderController = function(book) {
|
|||
page_no = 1;
|
||||
break;
|
||||
case 'last':
|
||||
// TODO
|
||||
page_no = reader.settings.numPages;
|
||||
break;
|
||||
case 'annotate':
|
||||
$note.click();
|
||||
|
@ -95,6 +96,9 @@ PDFJS.reader.ReaderController = function(book) {
|
|||
case 'reflow':
|
||||
$sidebarReflow.click();
|
||||
break;
|
||||
case 'toggleTitlebar':
|
||||
reader.ControlsController.toggle();
|
||||
break;
|
||||
case 'toggleSidebar':
|
||||
reader.SidebarController.toggle();
|
||||
break;
|
||||
|
@ -115,8 +119,7 @@ PDFJS.reader.ReaderController = function(book) {
|
|||
}
|
||||
|
||||
if (page_no) {
|
||||
|
||||
// TODO
|
||||
reader.queuePage(page_no);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
379
files_reader/vendor/pdfjs/controllers/textlayer_controller.js
vendored
Normal file
379
files_reader/vendor/pdfjs/controllers/textlayer_controller.js
vendored
Normal file
|
@ -0,0 +1,379 @@
|
|||
/* Copyright 2012 Mozilla Foundation
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {Object} TextLayerBuilderOptions
|
||||
* @property {HTMLDivElement} textLayerDiv - The text layer container.
|
||||
* @property {EventBus} eventBus - The application event bus.
|
||||
* @property {number} pageIndex - The page index.
|
||||
* @property {PageViewport} viewport - The viewport of the text layer.
|
||||
* @property {PDFFindController} findController
|
||||
* @property {boolean} enhanceTextSelection - Option to turn on improved
|
||||
* text selection.
|
||||
*/
|
||||
|
||||
/**
|
||||
* TextLayerBuilder provides text-selection functionality for the PDF.
|
||||
* It does this by creating overlay divs over the PDF text. These divs
|
||||
* contain text that matches the PDF text they are overlaying. This object
|
||||
* also provides a way to highlight text that is being searched for.
|
||||
* @class
|
||||
*/
|
||||
PDFJS.Reader.TextLayerController = function (options) {
|
||||
|
||||
var EXPAND_DIVS_TIMEOUT = 300; // ms
|
||||
|
||||
this.textLayerDiv = options.textLayerDiv;
|
||||
this.eventBus = options.eventBus || null;
|
||||
this.textContent = null;
|
||||
this.renderingDone = false;
|
||||
this.pageIdx = options.pageIndex;
|
||||
this.pageNumber = this.pageIdx + 1;
|
||||
this.matches = [];
|
||||
this.viewport = options.viewport;
|
||||
this.textDivs = [];
|
||||
this.findController = options.findController || null;
|
||||
this.textLayerRenderTask = null;
|
||||
this.enhanceTextSelection = options.enhanceTextSelection;
|
||||
this._bindMouse();
|
||||
|
||||
return this;
|
||||
};
|
||||
|
||||
PDFJS.Reader.TextLayerController.prototype._finishRendering = function () {
|
||||
this.renderingDone = true;
|
||||
|
||||
if (!this.enhanceTextSelection) {
|
||||
var endOfContent = document.createElement('div');
|
||||
endOfContent.className = 'endOfContent';
|
||||
this.textLayerDiv.appendChild(endOfContent);
|
||||
}
|
||||
|
||||
if (this.eventBus !== null) {
|
||||
this.eventBus.dispatch('textlayerrendered', {
|
||||
source: this,
|
||||
pageNumber: this.pageNumber,
|
||||
numTextDivs: this.textDivs.length,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Renders the text layer.
|
||||
* @param {number} timeout (optional) if specified, the rendering waits
|
||||
* for specified amount of ms.
|
||||
*/
|
||||
PDFJS.Reader.TextLayerController.prototype.render = function(timeout) {
|
||||
if (!this.textContent || this.renderingDone) {
|
||||
return;
|
||||
}
|
||||
this.cancel();
|
||||
|
||||
this.textDivs = [];
|
||||
var textLayerFrag = document.createDocumentFragment();
|
||||
this.textLayerRenderTask = PDFJS.renderTextLayer({
|
||||
textContent: this.textContent,
|
||||
container: textLayerFrag,
|
||||
viewport: this.viewport,
|
||||
textDivs: this.textDivs,
|
||||
timeout: timeout,
|
||||
enhanceTextSelection: this.enhanceTextSelection,
|
||||
});
|
||||
this.textLayerRenderTask.promise.then(function () {
|
||||
this.textLayerDiv.appendChild(textLayerFrag);
|
||||
this._finishRendering();
|
||||
this.updateMatches();
|
||||
}.bind(this), function (reason) {
|
||||
// cancelled or failed to render text layer -- skipping errors
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Cancels rendering of the text layer.
|
||||
*/
|
||||
PDFJS.Reader.TextLayerController.prototype.cancel = function () {
|
||||
if (this.textLayerRenderTask) {
|
||||
this.textLayerRenderTask.cancel();
|
||||
this.textLayerRenderTask = null;
|
||||
}
|
||||
};
|
||||
|
||||
PDFJS.Reader.TextLayerController.prototype.setTextContent = function (textContent) {
|
||||
this.cancel();
|
||||
this.textContent = textContent;
|
||||
};
|
||||
|
||||
PDFJS.Reader.TextLayerController.prototype.convertMatches = function(matches, matchesLength) {
|
||||
var i = 0;
|
||||
var iIndex = 0;
|
||||
var bidiTexts = this.textContent.items;
|
||||
var end = bidiTexts.length - 1;
|
||||
var queryLen = (this.findController === null ?
|
||||
0 : this.findController.state.query.length);
|
||||
var ret = [];
|
||||
if (!matches) {
|
||||
return ret;
|
||||
}
|
||||
for (var m = 0, len = matches.length; m < len; m++) {
|
||||
// Calculate the start position.
|
||||
var matchIdx = matches[m];
|
||||
|
||||
// Loop over the divIdxs.
|
||||
while (i !== end && matchIdx >= (iIndex + bidiTexts[i].str.length)) {
|
||||
iIndex += bidiTexts[i].str.length;
|
||||
i++;
|
||||
}
|
||||
|
||||
if (i === bidiTexts.length) {
|
||||
console.error('Could not find a matching mapping');
|
||||
}
|
||||
|
||||
var match = {
|
||||
begin: {
|
||||
divIdx: i,
|
||||
offset: matchIdx - iIndex
|
||||
}
|
||||
};
|
||||
|
||||
// Calculate the end position.
|
||||
if (matchesLength) { // multiterm search
|
||||
matchIdx += matchesLength[m];
|
||||
} else { // phrase search
|
||||
matchIdx += queryLen;
|
||||
}
|
||||
|
||||
// Somewhat the same array as above, but use > instead of >= to get
|
||||
// the end position right.
|
||||
while (i !== end && matchIdx > (iIndex + bidiTexts[i].str.length)) {
|
||||
iIndex += bidiTexts[i].str.length;
|
||||
i++;
|
||||
}
|
||||
|
||||
match.end = {
|
||||
divIdx: i,
|
||||
offset: matchIdx - iIndex
|
||||
};
|
||||
ret.push(match);
|
||||
}
|
||||
|
||||
return ret;
|
||||
};
|
||||
|
||||
PDFJS.Reader.TextLayerController.prototype.renderMatches = function (matches) {
|
||||
// Early exit if there is nothing to render.
|
||||
if (matches.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
var bidiTexts = this.textContent.items;
|
||||
var textDivs = this.textDivs;
|
||||
var prevEnd = null;
|
||||
var pageIdx = this.pageIdx;
|
||||
var isSelectedPage = (this.findController === null ?
|
||||
false : (pageIdx === this.findController.selected.pageIdx));
|
||||
var selectedMatchIdx = (this.findController === null ?
|
||||
-1 : this.findController.selected.matchIdx);
|
||||
var highlightAll = (this.findController === null ?
|
||||
false : this.findController.state.highlightAll);
|
||||
var infinity = {
|
||||
divIdx: -1,
|
||||
offset: undefined
|
||||
};
|
||||
|
||||
function beginText(begin, className) {
|
||||
var divIdx = begin.divIdx;
|
||||
textDivs[divIdx].textContent = '';
|
||||
appendTextToDiv(divIdx, 0, begin.offset, className);
|
||||
}
|
||||
|
||||
function appendTextToDiv(divIdx, fromOffset, toOffset, className) {
|
||||
var div = textDivs[divIdx];
|
||||
var content = bidiTexts[divIdx].str.substring(fromOffset, toOffset);
|
||||
var node = document.createTextNode(content);
|
||||
if (className) {
|
||||
var span = document.createElement('span');
|
||||
span.className = className;
|
||||
span.appendChild(node);
|
||||
div.appendChild(span);
|
||||
return;
|
||||
}
|
||||
div.appendChild(node);
|
||||
}
|
||||
|
||||
var i0 = selectedMatchIdx, i1 = i0 + 1;
|
||||
if (highlightAll) {
|
||||
i0 = 0;
|
||||
i1 = matches.length;
|
||||
} else if (!isSelectedPage) {
|
||||
// Not highlighting all and this isn't the selected page, so do nothing.
|
||||
return;
|
||||
}
|
||||
|
||||
for (var i = i0; i < i1; i++) {
|
||||
var match = matches[i];
|
||||
var begin = match.begin;
|
||||
var end = match.end;
|
||||
var isSelected = (isSelectedPage && i === selectedMatchIdx);
|
||||
var highlightSuffix = (isSelected ? ' selected' : '');
|
||||
|
||||
if (this.findController) {
|
||||
this.findController.updateMatchPosition(pageIdx, i, textDivs,
|
||||
begin.divIdx);
|
||||
}
|
||||
|
||||
// Match inside new div.
|
||||
if (!prevEnd || begin.divIdx !== prevEnd.divIdx) {
|
||||
// If there was a previous div, then add the text at the end.
|
||||
if (prevEnd !== null) {
|
||||
appendTextToDiv(prevEnd.divIdx, prevEnd.offset, infinity.offset);
|
||||
}
|
||||
// Clear the divs and set the content until the starting point.
|
||||
beginText(begin);
|
||||
} else {
|
||||
appendTextToDiv(prevEnd.divIdx, prevEnd.offset, begin.offset);
|
||||
}
|
||||
|
||||
if (begin.divIdx === end.divIdx) {
|
||||
appendTextToDiv(begin.divIdx, begin.offset, end.offset,
|
||||
'highlight' + highlightSuffix);
|
||||
} else {
|
||||
appendTextToDiv(begin.divIdx, begin.offset, infinity.offset,
|
||||
'highlight begin' + highlightSuffix);
|
||||
for (var n0 = begin.divIdx + 1, n1 = end.divIdx; n0 < n1; n0++) {
|
||||
textDivs[n0].className = 'highlight middle' + highlightSuffix;
|
||||
}
|
||||
beginText(end, 'highlight end' + highlightSuffix);
|
||||
}
|
||||
prevEnd = end;
|
||||
}
|
||||
|
||||
if (prevEnd) {
|
||||
appendTextToDiv(prevEnd.divIdx, prevEnd.offset, infinity.offset);
|
||||
}
|
||||
};
|
||||
|
||||
PDFJS.Reader.TextLayerController.prototype.updateMatches = function () {
|
||||
// Only show matches when all rendering is done.
|
||||
if (!this.renderingDone) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Clear all matches.
|
||||
var matches = this.matches;
|
||||
var textDivs = this.textDivs;
|
||||
var bidiTexts = this.textContent.items;
|
||||
var clearedUntilDivIdx = -1;
|
||||
|
||||
// Clear all current matches.
|
||||
for (var i = 0, len = matches.length; i < len; i++) {
|
||||
var match = matches[i];
|
||||
var begin = Math.max(clearedUntilDivIdx, match.begin.divIdx);
|
||||
for (var n = begin, end = match.end.divIdx; n <= end; n++) {
|
||||
var div = textDivs[n];
|
||||
div.textContent = bidiTexts[n].str;
|
||||
div.className = '';
|
||||
}
|
||||
clearedUntilDivIdx = match.end.divIdx + 1;
|
||||
}
|
||||
|
||||
if (this.findController === null || !this.findController.active) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Convert the matches on the page controller into the match format
|
||||
// used for the textLayer.
|
||||
var pageMatches, pageMatchesLength;
|
||||
if (this.findController !== null) {
|
||||
pageMatches = this.findController.pageMatches[this.pageIdx] || null;
|
||||
pageMatchesLength = (this.findController.pageMatchesLength) ?
|
||||
this.findController.pageMatchesLength[this.pageIdx] || null : null;
|
||||
}
|
||||
|
||||
this.matches = this.convertMatches(pageMatches, pageMatchesLength);
|
||||
this.renderMatches(this.matches);
|
||||
};
|
||||
|
||||
/**
|
||||
* Fixes text selection: adds additional div where mouse was clicked.
|
||||
* This reduces flickering of the content if mouse slowly dragged down/up.
|
||||
* @private
|
||||
*/
|
||||
PDFJS.Reader.TextLayerController.prototype._bindMouse = function () {
|
||||
var div = this.textLayerDiv;
|
||||
var self = this;
|
||||
var expandDivsTimer = null;
|
||||
|
||||
div.addEventListener('mousedown', function (e) {
|
||||
if (self.enhanceTextSelection && self.textLayerRenderTask) {
|
||||
self.textLayerRenderTask.expandTextDivs(true);
|
||||
if ((typeof PDFJSDev === 'undefined' ||
|
||||
!PDFJSDev.test('FIREFOX || MOZCENTRAL')) &&
|
||||
expandDivsTimer) {
|
||||
clearTimeout(expandDivsTimer);
|
||||
expandDivsTimer = null;
|
||||
}
|
||||
return;
|
||||
}
|
||||
var end = div.querySelector('.endOfContent');
|
||||
if (!end) {
|
||||
return;
|
||||
}
|
||||
if (typeof PDFJSDev === 'undefined' ||
|
||||
!PDFJSDev.test('FIREFOX || MOZCENTRAL')) {
|
||||
// On non-Firefox browsers, the selection will feel better if the height
|
||||
// of the endOfContent div will be adjusted to start at mouse click
|
||||
// location -- this will avoid flickering when selections moves up.
|
||||
// However it does not work when selection started on empty space.
|
||||
var adjustTop = e.target !== div;
|
||||
if (typeof PDFJSDev === 'undefined' || PDFJSDev.test('GENERIC')) {
|
||||
adjustTop = adjustTop && window.getComputedStyle(end).
|
||||
getPropertyValue('-moz-user-select') !== 'none';
|
||||
}
|
||||
if (adjustTop) {
|
||||
var divBounds = div.getBoundingClientRect();
|
||||
var r = Math.max(0, (e.pageY - divBounds.top) / divBounds.height);
|
||||
end.style.top = (r * 100).toFixed(2) + '%';
|
||||
}
|
||||
}
|
||||
end.classList.add('active');
|
||||
});
|
||||
|
||||
div.addEventListener('mouseup', function (e) {
|
||||
if (self.enhanceTextSelection && self.textLayerRenderTask) {
|
||||
if (typeof PDFJSDev === 'undefined' ||
|
||||
!PDFJSDev.test('FIREFOX || MOZCENTRAL')) {
|
||||
expandDivsTimer = setTimeout(function() {
|
||||
if (self.textLayerRenderTask) {
|
||||
self.textLayerRenderTask.expandTextDivs(false);
|
||||
}
|
||||
expandDivsTimer = null;
|
||||
}, 300);
|
||||
} else {
|
||||
self.textLayerRenderTask.expandTextDivs(false);
|
||||
}
|
||||
return;
|
||||
}
|
||||
var end = div.querySelector('.endOfContent');
|
||||
if (!end) {
|
||||
return;
|
||||
}
|
||||
if (typeof PDFJSDev === 'undefined' ||
|
||||
!PDFJSDev.test('FIREFOX || MOZCENTRAL')) {
|
||||
end.style.top = '';
|
||||
}
|
||||
end.classList.remove('active');
|
||||
});
|
||||
};
|
||||
|
185
files_reader/vendor/pdfjs/controllers/textlayer_controller.js.simple
vendored
Normal file
185
files_reader/vendor/pdfjs/controllers/textlayer_controller.js.simple
vendored
Normal file
|
@ -0,0 +1,185 @@
|
|||
PDFJS.reader.TextLayerController = function() {
|
||||
|
||||
var TextLayerBuilder = function textLayerBuilder(textLayerDiv, pageIdx) {
|
||||
var textLayerFrag = document.createDocumentFragment();
|
||||
|
||||
this.textLayerDiv = textLayerDiv;
|
||||
this.layoutDone = false;
|
||||
this.divContentDone = false;
|
||||
this.pageIdx = pageIdx;
|
||||
this.matches = [];
|
||||
|
||||
this.beginLayout = function textLayerBuilderBeginLayout() {
|
||||
this.textDivs = [];
|
||||
this.renderingDone = false;
|
||||
};
|
||||
|
||||
this.endLayout = function textLayerBuilderEndLayout() {
|
||||
this.layoutDone = true;
|
||||
this.insertDivContent();
|
||||
};
|
||||
|
||||
this.renderLayer = function textLayerBuilderRenderLayer() {
|
||||
var textDivs = this.textDivs;
|
||||
var bidiTexts = this.textContent.bidiTexts;
|
||||
var textLayerDiv = this.textLayerDiv;
|
||||
var canvas = document.createElement('canvas');
|
||||
var ctx = canvas.getContext('2d');
|
||||
|
||||
// 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;
|
||||
|
||||
textLayerDiv.appendChild(textLayerFrag);
|
||||
};
|
||||
|
||||
this.setupRenderLayoutTimer = function textLayerSetupRenderLayoutTimer() {
|
||||
// Schedule renderLayout() if user has been scrolling, otherwise
|
||||
// run it right away
|
||||
var RENDER_DELAY = 200; // in ms
|
||||
var self = this;
|
||||
//0 was originally PDFView.lastScroll
|
||||
if (Date.now() - 0 > RENDER_DELAY) {
|
||||
// Render right away
|
||||
this.renderLayer();
|
||||
} else {
|
||||
// Schedule
|
||||
if (this.renderTimer)
|
||||
clearTimeout(this.renderTimer);
|
||||
this.renderTimer = setTimeout(function () {
|
||||
self.setupRenderLayoutTimer();
|
||||
}, RENDER_DELAY);
|
||||
}
|
||||
};
|
||||
|
||||
this.appendText = function textLayerBuilderAppendText(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';
|
||||
|
||||
// The content of the div is set in the `setTextContent` function.
|
||||
|
||||
this.textDivs.push(textDiv);
|
||||
};
|
||||
|
||||
this.insertDivContent = function textLayerUpdateTextContent() {
|
||||
// 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.setupRenderLayoutTimer();
|
||||
};
|
||||
|
||||
this.setTextContent = function textLayerBuilderSetTextContent(textContent) {
|
||||
this.textContent = textContent;
|
||||
this.insertDivContent();
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
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;
|
||||
})();
|
||||
|
||||
|
BIN
files_reader/vendor/pdfjs/css/.main.css.swp
vendored
BIN
files_reader/vendor/pdfjs/css/.main.css.swp
vendored
Binary file not shown.
89
files_reader/vendor/pdfjs/css/main.css
vendored
89
files_reader/vendor/pdfjs/css/main.css
vendored
|
@ -41,6 +41,7 @@ body {
|
|||
|
||||
#titlebar {
|
||||
padding: 0.5em;
|
||||
height: 1.4em;
|
||||
color: #4f4f4f;
|
||||
font-weight: 100;
|
||||
font-family: Georgia, "Times New Roman", Times, serif;
|
||||
|
@ -54,7 +55,15 @@ body {
|
|||
}
|
||||
|
||||
#titlebar:hover {
|
||||
opacity: 1;
|
||||
opacity: 1;
|
||||
background-color: white;
|
||||
box-shadow: 0 1px 10px rgba(0, 0, 0, 0.4);
|
||||
}
|
||||
|
||||
.background_visible {
|
||||
background-color: white;
|
||||
box-shadow: 0 1px 10px rgba(0, 0, 0, 0.4);
|
||||
opacity: 1 !important;
|
||||
}
|
||||
|
||||
#titlebar a {
|
||||
|
@ -91,12 +100,24 @@ body {
|
|||
display: none;
|
||||
}
|
||||
|
||||
.controls-separator {
|
||||
width: 1em;
|
||||
}
|
||||
|
||||
.editable {
|
||||
display: inline-block;
|
||||
min-width: 2em;
|
||||
box-shadow: inset 0 1px 5px rgba(0, 0, 0, 0.4);
|
||||
}
|
||||
|
||||
#title-controls,
|
||||
#opener {
|
||||
margin-left: 0.3em;
|
||||
margin-right: 0.3em;
|
||||
position: fixed;
|
||||
top: 0.3em;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
#opener {
|
||||
|
@ -121,25 +142,61 @@ body {
|
|||
right: 0;
|
||||
}
|
||||
|
||||
#zioom_icon {
|
||||
text-align: center;
|
||||
font-family: 'icomoon', 'Helvetica' !important;
|
||||
font-size: 1.2em;
|
||||
speak: none;
|
||||
font-style: normal;
|
||||
font-weight: normal;
|
||||
font-variant: normal;
|
||||
text-transform: none;
|
||||
line-height: 1;
|
||||
border: none;
|
||||
}
|
||||
|
||||
#zoom_icon {
|
||||
padding: 0 0.5em;
|
||||
}
|
||||
|
||||
#zoom_options {
|
||||
font-family: "Lucida Console", "Monospace", "Courier New";
|
||||
position: fixed;
|
||||
box-shadow: 1px 1px 10px rgba(0, 0, 0, 0.4);
|
||||
background-color: white;
|
||||
font-size: 1em;
|
||||
padding: 0.2em 0.5em;
|
||||
}
|
||||
|
||||
.zoom_option:hover {
|
||||
background: black;
|
||||
color: white;
|
||||
}
|
||||
|
||||
#viewer {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
/* width: 100%; */
|
||||
/* height: 80%; */
|
||||
height: calc(100% - 2em);
|
||||
/* top: 10%; */
|
||||
top: 2em;
|
||||
height: 100%;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
z-index: 2;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
overflow: scroll;
|
||||
}
|
||||
|
||||
#viewer.flex {
|
||||
/* top: 2em; */
|
||||
display: -webkit-box;
|
||||
display: -moz-box;
|
||||
display: -ms-flexbox;
|
||||
display: -webkit-flex;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
#viewer canvas {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
#viewer iframe {
|
||||
border: none;
|
||||
/* position: relative; */
|
||||
/* height: auto; */
|
||||
/* width: auto; */
|
||||
}
|
||||
|
||||
#prev, #next {
|
||||
|
@ -150,7 +207,7 @@ body {
|
|||
}
|
||||
|
||||
.touch_nav {
|
||||
width: 35%;
|
||||
width: 25%;
|
||||
}
|
||||
|
||||
.arrow div {
|
||||
|
@ -295,11 +352,11 @@ body {
|
|||
width: 80%;
|
||||
margin-left: 10%;
|
||||
}
|
||||
*/
|
||||
#viewer {
|
||||
width: 95%;
|
||||
}
|
||||
|
||||
*/
|
||||
#prev {
|
||||
padding-left: 0;
|
||||
}
|
||||
|
|
82
files_reader/vendor/pdfjs/css/text_layer_builder.css
vendored
Normal file
82
files_reader/vendor/pdfjs/css/text_layer_builder.css
vendored
Normal file
|
@ -0,0 +1,82 @@
|
|||
/* Copyright 2014 Mozilla Foundation
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
.textLayer {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
overflow: hidden;
|
||||
opacity: 0.2;
|
||||
line-height: 1.0;
|
||||
}
|
||||
|
||||
.textLayer > div {
|
||||
color: transparent;
|
||||
position: absolute;
|
||||
white-space: pre;
|
||||
cursor: text;
|
||||
-webkit-transform-origin: 0% 0%;
|
||||
-moz-transform-origin: 0% 0%;
|
||||
-o-transform-origin: 0% 0%;
|
||||
-ms-transform-origin: 0% 0%;
|
||||
transform-origin: 0% 0%;
|
||||
}
|
||||
|
||||
.textLayer .highlight {
|
||||
margin: -1px;
|
||||
padding: 1px;
|
||||
|
||||
background-color: rgb(180, 0, 170);
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.textLayer .highlight.begin {
|
||||
border-radius: 4px 0px 0px 4px;
|
||||
}
|
||||
|
||||
.textLayer .highlight.end {
|
||||
border-radius: 0px 4px 4px 0px;
|
||||
}
|
||||
|
||||
.textLayer .highlight.middle {
|
||||
border-radius: 0px;
|
||||
}
|
||||
|
||||
.textLayer .highlight.selected {
|
||||
background-color: rgb(0, 100, 0);
|
||||
}
|
||||
|
||||
.textLayer ::selection { background: rgb(0,0,255); }
|
||||
.textLayer ::-moz-selection { background: rgb(0,0,255); }
|
||||
|
||||
.textLayer .endOfContent {
|
||||
display: block;
|
||||
position: absolute;
|
||||
left: 0px;
|
||||
top: 100%;
|
||||
right: 0px;
|
||||
bottom: 0px;
|
||||
z-index: -1;
|
||||
cursor: default;
|
||||
-webkit-user-select: none;
|
||||
-ms-user-select: none;
|
||||
-moz-user-select: none;
|
||||
}
|
||||
|
||||
.textLayer .endOfContent.active {
|
||||
top: 0px;
|
||||
}
|
424
files_reader/vendor/pdfjs/lib/text_layer_builder.js
vendored
Normal file
424
files_reader/vendor/pdfjs/lib/text_layer_builder.js
vendored
Normal file
|
@ -0,0 +1,424 @@
|
|||
/* Copyright 2012 Mozilla Foundation
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
(function (root, factory) {
|
||||
if (typeof define === 'function' && define.amd) {
|
||||
define('pdfjs-web/text_layer_builder', ['exports', 'pdfjs-web/dom_events',
|
||||
'pdfjs-web/pdfjs'], factory);
|
||||
} else if (typeof exports !== 'undefined') {
|
||||
factory(exports, require('./dom_events.js'), require('./pdfjs.js'));
|
||||
} else {
|
||||
factory((root.pdfjsWebTextLayerBuilder = {}), root.pdfjsWebDOMEvents,
|
||||
root.pdfjsWebPDFJS);
|
||||
}
|
||||
}(this, function (exports, domEvents, pdfjsLib) {
|
||||
|
||||
var EXPAND_DIVS_TIMEOUT = 300; // ms
|
||||
|
||||
/**
|
||||
* @typedef {Object} TextLayerBuilderOptions
|
||||
* @property {HTMLDivElement} textLayerDiv - The text layer container.
|
||||
* @property {EventBus} eventBus - The application event bus.
|
||||
* @property {number} pageIndex - The page index.
|
||||
* @property {PageViewport} viewport - The viewport of the text layer.
|
||||
* @property {PDFFindController} findController
|
||||
* @property {boolean} enhanceTextSelection - Option to turn on improved
|
||||
* text selection.
|
||||
*/
|
||||
|
||||
/**
|
||||
* TextLayerBuilder provides text-selection functionality for the PDF.
|
||||
* It does this by creating overlay divs over the PDF text. These divs
|
||||
* contain text that matches the PDF text they are overlaying. This object
|
||||
* also provides a way to highlight text that is being searched for.
|
||||
* @class
|
||||
*/
|
||||
var TextLayerBuilder = (function TextLayerBuilderClosure() {
|
||||
function TextLayerBuilder(options) {
|
||||
this.textLayerDiv = options.textLayerDiv;
|
||||
this.eventBus = options.eventBus || domEvents.getGlobalEventBus();
|
||||
this.textContent = null;
|
||||
this.renderingDone = false;
|
||||
this.pageIdx = options.pageIndex;
|
||||
this.pageNumber = this.pageIdx + 1;
|
||||
this.matches = [];
|
||||
this.viewport = options.viewport;
|
||||
this.textDivs = [];
|
||||
this.findController = options.findController || null;
|
||||
this.textLayerRenderTask = null;
|
||||
this.enhanceTextSelection = options.enhanceTextSelection;
|
||||
this._bindMouse();
|
||||
}
|
||||
|
||||
TextLayerBuilder.prototype = {
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
_finishRendering: function TextLayerBuilder_finishRendering() {
|
||||
this.renderingDone = true;
|
||||
|
||||
if (!this.enhanceTextSelection) {
|
||||
var endOfContent = document.createElement('div');
|
||||
endOfContent.className = 'endOfContent';
|
||||
this.textLayerDiv.appendChild(endOfContent);
|
||||
}
|
||||
|
||||
this.eventBus.dispatch('textlayerrendered', {
|
||||
source: this,
|
||||
pageNumber: this.pageNumber,
|
||||
numTextDivs: this.textDivs.length,
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Renders the text layer.
|
||||
* @param {number} timeout (optional) if specified, the rendering waits
|
||||
* for specified amount of ms.
|
||||
*/
|
||||
render: function TextLayerBuilder_render(timeout) {
|
||||
if (!this.textContent || this.renderingDone) {
|
||||
return;
|
||||
}
|
||||
this.cancel();
|
||||
|
||||
this.textDivs = [];
|
||||
var textLayerFrag = document.createDocumentFragment();
|
||||
this.textLayerRenderTask = pdfjsLib.renderTextLayer({
|
||||
textContent: this.textContent,
|
||||
container: textLayerFrag,
|
||||
viewport: this.viewport,
|
||||
textDivs: this.textDivs,
|
||||
timeout: timeout,
|
||||
enhanceTextSelection: this.enhanceTextSelection,
|
||||
});
|
||||
this.textLayerRenderTask.promise.then(function () {
|
||||
this.textLayerDiv.appendChild(textLayerFrag);
|
||||
this._finishRendering();
|
||||
this.updateMatches();
|
||||
}.bind(this), function (reason) {
|
||||
// cancelled or failed to render text layer -- skipping errors
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Cancels rendering of the text layer.
|
||||
*/
|
||||
cancel: function TextLayerBuilder_cancel() {
|
||||
if (this.textLayerRenderTask) {
|
||||
this.textLayerRenderTask.cancel();
|
||||
this.textLayerRenderTask = null;
|
||||
}
|
||||
},
|
||||
|
||||
setTextContent: function TextLayerBuilder_setTextContent(textContent) {
|
||||
this.cancel();
|
||||
this.textContent = textContent;
|
||||
},
|
||||
|
||||
convertMatches: function TextLayerBuilder_convertMatches(matches,
|
||||
matchesLength) {
|
||||
var i = 0;
|
||||
var iIndex = 0;
|
||||
var bidiTexts = this.textContent.items;
|
||||
var end = bidiTexts.length - 1;
|
||||
var queryLen = (this.findController === null ?
|
||||
0 : this.findController.state.query.length);
|
||||
var ret = [];
|
||||
if (!matches) {
|
||||
return ret;
|
||||
}
|
||||
for (var m = 0, len = matches.length; m < len; m++) {
|
||||
// Calculate the start position.
|
||||
var matchIdx = matches[m];
|
||||
|
||||
// Loop over the divIdxs.
|
||||
while (i !== end && matchIdx >= (iIndex + bidiTexts[i].str.length)) {
|
||||
iIndex += bidiTexts[i].str.length;
|
||||
i++;
|
||||
}
|
||||
|
||||
if (i === bidiTexts.length) {
|
||||
console.error('Could not find a matching mapping');
|
||||
}
|
||||
|
||||
var match = {
|
||||
begin: {
|
||||
divIdx: i,
|
||||
offset: matchIdx - iIndex
|
||||
}
|
||||
};
|
||||
|
||||
// Calculate the end position.
|
||||
if (matchesLength) { // multiterm search
|
||||
matchIdx += matchesLength[m];
|
||||
} else { // phrase search
|
||||
matchIdx += queryLen;
|
||||
}
|
||||
|
||||
// Somewhat the same array as above, but use > instead of >= to get
|
||||
// the end position right.
|
||||
while (i !== end && matchIdx > (iIndex + bidiTexts[i].str.length)) {
|
||||
iIndex += bidiTexts[i].str.length;
|
||||
i++;
|
||||
}
|
||||
|
||||
match.end = {
|
||||
divIdx: i,
|
||||
offset: matchIdx - iIndex
|
||||
};
|
||||
ret.push(match);
|
||||
}
|
||||
|
||||
return ret;
|
||||
},
|
||||
|
||||
renderMatches: function TextLayerBuilder_renderMatches(matches) {
|
||||
// Early exit if there is nothing to render.
|
||||
if (matches.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
var bidiTexts = this.textContent.items;
|
||||
var textDivs = this.textDivs;
|
||||
var prevEnd = null;
|
||||
var pageIdx = this.pageIdx;
|
||||
var isSelectedPage = (this.findController === null ?
|
||||
false : (pageIdx === this.findController.selected.pageIdx));
|
||||
var selectedMatchIdx = (this.findController === null ?
|
||||
-1 : this.findController.selected.matchIdx);
|
||||
var highlightAll = (this.findController === null ?
|
||||
false : this.findController.state.highlightAll);
|
||||
var infinity = {
|
||||
divIdx: -1,
|
||||
offset: undefined
|
||||
};
|
||||
|
||||
function beginText(begin, className) {
|
||||
var divIdx = begin.divIdx;
|
||||
textDivs[divIdx].textContent = '';
|
||||
appendTextToDiv(divIdx, 0, begin.offset, className);
|
||||
}
|
||||
|
||||
function appendTextToDiv(divIdx, fromOffset, toOffset, className) {
|
||||
var div = textDivs[divIdx];
|
||||
var content = bidiTexts[divIdx].str.substring(fromOffset, toOffset);
|
||||
var node = document.createTextNode(content);
|
||||
if (className) {
|
||||
var span = document.createElement('span');
|
||||
span.className = className;
|
||||
span.appendChild(node);
|
||||
div.appendChild(span);
|
||||
return;
|
||||
}
|
||||
div.appendChild(node);
|
||||
}
|
||||
|
||||
var i0 = selectedMatchIdx, i1 = i0 + 1;
|
||||
if (highlightAll) {
|
||||
i0 = 0;
|
||||
i1 = matches.length;
|
||||
} else if (!isSelectedPage) {
|
||||
// Not highlighting all and this isn't the selected page, so do nothing.
|
||||
return;
|
||||
}
|
||||
|
||||
for (var i = i0; i < i1; i++) {
|
||||
var match = matches[i];
|
||||
var begin = match.begin;
|
||||
var end = match.end;
|
||||
var isSelected = (isSelectedPage && i === selectedMatchIdx);
|
||||
var highlightSuffix = (isSelected ? ' selected' : '');
|
||||
|
||||
if (this.findController) {
|
||||
this.findController.updateMatchPosition(pageIdx, i, textDivs,
|
||||
begin.divIdx);
|
||||
}
|
||||
|
||||
// Match inside new div.
|
||||
if (!prevEnd || begin.divIdx !== prevEnd.divIdx) {
|
||||
// If there was a previous div, then add the text at the end.
|
||||
if (prevEnd !== null) {
|
||||
appendTextToDiv(prevEnd.divIdx, prevEnd.offset, infinity.offset);
|
||||
}
|
||||
// Clear the divs and set the content until the starting point.
|
||||
beginText(begin);
|
||||
} else {
|
||||
appendTextToDiv(prevEnd.divIdx, prevEnd.offset, begin.offset);
|
||||
}
|
||||
|
||||
if (begin.divIdx === end.divIdx) {
|
||||
appendTextToDiv(begin.divIdx, begin.offset, end.offset,
|
||||
'highlight' + highlightSuffix);
|
||||
} else {
|
||||
appendTextToDiv(begin.divIdx, begin.offset, infinity.offset,
|
||||
'highlight begin' + highlightSuffix);
|
||||
for (var n0 = begin.divIdx + 1, n1 = end.divIdx; n0 < n1; n0++) {
|
||||
textDivs[n0].className = 'highlight middle' + highlightSuffix;
|
||||
}
|
||||
beginText(end, 'highlight end' + highlightSuffix);
|
||||
}
|
||||
prevEnd = end;
|
||||
}
|
||||
|
||||
if (prevEnd) {
|
||||
appendTextToDiv(prevEnd.divIdx, prevEnd.offset, infinity.offset);
|
||||
}
|
||||
},
|
||||
|
||||
updateMatches: function TextLayerBuilder_updateMatches() {
|
||||
// Only show matches when all rendering is done.
|
||||
if (!this.renderingDone) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Clear all matches.
|
||||
var matches = this.matches;
|
||||
var textDivs = this.textDivs;
|
||||
var bidiTexts = this.textContent.items;
|
||||
var clearedUntilDivIdx = -1;
|
||||
|
||||
// Clear all current matches.
|
||||
for (var i = 0, len = matches.length; i < len; i++) {
|
||||
var match = matches[i];
|
||||
var begin = Math.max(clearedUntilDivIdx, match.begin.divIdx);
|
||||
for (var n = begin, end = match.end.divIdx; n <= end; n++) {
|
||||
var div = textDivs[n];
|
||||
div.textContent = bidiTexts[n].str;
|
||||
div.className = '';
|
||||
}
|
||||
clearedUntilDivIdx = match.end.divIdx + 1;
|
||||
}
|
||||
|
||||
if (this.findController === null || !this.findController.active) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Convert the matches on the page controller into the match format
|
||||
// used for the textLayer.
|
||||
var pageMatches, pageMatchesLength;
|
||||
if (this.findController !== null) {
|
||||
pageMatches = this.findController.pageMatches[this.pageIdx] || null;
|
||||
pageMatchesLength = (this.findController.pageMatchesLength) ?
|
||||
this.findController.pageMatchesLength[this.pageIdx] || null : null;
|
||||
}
|
||||
|
||||
this.matches = this.convertMatches(pageMatches, pageMatchesLength);
|
||||
this.renderMatches(this.matches);
|
||||
},
|
||||
|
||||
/**
|
||||
* Fixes text selection: adds additional div where mouse was clicked.
|
||||
* This reduces flickering of the content if mouse slowly dragged down/up.
|
||||
* @private
|
||||
*/
|
||||
_bindMouse: function TextLayerBuilder_bindMouse() {
|
||||
var div = this.textLayerDiv;
|
||||
var self = this;
|
||||
var expandDivsTimer = null;
|
||||
|
||||
div.addEventListener('mousedown', function (e) {
|
||||
if (self.enhanceTextSelection && self.textLayerRenderTask) {
|
||||
self.textLayerRenderTask.expandTextDivs(true);
|
||||
if ((typeof PDFJSDev === 'undefined' ||
|
||||
!PDFJSDev.test('FIREFOX || MOZCENTRAL')) &&
|
||||
expandDivsTimer) {
|
||||
clearTimeout(expandDivsTimer);
|
||||
expandDivsTimer = null;
|
||||
}
|
||||
return;
|
||||
}
|
||||
var end = div.querySelector('.endOfContent');
|
||||
if (!end) {
|
||||
return;
|
||||
}
|
||||
if (typeof PDFJSDev === 'undefined' ||
|
||||
!PDFJSDev.test('FIREFOX || MOZCENTRAL')) {
|
||||
// On non-Firefox browsers, the selection will feel better if the height
|
||||
// of the endOfContent div will be adjusted to start at mouse click
|
||||
// location -- this will avoid flickering when selections moves up.
|
||||
// However it does not work when selection started on empty space.
|
||||
var adjustTop = e.target !== div;
|
||||
if (typeof PDFJSDev === 'undefined' || PDFJSDev.test('GENERIC')) {
|
||||
adjustTop = adjustTop && window.getComputedStyle(end).
|
||||
getPropertyValue('-moz-user-select') !== 'none';
|
||||
}
|
||||
if (adjustTop) {
|
||||
var divBounds = div.getBoundingClientRect();
|
||||
var r = Math.max(0, (e.pageY - divBounds.top) / divBounds.height);
|
||||
end.style.top = (r * 100).toFixed(2) + '%';
|
||||
}
|
||||
}
|
||||
end.classList.add('active');
|
||||
});
|
||||
|
||||
div.addEventListener('mouseup', function (e) {
|
||||
if (self.enhanceTextSelection && self.textLayerRenderTask) {
|
||||
if (typeof PDFJSDev === 'undefined' ||
|
||||
!PDFJSDev.test('FIREFOX || MOZCENTRAL')) {
|
||||
expandDivsTimer = setTimeout(function() {
|
||||
if (self.textLayerRenderTask) {
|
||||
self.textLayerRenderTask.expandTextDivs(false);
|
||||
}
|
||||
expandDivsTimer = null;
|
||||
}, EXPAND_DIVS_TIMEOUT);
|
||||
} else {
|
||||
self.textLayerRenderTask.expandTextDivs(false);
|
||||
}
|
||||
return;
|
||||
}
|
||||
var end = div.querySelector('.endOfContent');
|
||||
if (!end) {
|
||||
return;
|
||||
}
|
||||
if (typeof PDFJSDev === 'undefined' ||
|
||||
!PDFJSDev.test('FIREFOX || MOZCENTRAL')) {
|
||||
end.style.top = '';
|
||||
}
|
||||
end.classList.remove('active');
|
||||
});
|
||||
},
|
||||
};
|
||||
return TextLayerBuilder;
|
||||
})();
|
||||
|
||||
/**
|
||||
* @constructor
|
||||
* @implements IPDFTextLayerFactory
|
||||
*/
|
||||
function DefaultTextLayerFactory() {}
|
||||
DefaultTextLayerFactory.prototype = {
|
||||
/**
|
||||
* @param {HTMLDivElement} textLayerDiv
|
||||
* @param {number} pageIndex
|
||||
* @param {PageViewport} viewport
|
||||
* @param {boolean} enhanceTextSelection
|
||||
* @returns {TextLayerBuilder}
|
||||
*/
|
||||
createTextLayerBuilder: function (textLayerDiv, pageIndex, viewport,
|
||||
enhanceTextSelection) {
|
||||
return new TextLayerBuilder({
|
||||
textLayerDiv: textLayerDiv,
|
||||
pageIndex: pageIndex,
|
||||
viewport: viewport,
|
||||
enhanceTextSelection: enhanceTextSelection
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
exports.TextLayerBuilder = TextLayerBuilder;
|
||||
exports.DefaultTextLayerFactory = DefaultTextLayerFactory;
|
||||
}));
|
507
files_reader/vendor/pdfjs/pdf.reader.js
vendored
507
files_reader/vendor/pdfjs/pdf.reader.js
vendored
|
@ -1,6 +1,8 @@
|
|||
PDFJS.reader = {};
|
||||
PDFJS.reader.plugins = {};
|
||||
|
||||
PDFJS.TextLayerBuilder = {};
|
||||
|
||||
(function(root, $) {
|
||||
|
||||
var previousReader = root.pdfReader || {};
|
||||
|
@ -18,13 +20,20 @@ PDFJS.Reader = function(bookPath, _options) {
|
|||
$viewer = $("#viewer"),
|
||||
search = window.location.search;
|
||||
|
||||
var TEXT_RENDER_DELAY = 200, // ms
|
||||
PAGE_RENDER_DELAY = 200; // ms
|
||||
|
||||
this.settings = this.defaults(_options || {}, {
|
||||
bookPath: bookPath,
|
||||
textRenderDelay: TEXT_RENDER_DELAY,
|
||||
pageRenderDelay: PAGE_RENDER_DELAY,
|
||||
textSelect: true, // true || false, add selectable text layer
|
||||
doubleBuffer: true, // true || false, draw to off-screen canvas
|
||||
numPages: 0,
|
||||
currentPage: 1,
|
||||
scale: 1,
|
||||
sideBySide: true, // when true, render 2 pages side-by-side
|
||||
oddPageRight: true, // when true, odd pages render on the right side
|
||||
zoomLevel: window.outerWidth > window.outerHeight ? "spread" : "fit_page", // spread, fit_page, fit_width, percentage
|
||||
history: true,
|
||||
keyboard: {
|
||||
32: 'next', // space
|
||||
|
@ -38,7 +47,7 @@ PDFJS.Reader = function(bookPath, _options) {
|
|||
66: 'bookmark', // b
|
||||
82: 'reflow', // r
|
||||
83: 'toggleSidebar', // s
|
||||
84: 'toolbar', // t
|
||||
84: 'toggleTitlebar', // t
|
||||
68: 'toggleDay', // d
|
||||
78: 'toggleNight', // n
|
||||
70: 'toggleFullscreen', // f
|
||||
|
@ -72,15 +81,32 @@ PDFJS.Reader = function(bookPath, _options) {
|
|||
this.extra = extra || null;
|
||||
};
|
||||
|
||||
this.canvaslst = [
|
||||
document.getElementById("left"),
|
||||
document.getElementById("right")
|
||||
this.resourcelst = [
|
||||
{
|
||||
canvas: document.getElementById("left"),
|
||||
ctx: document.getElementById("left").getContext('2d'),
|
||||
textdiv: document.getElementById("text_left"),
|
||||
textLayer: null,
|
||||
renderTask: null,
|
||||
oscanvas: null,
|
||||
osctx: null,
|
||||
pageNum: null
|
||||
},
|
||||
{
|
||||
canvas: document.getElementById("right"),
|
||||
ctx: document.getElementById("right").getContext('2d'),
|
||||
textdiv: document.getElementById("text_right"),
|
||||
textLayer: null,
|
||||
renderTask: null,
|
||||
oscanvas: null,
|
||||
osctx: null,
|
||||
pageNum: null
|
||||
}
|
||||
];
|
||||
|
||||
this.contextlst = [
|
||||
document.getElementById("left").getContext('2d'),
|
||||
document.getElementById("right").getContext('2d')
|
||||
];
|
||||
this.cancelPage = {};
|
||||
|
||||
this.renderQueue = false;
|
||||
|
||||
// Overide options with search parameters
|
||||
if(search) {
|
||||
|
@ -93,13 +119,12 @@ PDFJS.Reader = function(bookPath, _options) {
|
|||
});
|
||||
}
|
||||
|
||||
|
||||
//this.restoreDefaults(this.settings.session.defaults);
|
||||
//this.restorePreferences(this.settings.session.preferences);
|
||||
//this.restoreAnnotations(this.settings.session.annotations);
|
||||
this.sideBarOpen = false;
|
||||
this.viewerResized = false;
|
||||
//this.sideBySide = window.outerWidth > window.outerHeight ? true : false;
|
||||
this.sideBySide = false;
|
||||
this.pageNumPending = null;
|
||||
|
||||
PDFJS.getDocument(reader.settings.bookPath).then(function(_book) {
|
||||
|
@ -121,9 +146,11 @@ PDFJS.Reader = function(bookPath, _options) {
|
|||
//reader.SearchController = PDFJS.reader.SearchController.call(reader, book);
|
||||
//reader.MetaController = EPUBJS.reader.MetaController.call(reader, meta);
|
||||
//reader.TocController = EPUBJS.reader.TocController.call(reader, toc);
|
||||
//reader.TextLayerController = PDFJS.reader.TextLayerController();
|
||||
|
||||
//reader.queuePage(reader.settings.currentPage);
|
||||
var startPage = reader.settings.oddPageRight ? 0 : 1;
|
||||
console.log(reader.settings);
|
||||
var startPage = (reader.settings.zoomLevel === "spread" && reader.settings.oddPageRight) ? 0 : 1;
|
||||
reader.queuePage(startPage);
|
||||
reader.ReaderController.hideLoader();
|
||||
});
|
||||
|
@ -131,114 +158,273 @@ PDFJS.Reader = function(bookPath, _options) {
|
|||
return this;
|
||||
};
|
||||
|
||||
PDFJS.Reader.prototype.setZoom = function(zoom) {
|
||||
|
||||
var reader = this,
|
||||
page = reader.settings.currentPage;
|
||||
|
||||
reader.settings.zoomLevel = zoom;
|
||||
reader.queuePage(page);
|
||||
};
|
||||
|
||||
PDFJS.Reader.prototype.cancelRender = function (index) {
|
||||
|
||||
var reader = this,
|
||||
resourcelst = reader.resourcelst[index];
|
||||
|
||||
if (resourcelst.renderTask) {
|
||||
console.log("cancel render on canvas " + index + ", pageNum " + resourcelst.pageNum);
|
||||
resourcelst.renderTask.cancel();
|
||||
resourcelst.renderTask = resourcelst.pageNum = null;
|
||||
resourcelst.oscanvas = resourcelst.osctx = null;
|
||||
}
|
||||
|
||||
if (resourcelst.textLayer) {
|
||||
resourcelst.textLayer.cancel();
|
||||
resourcelst.textLayer = null;
|
||||
}
|
||||
};
|
||||
|
||||
PDFJS.Reader.prototype.renderPage = function(pageNum) {
|
||||
|
||||
var reader = this,
|
||||
pageShift = this.settings.sideBySide ? 2 : 1,
|
||||
oddPageShift = this.settings.oddPageRight ? 0 : 1,
|
||||
i = (pageNum - oddPageShift) % pageShift,
|
||||
canvas = this.canvaslst[i],
|
||||
ctx = this.contextlst[i],
|
||||
pixelratio = window.devicePixelRatio,
|
||||
max_view_height = parseInt(window.outerHeight * 0.95),
|
||||
max_view_width = reader.settings.sideBySide
|
||||
? parseInt((window.outerWidth / 2) * 1)
|
||||
: parseInt(window.outerWidth * 1),
|
||||
scale,
|
||||
transform,
|
||||
var reader = this,
|
||||
$viewer = $("#viewer"),
|
||||
$page_num = document.getElementById('page_num');
|
||||
|
||||
if (this.settings.sideBySide) {
|
||||
this.canvaslst[1].style.display = "block";
|
||||
var index,
|
||||
canvas, // actual canvas
|
||||
ctx, // actual context
|
||||
oscanvas, // off-screen canvas
|
||||
osctx, // off-screen context
|
||||
textdiv,
|
||||
textLayer,
|
||||
outputscale,
|
||||
max_view_width,
|
||||
max_view_height,
|
||||
scale_width,
|
||||
scale_height,
|
||||
view_aspect,
|
||||
document_aspect,
|
||||
scale,
|
||||
viewport,
|
||||
zoom,
|
||||
fraction,
|
||||
offset,
|
||||
renderContext,
|
||||
renderTask,
|
||||
resourcelst;
|
||||
|
||||
max_view_width = window.innerWidth;
|
||||
max_view_height = window.innerHeight;
|
||||
|
||||
if (this.settings.zoomLevel === "spread") {
|
||||
|
||||
// show second canvas
|
||||
reader.resourcelst[1].canvas.style.display = "block";
|
||||
max_view_width /= 2;
|
||||
// select canvas and ctx based on pageNum, pageShift and oddPageRight
|
||||
pageShift = 2;
|
||||
oddPageShift = reader.settings.oddPageRight ? 0 : 1;
|
||||
index = (pageNum - oddPageShift) % pageShift;
|
||||
|
||||
} else {
|
||||
this.canvaslst[1].style.display = "none";
|
||||
|
||||
index = 0;
|
||||
// hide second canvas
|
||||
reader.resourcelst[1].canvas.style.display = "none";
|
||||
|
||||
// don't try to render non-existing page 0 (which is used
|
||||
// to indicate the empty left page when oddPageRight === true)
|
||||
if (pageNum === 0)
|
||||
pageNum++;
|
||||
|
||||
}
|
||||
|
||||
resourcelst = reader.resourcelst[index];
|
||||
|
||||
canvas = resourcelst.canvas;
|
||||
ctx = resourcelst.ctx;
|
||||
textdiv = resourcelst.textdiv;
|
||||
outputscale = reader.getOutputScale(resourcelst.ctx);
|
||||
fraction = reader.approximateFraction(outputscale);
|
||||
|
||||
if (pageNum <= this.settings.numPages && pageNum >= 1) {
|
||||
|
||||
this.pageRendering = true;
|
||||
if (resourcelst.renderTask) {
|
||||
resourcelst.renderTask.cancel();
|
||||
resourcelst.renderTask = null;
|
||||
}
|
||||
|
||||
if (resourcelst.textLayer) {
|
||||
resourcelst.textLayer.cancel();
|
||||
resourcelst.textLayer = null;
|
||||
}
|
||||
|
||||
resourcelst.pageNum = pageNum;
|
||||
|
||||
if (reader.cancelPage[pageNum])
|
||||
delete reader.cancelPage[pageNum];
|
||||
|
||||
this.book.getPage(pageNum).then(function(page) {
|
||||
console.log(page);
|
||||
//console.log(page);
|
||||
var page_width = page.pageInfo.view[2];
|
||||
var page_height = page.pageInfo.view[3];
|
||||
document_aspect = parseFloat(page_width / page_height);
|
||||
view_aspect = parseFloat(max_view_width / max_view_height);
|
||||
|
||||
var scale_height = parseFloat(max_view_height / page_height);
|
||||
var scale_width = parseFloat(max_view_width / page_width);
|
||||
var document_aspect = parseFloat(page_width / page_height);
|
||||
var view_aspect = parseFloat(max_view_width / max_view_height);
|
||||
scale_height = parseFloat(max_view_height / page_height);
|
||||
scale_width = parseFloat(max_view_width / page_width);
|
||||
|
||||
console.log(max_view_width
|
||||
+ " " + max_view_height
|
||||
+ " " + page_width
|
||||
+ " " + page_height
|
||||
+ " " + document_aspect
|
||||
+ " " + view_aspect
|
||||
+ " " + scale_width
|
||||
+ " " + scale_height
|
||||
+ " " + pixelratio);
|
||||
/*
|
||||
console.log("m_v_w: " + max_view_width
|
||||
+ " m_v_h: " + max_view_height
|
||||
+ " p_w: " + page_width
|
||||
+ " p_h: " + page_height
|
||||
+ " d_a: " + document_aspect
|
||||
+ " v_a: " + view_aspect
|
||||
+ " s_w: " + scale_width
|
||||
+ " s_h: " + scale_height
|
||||
+ " o: " + outputscale);
|
||||
console.log("fraction: ");
|
||||
console.log(fraction);
|
||||
*/
|
||||
|
||||
scale = Math.min(scale_width, scale_height) / pixelratio;
|
||||
switch (reader.settings.zoomLevel) {
|
||||
|
||||
if (scale_width < scale_height) {
|
||||
canvas.width = max_view_width;
|
||||
canvas.height = parseInt(page_height * scale_width);
|
||||
canvas.style.width = parseInt(max_view_width / pixelratio);
|
||||
scale = scale_width;
|
||||
} else {
|
||||
canvas.height = max_view_height;
|
||||
canvas.width = parseInt(page_width * scale_height);
|
||||
canvas.style.width = parseInt(max_view_width / pixelratio);
|
||||
scale = scale_height;
|
||||
case "spread":
|
||||
|
||||
// INTENTIONAL FALL-THROUGH
|
||||
|
||||
case "fit_page":
|
||||
|
||||
$viewer.addClass("flex");
|
||||
|
||||
if (scale_width > scale_height) {
|
||||
scale = scale_height;
|
||||
canvas.height = reader.roundToDivide(max_view_height * outputscale, fraction[0]);
|
||||
canvas.width = reader.roundToDivide(parseInt(canvas.height * document_aspect), fraction[0]);
|
||||
} else {
|
||||
scale = scale_width;
|
||||
canvas.width = reader.roundToDivide(max_view_width * outputscale, fraction[0])
|
||||
canvas.height = reader.roundToDivide(parseInt(canvas.width / document_aspect), fraction[0]);
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case "fit_width":
|
||||
|
||||
$viewer.removeClass("flex");
|
||||
|
||||
if (scale_width < scale_height) {
|
||||
scale = scale_height;
|
||||
canvas.height = reader.roundToDivide(max_view_height * outputscale, fraction[0]);
|
||||
canvas.width = reader.roundToDivide(parseInt(canvas.height * document_aspect), fraction[0]);
|
||||
} else {
|
||||
scale = scale_width;
|
||||
canvas.width = reader.roundToDivide(max_view_width * outputscale, fraction[0])
|
||||
canvas.height = reader.roundToDivide(parseInt(canvas.width / document_aspect), fraction[0]);
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
|
||||
$viewer.removeClass("flex");
|
||||
scale = parseFloat(reader.settings.zoomLevel);
|
||||
canvas.width = reader.roundToDivide(parseInt(page_width * scale * outputscale), fraction[0]); ;
|
||||
canvas.height = reader.roundToDivide(parseInt(page_height * scale * outputscale), fraction[0]);
|
||||
break;
|
||||
}
|
||||
|
||||
if (document_aspect < view_aspect) {
|
||||
console.log("document aspect < view aspect, aspect ratio " + document_aspect);
|
||||
//canvas.height = parseInt(max_view_height / pixelratio);
|
||||
//canvas.width = parseInt(canvas.height * document_aspect);
|
||||
//scale = parseFloat(scale_height / pixelratio);
|
||||
transform = [ 1, 0, 0, 1, parseInt((canvas.width - (page_width*scale)) / 2), 0 ];
|
||||
//console.log(canvas.width + " " + canvas.height);
|
||||
|
||||
transform = (outputscale === 1)
|
||||
? null
|
||||
: [outputscale, 0, 0, outputscale, 0, 0];
|
||||
|
||||
viewport = page.getViewport(scale);
|
||||
|
||||
if (outputscale !== 1) {
|
||||
canvas.style.width = reader.roundToDivide(viewport.width, fraction[1]) + 'px';
|
||||
canvas.style.height = reader.roundToDivide(viewport.height, fraction[1]) + 'px';
|
||||
} else {
|
||||
console.log("document aspect > view_aspect, aspect ratio " + document_aspect);
|
||||
|
||||
//canvas.height = parseInt(max_view_height / pixelratio);
|
||||
//canvas.width = parseInt(canvas.height * document_aspect);
|
||||
//canvas.width = parseInt(max_view_width / pixelratio);
|
||||
//canvas.height = parseInt(canvas.width * document_aspect);
|
||||
//scale = parseFloat(scale_width / pixelratio);
|
||||
canvas.style.top = parseInt((max_view_height - canvas.height) / 2);
|
||||
transform = [ 1, 0, 0, 1, 0, parseInt((canvas.height - (page_height*scale)) / 2) ];
|
||||
canvas.style.width = "";
|
||||
canvas.style.height = "";
|
||||
}
|
||||
console.log(canvas.width + " " + canvas.height);
|
||||
|
||||
var viewport = page.getViewport(scale);
|
||||
console.log(viewport);
|
||||
/* textlayer */
|
||||
if (reader.settings.textSelect) {
|
||||
textdiv.style.width = reader.roundToDivide(viewport.width, fraction[1]) + 'px';
|
||||
textdiv.style.height = reader.roundToDivide(viewport.height, fraction[1]) + 'px';
|
||||
offset = $(canvas).offset();
|
||||
$(textdiv).offset({
|
||||
top: offset.top,
|
||||
left: offset.left
|
||||
});
|
||||
page.getTextContent().then(function (textContent) {
|
||||
resourcelst.textLayer = textLayer = new PDFJS.Reader.TextLayerController({
|
||||
textLayerDiv: textdiv,
|
||||
pageIdx: pageNum - 1,
|
||||
viewport: viewport,
|
||||
enhanceTextSelection: true
|
||||
});
|
||||
textLayer.setTextContent(textContent);
|
||||
});
|
||||
} else {
|
||||
resourcelst.textLayer = textLayer = null;
|
||||
}
|
||||
|
||||
var renderContext = {
|
||||
canvasContext: ctx,
|
||||
viewport: viewport
|
||||
//transform: transform
|
||||
/* /textLayer */
|
||||
|
||||
if (reader.settings.doubleBuffer) {
|
||||
resourcelst.oscanvas = oscanvas = document.createElement("canvas");
|
||||
resourcelst.osctx = context = osctx = oscanvas.getContext('2d');
|
||||
oscanvas.width = canvas.width;
|
||||
oscanvas.height = canvas.height;
|
||||
} else {
|
||||
context = ctx;
|
||||
}
|
||||
|
||||
renderContext = {
|
||||
canvasContext: context,
|
||||
viewport: viewport,
|
||||
transform: transform,
|
||||
textLayer: textLayer
|
||||
};
|
||||
|
||||
var renderTask = page.render(renderContext);
|
||||
renderTask.promise.then(function() {
|
||||
reader.pageRendering = false;
|
||||
if (reader.pageNumPending !== null) {
|
||||
reader.renderPage(reader.pageNumPending);
|
||||
reader.pageNumPending = null;
|
||||
resourcelst.renderTask = renderTask = page.render(renderContext);
|
||||
|
||||
renderTask.promise.then(
|
||||
function pdfPageRenderCallback (something) {
|
||||
if (reader.cancelPage[pageNum] === undefined) {
|
||||
console.log("finished rendering page " + pageNum);
|
||||
if (reader.settings.doubleBuffer)
|
||||
ctx.drawImage(oscanvas, 0, 0);
|
||||
if (textLayer)
|
||||
textLayer.render(reader.settings.textRenderDelay);
|
||||
} else {
|
||||
console.log("rendering page " + pageNum + " finished but cancelled");
|
||||
}
|
||||
},
|
||||
function pdfPageRenderError(error) {
|
||||
console.log("pdfPageRenderError: " + error);
|
||||
}
|
||||
});
|
||||
);
|
||||
});
|
||||
|
||||
} else {
|
||||
|
||||
canvas.width = max_view_width;
|
||||
canvas.height = max_view_height;
|
||||
// clear canvas, use maximum size
|
||||
canvas.width = reader.roundToDivide(max_view_width * outputscale, fraction[0]);
|
||||
canvas.height = reader.roundToDivide(max_view_height * outputscale, fraction[0]);
|
||||
if (outputscale !== 1) {
|
||||
canvas.style.width = reader.roundToDivide(max_view_width, fraction[1]) + 'px';
|
||||
canvas.style.height = reader.roundToDivide(max_view_height, fraction[1]) + 'px';
|
||||
}
|
||||
ctx.clearRect(0, 0, canvas.width, canvas.height);
|
||||
|
||||
}
|
||||
|
||||
if (i === 0) {
|
||||
/*
|
||||
if (index === 0) {
|
||||
|
||||
if (pageNum > 0) {
|
||||
$page_num.textContent = pageNum.toString();
|
||||
|
@ -251,7 +437,7 @@ PDFJS.Reader.prototype.renderPage = function(pageNum) {
|
|||
|
||||
$page_num.textContent = pageNum.toString();
|
||||
|
||||
} else {
|
||||
} else if (pageNum <= reader.settings.numPages) {
|
||||
|
||||
var text = $page_num.textContent;
|
||||
text += "-" + pageNum.toString();
|
||||
|
@ -259,40 +445,92 @@ PDFJS.Reader.prototype.renderPage = function(pageNum) {
|
|||
|
||||
}
|
||||
}
|
||||
*/
|
||||
};
|
||||
|
||||
PDFJS.Reader.prototype.queuePage = function(pageNum) {
|
||||
PDFJS.Reader.prototype.queuePage = function(page) {
|
||||
|
||||
var pageShift = this.settings.sideBySide ? 2 : 1;
|
||||
//var pageShift = (this.settings.zoomLevel === "spread") ? 2 : 1;
|
||||
//
|
||||
var reader = this,
|
||||
zoom = reader.settings.zoomLevel,
|
||||
oddPageRight = reader.settings.oddPageRight,
|
||||
pageShift,
|
||||
$page_num = document.getElementById('page_num'),
|
||||
text;
|
||||
|
||||
if (this.pageRendering) {
|
||||
this.pageNumPending = pageNum;
|
||||
} else {
|
||||
for (var i = 0; i < pageShift; i++) {
|
||||
this.renderPage(pageNum + i);
|
||||
if (zoom === "spread") {
|
||||
pageShift = 2;
|
||||
if (oddPageRight === true) {
|
||||
page -= page % 2;
|
||||
} else {
|
||||
page -= (page + 1) % 2;
|
||||
}
|
||||
|
||||
console.log("page: " + page);
|
||||
|
||||
if (page >= 0 && page <= reader.settings.numPages) {
|
||||
if (page === reader.settings.numPages) {
|
||||
text = page.toString();
|
||||
} else if (page === 0) {
|
||||
text = "1";
|
||||
} else {
|
||||
text = page.toString() + "-" + parseInt(page+1).toString();
|
||||
}
|
||||
|
||||
$page_num.textContent = text;
|
||||
}
|
||||
|
||||
} else {
|
||||
pageShift = 1;
|
||||
if (page >= 1 && page <= reader.settings.numPages)
|
||||
$page_num.textContent = page.toString();
|
||||
}
|
||||
|
||||
reader.settings.currentPage = page;
|
||||
|
||||
if (typeof reader.renderQueue === 'number') {
|
||||
window.clearTimeout(reader.renderQueue);
|
||||
reader.renderQueue = false;
|
||||
}
|
||||
|
||||
reader.renderQueue = window.setTimeout(function queuePages() {
|
||||
for (var i = 0; i < pageShift; i++) {
|
||||
reader.renderPage(page + i);
|
||||
}
|
||||
}, reader.settings.pageRenderDelay);
|
||||
};
|
||||
|
||||
PDFJS.Reader.prototype.prevPage = function() {
|
||||
|
||||
var pageShift = this.settings.sideBySide ? 2 : 1;
|
||||
var reader = this;
|
||||
|
||||
var pageShift = (this.settings.zoomLevel === "spread") ? 2 : 1;
|
||||
|
||||
var oddPageShift = this.settings.oddPageRight ? 0 : 1;
|
||||
|
||||
if (this.settings.currentPage - pageShift < oddPageShift) {
|
||||
return;
|
||||
} else {
|
||||
for (var i = 0; i < pageShift; i++) {
|
||||
reader.cancelPage[this.settings.currentPage - i] = true;
|
||||
}
|
||||
this.queuePage(this.settings.currentPage - pageShift);
|
||||
}
|
||||
};
|
||||
|
||||
PDFJS.Reader.prototype.nextPage = function() {
|
||||
|
||||
var pageShift = this.settings.sideBySide ? 2 : 1;
|
||||
var reader = this;
|
||||
|
||||
var pageShift = (this.settings.zoomLevel === "spread") ? 2 : 1;
|
||||
|
||||
if (this.settings.currentPage + pageShift > this.settings.numPages) {
|
||||
return;
|
||||
} else {
|
||||
for (var i = 0; i < pageShift; i++) {
|
||||
reader.cancelPage[this.settings.currentPage + i] = true;
|
||||
}
|
||||
this.queuePage(this.settings.currentPage + pageShift);
|
||||
}
|
||||
};
|
||||
|
@ -310,3 +548,76 @@ PDFJS.Reader.prototype.defaults = function (obj) {
|
|||
PDFJS.Reader.prototype.setScale = function (scale) {
|
||||
|
||||
};
|
||||
|
||||
PDFJS.Reader.prototype.getOutputScale = function (ctx) {
|
||||
var devicePixelRatio = window.devicePixelRatio || 1,
|
||||
backingStoreRatio = ctx.webkitBackingStorePixelRatio ||
|
||||
ctx.mozBackingStorePixelRatio ||
|
||||
ctx.msBackingStorePixelRatio ||
|
||||
ctx.oBackingStorePixelRatio ||
|
||||
ctx.backingStorePixelRatio || 1,
|
||||
pixelRatio = devicePixelRatio / backingStoreRatio;
|
||||
|
||||
return pixelRatio;
|
||||
};
|
||||
|
||||
PDFJS.Reader.prototype.roundToDivide = function (x, div) {
|
||||
|
||||
var r = x % div;
|
||||
|
||||
return r === 0
|
||||
? x
|
||||
: Math.round(x - r + div);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Approximates float number as a fraction using Farey sequence (max order
|
||||
* of 8).
|
||||
* @param {number} x - Positive float number.
|
||||
* @returns {Array} Estimated fraction: the first array item is a numerator,
|
||||
* the second one is a denominator.
|
||||
*/
|
||||
PDFJS.Reader.prototype.approximateFraction = function (x) {
|
||||
|
||||
// Fast paths for int numbers or their inversions.
|
||||
if (Math.floor(x) === x) {
|
||||
return [x, 1];
|
||||
}
|
||||
var xinv = 1 / x;
|
||||
var limit = 8;
|
||||
if (xinv > limit) {
|
||||
return [1, limit];
|
||||
} else if (Math.floor(xinv) === xinv) {
|
||||
return [1, xinv];
|
||||
}
|
||||
|
||||
var x_ = x > 1 ? xinv : x;
|
||||
// a/b and c/d are neighbours in Farey sequence.
|
||||
var a = 0, b = 1, c = 1, d = 1;
|
||||
// Limiting search to order 8.
|
||||
while (true) {
|
||||
// Generating next term in sequence (order of q).
|
||||
var p = a + c, q = b + d;
|
||||
if (q > limit) {
|
||||
break;
|
||||
}
|
||||
if (x_ <= p / q) {
|
||||
c = p; d = q;
|
||||
} else {
|
||||
a = p; b = q;
|
||||
}
|
||||
}
|
||||
var result;
|
||||
// Select closest of the neighbours to x.
|
||||
if (x_ - a / b < c / d - x_) {
|
||||
result = x_ === x ? [a, b] : [b, a];
|
||||
} else {
|
||||
result = x_ === x ? [c, d] : [d, c];
|
||||
}
|
||||
return result;
|
||||
};
|
||||
|
||||
PDFJS.Reader.prototype.isMobile = function () {
|
||||
return (typeof window.orientation !== "undefined") || (navigator.userAgent.indexOf('IEMobile') !== -1);
|
||||
};
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue