|
|
@ -208,6 +208,345 @@ var Settings = (function SettingsClosure() { |
|
|
|
var cache = new Cache(kCacheSize); |
|
|
|
var cache = new Cache(kCacheSize); |
|
|
|
var currentPageNumber = 1; |
|
|
|
var currentPageNumber = 1; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
var PDFFindController = { |
|
|
|
|
|
|
|
startedTextExtraction: false, |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// If active, search resulsts will be highlighted.
|
|
|
|
|
|
|
|
active: false, |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Stores the text for each page.
|
|
|
|
|
|
|
|
pageContents: [], |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
pageMatches: [], |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
selected: { |
|
|
|
|
|
|
|
pageIdx: 0, |
|
|
|
|
|
|
|
matchIdx: 0 |
|
|
|
|
|
|
|
}, |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
dirtyMatch: false, |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
findTimeout: null, |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
initialize: function() { |
|
|
|
|
|
|
|
var events = [ |
|
|
|
|
|
|
|
'find', |
|
|
|
|
|
|
|
'findagain', |
|
|
|
|
|
|
|
'findhighlightallchange', |
|
|
|
|
|
|
|
'findcasesensitivitychange' |
|
|
|
|
|
|
|
]; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
this.handelEvent = this.handelEvent.bind(this); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
for (var i = 0; i < events.length; i++) { |
|
|
|
|
|
|
|
window.addEventListener(events[i], this.handelEvent); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
}, |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
calcFindMatch: function(pageContent) { |
|
|
|
|
|
|
|
// TODO: Handle the other search options here as well.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
var query = this.state.query; |
|
|
|
|
|
|
|
var queryLen = query.length; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (queryLen === 0) |
|
|
|
|
|
|
|
return []; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
var matches = []; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
var matchIdx = -queryLen; |
|
|
|
|
|
|
|
while (true) { |
|
|
|
|
|
|
|
matchIdx = pageContent.indexOf(query, matchIdx + queryLen); |
|
|
|
|
|
|
|
if (matchIdx === -1) { |
|
|
|
|
|
|
|
break; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
matches.push(matchIdx); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
return matches; |
|
|
|
|
|
|
|
}, |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
extractText: function() { |
|
|
|
|
|
|
|
if (this.startedTextExtraction) |
|
|
|
|
|
|
|
return; |
|
|
|
|
|
|
|
this.startedTextExtraction = true; |
|
|
|
|
|
|
|
var self = this; |
|
|
|
|
|
|
|
function extractPageText(pageIndex) { |
|
|
|
|
|
|
|
PDFView.pages[pageIndex].getTextContent().then( |
|
|
|
|
|
|
|
function textContentResolved(data) { |
|
|
|
|
|
|
|
// Bulid the search string.
|
|
|
|
|
|
|
|
var bidiTexts = data.bidiTexts; |
|
|
|
|
|
|
|
var str = ''; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
for (var i = 0; i < bidiTexts.length; i++) { |
|
|
|
|
|
|
|
str += bidiTexts[i].str; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Store the pageContent as a string.
|
|
|
|
|
|
|
|
self.pageContents.push(str); |
|
|
|
|
|
|
|
// Ensure there is a empty array of matches.
|
|
|
|
|
|
|
|
self.pageMatches.push([]); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if ((pageIndex + 1) < PDFView.pages.length) |
|
|
|
|
|
|
|
extractPageText(pageIndex + 1); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
extractPageText(0); |
|
|
|
|
|
|
|
}, |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
handelEvent: function(e) { |
|
|
|
|
|
|
|
this.state = e.detail; |
|
|
|
|
|
|
|
if (e.detail.findPrevious === undefined) { |
|
|
|
|
|
|
|
this.dirtyMatch = true; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
clearTimeout(this.findTimeout); |
|
|
|
|
|
|
|
if (e.type === 'find') { |
|
|
|
|
|
|
|
// Only trigger the find action after 250ms of silence.
|
|
|
|
|
|
|
|
this.findTimeout = setTimeout(this.performFind.bind(this), 250); |
|
|
|
|
|
|
|
} else { |
|
|
|
|
|
|
|
this.performFind(); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
}, |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
updatePage: function(idx) { |
|
|
|
|
|
|
|
var page = PDFView.pages[idx]; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (page.textLayer) { |
|
|
|
|
|
|
|
page.textLayer.updateMatches(); |
|
|
|
|
|
|
|
} else if (this.selected.pageIdx === idx) { |
|
|
|
|
|
|
|
// If the page is selected, scroll the page into view, which triggers
|
|
|
|
|
|
|
|
// rendering the page, which adds the textLayer. Once the textLayer is
|
|
|
|
|
|
|
|
// build, it will scroll onto the selected match.
|
|
|
|
|
|
|
|
page.scrollIntoView(); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
}, |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
performFind: function() { |
|
|
|
|
|
|
|
// Recalculate all the matches.
|
|
|
|
|
|
|
|
// TODO: Make one match show up as the current match
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
var pages = PDFView.pages; |
|
|
|
|
|
|
|
var pageContents = this.pageContents; |
|
|
|
|
|
|
|
var pageMatches = this.pageMatches; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
this.active = true; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (this.dirtyMatch) { |
|
|
|
|
|
|
|
// Need to recalculate the matches.
|
|
|
|
|
|
|
|
this.dirtyMatch = false; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
this.selected = { |
|
|
|
|
|
|
|
pageIdx: -1, |
|
|
|
|
|
|
|
matchIdx: -1 |
|
|
|
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// TODO: Make this way more lasily (aka. efficient) - e.g. calculate only
|
|
|
|
|
|
|
|
// the matches for the current visible pages.
|
|
|
|
|
|
|
|
var firstMatch = true; |
|
|
|
|
|
|
|
for (var i = 0; i < pageContents.length; i++) { |
|
|
|
|
|
|
|
var matches = pageMatches[i] = this.calcFindMatch(pageContents[i]); |
|
|
|
|
|
|
|
if (firstMatch && matches.length !== 0) { |
|
|
|
|
|
|
|
firstMatch = false; |
|
|
|
|
|
|
|
this.selected = { |
|
|
|
|
|
|
|
pageIdx: i, |
|
|
|
|
|
|
|
matchIdx: 0 |
|
|
|
|
|
|
|
}; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
this.updatePage(i, true); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} else { |
|
|
|
|
|
|
|
// If there is NO selection, then there is no match at all -> no sense to
|
|
|
|
|
|
|
|
// handel previous/next action.
|
|
|
|
|
|
|
|
if (this.selected.pageIdx === -1) |
|
|
|
|
|
|
|
return; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Handle findAgain case.
|
|
|
|
|
|
|
|
var previous = this.state.findPrevious; |
|
|
|
|
|
|
|
var sPageIdx = this.selected.pageIdx; |
|
|
|
|
|
|
|
var sMatchIdx = this.selected.matchIdx; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (previous) { |
|
|
|
|
|
|
|
// Select previous match.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (sMatchIdx !== 0) { |
|
|
|
|
|
|
|
this.selected.matchIdx -= 1; |
|
|
|
|
|
|
|
} else { |
|
|
|
|
|
|
|
var len = pageMatches.length; |
|
|
|
|
|
|
|
for (var i = sPageIdx - 1; i != sPageIdx; i--) { |
|
|
|
|
|
|
|
if (i < 0) |
|
|
|
|
|
|
|
i += len; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (pageMatches[i].length !== 0) { |
|
|
|
|
|
|
|
this.selected = { |
|
|
|
|
|
|
|
pageIdx: i, |
|
|
|
|
|
|
|
matchIdx: pageMatches[i].length - 1 |
|
|
|
|
|
|
|
}; |
|
|
|
|
|
|
|
break; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
// If pageIdx stayed the same, select last match on the page.
|
|
|
|
|
|
|
|
if (this.selected.pageIdx === sPageIdx) { |
|
|
|
|
|
|
|
this.selected.matchIdx = pageMatches[sPageIdx].length - 1; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} else { |
|
|
|
|
|
|
|
// Select next match.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (pageMatches[sPageIdx].length !== sMatchIdx + 1) { |
|
|
|
|
|
|
|
this.selected.matchIdx += 1; |
|
|
|
|
|
|
|
} else { |
|
|
|
|
|
|
|
var len = pageMatches.length; |
|
|
|
|
|
|
|
for (var i = sPageIdx + 1; i < len + sPageIdx; i++) { |
|
|
|
|
|
|
|
if (pageMatches[i % len].length !== 0) { |
|
|
|
|
|
|
|
this.selected = { |
|
|
|
|
|
|
|
pageIdx: i % len, |
|
|
|
|
|
|
|
matchIdx: 0 |
|
|
|
|
|
|
|
}; |
|
|
|
|
|
|
|
break; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
// If pageIdx stayed the same, select last match on the page.
|
|
|
|
|
|
|
|
if (this.selected.pageIdx === sPageIdx) { |
|
|
|
|
|
|
|
this.selected.matchIdx = 0; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
this.updatePage(sPageIdx, sPageIdx === this.selected.pageIdx); |
|
|
|
|
|
|
|
if (sPageIdx !== this.selected.pageIdx) { |
|
|
|
|
|
|
|
this.updatePage(this.selected.pageIdx, true); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
var PDFFindBar = { |
|
|
|
|
|
|
|
// TODO: Enable the FindBar *AFTER* the pagesPromise in the load function
|
|
|
|
|
|
|
|
// got resolved
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
opened: false, |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
FIND_FOUND: 0, // Successful find
|
|
|
|
|
|
|
|
FIND_NOTFOUND: 1, // Unsuccessful find
|
|
|
|
|
|
|
|
FIND_WRAPPED: 2, // Successful find, but wrapped around
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
initialize: function() { |
|
|
|
|
|
|
|
this.bar = document.getElementById('findbar'); |
|
|
|
|
|
|
|
this.toggleButton = document.getElementById('viewSearch'); |
|
|
|
|
|
|
|
this.findField = document.getElementById('findInput'); |
|
|
|
|
|
|
|
this.highlightAll = document.getElementById('findHighlightAll'); |
|
|
|
|
|
|
|
this.caseSensitive = document.getElementById('findMatchCase'); |
|
|
|
|
|
|
|
this.findMsgWrap = document.getElementById('findMsgWrap'); |
|
|
|
|
|
|
|
this.findMsgNotFound = document.getElementById('findMsgNotFound'); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
var self = this; |
|
|
|
|
|
|
|
this.toggleButton.addEventListener('click', |
|
|
|
|
|
|
|
function() { |
|
|
|
|
|
|
|
self.toggle(); |
|
|
|
|
|
|
|
}); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
this.findField.addEventListener('input', function() { |
|
|
|
|
|
|
|
self.dispatchEvent(''); |
|
|
|
|
|
|
|
}); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// TODO: Add keybindings like enter, shift-enter, CMD-G etc. to go to prev/
|
|
|
|
|
|
|
|
// next match when the findField is selected.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
document.getElementById('findPrevious').addEventListener('click', |
|
|
|
|
|
|
|
function() { |
|
|
|
|
|
|
|
self.dispatchEvent('again', true); |
|
|
|
|
|
|
|
}); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
document.getElementById('findNext').addEventListener('click', |
|
|
|
|
|
|
|
function() { |
|
|
|
|
|
|
|
self.dispatchEvent('again', false); |
|
|
|
|
|
|
|
}); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
this.highlightAll.addEventListener('click', |
|
|
|
|
|
|
|
function() { |
|
|
|
|
|
|
|
self.dispatchEvent('highlightallchange'); |
|
|
|
|
|
|
|
}); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
this.caseSensitive.addEventListener('click', |
|
|
|
|
|
|
|
function() { |
|
|
|
|
|
|
|
self.dispatchEvent('casesensitivitychange'); |
|
|
|
|
|
|
|
}); |
|
|
|
|
|
|
|
}, |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
dispatchEvent: function(aType, aFindPrevious) { |
|
|
|
|
|
|
|
var event = document.createEvent('CustomEvent'); |
|
|
|
|
|
|
|
event.initCustomEvent('find' + aType, true, true, { |
|
|
|
|
|
|
|
query: this.findField.value, |
|
|
|
|
|
|
|
caseSensitive: this.caseSensitive.checked, |
|
|
|
|
|
|
|
highlightAll: this.highlightAll.checked, |
|
|
|
|
|
|
|
findPrevious: aFindPrevious |
|
|
|
|
|
|
|
}); |
|
|
|
|
|
|
|
return window.dispatchEvent(event); |
|
|
|
|
|
|
|
}, |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
updateUIState: function(aState) { |
|
|
|
|
|
|
|
var notFound = false; |
|
|
|
|
|
|
|
var wrapped = false; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
switch (aState) { |
|
|
|
|
|
|
|
case this.FIND_FOUND: |
|
|
|
|
|
|
|
break; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
case this.FIND_NOTFOUND: |
|
|
|
|
|
|
|
notFound = true; |
|
|
|
|
|
|
|
break; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
case this.FIND_WRAPPED: |
|
|
|
|
|
|
|
wrapped = true; |
|
|
|
|
|
|
|
break; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (notFound) { |
|
|
|
|
|
|
|
this.findField.classList.add('notFound'); |
|
|
|
|
|
|
|
this.findMsgNotFound.classList.remove('hidden'); |
|
|
|
|
|
|
|
} else { |
|
|
|
|
|
|
|
this.findField.classList.remove('notFound'); |
|
|
|
|
|
|
|
this.findMsgNotFound.classList.add('hidden'); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (wrapped) { |
|
|
|
|
|
|
|
this.findMsgWrap.classList.remove('hidden'); |
|
|
|
|
|
|
|
} else { |
|
|
|
|
|
|
|
this.findMsgWrap.classList.add('hidden'); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
}, |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
open: function() { |
|
|
|
|
|
|
|
if (this.opened) return; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
this.opened = true; |
|
|
|
|
|
|
|
this.toggleButton.classList.add('toggled'); |
|
|
|
|
|
|
|
this.bar.classList.remove('hidden'); |
|
|
|
|
|
|
|
this.findField.select(); |
|
|
|
|
|
|
|
this.findField.focus(); |
|
|
|
|
|
|
|
}, |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
close: function() { |
|
|
|
|
|
|
|
if (!this.opened) return; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
this.opened = false; |
|
|
|
|
|
|
|
this.toggleButton.classList.remove('toggled'); |
|
|
|
|
|
|
|
this.bar.classList.add('hidden'); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
PDFFindController.active = false; |
|
|
|
|
|
|
|
}, |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
toggle: function() { |
|
|
|
|
|
|
|
if (this.opened) { |
|
|
|
|
|
|
|
this.close(); |
|
|
|
|
|
|
|
} else { |
|
|
|
|
|
|
|
this.open(); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
var PDFView = { |
|
|
|
var PDFView = { |
|
|
|
pages: [], |
|
|
|
pages: [], |
|
|
|
thumbnails: [], |
|
|
|
thumbnails: [], |
|
|
@ -242,6 +581,9 @@ var PDFView = { |
|
|
|
this.watchScroll(thumbnailContainer, this.thumbnailViewScroll, |
|
|
|
this.watchScroll(thumbnailContainer, this.thumbnailViewScroll, |
|
|
|
this.renderHighestPriority.bind(this)); |
|
|
|
this.renderHighestPriority.bind(this)); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
PDFFindBar.initialize(); |
|
|
|
|
|
|
|
PDFFindController.initialize(); |
|
|
|
|
|
|
|
|
|
|
|
this.initialized = true; |
|
|
|
this.initialized = true; |
|
|
|
container.addEventListener('scroll', function() { |
|
|
|
container.addEventListener('scroll', function() { |
|
|
|
self.lastScroll = Date.now(); |
|
|
|
self.lastScroll = Date.now(); |
|
|
@ -736,6 +1078,9 @@ var PDFView = { |
|
|
|
thumbnails.push(thumbnailView); |
|
|
|
thumbnails.push(thumbnailView); |
|
|
|
var pageRef = page.ref; |
|
|
|
var pageRef = page.ref; |
|
|
|
pagesRefMap[pageRef.num + ' ' + pageRef.gen + ' R'] = i; |
|
|
|
pagesRefMap[pageRef.num + ' ' + pageRef.gen + ' R'] = i; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Trigger text extraction. TODO: Make this happen lasyliy if needed.
|
|
|
|
|
|
|
|
PDFFindController.extractText(); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
self.pagesRefMap = pagesRefMap; |
|
|
|
self.pagesRefMap = pagesRefMap; |
|
|
@ -1229,6 +1574,7 @@ var PageView = function pageView(container, pdfPage, id, scale, |
|
|
|
this.resume = null; |
|
|
|
this.resume = null; |
|
|
|
|
|
|
|
|
|
|
|
this.textContent = null; |
|
|
|
this.textContent = null; |
|
|
|
|
|
|
|
this.textLayer = null; |
|
|
|
|
|
|
|
|
|
|
|
var anchor = document.createElement('a'); |
|
|
|
var anchor = document.createElement('a'); |
|
|
|
anchor.name = '' + this.id; |
|
|
|
anchor.name = '' + this.id; |
|
|
@ -1475,7 +1821,8 @@ var PageView = function pageView(container, pdfPage, id, scale, |
|
|
|
textLayerDiv.className = 'textLayer'; |
|
|
|
textLayerDiv.className = 'textLayer'; |
|
|
|
div.appendChild(textLayerDiv); |
|
|
|
div.appendChild(textLayerDiv); |
|
|
|
} |
|
|
|
} |
|
|
|
var textLayer = textLayerDiv ? new TextLayerBuilder(textLayerDiv) : null; |
|
|
|
var textLayer = this.textLayer = |
|
|
|
|
|
|
|
textLayerDiv ? new TextLayerBuilder(textLayerDiv, this.id - 1) : null; |
|
|
|
|
|
|
|
|
|
|
|
var scale = this.scale, viewport = this.viewport; |
|
|
|
var scale = this.scale, viewport = this.viewport; |
|
|
|
canvas.width = viewport.width; |
|
|
|
canvas.width = viewport.width; |
|
|
@ -1834,21 +2181,25 @@ var CustomStyle = (function CustomStyleClosure() { |
|
|
|
return CustomStyle; |
|
|
|
return CustomStyle; |
|
|
|
})(); |
|
|
|
})(); |
|
|
|
|
|
|
|
|
|
|
|
var TextLayerBuilder = function textLayerBuilder(textLayerDiv) { |
|
|
|
var TextLayerBuilder = function textLayerBuilder(textLayerDiv, pageIdx) { |
|
|
|
var textLayerFrag = document.createDocumentFragment(); |
|
|
|
var textLayerFrag = document.createDocumentFragment(); |
|
|
|
|
|
|
|
|
|
|
|
this.textLayerDiv = textLayerDiv; |
|
|
|
this.textLayerDiv = textLayerDiv; |
|
|
|
this.layoutDone = false; |
|
|
|
this.layoutDone = false; |
|
|
|
this.divContentDone = false; |
|
|
|
this.divContentDone = false; |
|
|
|
|
|
|
|
this.pageIdx = pageIdx; |
|
|
|
|
|
|
|
this.matches = []; |
|
|
|
|
|
|
|
|
|
|
|
this.beginLayout = function textLayerBuilderBeginLayout() { |
|
|
|
this.beginLayout = function textLayerBuilderBeginLayout() { |
|
|
|
this.textDivs = []; |
|
|
|
this.textDivs = []; |
|
|
|
this.textLayerQueue = []; |
|
|
|
this.textLayerQueue = []; |
|
|
|
|
|
|
|
this.renderingDone = false; |
|
|
|
}; |
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
this.endLayout = function textLayerBuilderEndLayout() { |
|
|
|
this.endLayout = function textLayerBuilderEndLayout() { |
|
|
|
this.layoutDone = true; |
|
|
|
this.layoutDone = true; |
|
|
|
this.insertDivContent(); |
|
|
|
this.insertDivContent(); |
|
|
|
}, |
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
this.renderLayer = function textLayerBuilderRenderLayer() { |
|
|
|
this.renderLayer = function textLayerBuilderRenderLayer() { |
|
|
|
var self = this; |
|
|
|
var self = this; |
|
|
@ -1862,8 +2213,11 @@ var TextLayerBuilder = function textLayerBuilder(textLayerDiv) { |
|
|
|
if (textDivs.length > 100000) |
|
|
|
if (textDivs.length > 100000) |
|
|
|
return; |
|
|
|
return; |
|
|
|
|
|
|
|
|
|
|
|
while (textDivs.length > 0) { |
|
|
|
var i = textDivs.length; |
|
|
|
var textDiv = textDivs.shift(); |
|
|
|
|
|
|
|
|
|
|
|
while (i !== 0) { |
|
|
|
|
|
|
|
i--; |
|
|
|
|
|
|
|
var textDiv = textDivs[i]; |
|
|
|
textLayerFrag.appendChild(textDiv); |
|
|
|
textLayerFrag.appendChild(textDiv); |
|
|
|
|
|
|
|
|
|
|
|
ctx.font = textDiv.style.fontSize + ' ' + textDiv.style.fontFamily; |
|
|
|
ctx.font = textDiv.style.fontSize + ' ' + textDiv.style.fontFamily; |
|
|
@ -1875,9 +2229,14 @@ var TextLayerBuilder = function textLayerBuilder(textLayerDiv) { |
|
|
|
CustomStyle.setProp('transform' , textDiv, |
|
|
|
CustomStyle.setProp('transform' , textDiv, |
|
|
|
'scale(' + textScale + ', 1)'); |
|
|
|
'scale(' + textScale + ', 1)'); |
|
|
|
CustomStyle.setProp('transformOrigin' , textDiv, '0% 0%'); |
|
|
|
CustomStyle.setProp('transformOrigin' , textDiv, '0% 0%'); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
textLayerDiv.appendChild(textDiv); |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
this.renderingDone = true; |
|
|
|
|
|
|
|
this.updateMatches(); |
|
|
|
|
|
|
|
|
|
|
|
textLayerDiv.appendChild(textLayerFrag); |
|
|
|
textLayerDiv.appendChild(textLayerFrag); |
|
|
|
}; |
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
@ -1944,6 +2303,184 @@ var TextLayerBuilder = function textLayerBuilder(textLayerDiv) { |
|
|
|
this.textContent = textContent; |
|
|
|
this.textContent = textContent; |
|
|
|
this.insertDivContent(); |
|
|
|
this.insertDivContent(); |
|
|
|
}; |
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
this.convertMatches = function textLayerBuilderConvertMatches(matches) { |
|
|
|
|
|
|
|
var i = 0; |
|
|
|
|
|
|
|
var iIndex = 0; |
|
|
|
|
|
|
|
var bidiTexts = this.textContent.bidiTexts; |
|
|
|
|
|
|
|
var end = bidiTexts.length - 1; |
|
|
|
|
|
|
|
var queryLen = PDFFindController.state.query.length; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
var lastDivIdx = -1; |
|
|
|
|
|
|
|
var pos; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
var ret = []; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Loop over all the matches.
|
|
|
|
|
|
|
|
for (var m = 0; m < matches.length; m++) { |
|
|
|
|
|
|
|
var matchIdx = matches[m]; |
|
|
|
|
|
|
|
// # Calculate the begin position.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Loop over the divIdxs.
|
|
|
|
|
|
|
|
while (i !== end && matchIdx >= (iIndex + bidiTexts[i].str.length)) { |
|
|
|
|
|
|
|
iIndex += bidiTexts[i].str.length; |
|
|
|
|
|
|
|
i++; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// TODO: Do proper handling here if something goes wrong.
|
|
|
|
|
|
|
|
if (i == bidiTexts.length) { |
|
|
|
|
|
|
|
console.error('Could not find matching mapping'); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
var match = { |
|
|
|
|
|
|
|
begin: { |
|
|
|
|
|
|
|
divIdx: i, |
|
|
|
|
|
|
|
offset: matchIdx - iIndex |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// # Calculate the end position.
|
|
|
|
|
|
|
|
matchIdx += queryLen; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Somewhat same array as above, but use a > 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; |
|
|
|
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
this.renderMatches = function textLayerBuilder_renderMatches(matches) { |
|
|
|
|
|
|
|
var bidiTexts = this.textContent.bidiTexts; |
|
|
|
|
|
|
|
var textDivs = this.textDivs; |
|
|
|
|
|
|
|
var prevEnd = null; |
|
|
|
|
|
|
|
var isSelectedPage = this.pageIdx === PDFFindController.selected.pageIdx; |
|
|
|
|
|
|
|
var selectedMatchIdx = PDFFindController.selected.matchIdx; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
var infty = { |
|
|
|
|
|
|
|
divIdx: -1, |
|
|
|
|
|
|
|
offset: undefined |
|
|
|
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
function beginText(begin, className) { |
|
|
|
|
|
|
|
var divIdx = begin.divIdx; |
|
|
|
|
|
|
|
var div = textDivs[divIdx]; |
|
|
|
|
|
|
|
div.innerHTML = ''; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
var content = bidiTexts[divIdx].str.substring(0, begin.offset); |
|
|
|
|
|
|
|
var node = document.createTextNode(content); |
|
|
|
|
|
|
|
if (className) { |
|
|
|
|
|
|
|
var isSelected = isSelectedPage && |
|
|
|
|
|
|
|
divIdx === selectedMatchIdx; |
|
|
|
|
|
|
|
var span = document.createElement('span'); |
|
|
|
|
|
|
|
span.className = className + (isSelected ? ' selected' : ''); |
|
|
|
|
|
|
|
span.appendChild(node); |
|
|
|
|
|
|
|
div.appendChild(span); |
|
|
|
|
|
|
|
return; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
div.appendChild(node); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
function appendText(from, to, className) { |
|
|
|
|
|
|
|
var divIdx = from.divIdx; |
|
|
|
|
|
|
|
var div = textDivs[divIdx]; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
var content = bidiTexts[divIdx].str.substring(from.offset, to.offset); |
|
|
|
|
|
|
|
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); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
function highlightDiv(divIdx, className) { |
|
|
|
|
|
|
|
textDivs[divIdx].className = className; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
for (var i = 0; i < matches.length; i++) { |
|
|
|
|
|
|
|
var match = matches[i]; |
|
|
|
|
|
|
|
var begin = match.begin; |
|
|
|
|
|
|
|
var end = match.end; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
var isSelected = isSelectedPage && i === selectedMatchIdx; |
|
|
|
|
|
|
|
var highlightSuffix = (isSelected ? ' selected' : ''); |
|
|
|
|
|
|
|
if (isSelected) |
|
|
|
|
|
|
|
scrollIntoView(textDivs[begin.divIdx], {top: -50}); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 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) { |
|
|
|
|
|
|
|
appendText(prevEnd, infty); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
// clears the divs and set the content until the begin point.
|
|
|
|
|
|
|
|
beginText(begin); |
|
|
|
|
|
|
|
} else { |
|
|
|
|
|
|
|
appendText(prevEnd, begin); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (begin.divIdx === end.divIdx) { |
|
|
|
|
|
|
|
appendText(begin, end, 'highlight' + highlightSuffix); |
|
|
|
|
|
|
|
} else { |
|
|
|
|
|
|
|
appendText(begin, infty, 'highlight begin' + highlightSuffix); |
|
|
|
|
|
|
|
for (var n = begin.divIdx + 1; n < end.divIdx; n++) { |
|
|
|
|
|
|
|
highlightDiv(n, 'highlight middle' + highlightSuffix); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
beginText(end, 'highlight end' + highlightSuffix); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
prevEnd = end; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (prevEnd) { |
|
|
|
|
|
|
|
appendText(prevEnd, infty); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
this.updateMatches = function textLayerUpdateMatches() { |
|
|
|
|
|
|
|
// Only show matches, once all rendering is done.
|
|
|
|
|
|
|
|
if (!this.renderingDone) |
|
|
|
|
|
|
|
return; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Clear out all matches.
|
|
|
|
|
|
|
|
var matches = this.matches; |
|
|
|
|
|
|
|
var textDivs = this.textDivs; |
|
|
|
|
|
|
|
var bidiTexts = this.textContent.bidiTexts; |
|
|
|
|
|
|
|
var clearedUntilDivIdx = -1; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
for (var i = 0; i < matches.length; i++) { |
|
|
|
|
|
|
|
var match = matches[i]; |
|
|
|
|
|
|
|
var begin = Math.max(clearedUntilDivIdx, match.begin.divIdx); |
|
|
|
|
|
|
|
for (var n = begin; n <= match.end.divIdx; n++) { |
|
|
|
|
|
|
|
var div = bidiTexts[n].str; |
|
|
|
|
|
|
|
div.textContent = div.textContent; |
|
|
|
|
|
|
|
div.className = ''; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
clearedUntilDivIdx = match.end.divIdx + 1; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (!PDFFindController.active) |
|
|
|
|
|
|
|
return; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Convert the matches on the page controller into the match format used
|
|
|
|
|
|
|
|
// for the textLayer.
|
|
|
|
|
|
|
|
this.matches = matches = |
|
|
|
|
|
|
|
this.convertMatches(PDFFindController.pageMatches[this.pageIdx] || []); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
this.renderMatches(this.matches); |
|
|
|
|
|
|
|
}; |
|
|
|
}; |
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
document.addEventListener('DOMContentLoaded', function webViewerLoad(evt) { |
|
|
|
document.addEventListener('DOMContentLoaded', function webViewerLoad(evt) { |
|
|
@ -2059,16 +2596,6 @@ document.addEventListener('DOMContentLoaded', function webViewerLoad(evt) { |
|
|
|
PDFView.switchSidebarView('outline'); |
|
|
|
PDFView.switchSidebarView('outline'); |
|
|
|
}); |
|
|
|
}); |
|
|
|
|
|
|
|
|
|
|
|
document.getElementById('viewSearch').addEventListener('click', |
|
|
|
|
|
|
|
function() { |
|
|
|
|
|
|
|
PDFView.switchSidebarView('search'); |
|
|
|
|
|
|
|
}); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
document.getElementById('searchButton').addEventListener('click', |
|
|
|
|
|
|
|
function() { |
|
|
|
|
|
|
|
PDFView.search(); |
|
|
|
|
|
|
|
}); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
document.getElementById('previous').addEventListener('click', |
|
|
|
document.getElementById('previous').addEventListener('click', |
|
|
|
function() { |
|
|
|
function() { |
|
|
|
PDFView.page--; |
|
|
|
PDFView.page--; |
|
|
@ -2331,6 +2858,12 @@ window.addEventListener('keydown', function keydown(evt) { |
|
|
|
// control is selected or not.
|
|
|
|
// control is selected or not.
|
|
|
|
if (cmd == 1 || cmd == 8) { // either CTRL or META key.
|
|
|
|
if (cmd == 1 || cmd == 8) { // either CTRL or META key.
|
|
|
|
switch (evt.keyCode) { |
|
|
|
switch (evt.keyCode) { |
|
|
|
|
|
|
|
//#if !(FIREFOX || MOZCENTRAL)
|
|
|
|
|
|
|
|
case 70: |
|
|
|
|
|
|
|
PDFFindBar.toggle(); |
|
|
|
|
|
|
|
handled = true; |
|
|
|
|
|
|
|
break; |
|
|
|
|
|
|
|
//#endif
|
|
|
|
case 61: // FF/Mac '='
|
|
|
|
case 61: // FF/Mac '='
|
|
|
|
case 107: // FF '+' and '='
|
|
|
|
case 107: // FF '+' and '='
|
|
|
|
case 187: // Chrome '+'
|
|
|
|
case 187: // Chrome '+'
|
|
|
|