vue3 プロジェクトは、pdf.js プラグインを使用して、検索の強調表示、pdf.js によって表示されるページ番号の変更、pdf.js への値の受け渡し、検索の制御、インターフェイス ファイル フローの処理を実装します。

1. pdf.js の概要

公式 Web サイトのアドレス: http://mozilla.github.io/pdf.js/
中国語ドキュメントのアドレス: https://gitcode.gitcode.host/docs-cn/pdf.js-docs-cn/print.html
ここに画像の説明を挿入します

PDF.js は HTML5 テクノロジに基づいて構築されており、Portable Document Format ファイル (PDF) を表示するために使用され、サードパーティのプラグインをインストールしなくても最新のブラウザで使用できます。

pdf.js には主に 2 つのライブラリ ファイルが含まれています

pdf.js: API 解析を担当します
pdf.worker.js: コア解析を担当します

2. PDF プレビューを実装する 2 つの方法

1.viewer.htmlを使用する

viewer.html は主に、outerContainer 層、printContainer 層 (この層は現在空です)、xl-chrome-ext-bar 層、fileInput ドメインの 3 つの層に分かれています。

  1. 公式 Web サイトから pdf.js パッケージをダウンロードします
    。 ダウンロード アドレス: https://mozilla.github.io/pdf.js/getting_started/#download

  2. pdf.js パッケージを導入します。pdf.js パッケージ
    は、http://xxxx:8080/static/pdfjs のようにサーバー上に置くことができます。
    また、pdf.js パッケージをパブリック フォルダーに直接解凍することもできます。

pdf.js パッケージのディレクトリ構造

│   ├── pdf.js                             - 显示层
│   ├── pdf.js.map                         - 显示层source map
│   ├── pdf.worker.js                      - 核心层
│   └── pdf.worker.js.map                  - 核心层source map
├── web/
│   ├── cmaps/                             - character maps (required by core)
│   ├── compressed.tracemonkey-pldi-09.pdf - PDF文件,用于测试目的
│   ├── debugger.js                        - 用于debug
│   ├── images/                            - 图标
│   ├── locale/                            - 本地化文件
│   ├── viewer.css                         - 样式
│   ├── viewer.html                        - 用于展示的html文件
│   ├── viewer.js                          - 展示层
│   └── viewer.js.map                      - 展示层source map
└── LICENSE
  1. iframeタグを使ってPDFを表示する

1) pdf.js パッケージと pdf ファイルの両方がサーバーにデプロイされている場合

<iframe :src="url" width="100%" height="100%" frameborder="0"></iframe>
pdfServerUrl = 'http://xxxx:8080/static/pdfjs/web/viewer.html'
pdfInfoUrl = 'http://xxxx:8080/XXX/getClausePdf?Code=1234' 
url = `${
      
      pdfServerUrl}?file=${
      
      encodeURIComponent(pdfInfoUrl)}`  // 调取接口返回文件流

2) pdf.js パッケージと pdf ファイルが両方ともローカルにある場合

<iframe :src="`/PDF.js/web/viewer.html?file=${pdf}` width="100%" height="100%" frameborder="0"></iframe>

<script>
	import pdf from '/images/file/11.pdf'
</script>

注記:

この方法により、PDF のプレビュー、全文検索、検索内容の強調表示、テキストのコピーなどの機能を実現できます。

2. PDF ファイルを Canvas にレンダリングする

インストール

pnpm i pdfjs-dist  // "pdfjs-dist": "^3.5.141"

vueページで

<template>
  <div id="pdf-container">
      <canvas v-for="page in state.pdfPages" :key="page" :id="`pdfCanvas${page}`" style="border-bottom:1px solid #d4d2d2" />
  </div>
</template>
import * as PDF from 'pdfjs-dist'
const pdfjsWorker = import('pdfjs-dist/build/pdf.worker.entry')
PDF.GlobalWorkerOptions.workerSrc = pdfjsWorker
import pdf from '/images/file/11.pdf'

