pdf.js實現多個不同詞的高亮顯示

這裏我是沒有進行壓縮過的版本進行修改,該版本目錄分析參考:https://blog.csdn.net/a973685825/article/details/81285288

目標:實現多個詞的高亮顯示

原理:通過對pdf.js本身提供的搜索功能(即ctrl+f)進行修改來達到多個詞的高亮顯示

注意:經過實踐證明,多個詞的高亮我所採用的這種方法只能在文檔加載的進行一次性高亮,由於我的方法是在搜索功能上進行修改的,故有很多限制,理論上可以自己寫一個擴展類來實現這個功能,但難度比較大,我是個小白寫不出來。

web目錄下是很多擴展功能的js文件,我們主要是修改裏面的js文件:

       view.js是入口文件,即整個pdf文檔加載的入口js文件

       viewer.html 即展示頁面

        app_option.js 即整個的參數配置文件

        app.js 即整個程序所有函數的執行處,類似於java中的main函數處

其他的js文件主要就是pdf.js所提供的一些擴展功能,如搜索功能,下載功能等等,而我需要修改的就是這些文件的其中一小部分

這裏我主要關注三個文件:app.js 、pdf_find_controller.js 

實現過程概述:pdf.js將文本層轉爲html後,這樣所有的文本就被div包裹起來了,這些div在也頁面中都是有序的。它首先將這些文本從div中提取出來進行整合成一個字符串,通過字符串匹配得到關鍵詞的位置,然後將這些位置放在一個數組中,然後通過text_layer_builder.js中的convertMatches函數,將該位置進行轉化成包含有div的字符串中的位置:比如:這個關鍵詞的首位置在第幾個div偏移多少等,這樣就可以根據這個轉化的位置進行添加span標籤和類名,來對關鍵詞背景進行高亮。

原本pdf.js只是計算了單個詞位置,而我可以通過循環計算多個不同詞的位置,存到數組裏,然後轉化,就可以對多個不同的詞進行高亮

注意:我這是壓縮版的pdf.js實際運行特別慢,所以你需要在壓縮版的view.js(即官網提供的demo)找到對應的函數進行修改。另外最好把把pdf.js提供的搜索功能屏蔽掉,通過display=none即可,因爲多個詞的高亮和該搜索功能,不能同時使用,除非你自己單獨寫一個擴展類

1。首先:在app.js中,創建一個函數如:

function wordHighLight(hightLightWords) { // 目前只能匹配一個,不能全部高亮
  let evt = {
    // source: PDFFindBar, // PDFFindBar的實例,不確定是幹嘛用的?
    type: '',  // 這裏默認應該是空的
    // 這裏能默認跳轉到query的位置,剛好能滿足要求
    query: hightLightWords, // 高亮的關鍵詞
    phraseSearch: false, // 支持整段文字匹配,如果時多個詞的匹配只能是false
    caseSensitive: false, // 默認爲false,搜索時忽略大小寫
    highlightAll: true, // 設爲true即關鍵詞全部高亮
    // findPrevious: true,
  };
  PDFViewerApplication.findController.executeCommand('find' + evt.type, {//搜索執行函數
    query: evt.query,
    phraseSearch: evt.phraseSearch,
    caseSensitive: evt.caseSensitive,
    highlightAll: evt.highlightAll,
    findPrevious: evt.findPrevious,
  });
}

2. wordHighLight函數的調用

找到:PDFFindBar類(即搜索功能類)實例化處,在下面調用wordHighLight

     this.findBar = new PDFFindBar(findBarConfig, this.l10n);// 實例化PDFFindBar
      // 這時我要是時點擊keydown就會觸發查找事件
      // 同樣的,我可以在這裏直接觸發查找函數
      let highLightWords = ['雲林街菜鳥物流園職工,非人大代表或政協委員,租住雄楚市雄楚區金港一號小區11棟504室', '李毅', '張昌', '聊城', '犯罪經歷', '犯罪嫌疑人張昌'];
       wordHighLight(highLightWords);

3.因爲executeComand函數是在pdf_find_controller.js中執行,

