diff --git a/bower.json b/bower.json index 52b5a16cb..05958f4cc 100644 --- a/bower.json +++ b/bower.json @@ -1,6 +1,6 @@ { "name": "pdfjs-dist", - "version": "1.5.276", + "version": "1.5.278", "main": [ "build/pdf.js", "build/pdf.worker.js" diff --git a/build/pdf.combined.js b/build/pdf.combined.js index 5b1763709..bb74a761e 100644 --- a/build/pdf.combined.js +++ b/build/pdf.combined.js @@ -28,8 +28,8 @@ factory((root.pdfjsDistBuildPdfCombined = {})); // Use strict in our context only - users might not want it 'use strict'; -var pdfjsVersion = '1.5.276'; -var pdfjsBuild = '41f978c'; +var pdfjsVersion = '1.5.278'; +var pdfjsBuild = '5aefce6'; var pdfjsFilePath = typeof document !== 'undefined' && document.currentScript ? diff --git a/build/pdf.js b/build/pdf.js index 3f0978fc8..c6c392b26 100644 --- a/build/pdf.js +++ b/build/pdf.js @@ -28,8 +28,8 @@ factory((root.pdfjsDistBuildPdf = {})); // Use strict in our context only - users might not want it 'use strict'; -var pdfjsVersion = '1.5.276'; -var pdfjsBuild = '41f978c'; +var pdfjsVersion = '1.5.278'; +var pdfjsBuild = '5aefce6'; var pdfjsFilePath = typeof document !== 'undefined' && document.currentScript ? diff --git a/build/pdf.worker.js b/build/pdf.worker.js index 633242698..e2bcb84b0 100644 --- a/build/pdf.worker.js +++ b/build/pdf.worker.js @@ -28,8 +28,8 @@ factory((root.pdfjsDistBuildPdfWorker = {})); // Use strict in our context only - users might not want it 'use strict'; -var pdfjsVersion = '1.5.276'; -var pdfjsBuild = '41f978c'; +var pdfjsVersion = '1.5.278'; +var pdfjsBuild = '5aefce6'; var pdfjsFilePath = typeof document !== 'undefined' && document.currentScript ? diff --git a/package.json b/package.json index 5695ae4ed..6da3806fc 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "pdfjs-dist", - "version": "1.5.276", + "version": "1.5.278", "main": "build/pdf.js", "description": "Generic build of Mozilla's PDF.js library.", "keywords": [ diff --git a/web/pdf_viewer.js b/web/pdf_viewer.js index a6121d7c9..bf708ca04 100644 --- a/web/pdf_viewer.js +++ b/web/pdf_viewer.js @@ -875,6 +875,7 @@ exports.binarySearchFirstItem = binarySearchFirstItem; var event = document.createEvent('CustomEvent'); event.initCustomEvent('find' + e.type, true, true, { query: e.query, + phraseSearch: e.phraseSearch, caseSensitive: e.caseSensitive, highlightAll: e.highlightAll, findPrevious: e.findPrevious @@ -999,6 +1000,7 @@ var PDFFindController = (function PDFFindControllerClosure() { this.active = false; // If active, find results will be highlighted. this.pageContents = []; // Stores the text for each page. this.pageMatches = []; + this.pageMatchesLength = null; this.matchCount = 0; this.selected = { // Currently selected match. pageIdx: -1, @@ -1025,10 +1027,114 @@ var PDFFindController = (function PDFFindControllerClosure() { }); }, + // Helper for multiple search - fills matchesWithLength array + // and takes into account cases when one search term + // include another search term (for example, "tamed tame" or "this is"). + // Looking for intersecting terms in the 'matches' and + // leave elements with a longer match-length. + + _prepareMatches: function PDFFindController_prepareMatches( + matchesWithLength, matches, matchesLength) { + + function isSubTerm(matchesWithLength, currentIndex) { + var currentElem, prevElem, nextElem; + currentElem = matchesWithLength[currentIndex]; + nextElem = matchesWithLength[currentIndex + 1]; + // checking for cases like "TAMEd TAME" + if (currentIndex < matchesWithLength.length - 1 && + currentElem.match === nextElem.match) { + currentElem.skipped = true; + return true; + } + // checking for cases like "thIS IS" + for (var i = currentIndex - 1; i >= 0; i--) { + 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; + } + + var i, len; + // Sorting array of objects { match: , matchLength: } + // in increasing index first and then the lengths. + matchesWithLength.sort(function(a, b) { + return a.match === b.match ? + a.matchLength - b.matchLength : a.match - b.match; + }); + for (i = 0, len = matchesWithLength.length; i < len; i++) { + if (isSubTerm(matchesWithLength, i)) { + continue; + } + matches.push(matchesWithLength[i].match); + matchesLength.push(matchesWithLength[i].matchLength); + } + }, + + calcFindPhraseMatch: function PDFFindController_calcFindPhraseMatch( + query, pageIndex, pageContent) { + var matches = []; + var queryLen = query.length; + var matchIdx = -queryLen; + while (true) { + matchIdx = pageContent.indexOf(query, matchIdx + queryLen); + if (matchIdx === -1) { + break; + } + matches.push(matchIdx); + } + this.pageMatches[pageIndex] = matches; + }, + + calcFindWordMatch: function PDFFindController_calcFindWordMatch( + query, pageIndex, pageContent) { + var matchesWithLength = []; + // Divide the query into pieces and search for text on each piece. + var queryArray = query.match(/\S+/g); + var subquery, subqueryLen, matchIdx; + for (var i = 0, len = queryArray.length; i < len; i++) { + subquery = queryArray[i]; + subqueryLen = subquery.length; + matchIdx = -subqueryLen; + while (true) { + matchIdx = pageContent.indexOf(subquery, matchIdx + subqueryLen); + if (matchIdx === -1) { + break; + } + // Other searches do not, so we store the length. + matchesWithLength.push({ + match: matchIdx, + matchLength: subqueryLen, + skipped: false + }); + } + } + // Prepare arrays for store the matches. + if (!this.pageMatchesLength) { + this.pageMatchesLength = []; + } + this.pageMatchesLength[pageIndex] = []; + this.pageMatches[pageIndex] = []; + // Sort matchesWithLength, clean up intersecting terms + // and put the result into the two arrays. + this._prepareMatches(matchesWithLength, this.pageMatches[pageIndex], + this.pageMatchesLength[pageIndex]); + }, + calcFindMatch: function PDFFindController_calcFindMatch(pageIndex) { var pageContent = this.normalize(this.pageContents[pageIndex]); var query = this.normalize(this.state.query); var caseSensitive = this.state.caseSensitive; + var phraseSearch = this.state.phraseSearch; var queryLen = query.length; if (queryLen === 0) { @@ -1041,16 +1147,12 @@ var PDFFindController = (function PDFFindControllerClosure() { query = query.toLowerCase(); } - var matches = []; - var matchIdx = -queryLen; - while (true) { - matchIdx = pageContent.indexOf(query, matchIdx + queryLen); - if (matchIdx === -1) { - break; - } - matches.push(matchIdx); + if (phraseSearch) { + this.calcFindPhraseMatch(query, pageIndex, pageContent); + } else { + this.calcFindWordMatch(query, pageIndex, pageContent); } - this.pageMatches[pageIndex] = matches; + this.updatePage(pageIndex); if (this.resumePageIdx === pageIndex) { this.resumePageIdx = null; @@ -1058,8 +1160,8 @@ var PDFFindController = (function PDFFindControllerClosure() { } // Update the matches count - if (matches.length > 0) { - this.matchCount += matches.length; + if (this.pageMatches[pageIndex].length > 0) { + this.matchCount += this.pageMatches[pageIndex].length; this.updateUIResultsCount(); } }, @@ -1154,6 +1256,7 @@ var PDFFindController = (function PDFFindControllerClosure() { this.resumePageIdx = null; this.pageMatches = []; this.matchCount = 0; + this.pageMatchesLength = null; var self = this; for (var i = 0; i < numPages; i++) { @@ -1901,6 +2004,13 @@ var PDFLinkService = (function () { setHash: function PDFLinkService_setHash(hash) { if (hash.indexOf('=') >= 0) { var params = parseQueryString(hash); + if ('search' in params) { + this.eventBus.dispatch('findfromurlhash', { + source: this, + query: params['search'].replace(/"/g, ''), + phraseSearch: (params['phrase'] === 'true') + }); + } // borrowing syntax from "Parameters for Opening PDF Files" if ('nameddest' in params) { if (this.pdfHistory) { @@ -2749,7 +2859,8 @@ var TextLayerBuilder = (function TextLayerBuilderClosure() { this.divContentDone = true; }, - convertMatches: function TextLayerBuilder_convertMatches(matches) { + convertMatches: function TextLayerBuilder_convertMatches(matches, + matchesLength) { var i = 0; var iIndex = 0; var bidiTexts = this.textContent.items; @@ -2757,7 +2868,9 @@ var TextLayerBuilder = (function TextLayerBuilderClosure() { 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]; @@ -2780,7 +2893,11 @@ var TextLayerBuilder = (function TextLayerBuilderClosure() { }; // Calculate the end position. - matchIdx += queryLen; + 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. @@ -2922,8 +3039,14 @@ var TextLayerBuilder = (function TextLayerBuilderClosure() { // Convert the matches on the page controller into the match format // used for the textLayer. - this.matches = this.convertMatches(this.findController === null ? - [] : (this.findController.pageMatches[this.pageIdx] || [])); + 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); },