const state = reactive<any>({
    
    
  pdfPath: pdf, // 本地PDF文件路径放在/public中
  pdfPages: '', // 页数
  pdfWidth: '', // 宽度
  pdfSrc: '', // 地址
  pdfScale: 1.0, // 放大倍数
})
let pdfDoc: any = null
onMounted(() => {
    
    
  loadFile(state.pdfPath)
})
function loadFile(url: string) {
    
    
  PDF.getDocument(url).promise.then((p: any) => {
    
    
    pdfDoc = p
    const {
    
     numPages } = p
    state.pdfPages = numPages
    nextTick(() => {
    
    
      renderPage(1) // 从第一页开始渲染
    })
  })
}
function renderPage(num: number) {
    
    
  pdfDoc.getPage(num).then((page: any) => {
    
    
    const canvas: any = document.getElementById(`pdfCanvas${
      
      num}`)
    const ctx = canvas.getContext('2d')
    const dpr = window.devicePixelRatio || 1
    const bsr
        = ctx.webkitBackingStorePixelRatio
        || ctx.mozBackingStorePixelRatio
        || ctx.msBackingStorePixelRatio
        || ctx.oBackingStorePixelRatio
        || ctx.backingStorePixelRatio
        || 1
    const ratio = dpr / bsr
    const viewport = page.getViewport({
    
     scale: state.pdfScale })
    canvas.width = viewport.width * ratio
    canvas.height = viewport.height * ratio
    canvas.style.width = '100%'
    canvas.style.height = '100%'
    state.pdfWidth = `${
      
      viewport.width}px`
    ctx.setTransform(ratio, 0, 0, ratio, 0, 0)
    // 将 PDF 页面渲染到 canvas 上下文中
    const renderContext = {
    
    
      canvasContext: ctx,
      viewport,
    }
    page.render(renderContext)
    if (state.pdfPages > num)
      renderPage(num + 1)
  })
}

注記:

このコードは PDF プレビュー機能のみを実装できます。テキストをコピーする場合は、レンダリングに Text-Layers を使用する必要があります。

使用される関数の解釈

getDocument():用于异步获取PDf文档,发送多个Ajax请求以块的形式下载文档。它返回一个Promise,该Promise的成功回调传递一个对象,该对象包含PDF文档的信息,该回调中的代码将在完成PDf文档获取时执行。
getPage():用于获取PDF文档中的各个页面。
getViewport():针对提供的展示比例,返回PDf文档的页面尺寸。
render():渲染PDF

テキストをコピーしたい場合は、page.render(renderContext) を次のコードに変更する必要があります。

// 要引入组件
import * as pdfjsViewer from 'pdfjs-dist/web/pdf_viewer.js'
import 'pdfjs-dist/web/pdf_viewer.css'
page.render(renderContext).then(() => {
    
    
    return page.getTextContent();
}).then((textContent) => {
    
    
    // 创建文本图层div
    const textLayerDiv = document.createElement('div');
    textLayerDiv.setAttribute('class', 'textLayer');
    // 将文本图层div添加至每页pdf的div中
    pageDiv.appendChild(textLayerDiv);
    // 创建新的TextLayerBuilder实例
    var textLayer = new TextLayerBuilder({
    
    
        textLayerDiv: textLayerDiv,
        pageIndex: page.pageIndex,
        viewport: viewport
    });  
    textLayer.setTextContent(textContent); 
    textLayer.render();
});

主要な機能の解釈:

page.render():该函数返回一个当PDF页面成功渲染到界面上时解析的promise,我们可以使用成功回调来渲染文本图层。
page.getTextContent():该函数的成功回调会返回PDF页面上的文本片段。
TextLayerBuilder:该类的实例有两个重要的方法。setTextContent()用于设置page.getTextContent()函数返回的文本片段;render()用于渲染文本图层。

3. viewer.js を使用する場合のいくつかの方法とテクニック

1. 特定のページにジャンプする外部操作を実装する