在pdf_find_controller.js中,找到executeCommand(cmd, state)——》_nextMatch()——》calculateMatch(pageIndex)函數,由於query以前是單個詞搜索(pdf.js提供的功能),這裏我需要對涉及到query的做一些循環處理如下(我只是做了一些循環處理,請根據具體代碼進行修改):

  _calculateMatch(pageIndex) {  // 計算結果都返回到了對象的屬性裏,所以不需要通過
    // return 來獲取想要的值
    // _normalize應該是規範化的意思
    let pageContent = this._normalize(this.pageContents[pageIndex]);
    let query_words = this.state.query;
    for (let i = 0; i < query_words.length; i++) {
      query_words[i] = this._normalize(query_words[i]);
      let caseSensitive = this.state.caseSensitive;

      if (!caseSensitive) { // 判斷是否對大小寫敏感
        // 如果不區分大小寫,就把頁面內容全部轉爲小寫
        // 這裏pagecont
        pageContent = pageContent.toLowerCase();
        query_words[i] = query_words[i].toLowerCase();
      }
    }
    // let query = this._normalize(this.state.query); // 這裏只傳只傳了一個詞,我需要傳多個詞
    let phraseSearch = this.state.phraseSearch;
    // let queryLen = query.length;
    // 查詢內容爲空,返回
    if (query_words.length === 0) {
      // Do nothing: the matches should be wiped out already.
      return;
    }
    // 不區分大小寫的話
    // 以上內容規範了當前頁和查詢內容的規範化
    // 以下爲真正的匹配的內容
    if (phraseSearch) {  // 若爲true則可匹配整段文字
      this._calculatePhraseMatch(query_words, pageIndex, pageContent); // 詞組匹配功能
    } else { // 若爲false則只能匹配單個的詞,特徵:單個的詞一般 兩邊有空格
      this._calculateWordMatch(query_words, pageIndex, pageContent); // 單詞匹配功能
    }
    // 將計算的匹配結果,用來更新匹配結果
    this._updatePage(pageIndex); // 清除以前的匹配結果,渲染最新的匹配結果
    if (this.resumePageIdx === pageIndex) { // 如果恢復的頁面索引等於當前索引
      this.resumePageIdx = null; // 將恢復頁面的索引設爲空
      this._nextPageMatch(); //
    }

    // Update the match count.
    // 更新匹配個數
    // this.pageMatches[pageIndex]裏記錄了第pageIndex頁匹配的關鍵詞的位置
    // 通過下面這樣就可以獲得匹配的個數了
    // 問題:我在搜索時,沒有全部高亮時,就可以顯示全部匹配的單詞個數
    // 那我爲什麼不能全部高亮呢
    if (this.pageMatches[pageIndex].length > 0) {
      this.matchCount += this.pageMatches[pageIndex].length;
      this._updateUIResultsCount();
    }
  }

然後切換到——》_calculateWordMatch()函數中做一些循環處理:

_calculateWordMatch(query, pageIndex, pageContent) { // 單詞匹配
    let matchesWithLength = [];
    // Divide the query into pieces and search for text in each piece.
    // 把搜索的詞分開,然後搜索分開的詞,最小單位爲字母
    // query.match 清除了搜索值的空格,返回了一個有着被搜索詞的數組
    // 例:hello world -->"hello","world"
    let test_query = query;

    for (let x = 0; x < test_query.length; x++) {
      // 只有在這裏纔可以給段落設置一個sign,後面我才能進行匹配,滾動到該段落位置
      let queryArray = test_query[x].match(/\S+/g); // '/S':任何一個非空白字符
      for (let i = 0, len = queryArray.length; i < len; i++) {
        // 若果是英文單詞,就是單個字母的循環
        let subquery = queryArray[i];
        let subqueryLen = subquery.length;
        let matchIdx = -subqueryLen; // 這裏設爲負數是什麼意思
        while (true) {
          // matchInd+subqueryLen  == 0:爲開始查找的位置
          // matchIdx 爲返回的匹配的的位置
          matchIdx = pageContent.indexOf(subquery, matchIdx + subqueryLen);
          if (matchIdx === -1) { // 說明沒有匹配到
            break;
          }
          // Other searches do not, so we store the length.
          matchesWithLength.push({ // 將搜索的索引存到 matchesWithLength裏
            match: matchIdx,
            matchLength: subqueryLen,
            skipped: false,
          });
        }
      }
    }

    // Prepare arrays for storing the matches.
    if (!this.pageMatchesLength) {
      this.pageMatchesLength = []; // 清空這個要存儲的數組
    }
    this.pageMatchesLength[pageIndex] = []; // 某一頁的匹配結果,存到每一頁上
    this.pageMatches[pageIndex] = [];

    // Sort `matchesWithLength`, remove intersecting terms and put the result
    // into the two arrays.
    // this.pageMatches[pageIndex] 存索引
    // this.pageMatchesLength[pageIndex] 存對應的長度
    // 還是和phrase一樣,pageMatches用來存索引
    this._prepareMatches(matchesWithLength, this.pageMatches[pageIndex],
      this.pageMatchesLength[pageIndex]);
  }

效果截圖展示:

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章