pdf_find_controller.js 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805
  1. /**
  2. * @licstart The following is the entire license notice for the
  3. * Javascript code in this page
  4. *
  5. * Copyright 2022 Mozilla Foundation
  6. *
  7. * Licensed under the Apache License, Version 2.0 (the "License");
  8. * you may not use this file except in compliance with the License.
  9. * You may obtain a copy of the License at
  10. *
  11. * http://www.apache.org/licenses/LICENSE-2.0
  12. *
  13. * Unless required by applicable law or agreed to in writing, software
  14. * distributed under the License is distributed on an "AS IS" BASIS,
  15. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  16. * See the License for the specific language governing permissions and
  17. * limitations under the License.
  18. *
  19. * @licend The above is the entire license notice for the
  20. * Javascript code in this page
  21. */
  22. "use strict";
  23. Object.defineProperty(exports, "__esModule", {
  24. value: true
  25. });
  26. exports.PDFFindController = exports.FindState = void 0;
  27. var _ui_utils = require("./ui_utils.js");
  28. var _pdf = require("../pdf");
  29. var _pdf_find_utils = require("./pdf_find_utils.js");
  30. const FindState = {
  31. FOUND: 0,
  32. NOT_FOUND: 1,
  33. WRAPPED: 2,
  34. PENDING: 3
  35. };
  36. exports.FindState = FindState;
  37. const FIND_TIMEOUT = 250;
  38. const MATCH_SCROLL_OFFSET_TOP = -50;
  39. const MATCH_SCROLL_OFFSET_LEFT = -400;
  40. const CHARACTERS_TO_NORMALIZE = {
  41. "\u2010": "-",
  42. "\u2018": "'",
  43. "\u2019": "'",
  44. "\u201A": "'",
  45. "\u201B": "'",
  46. "\u201C": '"',
  47. "\u201D": '"',
  48. "\u201E": '"',
  49. "\u201F": '"',
  50. "\u00BC": "1/4",
  51. "\u00BD": "1/2",
  52. "\u00BE": "3/4"
  53. };
  54. const DIACRITICS_EXCEPTION = new Set([0x3099, 0x309a, 0x094d, 0x09cd, 0x0a4d, 0x0acd, 0x0b4d, 0x0bcd, 0x0c4d, 0x0ccd, 0x0d3b, 0x0d3c, 0x0d4d, 0x0dca, 0x0e3a, 0x0eba, 0x0f84, 0x1039, 0x103a, 0x1714, 0x1734, 0x17d2, 0x1a60, 0x1b44, 0x1baa, 0x1bab, 0x1bf2, 0x1bf3, 0x2d7f, 0xa806, 0xa82c, 0xa8c4, 0xa953, 0xa9c0, 0xaaf6, 0xabed, 0x0c56, 0x0f71, 0x0f72, 0x0f7a, 0x0f7b, 0x0f7c, 0x0f7d, 0x0f80, 0x0f74]);
  55. const DIACRITICS_EXCEPTION_STR = [...DIACRITICS_EXCEPTION.values()].map(x => String.fromCharCode(x)).join("");
  56. const DIACRITICS_REG_EXP = /\p{M}+/gu;
  57. const SPECIAL_CHARS_REG_EXP = /([.*+?^${}()|[\]\\])|(\p{P})|(\s+)|(\p{M})|(\p{L})/gu;
  58. const NOT_DIACRITIC_FROM_END_REG_EXP = /([^\p{M}])\p{M}*$/u;
  59. const NOT_DIACRITIC_FROM_START_REG_EXP = /^\p{M}*([^\p{M}])/u;
  60. let normalizationRegex = null;
  61. function normalize(text) {
  62. if (!normalizationRegex) {
  63. const replace = Object.keys(CHARACTERS_TO_NORMALIZE).join("");
  64. normalizationRegex = new RegExp(`([${replace}])|(\\p{M}+(?:-\\n)?)|(\\S-\\n)|(\\n)`, "gum");
  65. }
  66. const rawDiacriticsPositions = [];
  67. let m;
  68. while ((m = DIACRITICS_REG_EXP.exec(text)) !== null) {
  69. rawDiacriticsPositions.push([m[0].length, m.index]);
  70. }
  71. let normalized = text.normalize("NFD");
  72. const positions = [[0, 0]];
  73. let k = 0;
  74. let shift = 0;
  75. let shiftOrigin = 0;
  76. let eol = 0;
  77. let hasDiacritics = false;
  78. normalized = normalized.replace(normalizationRegex, (match, p1, p2, p3, p4, i) => {
  79. i -= shiftOrigin;
  80. if (p1) {
  81. const replacement = CHARACTERS_TO_NORMALIZE[match];
  82. const jj = replacement.length;
  83. for (let j = 1; j < jj; j++) {
  84. positions.push([i - shift + j, shift - j]);
  85. }
  86. shift -= jj - 1;
  87. return replacement;
  88. }
  89. if (p2) {
  90. const hasTrailingDashEOL = p2.endsWith("\n");
  91. const len = hasTrailingDashEOL ? p2.length - 2 : p2.length;
  92. hasDiacritics = true;
  93. let jj = len;
  94. if (i + eol === rawDiacriticsPositions[k]?.[1]) {
  95. jj -= rawDiacriticsPositions[k][0];
  96. ++k;
  97. }
  98. for (let j = 1; j < jj + 1; j++) {
  99. positions.push([i - 1 - shift + j, shift - j]);
  100. }
  101. shift -= jj;
  102. shiftOrigin += jj;
  103. if (hasTrailingDashEOL) {
  104. i += len - 1;
  105. positions.push([i - shift + 1, 1 + shift]);
  106. shift += 1;
  107. shiftOrigin += 1;
  108. eol += 1;
  109. return p2.slice(0, len);
  110. }
  111. return p2;
  112. }
  113. if (p3) {
  114. positions.push([i - shift + 1, 1 + shift]);
  115. shift += 1;
  116. shiftOrigin += 1;
  117. eol += 1;
  118. return p3.charAt(0);
  119. }
  120. positions.push([i - shift + 1, shift - 1]);
  121. shift -= 1;
  122. shiftOrigin += 1;
  123. eol += 1;
  124. return " ";
  125. });
  126. positions.push([normalized.length, shift]);
  127. return [normalized, positions, hasDiacritics];
  128. }
  129. function getOriginalIndex(diffs, pos, len) {
  130. if (!diffs) {
  131. return [pos, len];
  132. }
  133. const start = pos;
  134. const end = pos + len;
  135. let i = (0, _ui_utils.binarySearchFirstItem)(diffs, x => x[0] >= start);
  136. if (diffs[i][0] > start) {
  137. --i;
  138. }
  139. let j = (0, _ui_utils.binarySearchFirstItem)(diffs, x => x[0] >= end, i);
  140. if (diffs[j][0] > end) {
  141. --j;
  142. }
  143. return [start + diffs[i][1], len + diffs[j][1] - diffs[i][1]];
  144. }
  145. class PDFFindController {
  146. constructor({
  147. linkService,
  148. eventBus
  149. }) {
  150. this._linkService = linkService;
  151. this._eventBus = eventBus;
  152. this._reset();
  153. eventBus._on("find", this._onFind.bind(this));
  154. eventBus._on("findbarclose", this._onFindBarClose.bind(this));
  155. this.executeCommand = (cmd, state) => {
  156. console.error("Deprecated method `PDFFindController.executeCommand` called, " + 'please dispatch a "find"-event using the EventBus instead.');
  157. const eventState = Object.assign(Object.create(null), state, {
  158. type: cmd.substring("find".length)
  159. });
  160. this._onFind(eventState);
  161. };
  162. }
  163. get highlightMatches() {
  164. return this._highlightMatches;
  165. }
  166. get pageMatches() {
  167. return this._pageMatches;
  168. }
  169. get pageMatchesLength() {
  170. return this._pageMatchesLength;
  171. }
  172. get selected() {
  173. return this._selected;
  174. }
  175. get state() {
  176. return this._state;
  177. }
  178. setDocument(pdfDocument) {
  179. if (this._pdfDocument) {
  180. this._reset();
  181. }
  182. if (!pdfDocument) {
  183. return;
  184. }
  185. this._pdfDocument = pdfDocument;
  186. this._firstPageCapability.resolve();
  187. }
  188. _onFind(state) {
  189. if (!state) {
  190. return;
  191. }
  192. const pdfDocument = this._pdfDocument;
  193. const {
  194. type
  195. } = state;
  196. if (this._state === null || this._shouldDirtyMatch(state)) {
  197. this._dirtyMatch = true;
  198. }
  199. this._state = state;
  200. if (type !== "highlightallchange") {
  201. this._updateUIState(FindState.PENDING);
  202. }
  203. this._firstPageCapability.promise.then(() => {
  204. if (!this._pdfDocument || pdfDocument && this._pdfDocument !== pdfDocument) {
  205. return;
  206. }
  207. this._extractText();
  208. const findbarClosed = !this._highlightMatches;
  209. const pendingTimeout = !!this._findTimeout;
  210. if (this._findTimeout) {
  211. clearTimeout(this._findTimeout);
  212. this._findTimeout = null;
  213. }
  214. if (!type) {
  215. this._findTimeout = setTimeout(() => {
  216. this._nextMatch();
  217. this._findTimeout = null;
  218. }, FIND_TIMEOUT);
  219. } else if (this._dirtyMatch) {
  220. this._nextMatch();
  221. } else if (type === "again") {
  222. this._nextMatch();
  223. if (findbarClosed && this._state.highlightAll) {
  224. this._updateAllPages();
  225. }
  226. } else if (type === "highlightallchange") {
  227. if (pendingTimeout) {
  228. this._nextMatch();
  229. } else {
  230. this._highlightMatches = true;
  231. }
  232. this._updateAllPages();
  233. } else {
  234. this._nextMatch();
  235. }
  236. });
  237. }
  238. scrollMatchIntoView({
  239. element = null,
  240. selectedLeft = 0,
  241. pageIndex = -1,
  242. matchIndex = -1
  243. }) {
  244. if (!this._scrollMatches || !element) {
  245. return;
  246. } else if (matchIndex === -1 || matchIndex !== this._selected.matchIdx) {
  247. return;
  248. } else if (pageIndex === -1 || pageIndex !== this._selected.pageIdx) {
  249. return;
  250. }
  251. this._scrollMatches = false;
  252. const spot = {
  253. top: MATCH_SCROLL_OFFSET_TOP,
  254. left: selectedLeft + MATCH_SCROLL_OFFSET_LEFT
  255. };
  256. (0, _ui_utils.scrollIntoView)(element, spot, true);
  257. }
  258. _reset() {
  259. this._highlightMatches = false;
  260. this._scrollMatches = false;
  261. this._pdfDocument = null;
  262. this._pageMatches = [];
  263. this._pageMatchesLength = [];
  264. this._state = null;
  265. this._selected = {
  266. pageIdx: -1,
  267. matchIdx: -1
  268. };
  269. this._offset = {
  270. pageIdx: null,
  271. matchIdx: null,
  272. wrapped: false
  273. };
  274. this._extractTextPromises = [];
  275. this._pageContents = [];
  276. this._pageDiffs = [];
  277. this._hasDiacritics = [];
  278. this._matchesCountTotal = 0;
  279. this._pagesToSearch = null;
  280. this._pendingFindMatches = new Set();
  281. this._resumePageIdx = null;
  282. this._dirtyMatch = false;
  283. clearTimeout(this._findTimeout);
  284. this._findTimeout = null;
  285. this._firstPageCapability = (0, _pdf.createPromiseCapability)();
  286. }
  287. get _query() {
  288. if (this._state.query !== this._rawQuery) {
  289. this._rawQuery = this._state.query;
  290. [this._normalizedQuery] = normalize(this._state.query);
  291. }
  292. return this._normalizedQuery;
  293. }
  294. _shouldDirtyMatch(state) {
  295. if (state.query !== this._state.query) {
  296. return true;
  297. }
  298. switch (state.type) {
  299. case "again":
  300. const pageNumber = this._selected.pageIdx + 1;
  301. const linkService = this._linkService;
  302. if (pageNumber >= 1 && pageNumber <= linkService.pagesCount && pageNumber !== linkService.page && !linkService.isPageVisible(pageNumber)) {
  303. return true;
  304. }
  305. return false;
  306. case "highlightallchange":
  307. return false;
  308. }
  309. return true;
  310. }
  311. _isEntireWord(content, startIdx, length) {
  312. let match = content.slice(0, startIdx).match(NOT_DIACRITIC_FROM_END_REG_EXP);
  313. if (match) {
  314. const first = content.charCodeAt(startIdx);
  315. const limit = match[1].charCodeAt(0);
  316. if ((0, _pdf_find_utils.getCharacterType)(first) === (0, _pdf_find_utils.getCharacterType)(limit)) {
  317. return false;
  318. }
  319. }
  320. match = content.slice(startIdx + length).match(NOT_DIACRITIC_FROM_START_REG_EXP);
  321. if (match) {
  322. const last = content.charCodeAt(startIdx + length - 1);
  323. const limit = match[1].charCodeAt(0);
  324. if ((0, _pdf_find_utils.getCharacterType)(last) === (0, _pdf_find_utils.getCharacterType)(limit)) {
  325. return false;
  326. }
  327. }
  328. return true;
  329. }
  330. _calculateRegExpMatch(query, entireWord, pageIndex, pageContent) {
  331. const matches = [],
  332. matchesLength = [];
  333. const diffs = this._pageDiffs[pageIndex];
  334. let match;
  335. while ((match = query.exec(pageContent)) !== null) {
  336. if (entireWord && !this._isEntireWord(pageContent, match.index, match[0].length)) {
  337. continue;
  338. }
  339. const [matchPos, matchLen] = getOriginalIndex(diffs, match.index, match[0].length);
  340. if (matchLen) {
  341. matches.push(matchPos);
  342. matchesLength.push(matchLen);
  343. }
  344. }
  345. this._pageMatches[pageIndex] = matches;
  346. this._pageMatchesLength[pageIndex] = matchesLength;
  347. }
  348. _convertToRegExpString(query, hasDiacritics) {
  349. const {
  350. matchDiacritics
  351. } = this._state;
  352. let isUnicode = false;
  353. query = query.replace(SPECIAL_CHARS_REG_EXP, (match, p1, p2, p3, p4, p5) => {
  354. if (p1) {
  355. return `[ ]*\\${p1}[ ]*`;
  356. }
  357. if (p2) {
  358. return `[ ]*${p2}[ ]*`;
  359. }
  360. if (p3) {
  361. return "[ ]+";
  362. }
  363. if (matchDiacritics) {
  364. return p4 || p5;
  365. }
  366. if (p4) {
  367. return DIACRITICS_EXCEPTION.has(p4.charCodeAt(0)) ? p4 : "";
  368. }
  369. if (hasDiacritics) {
  370. isUnicode = true;
  371. return `${p5}\\p{M}*`;
  372. }
  373. return p5;
  374. });
  375. const trailingSpaces = "[ ]*";
  376. if (query.endsWith(trailingSpaces)) {
  377. query = query.slice(0, query.length - trailingSpaces.length);
  378. }
  379. if (matchDiacritics) {
  380. if (hasDiacritics) {
  381. isUnicode = true;
  382. query = `${query}(?=[${DIACRITICS_EXCEPTION_STR}]|[^\\p{M}]|$)`;
  383. }
  384. }
  385. return [isUnicode, query];
  386. }
  387. _calculateMatch(pageIndex) {
  388. let query = this._query;
  389. if (query.length === 0) {
  390. return;
  391. }
  392. const {
  393. caseSensitive,
  394. entireWord,
  395. phraseSearch
  396. } = this._state;
  397. const pageContent = this._pageContents[pageIndex];
  398. const hasDiacritics = this._hasDiacritics[pageIndex];
  399. let isUnicode = false;
  400. if (phraseSearch) {
  401. [isUnicode, query] = this._convertToRegExpString(query, hasDiacritics);
  402. } else {
  403. const match = query.match(/\S+/g);
  404. if (match) {
  405. query = match.sort().reverse().map(q => {
  406. const [isUnicodePart, queryPart] = this._convertToRegExpString(q, hasDiacritics);
  407. isUnicode ||= isUnicodePart;
  408. return `(${queryPart})`;
  409. }).join("|");
  410. }
  411. }
  412. const flags = `g${isUnicode ? "u" : ""}${caseSensitive ? "" : "i"}`;
  413. query = new RegExp(query, flags);
  414. this._calculateRegExpMatch(query, entireWord, pageIndex, pageContent);
  415. if (this._state.highlightAll) {
  416. this._updatePage(pageIndex);
  417. }
  418. if (this._resumePageIdx === pageIndex) {
  419. this._resumePageIdx = null;
  420. this._nextPageMatch();
  421. }
  422. const pageMatchesCount = this._pageMatches[pageIndex].length;
  423. if (pageMatchesCount > 0) {
  424. this._matchesCountTotal += pageMatchesCount;
  425. this._updateUIResultsCount();
  426. }
  427. }
  428. _extractText() {
  429. if (this._extractTextPromises.length > 0) {
  430. return;
  431. }
  432. let promise = Promise.resolve();
  433. for (let i = 0, ii = this._linkService.pagesCount; i < ii; i++) {
  434. const extractTextCapability = (0, _pdf.createPromiseCapability)();
  435. this._extractTextPromises[i] = extractTextCapability.promise;
  436. promise = promise.then(() => {
  437. return this._pdfDocument.getPage(i + 1).then(pdfPage => {
  438. return pdfPage.getTextContent();
  439. }).then(textContent => {
  440. const textItems = textContent.items;
  441. const strBuf = [];
  442. for (let j = 0, jj = textItems.length; j < jj; j++) {
  443. strBuf.push(textItems[j].str);
  444. if (textItems[j].hasEOL) {
  445. strBuf.push("\n");
  446. }
  447. }
  448. [this._pageContents[i], this._pageDiffs[i], this._hasDiacritics[i]] = normalize(strBuf.join(""));
  449. extractTextCapability.resolve(i);
  450. }, reason => {
  451. console.error(`Unable to get text content for page ${i + 1}`, reason);
  452. this._pageContents[i] = "";
  453. this._pageDiffs[i] = null;
  454. this._hasDiacritics[i] = false;
  455. extractTextCapability.resolve(i);
  456. });
  457. });
  458. }
  459. }
  460. _updatePage(index) {
  461. if (this._scrollMatches && this._selected.pageIdx === index) {
  462. this._linkService.page = index + 1;
  463. }
  464. this._eventBus.dispatch("updatetextlayermatches", {
  465. source: this,
  466. pageIndex: index
  467. });
  468. }
  469. _updateAllPages() {
  470. this._eventBus.dispatch("updatetextlayermatches", {
  471. source: this,
  472. pageIndex: -1
  473. });
  474. }
  475. _nextMatch() {
  476. const previous = this._state.findPrevious;
  477. const currentPageIndex = this._linkService.page - 1;
  478. const numPages = this._linkService.pagesCount;
  479. this._highlightMatches = true;
  480. if (this._dirtyMatch) {
  481. this._dirtyMatch = false;
  482. this._selected.pageIdx = this._selected.matchIdx = -1;
  483. this._offset.pageIdx = currentPageIndex;
  484. this._offset.matchIdx = null;
  485. this._offset.wrapped = false;
  486. this._resumePageIdx = null;
  487. this._pageMatches.length = 0;
  488. this._pageMatchesLength.length = 0;
  489. this._matchesCountTotal = 0;
  490. this._updateAllPages();
  491. for (let i = 0; i < numPages; i++) {
  492. if (this._pendingFindMatches.has(i)) {
  493. continue;
  494. }
  495. this._pendingFindMatches.add(i);
  496. this._extractTextPromises[i].then(pageIdx => {
  497. this._pendingFindMatches.delete(pageIdx);
  498. this._calculateMatch(pageIdx);
  499. });
  500. }
  501. }
  502. if (this._query === "") {
  503. this._updateUIState(FindState.FOUND);
  504. return;
  505. }
  506. if (this._resumePageIdx) {
  507. return;
  508. }
  509. const offset = this._offset;
  510. this._pagesToSearch = numPages;
  511. if (offset.matchIdx !== null) {
  512. const numPageMatches = this._pageMatches[offset.pageIdx].length;
  513. if (!previous && offset.matchIdx + 1 < numPageMatches || previous && offset.matchIdx > 0) {
  514. offset.matchIdx = previous ? offset.matchIdx - 1 : offset.matchIdx + 1;
  515. this._updateMatch(true);
  516. return;
  517. }
  518. this._advanceOffsetPage(previous);
  519. }
  520. this._nextPageMatch();
  521. }
  522. _matchesReady(matches) {
  523. const offset = this._offset;
  524. const numMatches = matches.length;
  525. const previous = this._state.findPrevious;
  526. if (numMatches) {
  527. offset.matchIdx = previous ? numMatches - 1 : 0;
  528. this._updateMatch(true);
  529. return true;
  530. }
  531. this._advanceOffsetPage(previous);
  532. if (offset.wrapped) {
  533. offset.matchIdx = null;
  534. if (this._pagesToSearch < 0) {
  535. this._updateMatch(false);
  536. return true;
  537. }
  538. }
  539. return false;
  540. }
  541. _nextPageMatch() {
  542. if (this._resumePageIdx !== null) {
  543. console.error("There can only be one pending page.");
  544. }
  545. let matches = null;
  546. do {
  547. const pageIdx = this._offset.pageIdx;
  548. matches = this._pageMatches[pageIdx];
  549. if (!matches) {
  550. this._resumePageIdx = pageIdx;
  551. break;
  552. }
  553. } while (!this._matchesReady(matches));
  554. }
  555. _advanceOffsetPage(previous) {
  556. const offset = this._offset;
  557. const numPages = this._linkService.pagesCount;
  558. offset.pageIdx = previous ? offset.pageIdx - 1 : offset.pageIdx + 1;
  559. offset.matchIdx = null;
  560. this._pagesToSearch--;
  561. if (offset.pageIdx >= numPages || offset.pageIdx < 0) {
  562. offset.pageIdx = previous ? numPages - 1 : 0;
  563. offset.wrapped = true;
  564. }
  565. }
  566. _updateMatch(found = false) {
  567. let state = FindState.NOT_FOUND;
  568. const wrapped = this._offset.wrapped;
  569. this._offset.wrapped = false;
  570. if (found) {
  571. const previousPage = this._selected.pageIdx;
  572. this._selected.pageIdx = this._offset.pageIdx;
  573. this._selected.matchIdx = this._offset.matchIdx;
  574. state = wrapped ? FindState.WRAPPED : FindState.FOUND;
  575. if (previousPage !== -1 && previousPage !== this._selected.pageIdx) {
  576. this._updatePage(previousPage);
  577. }
  578. }
  579. this._updateUIState(state, this._state.findPrevious);
  580. if (this._selected.pageIdx !== -1) {
  581. this._scrollMatches = true;
  582. this._updatePage(this._selected.pageIdx);
  583. }
  584. }
  585. _onFindBarClose(evt) {
  586. const pdfDocument = this._pdfDocument;
  587. this._firstPageCapability.promise.then(() => {
  588. if (!this._pdfDocument || pdfDocument && this._pdfDocument !== pdfDocument) {
  589. return;
  590. }
  591. if (this._findTimeout) {
  592. clearTimeout(this._findTimeout);
  593. this._findTimeout = null;
  594. }
  595. if (this._resumePageIdx) {
  596. this._resumePageIdx = null;
  597. this._dirtyMatch = true;
  598. }
  599. this._updateUIState(FindState.FOUND);
  600. this._highlightMatches = false;
  601. this._updateAllPages();
  602. });
  603. }
  604. _requestMatchesCount() {
  605. const {
  606. pageIdx,
  607. matchIdx
  608. } = this._selected;
  609. let current = 0,
  610. total = this._matchesCountTotal;
  611. if (matchIdx !== -1) {
  612. for (let i = 0; i < pageIdx; i++) {
  613. current += this._pageMatches[i]?.length || 0;
  614. }
  615. current += matchIdx + 1;
  616. }
  617. if (current < 1 || current > total) {
  618. current = total = 0;
  619. }
  620. return {
  621. current,
  622. total
  623. };
  624. }
  625. _updateUIResultsCount() {
  626. this._eventBus.dispatch("updatefindmatchescount", {
  627. source: this,
  628. matchesCount: this._requestMatchesCount()
  629. });
  630. }
  631. _updateUIState(state, previous = false) {
  632. this._eventBus.dispatch("updatefindcontrolstate", {
  633. source: this,
  634. state,
  635. previous,
  636. matchesCount: this._requestMatchesCount(),
  637. rawQuery: this._state?.query ?? null
  638. });
  639. }
  640. }
  641. exports.PDFFindController = PDFFindController;