方法1) viewer.jsのソースコードを修正し、ページジャンプ用のパラメータページを追加する
<iframe :src="`/PDF.js/web/viewer.html?file=${pdf}&page=${pageNum}`` width="100%" height="100%" frameborder="0"></iframe>

<script>
	import pdf from '/images/file/11.pdf'
	const pageNum = 1
</script>

欠点: PDF はジャンプするたびに再ロードされ、対応する場所にジャンプします。

方法 2)、pdf.js のページ番号を変更する
const pdfFrame = document.getElementById('myIframe').contentWindow
pdfFrame.PDFViewerApplication.page = 10  // 传入需要让跳转的值

pdf.js の組み込み関数によれば、あまりジャンプせずに現在のページを直接変更できるため、上記の方法の欠点が解決されます。

2. pdf.jsでページ番号を取得する

pdf.js の組み込みページ属性は、動的な現在のページ番号をエクスポートします。
ここに画像の説明を挿入します

onMounted(() => {
    
    
  interval.value = setInterval(checkPdf, 300)
})
const checkPdf = () => {
    
    
  const pdfFrame = document.getElementById('myIframe').contentWindow
  currentPage.value = pdfFrame.PDFViewerApplication.page
}

注記:
ここに画像の説明を挿入します

3. pdf.js の組み込み postMessage 関数と findBar 関数に基づいて外部テキスト検索を実装します。

 <a-button @click="getTextHighLight">
    点击获取文本位置
</a-button>
const getTextHighLight= () => {
    
    
  const iframe = document.getElementById('myIframe')
  iframe.contentWindow.postMessage('经依法审查查明:', '*')
  iframe.contentWindow.addEventListener('message', (e) => {
    
    
    iframe.contentWindow.PDFViewerApplication.findBar.findField.value = e.data
    iframe.contentWindow.PDFViewerApplication.findBar.highlightAll.checked = true    iframe.contentWindow.PDFViewerApplication.findBar.dispatchEvent('highlightallchange')
  }, false)
iframe.contentWindow.PDFViewerApplication.pagesCount)
}

4. pdfjs-3.7.107-dist版では、pdf.jsにパラメータを渡す処理

①同じグローバル位置にpageRangeStart、pageRangeEnd、this.isPageRangeの3つのパラメータを追加し、クラスPDFFindControllerに追加し、
ここに画像の説明を挿入します
別の場所にあるpageRangeStartとpageRangeEndの値を取得します。

②.setInitialViewでは外部iframeからパラメータを取得します

  var c_url=window.location.href;
  if(c_url.indexOf("&")&&c_url.indexOf("=")){
    
    
    var c_urlArray={
    
    }
    var c_val=c_url.split('?')[1];
    var c_valArray=c_val.split('&');
    for(let i=0;i<c_valArray.length;i++){
    
    
      let c_key=c_valArray[i].split('=')[0];
      let c_value=c_valArray[i].split('=')[1];
      c_urlArray[c_key]=c_value;
    }
    if(c_urlArray['pageRangeStart']){
    
    
      this.pageRangeStart = Number(c_urlArray['pageRangeStart'])
      PDFViewerApplication.findController.pageRangeStart = this.pageRangeStart
    }
    if(c_urlArray['pageRangeEnd']){
    
    
      this.pageRangeEnd =  Number(c_urlArray['pageRangeEnd'])
      PDFViewerApplication.findController.pageRangeEnd = this.pageRangeEnd
    }     
    if(this.pageRangeStart!==0 && this.pageRangeEnd!==0){
    
                  
      PDFViewerApplication.findController.isPageRange = true
    }
  }

ページ番号インデックスに関するいくつかの値

_offset:{
    
    
    matchIdx: null
    pageIdx: 29
    wrapped: false
}
_resumePageIdx: null  ,基本都是null
_selected: {
    
    
    matchIdx: -1
    pageIdx: -1
}
_pageMatches : 页面所有匹配搜索的内容,是个数组
this._linkService:{
    
    
    page: 50
    pagesCount: 66
    rotation: 0
}
_linkService 的page控制页码显示 影响 _offset 
【currentPage.value = pdfFrame.PDFViewerApplication.page】

