Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
153 changes: 153 additions & 0 deletions test/integration/reorganize_pages_spec.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@

import {
awaitPromise,
clearInput,
closePages,
createPromise,
dragAndDrop,
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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"]],
]);
})
);
});
});
});
36 changes: 31 additions & 5 deletions web/pdf_find_controller.js
Original file line number Diff line number Diff line change
Expand Up @@ -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 = {
Expand Down Expand Up @@ -422,6 +426,8 @@ class PDFFindController {

#visitedPagesCount = 0;

#pagesMapper = PagesMapper.instance;

/**
* @param {PDFFindControllerOptions} options
*/
Expand All @@ -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() {
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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") {
Expand Down Expand Up @@ -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
Expand All @@ -960,13 +976,15 @@ class PDFFindController {
this._eventBus.dispatch("updatetextlayermatches", {
source: this,
pageIndex: index,
pageId: this.getPageId(index),
});
}

#updateAllPages() {
this._eventBus.dispatch("updatetextlayermatches", {
source: this,
pageIndex: -1,
pageId: -1,
});
}

Expand Down Expand Up @@ -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);
});
Expand Down Expand Up @@ -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
Expand Down
4 changes: 4 additions & 0 deletions web/pdf_viewer.js
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
13 changes: 8 additions & 5 deletions web/text_highlighter.js
Original file line number Diff line number Diff line change
Expand Up @@ -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();
}
},
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -273,7 +274,7 @@ class TextHighlighter {
findController.scrollMatchIntoView({
element: textDivs[begin.divIdx],
selectedLeft,
pageIndex: pageIdx,
pageIndex: findController.getPageNumber(pageIdx),
matchIndex: selectedMatchIdx,
});
}
Expand Down Expand Up @@ -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);
Expand Down
Loading