mirror of
https://github.com/mozilla/pdf.js.git
synced 2026-02-08 00:21:11 +01:00
Merge pull request #20577 from calixteman/update_search
The 'find in page' feature must correctly work after the pages have been reorganized (bug 2010814)
This commit is contained in:
commit
f04deeeddf
@ -15,6 +15,7 @@
|
||||
|
||||
import {
|
||||
awaitPromise,
|
||||
clearInput,
|
||||
closePages,
|
||||
createPromise,
|
||||
dragAndDrop,
|
||||
@ -56,6 +57,46 @@ function waitForPagesEdited(page) {
|
||||
});
|
||||
}
|
||||
|
||||
function getSearchResults(page) {
|
||||
return page.evaluate(() => {
|
||||
const pages = document.querySelectorAll(".page");
|
||||
const results = [];
|
||||
for (let i = 0; i < pages.length; i++) {
|
||||
const domPage = pages[i];
|
||||
const pageNumber = parseInt(domPage.getAttribute("data-page-number"), 10);
|
||||
const highlights = domPage.querySelectorAll("span.highlight");
|
||||
if (highlights.length === 0) {
|
||||
continue;
|
||||
}
|
||||
results.push([
|
||||
i + 1,
|
||||
pageNumber,
|
||||
Array.from(highlights).map(span => span.textContent),
|
||||
]);
|
||||
}
|
||||
return results;
|
||||
});
|
||||
}
|
||||
|
||||
function movePages(page, selectedPages, atIndex) {
|
||||
return page.evaluate(
|
||||
(selected, index) => {
|
||||
const viewer = window.PDFViewerApplication.pdfViewer;
|
||||
const pagesToMove = Array.from(selected).sort((a, b) => a - b);
|
||||
viewer.pagesMapper.pagesNumber =
|
||||
document.querySelectorAll(".page").length;
|
||||
viewer.pagesMapper.movePages(new Set(pagesToMove), pagesToMove, index);
|
||||
window.PDFViewerApplication.eventBus.dispatch("pagesedited", {
|
||||
pagesMapper: viewer.pagesMapper,
|
||||
index,
|
||||
pagesToMove,
|
||||
});
|
||||
},
|
||||
selectedPages,
|
||||
atIndex
|
||||
);
|
||||
}
|
||||
|
||||
describe("Reorganize Pages View", () => {
|
||||
describe("Drag & Drop", () => {
|
||||
let pages;
|
||||
@ -262,4 +303,116 @@ describe("Reorganize Pages View", () => {
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe("Search in pdf", () => {
|
||||
let pages;
|
||||
|
||||
beforeEach(async () => {
|
||||
pages = await loadAndWait(
|
||||
"page_with_number.pdf",
|
||||
"#viewsManagerToggleButton",
|
||||
"1",
|
||||
null,
|
||||
{ enableSplitMerge: true }
|
||||
);
|
||||
});
|
||||
|
||||
afterEach(async () => {
|
||||
await closePages(pages);
|
||||
});
|
||||
|
||||
it("should check if the search is working after moving pages", async () => {
|
||||
await Promise.all(
|
||||
pages.map(async ([browserName, page]) => {
|
||||
await page.click("#viewFindButton");
|
||||
await page.waitForSelector(":has(> #findHighlightAll)", {
|
||||
visible: true,
|
||||
});
|
||||
await page.click(":has(> #findHighlightAll)");
|
||||
|
||||
await page.waitForSelector("#findInput", { visible: true });
|
||||
await page.type("#findInput", "1");
|
||||
await page.keyboard.press("Enter");
|
||||
|
||||
await page.waitForFunction(
|
||||
() => document.querySelectorAll("span.highlight").length === 10
|
||||
);
|
||||
|
||||
let results = await getSearchResults(page);
|
||||
expect(results)
|
||||
.withContext(`In ${browserName}`)
|
||||
.toEqual([
|
||||
// Page number, Id, [matches]
|
||||
[1, 1, ["1"]],
|
||||
[10, 10, ["1"]],
|
||||
[11, 11, ["1", "1"]],
|
||||
[12, 12, ["1"]],
|
||||
[13, 13, ["1"]],
|
||||
[14, 14, ["1"]],
|
||||
[15, 15, ["1"]],
|
||||
[16, 16, ["1"]],
|
||||
[17, 17, ["1"]],
|
||||
]);
|
||||
|
||||
await movePages(page, [11, 2], 3);
|
||||
await page.waitForFunction(
|
||||
() => document.querySelectorAll("span.highlight").length === 0
|
||||
);
|
||||
|
||||
await clearInput(page, "#findInput", true);
|
||||
await page.type("#findInput", "1");
|
||||
await page.keyboard.press("Enter");
|
||||
|
||||
await page.waitForFunction(
|
||||
() => document.querySelectorAll("span.highlight").length === 10
|
||||
);
|
||||
|
||||
results = await getSearchResults(page);
|
||||
expect(results)
|
||||
.withContext(`In ${browserName}`)
|
||||
.toEqual([
|
||||
// Page number, Id, [matches]
|
||||
[1, 1, ["1"]],
|
||||
[4, 11, ["1", "1"]],
|
||||
[11, 10, ["1"]],
|
||||
[12, 12, ["1"]],
|
||||
[13, 13, ["1"]],
|
||||
[14, 14, ["1"]],
|
||||
[15, 15, ["1"]],
|
||||
[16, 16, ["1"]],
|
||||
[17, 17, ["1"]],
|
||||
]);
|
||||
|
||||
await movePages(page, [13], 0);
|
||||
await page.waitForFunction(
|
||||
() => document.querySelectorAll("span.highlight").length === 0
|
||||
);
|
||||
|
||||
await clearInput(page, "#findInput", true);
|
||||
await page.type("#findInput", "1");
|
||||
await page.keyboard.press("Enter");
|
||||
|
||||
await page.waitForFunction(
|
||||
() => document.querySelectorAll("span.highlight").length === 10
|
||||
);
|
||||
|
||||
results = await getSearchResults(page);
|
||||
expect(results)
|
||||
.withContext(`In ${browserName}`)
|
||||
.toEqual([
|
||||
// Page number, Id, [matches]
|
||||
[1, 13, ["1"]],
|
||||
[2, 1, ["1"]],
|
||||
[5, 11, ["1", "1"]],
|
||||
[12, 10, ["1"]],
|
||||
[13, 12, ["1"]],
|
||||
[14, 14, ["1"]],
|
||||
[15, 15, ["1"]],
|
||||
[16, 16, ["1"]],
|
||||
[17, 17, ["1"]],
|
||||
]);
|
||||
})
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@ -17,7 +17,11 @@
|
||||
/** @typedef {import("./event_utils").EventBus} EventBus */
|
||||
/** @typedef {import("./interfaces").IPDFLinkService} IPDFLinkService */
|
||||
|
||||
import { binarySearchFirstItem, scrollIntoView } from "./ui_utils.js";
|
||||
import {
|
||||
binarySearchFirstItem,
|
||||
PagesMapper,
|
||||
scrollIntoView,
|
||||
} from "./ui_utils.js";
|
||||
import { getCharacterType, getNormalizeWithNFKC } from "./pdf_find_utils.js";
|
||||
|
||||
const FindState = {
|
||||
@ -422,6 +426,8 @@ class PDFFindController {
|
||||
|
||||
#visitedPagesCount = 0;
|
||||
|
||||
#pagesMapper = PagesMapper.instance;
|
||||
|
||||
/**
|
||||
* @param {PDFFindControllerOptions} options
|
||||
*/
|
||||
@ -439,6 +445,7 @@ class PDFFindController {
|
||||
this.#reset();
|
||||
eventBus._on("find", this.#onFind.bind(this));
|
||||
eventBus._on("findbarclose", this.#onFindBarClose.bind(this));
|
||||
eventBus._on("pagesedited", this.#onPagesEdited.bind(this));
|
||||
}
|
||||
|
||||
get highlightMatches() {
|
||||
@ -794,12 +801,13 @@ class PDFFindController {
|
||||
if (query.length === 0) {
|
||||
return; // Do nothing: the matches should be wiped out already.
|
||||
}
|
||||
const pageContent = this._pageContents[pageIndex];
|
||||
const pageId = this.getPageId(pageIndex);
|
||||
const pageContent = this._pageContents[pageId];
|
||||
const matcherResult = this.match(query, pageContent, pageIndex);
|
||||
|
||||
const matches = (this._pageMatches[pageIndex] = []);
|
||||
const matchesLength = (this._pageMatchesLength[pageIndex] = []);
|
||||
const diffs = this._pageDiffs[pageIndex];
|
||||
const diffs = this._pageDiffs[pageId];
|
||||
|
||||
matcherResult?.forEach(({ index, length }) => {
|
||||
const [matchPos, matchLen] = getOriginalIndex(diffs, index, length);
|
||||
@ -848,7 +856,7 @@ class PDFFindController {
|
||||
* page.
|
||||
*/
|
||||
match(query, pageContent, pageIndex) {
|
||||
const hasDiacritics = this._hasDiacritics[pageIndex];
|
||||
const hasDiacritics = this._hasDiacritics[this.getPageId(pageIndex)];
|
||||
|
||||
let isUnicode = false;
|
||||
if (typeof query === "string") {
|
||||
@ -949,6 +957,14 @@ class PDFFindController {
|
||||
}
|
||||
}
|
||||
|
||||
getPageNumber(idx) {
|
||||
return this.#pagesMapper.getPageNumber(idx + 1) - 1;
|
||||
}
|
||||
|
||||
getPageId(pageNumber) {
|
||||
return this.#pagesMapper.getPageId(pageNumber + 1) - 1;
|
||||
}
|
||||
|
||||
#updatePage(index) {
|
||||
if (this._scrollMatches && this._selected.pageIdx === index) {
|
||||
// If the page is selected, scroll the page into view, which triggers
|
||||
@ -960,6 +976,7 @@ class PDFFindController {
|
||||
this._eventBus.dispatch("updatetextlayermatches", {
|
||||
source: this,
|
||||
pageIndex: index,
|
||||
pageId: this.getPageId(index),
|
||||
});
|
||||
}
|
||||
|
||||
@ -967,6 +984,7 @@ class PDFFindController {
|
||||
this._eventBus.dispatch("updatetextlayermatches", {
|
||||
source: this,
|
||||
pageIndex: -1,
|
||||
pageId: -1,
|
||||
});
|
||||
}
|
||||
|
||||
@ -998,7 +1016,7 @@ class PDFFindController {
|
||||
continue;
|
||||
}
|
||||
this._pendingFindMatches.add(i);
|
||||
this._extractTextPromises[i].then(() => {
|
||||
this._extractTextPromises[this.getPageId(i)].then(() => {
|
||||
this._pendingFindMatches.delete(i);
|
||||
this.#calculateMatch(i);
|
||||
});
|
||||
@ -1126,6 +1144,14 @@ class PDFFindController {
|
||||
}
|
||||
}
|
||||
|
||||
#onPagesEdited() {
|
||||
if (this._extractTextPromises.length === 0) {
|
||||
return;
|
||||
}
|
||||
this.#onFindBarClose();
|
||||
this._dirtyMatch = true;
|
||||
}
|
||||
|
||||
#onFindBarClose(evt) {
|
||||
const pdfDocument = this._pdfDocument;
|
||||
// Since searching is asynchronous, ensure that the removal of highlighted
|
||||
|
||||
@ -304,6 +304,10 @@ class PDFViewer {
|
||||
`The API version "${version}" does not match the Viewer version "${viewerVersion}".`
|
||||
);
|
||||
}
|
||||
if (typeof PDFJSDev === "undefined" || PDFJSDev.test("TESTING")) {
|
||||
this.pagesMapper = PagesMapper.instance;
|
||||
}
|
||||
|
||||
this.container = options.container;
|
||||
this.viewer = options.viewer || options.container.firstElementChild;
|
||||
this.#viewerAlert = options.viewerAlert || null;
|
||||
|
||||
@ -77,7 +77,7 @@ class TextHighlighter {
|
||||
this.eventBus._on(
|
||||
"updatetextlayermatches",
|
||||
evt => {
|
||||
if (evt.pageIndex === this.pageIdx || evt.pageIndex === -1) {
|
||||
if (evt.pageId === this.pageIdx || evt.pageId === -1) {
|
||||
this._updateMatches();
|
||||
}
|
||||
},
|
||||
@ -159,7 +159,8 @@ class TextHighlighter {
|
||||
const { findController, pageIdx } = this;
|
||||
const { textContentItemsStr, textDivs } = this;
|
||||
|
||||
const isSelectedPage = pageIdx === findController.selected.pageIdx;
|
||||
const isSelectedPage =
|
||||
findController.getPageNumber(pageIdx) === findController.selected.pageIdx;
|
||||
const selectedMatchIdx = findController.selected.matchIdx;
|
||||
const highlightAll = findController.state.highlightAll;
|
||||
let prevEnd = null;
|
||||
@ -273,7 +274,7 @@ class TextHighlighter {
|
||||
findController.scrollMatchIntoView({
|
||||
element: textDivs[begin.divIdx],
|
||||
selectedLeft,
|
||||
pageIndex: pageIdx,
|
||||
pageIndex: findController.getPageNumber(pageIdx),
|
||||
matchIndex: selectedMatchIdx,
|
||||
});
|
||||
}
|
||||
@ -308,8 +309,10 @@ class TextHighlighter {
|
||||
}
|
||||
// Convert the matches on the `findController` into the match format
|
||||
// used for the textLayer.
|
||||
const pageMatches = findController.pageMatches[pageIdx] || null;
|
||||
const pageMatchesLength = findController.pageMatchesLength[pageIdx] || null;
|
||||
const pageNumber = findController.getPageNumber(pageIdx);
|
||||
const pageMatches = findController.pageMatches[pageNumber] || null;
|
||||
const pageMatchesLength =
|
||||
findController.pageMatchesLength[pageNumber] || null;
|
||||
|
||||
this.matches = this._convertMatches(pageMatches, pageMatchesLength);
|
||||
this._renderMatches(this.matches);
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user