5. pdfjs-3.7.107-dist バージョンでは、convertToRegExpString メソッドを変更し、マッチング方法を変更します (スペースを含むドキュメントをマッチングできます)。

元のコード

 #convertToRegExpString(query, hasDiacritics) {
    
    
      const {
    
    
        matchDiacritics
      } = this.#state;
      let isUnicode = false;
      query = query.replaceAll(SPECIAL_CHARS_REG_EXP, (match, p1, p2, p3, p4, p5) => {
    
    
        if (p1) {
    
    
          return `[ ]*\\${
      
      p1}[ ]*`;
        }
        if (p2) {
    
    
          return `[ ]*${
      
      p2}[ ]*`;
        }
        if (p3) {
    
    
          return "[ ]+";
        }
        if (matchDiacritics) {
    
    
          return p4 || p5;
        }
        if (p4) {
    
    
          return DIACRITICS_EXCEPTION.has(p4.charCodeAt(0)) ? p4 : "";
        }
        if (hasDiacritics) {
    
    
          isUnicode = true;
          return `${
      
      p5}\\p{M}*`;
        }
        return p5;
      });
      const trailingSpaces = "[ ]*";
      if (query.endsWith(trailingSpaces)) {
    
    
        query = query.slice(0, query.length - trailingSpaces.length);
      }
      if (matchDiacritics) {
    
    
        if (hasDiacritics) {
    
    
          DIACRITICS_EXCEPTION_STR ||= String.fromCharCode(...DIACRITICS_EXCEPTION);
          isUnicode = true;
          query = `${
      
      query}(?=[${
      
      DIACRITICS_EXCEPTION_STR}]|[^\\p{M}]|$)`;
        }
      }
      return [isUnicode, query];
    }

変更後:

 #convertToRegExpStringWL(query, hasDiacritics) {
    
    
      const {
    
    
        matchDiacritics
      } = this.#state;
      let isUnicode = false;
  
      // const SPECIAL_CHARS_REG_EXP = /([.*+?^${}()|[\]\\])|(\p{P})|(\s+)|(\p{M})|(\p{L})/gu;
      const SPECIAL_CHARS_REG_EXP_wl = /([.*+?^${}()|[\]\\])|(\p{P})|(\s+)|(\p{M})|(\p{L})/gu;
      // 匹配文本中的特殊字符、标点符号、空格、语言符号和字母的。
      // [.*+?^${}()|[\]\\]匹配特殊字符  其中,.表示匹配任意字符,*表示匹配前面的字符0次或多次,+表示匹配前面的字符1次或多次,?表示匹配前面的字符0次或1次,^表示匹配字符串的开头,$表示匹配字符串的结尾,{和}表示匹配前面的字符指定的次数,()表示分组,|表示或,[和]表示字符集,\表示转义字符。 
      //其中,\p{P}表示标点符号,\s表示空格,\p{M}表示语言符号,\p{L}表示字母。
      query = query.replaceAll(SPECIAL_CHARS_REG_EXP_wl,(match, p1, p2, p3, p4, p5)=>{
    
    
        // return `${match}\\s*`
        if (p1) {
    
    
          return `[ ]*\\${
      
      p1}[ ]*`;
        }
        if (p2) {
    
    
          return `[ ]*${
      
      p2}[ ]*`;
        }
        if (p3) {
    
    
          return "[ ]+";
        }
        if (matchDiacritics) {
    
    
          return p4 || p5;
        }
        if (p4) {
    
    
          return DIACRITICS_EXCEPTION.has(p4.charCodeAt(0)) ? p4 : "";
        }
        if (hasDiacritics) {
    
    
          isUnicode = true;
          return `${
      
      p5}\\p{M}*`;
        }
        return `${
      
      match}\\s*`;
      })
      const trailingSpaces = "[ ]*";
      if (query.endsWith(trailingSpaces)) {
    
    
        query = query.slice(0, query.length - trailingSpaces.length);
      }
      if (matchDiacritics) {
    
    
        if (hasDiacritics) {
    
    
          DIACRITICS_EXCEPTION_STR ||= String.fromCharCode(...DIACRITICS_EXCEPTION);
          isUnicode = true;
          query = `${
      
      query}(?=[${
      
      DIACRITICS_EXCEPTION_STR}]|[^\\p{M}]|$)`;
        }
      }
  
      return [isUnicode, query];
    }

