You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
552 lines
18 KiB
552 lines
18 KiB
/** |
|
* @licstart The following is the entire license notice for the |
|
* Javascript code in this page |
|
* |
|
* Copyright 2018 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. |
|
* |
|
* @licend The above is the entire license notice for the |
|
* Javascript code in this page |
|
*/ |
|
'use strict'; |
|
|
|
Object.defineProperty(exports, "__esModule", { |
|
value: true |
|
}); |
|
exports.PDFFindController = exports.FindState = undefined; |
|
|
|
var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); |
|
|
|
var _pdf = require('../pdf'); |
|
|
|
var _pdf_find_utils = require('./pdf_find_utils'); |
|
|
|
var _dom_events = require('./dom_events'); |
|
|
|
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } |
|
|
|
var FindState = { |
|
FOUND: 0, |
|
NOT_FOUND: 1, |
|
WRAPPED: 2, |
|
PENDING: 3 |
|
}; |
|
var FIND_TIMEOUT = 250; |
|
var CHARACTERS_TO_NORMALIZE = { |
|
'\u2018': '\'', |
|
'\u2019': '\'', |
|
'\u201A': '\'', |
|
'\u201B': '\'', |
|
'\u201C': '"', |
|
'\u201D': '"', |
|
'\u201E': '"', |
|
'\u201F': '"', |
|
'\xBC': '1/4', |
|
'\xBD': '1/2', |
|
'\xBE': '3/4' |
|
}; |
|
|
|
var PDFFindController = function () { |
|
function PDFFindController(_ref) { |
|
var linkService = _ref.linkService, |
|
_ref$eventBus = _ref.eventBus, |
|
eventBus = _ref$eventBus === undefined ? (0, _dom_events.getGlobalEventBus)() : _ref$eventBus; |
|
|
|
_classCallCheck(this, PDFFindController); |
|
|
|
this._linkService = linkService; |
|
this._eventBus = eventBus; |
|
this._reset(); |
|
eventBus.on('findbarclose', this._onFindBarClose.bind(this)); |
|
var replace = Object.keys(CHARACTERS_TO_NORMALIZE).join(''); |
|
this._normalizationRegex = new RegExp('[' + replace + ']', 'g'); |
|
} |
|
|
|
_createClass(PDFFindController, [{ |
|
key: 'setDocument', |
|
value: function setDocument(pdfDocument) { |
|
if (this._pdfDocument) { |
|
this._reset(); |
|
} |
|
if (!pdfDocument) { |
|
return; |
|
} |
|
this._pdfDocument = pdfDocument; |
|
this._firstPageCapability.resolve(); |
|
} |
|
}, { |
|
key: 'executeCommand', |
|
value: function executeCommand(cmd, state) { |
|
var _this = this; |
|
|
|
var pdfDocument = this._pdfDocument; |
|
if (this._state === null || cmd !== 'findagain') { |
|
this._dirtyMatch = true; |
|
} |
|
this._state = state; |
|
this._updateUIState(FindState.PENDING); |
|
this._firstPageCapability.promise.then(function () { |
|
if (!_this._pdfDocument || pdfDocument && _this._pdfDocument !== pdfDocument) { |
|
return; |
|
} |
|
_this._extractText(); |
|
if (_this._findTimeout) { |
|
clearTimeout(_this._findTimeout); |
|
_this._findTimeout = null; |
|
} |
|
if (cmd === 'find') { |
|
_this._findTimeout = setTimeout(function () { |
|
_this._nextMatch(); |
|
_this._findTimeout = null; |
|
}, FIND_TIMEOUT); |
|
} else { |
|
_this._nextMatch(); |
|
} |
|
}); |
|
} |
|
}, { |
|
key: '_reset', |
|
value: function _reset() { |
|
this._highlightMatches = false; |
|
this._pdfDocument = null; |
|
this._pageMatches = []; |
|
this._pageMatchesLength = null; |
|
this._state = null; |
|
this._selected = { |
|
pageIdx: -1, |
|
matchIdx: -1 |
|
}; |
|
this._offset = { |
|
pageIdx: null, |
|
matchIdx: null |
|
}; |
|
this._extractTextPromises = []; |
|
this._pageContents = []; |
|
this._matchesCountTotal = 0; |
|
this._pagesToSearch = null; |
|
this._pendingFindMatches = Object.create(null); |
|
this._resumePageIdx = null; |
|
this._dirtyMatch = false; |
|
clearTimeout(this._findTimeout); |
|
this._findTimeout = null; |
|
this._firstPageCapability = (0, _pdf.createPromiseCapability)(); |
|
} |
|
}, { |
|
key: '_normalize', |
|
value: function _normalize(text) { |
|
return text.replace(this._normalizationRegex, function (ch) { |
|
return CHARACTERS_TO_NORMALIZE[ch]; |
|
}); |
|
} |
|
}, { |
|
key: '_prepareMatches', |
|
value: function _prepareMatches(matchesWithLength, matches, matchesLength) { |
|
function isSubTerm(matchesWithLength, currentIndex) { |
|
var currentElem = matchesWithLength[currentIndex]; |
|
var nextElem = matchesWithLength[currentIndex + 1]; |
|
if (currentIndex < matchesWithLength.length - 1 && currentElem.match === nextElem.match) { |
|
currentElem.skipped = true; |
|
return true; |
|
} |
|
for (var i = currentIndex - 1; i >= 0; i--) { |
|
var prevElem = matchesWithLength[i]; |
|
if (prevElem.skipped) { |
|
continue; |
|
} |
|
if (prevElem.match + prevElem.matchLength < currentElem.match) { |
|
break; |
|
} |
|
if (prevElem.match + prevElem.matchLength >= currentElem.match + currentElem.matchLength) { |
|
currentElem.skipped = true; |
|
return true; |
|
} |
|
} |
|
return false; |
|
} |
|
matchesWithLength.sort(function (a, b) { |
|
return a.match === b.match ? a.matchLength - b.matchLength : a.match - b.match; |
|
}); |
|
for (var i = 0, len = matchesWithLength.length; i < len; i++) { |
|
if (isSubTerm(matchesWithLength, i)) { |
|
continue; |
|
} |
|
matches.push(matchesWithLength[i].match); |
|
matchesLength.push(matchesWithLength[i].matchLength); |
|
} |
|
} |
|
}, { |
|
key: '_isEntireWord', |
|
value: function _isEntireWord(content, startIdx, length) { |
|
if (startIdx > 0) { |
|
var first = content.charCodeAt(startIdx); |
|
var limit = content.charCodeAt(startIdx - 1); |
|
if ((0, _pdf_find_utils.getCharacterType)(first) === (0, _pdf_find_utils.getCharacterType)(limit)) { |
|
return false; |
|
} |
|
} |
|
var endIdx = startIdx + length - 1; |
|
if (endIdx < content.length - 1) { |
|
var last = content.charCodeAt(endIdx); |
|
var _limit = content.charCodeAt(endIdx + 1); |
|
if ((0, _pdf_find_utils.getCharacterType)(last) === (0, _pdf_find_utils.getCharacterType)(_limit)) { |
|
return false; |
|
} |
|
} |
|
return true; |
|
} |
|
}, { |
|
key: '_calculatePhraseMatch', |
|
value: function _calculatePhraseMatch(query, pageIndex, pageContent, entireWord) { |
|
var matches = []; |
|
var queryLen = query.length; |
|
var matchIdx = -queryLen; |
|
while (true) { |
|
matchIdx = pageContent.indexOf(query, matchIdx + queryLen); |
|
if (matchIdx === -1) { |
|
break; |
|
} |
|
if (entireWord && !this._isEntireWord(pageContent, matchIdx, queryLen)) { |
|
continue; |
|
} |
|
matches.push(matchIdx); |
|
} |
|
this._pageMatches[pageIndex] = matches; |
|
} |
|
}, { |
|
key: '_calculateWordMatch', |
|
value: function _calculateWordMatch(query, pageIndex, pageContent, entireWord) { |
|
var matchesWithLength = []; |
|
var queryArray = query.match(/\S+/g); |
|
for (var i = 0, len = queryArray.length; i < len; i++) { |
|
var subquery = queryArray[i]; |
|
var subqueryLen = subquery.length; |
|
var matchIdx = -subqueryLen; |
|
while (true) { |
|
matchIdx = pageContent.indexOf(subquery, matchIdx + subqueryLen); |
|
if (matchIdx === -1) { |
|
break; |
|
} |
|
if (entireWord && !this._isEntireWord(pageContent, matchIdx, subqueryLen)) { |
|
continue; |
|
} |
|
matchesWithLength.push({ |
|
match: matchIdx, |
|
matchLength: subqueryLen, |
|
skipped: false |
|
}); |
|
} |
|
} |
|
if (!this._pageMatchesLength) { |
|
this._pageMatchesLength = []; |
|
} |
|
this._pageMatchesLength[pageIndex] = []; |
|
this._pageMatches[pageIndex] = []; |
|
this._prepareMatches(matchesWithLength, this._pageMatches[pageIndex], this._pageMatchesLength[pageIndex]); |
|
} |
|
}, { |
|
key: '_calculateMatch', |
|
value: function _calculateMatch(pageIndex) { |
|
var pageContent = this._normalize(this._pageContents[pageIndex]); |
|
var query = this._normalize(this._state.query); |
|
var _state = this._state, |
|
caseSensitive = _state.caseSensitive, |
|
entireWord = _state.entireWord, |
|
phraseSearch = _state.phraseSearch; |
|
|
|
if (query.length === 0) { |
|
return; |
|
} |
|
if (!caseSensitive) { |
|
pageContent = pageContent.toLowerCase(); |
|
query = query.toLowerCase(); |
|
} |
|
if (phraseSearch) { |
|
this._calculatePhraseMatch(query, pageIndex, pageContent, entireWord); |
|
} else { |
|
this._calculateWordMatch(query, pageIndex, pageContent, entireWord); |
|
} |
|
this._updatePage(pageIndex); |
|
if (this._resumePageIdx === pageIndex) { |
|
this._resumePageIdx = null; |
|
this._nextPageMatch(); |
|
} |
|
var pageMatchesCount = this._pageMatches[pageIndex].length; |
|
if (pageMatchesCount > 0) { |
|
this._matchesCountTotal += pageMatchesCount; |
|
this._updateUIResultsCount(); |
|
} |
|
} |
|
}, { |
|
key: '_extractText', |
|
value: function _extractText() { |
|
var _this2 = this; |
|
|
|
if (this._extractTextPromises.length > 0) { |
|
return; |
|
} |
|
var promise = Promise.resolve(); |
|
|
|
var _loop = function _loop(i, ii) { |
|
var extractTextCapability = (0, _pdf.createPromiseCapability)(); |
|
_this2._extractTextPromises[i] = extractTextCapability.promise; |
|
promise = promise.then(function () { |
|
return _this2._pdfDocument.getPage(i + 1).then(function (pdfPage) { |
|
return pdfPage.getTextContent({ normalizeWhitespace: true }); |
|
}).then(function (textContent) { |
|
var textItems = textContent.items; |
|
var strBuf = []; |
|
for (var j = 0, jj = textItems.length; j < jj; j++) { |
|
strBuf.push(textItems[j].str); |
|
} |
|
_this2._pageContents[i] = strBuf.join(''); |
|
extractTextCapability.resolve(i); |
|
}, function (reason) { |
|
console.error('Unable to get text content for page ' + (i + 1), reason); |
|
_this2._pageContents[i] = ''; |
|
extractTextCapability.resolve(i); |
|
}); |
|
}); |
|
}; |
|
|
|
for (var i = 0, ii = this._linkService.pagesCount; i < ii; i++) { |
|
_loop(i, ii); |
|
} |
|
} |
|
}, { |
|
key: '_updatePage', |
|
value: function _updatePage(index) { |
|
if (this._selected.pageIdx === index) { |
|
this._linkService.page = index + 1; |
|
} |
|
this._eventBus.dispatch('updatetextlayermatches', { |
|
source: this, |
|
pageIndex: index |
|
}); |
|
} |
|
}, { |
|
key: '_nextMatch', |
|
value: function _nextMatch() { |
|
var _this3 = this; |
|
|
|
var previous = this._state.findPrevious; |
|
var currentPageIndex = this._linkService.page - 1; |
|
var numPages = this._linkService.pagesCount; |
|
this._highlightMatches = true; |
|
if (this._dirtyMatch) { |
|
this._dirtyMatch = false; |
|
this._selected.pageIdx = this._selected.matchIdx = -1; |
|
this._offset.pageIdx = currentPageIndex; |
|
this._offset.matchIdx = null; |
|
this._resumePageIdx = null; |
|
this._pageMatches.length = 0; |
|
this._pageMatchesLength = null; |
|
this._matchesCountTotal = 0; |
|
for (var i = 0; i < numPages; i++) { |
|
this._updatePage(i); |
|
if (!(i in this._pendingFindMatches)) { |
|
this._pendingFindMatches[i] = true; |
|
this._extractTextPromises[i].then(function (pageIdx) { |
|
delete _this3._pendingFindMatches[pageIdx]; |
|
_this3._calculateMatch(pageIdx); |
|
}); |
|
} |
|
} |
|
} |
|
if (this._state.query === '') { |
|
this._updateUIState(FindState.FOUND); |
|
return; |
|
} |
|
if (this._resumePageIdx) { |
|
return; |
|
} |
|
var offset = this._offset; |
|
this._pagesToSearch = numPages; |
|
if (offset.matchIdx !== null) { |
|
var numPageMatches = this._pageMatches[offset.pageIdx].length; |
|
if (!previous && offset.matchIdx + 1 < numPageMatches || previous && offset.matchIdx > 0) { |
|
offset.matchIdx = previous ? offset.matchIdx - 1 : offset.matchIdx + 1; |
|
this._updateMatch(true); |
|
return; |
|
} |
|
this._advanceOffsetPage(previous); |
|
} |
|
this._nextPageMatch(); |
|
} |
|
}, { |
|
key: '_matchesReady', |
|
value: function _matchesReady(matches) { |
|
var offset = this._offset; |
|
var numMatches = matches.length; |
|
var previous = this._state.findPrevious; |
|
if (numMatches) { |
|
offset.matchIdx = previous ? numMatches - 1 : 0; |
|
this._updateMatch(true); |
|
return true; |
|
} |
|
this._advanceOffsetPage(previous); |
|
if (offset.wrapped) { |
|
offset.matchIdx = null; |
|
if (this._pagesToSearch < 0) { |
|
this._updateMatch(false); |
|
return true; |
|
} |
|
} |
|
return false; |
|
} |
|
}, { |
|
key: '_nextPageMatch', |
|
value: function _nextPageMatch() { |
|
if (this._resumePageIdx !== null) { |
|
console.error('There can only be one pending page.'); |
|
} |
|
var matches = null; |
|
do { |
|
var pageIdx = this._offset.pageIdx; |
|
matches = this._pageMatches[pageIdx]; |
|
if (!matches) { |
|
this._resumePageIdx = pageIdx; |
|
break; |
|
} |
|
} while (!this._matchesReady(matches)); |
|
} |
|
}, { |
|
key: '_advanceOffsetPage', |
|
value: function _advanceOffsetPage(previous) { |
|
var offset = this._offset; |
|
var numPages = this._linkService.pagesCount; |
|
offset.pageIdx = previous ? offset.pageIdx - 1 : offset.pageIdx + 1; |
|
offset.matchIdx = null; |
|
this._pagesToSearch--; |
|
if (offset.pageIdx >= numPages || offset.pageIdx < 0) { |
|
offset.pageIdx = previous ? numPages - 1 : 0; |
|
offset.wrapped = true; |
|
} |
|
} |
|
}, { |
|
key: '_updateMatch', |
|
value: function _updateMatch() { |
|
var found = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : false; |
|
|
|
var state = FindState.NOT_FOUND; |
|
var wrapped = this._offset.wrapped; |
|
this._offset.wrapped = false; |
|
if (found) { |
|
var previousPage = this._selected.pageIdx; |
|
this._selected.pageIdx = this._offset.pageIdx; |
|
this._selected.matchIdx = this._offset.matchIdx; |
|
state = wrapped ? FindState.WRAPPED : FindState.FOUND; |
|
if (previousPage !== -1 && previousPage !== this._selected.pageIdx) { |
|
this._updatePage(previousPage); |
|
} |
|
} |
|
this._updateUIState(state, this._state.findPrevious); |
|
if (this._selected.pageIdx !== -1) { |
|
this._updatePage(this._selected.pageIdx); |
|
} |
|
} |
|
}, { |
|
key: '_onFindBarClose', |
|
value: function _onFindBarClose(evt) { |
|
var _this4 = this; |
|
|
|
var pdfDocument = this._pdfDocument; |
|
this._firstPageCapability.promise.then(function () { |
|
if (!_this4._pdfDocument || pdfDocument && _this4._pdfDocument !== pdfDocument) { |
|
return; |
|
} |
|
if (_this4._findTimeout) { |
|
clearTimeout(_this4._findTimeout); |
|
_this4._findTimeout = null; |
|
_this4._updateUIState(FindState.FOUND); |
|
} |
|
_this4._highlightMatches = false; |
|
_this4._eventBus.dispatch('updatetextlayermatches', { |
|
source: _this4, |
|
pageIndex: -1 |
|
}); |
|
}); |
|
} |
|
}, { |
|
key: '_requestMatchesCount', |
|
value: function _requestMatchesCount() { |
|
var _selected = this._selected, |
|
pageIdx = _selected.pageIdx, |
|
matchIdx = _selected.matchIdx; |
|
|
|
var current = 0, |
|
total = this._matchesCountTotal; |
|
if (matchIdx !== -1) { |
|
for (var i = 0; i < pageIdx; i++) { |
|
current += this._pageMatches[i] && this._pageMatches[i].length || 0; |
|
} |
|
current += matchIdx + 1; |
|
} |
|
if (current < 1 || current > total) { |
|
current = total = 0; |
|
} |
|
return { |
|
current: current, |
|
total: total |
|
}; |
|
} |
|
}, { |
|
key: '_updateUIResultsCount', |
|
value: function _updateUIResultsCount() { |
|
this._eventBus.dispatch('updatefindmatchescount', { |
|
source: this, |
|
matchesCount: this._requestMatchesCount() |
|
}); |
|
} |
|
}, { |
|
key: '_updateUIState', |
|
value: function _updateUIState(state, previous) { |
|
this._eventBus.dispatch('updatefindcontrolstate', { |
|
source: this, |
|
state: state, |
|
previous: previous, |
|
matchesCount: this._requestMatchesCount() |
|
}); |
|
} |
|
}, { |
|
key: 'highlightMatches', |
|
get: function get() { |
|
return this._highlightMatches; |
|
} |
|
}, { |
|
key: 'pageMatches', |
|
get: function get() { |
|
return this._pageMatches; |
|
} |
|
}, { |
|
key: 'pageMatchesLength', |
|
get: function get() { |
|
return this._pageMatchesLength; |
|
} |
|
}, { |
|
key: 'selected', |
|
get: function get() { |
|
return this._selected; |
|
} |
|
}, { |
|
key: 'state', |
|
get: function get() { |
|
return this._state; |
|
} |
|
}]); |
|
|
|
return PDFFindController; |
|
}(); |
|
|
|
exports.FindState = FindState; |
|
exports.PDFFindController = PDFFindController; |