6. バックエンドがストリームを返す場合は、このメソッドを使用してストリームを転送します。

<iframe id="myIframe" width="100%" height="100%" :src="`${serverUrl}?file=${pdfUrl}`" frameborder="0" />
  pdfFile('44.pdf').then((res) => {
    
    
    pdfUrl.value = getObjectURL(res)
  })
/**
 * 流转成url
 */
 getObjectURL(file) {
    
    
    let url = null
    if (window.createObjectURL !== undefined) {
    
     // basic
      url = window.createObjectURL(file)
    } else if (window.webkitURL !== undefined) {
    
     // webkit or chrome
        try {
    
    
          url = window.webkitURL.createObjectURL(file)
        } catch (error) {
    
    
          console.log(error)
        }
    } else if (window.URL !== undefined) {
    
     // mozilla(firefox)
        try {
    
    
          url = window.URL.createObjectURL(file)
        } catch (error) {
    
    
          console.log(error)
        }
    }
      return url
    },

このメソッドは、インターフェイスから返されたストリームを、使用可能なローカル PDF ファイルのリンク アドレスに変換できます。

7. ダウンロード

fileDownload() {
    
    
     if (this.src) {
    
    
       var tempLink = document.createElement('a')
       tempLink.style.display = 'none'
       tempLink.href = this.PDF // 解析好的地址
       tempLink.setAttribute('download', this.fileName)
       if (typeof tempLink.download === 'undefined') {
    
    
         tempLink.setAttribute('target', '_blank')
       }
       document.body.appendChild(tempLink)
       tempLink.click()
       document.body.removeChild(tempLink)
       window.URL.revokeObjectURL(this.PDF)
     } else {
    
    
       this.$message.error('请选择需要导出的算法')
     }
   },

4. 原則

まず、ベース画像は PDF と同じ内容の Canvas です (以下で紹介する page.render メソッドで取得できます) ベース画像の上には textLayer があり、このレイヤはページを通じてフォントの位置とスタイルを取得します.getTextContent(). 次に、それを Canvas 上でカバーします。

//Add this piece of code to webViewerInitialized function in viewer.js
if ('search' in params) {
    
    
    searchPDF(params['search']);
}

//New function in viewer.js
function searchPDF(td_text) {
    
    
    PDFViewerApplication.findBar.open();
    PDFViewerApplication.findBar.findField.value = td_text;
    PDFViewerApplication.findBar.caseSensitive.checked = true;
    PDFViewerApplication.findBar.highlightAll.checked = true;
    PDFViewerApplication.findBar.findNextButton.click();
    PDFViewerApplication.findBar.close();
}

#extractText() メソッド:
このメソッドの機能は、PDF ファイルのすべてのテキストを抽出し、ページごとに PDFFindController._pageContents[] に保存することです。このメソッドによって抽出されたテキストは、以降の検索に使用されます。

#nextMatch() メソッド

_nextMatch() {
    
    
    const previous = this._state.findPrevious;      // findPrevious是点击寻找区的按钮发布的事件的参数,只有点击左右箭头时才有这个参数,分别是true和false
    const currentPageIndex = this._linkService.page - 1;  // 当前查找所在页码-1
    const numPages = this._linkService.pagesCount;  // pdf页数
    this._highlightMatches = true;

    if (this._dirtyMatch) {
    
       // this._dirtyMatch应该是不区分大小写查找    这下面设置的都是this._reset()方法中定义的变量
      this._dirtyMatch = false;
      this._selected.pageIdx = this._selected.matchIdx = -1;  // this._selected并无实际含义
      this._offset.pageIdx = currentPageIndex;  // this._offset并无实际含义
      this._offset.matchIdx = null;
      this._offset.wrapped = false;
      this._resumePageIdx = null;
      this._pageMatches.length = 0;
      this._pageMatchesLength.length = 0;
      this._matchesCountTotal = 0;

      this._updateAllPages(); // 发布名为updatetextlayermatches的事件,与_onUpdateTextLayerMatches绑定,其调用 _updateMatches()函数,位于11521行

      for (let i = 0; i < numPages; i++) {
    
      // _pendingFindMatches这个变量只在这个循环内被调用,别处没有调用
        if (this._pendingFindMatches[i] === true) {
    
    
          continue;
        }

        this._pendingFindMatches[i] = true;

        this._extractTextPromises[i].then(pageIdx => {
    
      // 相当于将padeIdx传入箭头右边花括号内的函数中 _extractTextPromises内的元素都是Promise类实例
          delete this._pendingFindMatches[pageIdx];     // 这里传入的参数pageIdx为_extractTextPromises[i]异步操作的结果

          this._calculateMatch(pageIdx);
        });
      }
    }

注: このリストは、実際の開発プロセス中に発生し、解決された問題を要約したものです。

5. pdf.js の過去のバージョンをダウンロードする

私が使用している pdf.js バージョン: pdfjs-3.7.107-dist 2.5.207 は、IE との互換性が高いため、主流のバージョンです。多くの記事で、このバージョンは IE pdfjs es5 バージョン
と完全に互換性があると記載されています。(pdfjs
-2.5.207-es5-dist) [es5 バージョンを必ず含めてください。主要なブラウザと完全に互換性を保つために、上記の使用方法に従ってください] バージョン 2.4.456 は、古いバージョンのブラウザを最初からサポートしていません
。 Google 76+ および Safari 13+ をサポートしています。ドキュメントを注意深く読む必要があります。
現在のプロジェクトの PDF バージョンは "pdfjs-dist": "2.13.216" で
、他の 2 つのプロジェクトの PDF バージョンは "pdfjs-dist" です。 ": "2.0 .943" の
バージョン "pdfjs-dist": "2.13.216" のみがハングします。クールです。バージョン "pdfjs-dist": "2.0.943" は 2018 年 10 月にリリースされました。このバージョン。

pdfjs-1.9.426 ダウンロードアドレス

Baidu ネットワーク ディスクの抽出: https://pan.baidu.com/s/1SRdUUF6osHVAqLUhtjediQ?pwd=w361
w361

pdfjs-3.7.107 ダウンロードアドレス

https://pan.baidu.com/s/1pSWh04jGJJ63eMnJD-BjSQ?pwd=w361
w361

ダウンロード方法

https://github.com/mozilla/pdf.js/releases/tag/v2.5.207
ここに画像の説明を挿入します

1.9、2.4、2.5、2.5-es は黒枠です-----すべての
バージョンは問題ありません

バージョン 2.6.347
バージョン 2.6.347-esを開けません
白い枠です
ここに画像の説明を挿入します

2.11.338のレガシー-----は白枠でどちらのブラウザでもOKですが、検察専用ブラウザのPDFファイルは文字化けします
2.12.313のレガシー-----は白枠です両方のブラウザでOKですが、検察専用ブラウザのPDFファイルが文字化けしています
レガシーバージョン2.13.216はどちらのブラウザでも開けません
レガシーバージョン2.14.305はどちらのブラウザでも開けません

2.16 のレガシー-----どちらのブラウザでも開けません
3.7.107 のレガシー-----両方のブラウザでも開けません
ここに画像の説明を挿入します
このようには開けません。
ここに画像の説明を挿入します

バージョン 2.8.335----PDF を開けません。
従来のバージョン 2.8.335 と互換性があります-----PDF が文字化けします
ここに画像の説明を挿入します

バージョン 2.7.570-es - 一部の PDF ファイルの表示が文字化けします。
ここに画像の説明を挿入します

おすすめ

転載: blog.csdn.net/wang13679201813/article/details/129798858