当前位置: 首页 > news >正文

vue实现一个pdf在线预览,pdf选择文本并提取复制文字触发弹窗效果

[TOC]在这里插入图片描述

一、文件预览

1、安装依赖包

这里安装了disjs-dist@2.16版本,安装过程中报错缺少worker-loader

npm i pdfjs-dist@2.16.105 worker-loader@3.0.8

2、模板部分

<template>
  <div id="pdf-view">
    <canvas v-for="page in pdfPages" :key="page" :id="pdfCanvas" />
    <div id="text-view"></div>
  </div>
</template>

3、js部分(核心)

核心代码如下:

  1. 利用 PDF.getDocument获取pdf基础数据
  2. 通过canvas将pdf渲染到canvas画布上
<script>
  import * as pdfjsViewer from "pdfjs-dist/web/pdf_viewer.js";
  import "pdfjs-dist/web/pdf_viewer.css";
  import * as PDF from "pdfjs-dist/webpack";

  export default {
    name: "",
    components: {},
    data() {
      return {
        pdfPages: 1,
        pdfPath: "http://localhost:8080/qfnext.pdf",
        // 总页数
        pdfPages: 1,
        // 页面缩放
        pdfScale: 1,
        pdfDoc: null,
      };
    },
    mounted() {
      this.loadFile(this.pdfPath);
    },
    methods: {
      loadFile(url) {
        PDF.getDocument({
          url,
          cMapUrl: "https://cdn.jsdelivr.net/npm/pdfjs-dist@2.16.105/cmaps/",
          cMapPacked: true,
        }).promise.then((pdf) => {
          this.pdfDoc = pdf;
          // 获取pdf文件总页数
          this.pdfPages = pdf.numPages;
          this.$nextTick(() => {
            this.renderPage(1); // 从第一页开始渲染
          });
        });
      },
      renderPage(num) {
        this.pdfDoc.getPage(num).then((page) => {
          const canvas = document.getElementById(`pdfCanvas`);
          const ctx = canvas.getContext("2d");
          const viewport = page.getViewport({ scale: this.pdfScale });
          canvas.width = viewport.width;
          canvas.height = viewport.height;
          const renderContext = {
            canvasContext: ctx,
            viewport,
          };
            page.render(renderContext);
        });
      },
    },
  };
</script>
可能出现的问题:

(1) 页面文字可选中,但文本不可见
通过测试发现,将 pdfjs-dist/web/pdf_viewer.css 路径下的 color 属性注释后可显示文本。

.textLayer span,
.textLayer br {
  /* color: transparent; */
  position: absolute;
  white-space: pre;
  cursor: text;
  transform-origin: 0% 0%;
}
pdf多页面处理
  1. 模板处理id作为唯一标识
 <canvas v-for="page in pdfPages" :key="page" :id="`page-${page}`" />
  1. 修改canvas渲染逻辑,主要通过递归的方式逐一渲染
 renderPage(num) {
        this.pdfDoc.getPage(num).then((page) => {
          const canvas = document.getElementById(`page-${num}`);
          const ctx = canvas.getContext("2d");
          const viewport = page.getViewport({ scale: this.pdfScale });
          canvas.width = viewport.width;
          canvas.height = viewport.height;
          const renderContext = {
            canvasContext: ctx,
            viewport,
          };
            page.render(renderContext);
             if (num < this.pdfPages) {
	            this.renderPage(num + 1);
	          }
        });
  },

二、文本选中与弹窗(核心代码)

   Promise.all([getTextContentPromise, renderPagePromise])
            .then(([textContent]) => {
              const textLayerDiv = document.createElement("div");
              // 注意:此处不要修改该元素的class名称,该元素的样式通过外部导入,名称是固定的
              textLayerDiv.setAttribute("class", "textLayer");
              // 设置容器样式
              textLayerDiv.setAttribute(
                "style",
                `
                  z-index: 1;
                  opacity: .2;
                  // background-color:#fff;
                  // transform: scale(1.1);
                  width: 100%,
                  height: 100%,
              `,
              );
              // 设置容器的位置和宽高
              textLayerDiv.style.left = canvas.offsetLeft + "px";
              textLayerDiv.style.top = canvas.offsetTop + "px";
              textLayerDiv.style.height = canvas.offsetHeight + "px";
              textLayerDiv.style.width = canvas.offsetWidth + "px";

              const textView = document.querySelector("#text-view");
              textView.appendChild(textLayerDiv);

              const textLayer = new TextLayerBuilder({
                // container: ,
                textLayerDiv: textLayerDiv,
                pageIndex: page.pageIndex,
                viewport: viewport,
                eventBus,
                // textDivs: []
              });

              textLayer.setTextContent(textContent);
              textLayer.render();
              // 当选择文本后鼠标取消点击时触发
              textLayerDiv.addEventListener("mouseup", () => {
                // // 隐藏文本层
                // textLayerDiv.style.display = 'none';
                // 是否选择了文本
                const isTextSelected =
                  window.getSelection().toString().trim() !== "";
                if (isTextSelected) {
                  //选择的文本内容
                  const selectedText = window.getSelection().toString();
                  console.log("Selected text:", selectedText);
                  if (selectedText) {
                    alert(selectedText);
                  }
                }
              });
            })
            .catch((error) => {
              console.error("Error rendering page:", error);
            });

三、完整代码如下

<template>
  <div id="pdf-view">
    <canvas v-for="page in pdfPages" :key="page" :id="`page-${page}`" />
    <div id="text-view"></div>
  </div>
</template>

<script>
  import * as pdfjsViewer from "pdfjs-dist/web/pdf_viewer.js";
  import "pdfjs-dist/web/pdf_viewer.css";
  import * as PDF from "pdfjs-dist/webpack";
  // import { getDocument } from 'pdfjs-dist/webpack';
  import { TextLayerBuilder } from "pdfjs-dist/web/pdf_viewer.js";
  const pdfjsWorker = import("pdfjs-dist/build/pdf.worker.entry");
  PDF.GlobalWorkerOptions.workerSrc = pdfjsWorker;
  const eventBus = new pdfjsViewer.EventBus();
  export default {
    name: "",
    components: {},
    data() {
      return {
        pdfPages: 1,
        pdfPath: "http://localhost:8080/qfnext.pdf",
        // 总页数
        pdfPages: 1,
        // 页面缩放
        pdfScale: 1,
        pdfDoc: null,
      };
    },
    mounted() {
      this.loadFile(this.pdfPath);
    },
    methods: {
      loadFile(url) {
        PDF.getDocument({
          url,
          cMapUrl: "https://cdn.jsdelivr.net/npm/pdfjs-dist@2.16.105/cmaps/",
          cMapPacked: true,
        }).promise.then((pdf) => {
          this.pdfDoc = pdf;
          // 获取pdf文件总页数
          this.pdfPages = pdf.numPages;
          this.$nextTick(() => {
            this.renderPage(1); // 从第一页开始渲染
          });
        });
      },
      renderPage(num) {
        this.pdfDoc.getPage(num).then((page) => {
          const canvas = document.getElementById(`page-${num}`);
          const ctx = canvas.getContext("2d");
          const viewport = page.getViewport({ scale: this.pdfScale });
          canvas.width = viewport.width;
          canvas.height = viewport.height;
          const renderContext = {
            canvasContext: ctx,
            viewport,
          };

          // 获取文本内容和渲染页面的 Promise
          const getTextContentPromise = page.getTextContent();
          const renderPagePromise = page.render(renderContext);
          if (num < this.pdfPages) {
            this.renderPage(num + 1);
          }
          Promise.all([getTextContentPromise, renderPagePromise])
            .then(([textContent]) => {
              const textLayerDiv = document.createElement("div");
              // 注意:此处不要修改该元素的class名称,该元素的样式通过外部导入,名称是固定的
              textLayerDiv.setAttribute("class", "textLayer");
              // 设置容器样式
              textLayerDiv.setAttribute(
                "style",
                `
                  z-index: 1;
                  opacity: .2;
                  // background-color:#fff;
                  // transform: scale(1.1);
                  width: 100%,
                  height: 100%,
              `,
              );
              // 设置容器的位置和宽高
              textLayerDiv.style.left = canvas.offsetLeft + "px";
              textLayerDiv.style.top = canvas.offsetTop + "px";
              textLayerDiv.style.height = canvas.offsetHeight + "px";
              textLayerDiv.style.width = canvas.offsetWidth + "px";

              const textView = document.querySelector("#text-view");
              textView.appendChild(textLayerDiv);

              const textLayer = new TextLayerBuilder({
                // container: ,
                textLayerDiv: textLayerDiv,
                pageIndex: page.pageIndex,
                viewport: viewport,
                eventBus,
                // textDivs: []
              });

              textLayer.setTextContent(textContent);
              textLayer.render();
              // 当选择文本后鼠标取消点击时触发
              textLayerDiv.addEventListener("mouseup", () => {
                // // 隐藏文本层
                // textLayerDiv.style.display = 'none';
                // 是否选择了文本
                const isTextSelected =
                  window.getSelection().toString().trim() !== "";
                if (isTextSelected) {
                  //选择的文本内容
                  const selectedText = window.getSelection().toString();
                  console.log("Selected text:", selectedText);
                  if (selectedText) {
                    alert(selectedText);
                  }
                }
              });
            })
            .catch((error) => {
              console.error("Error rendering page:", error);
            });
        });
      },
    },
  };
</script>
<style lang="scss" scoped>
  .pdf-con {
    border: 2px solid #ccc;
    width: 80%;
    margin: auto;
    height: 800px;
    overflow: auto;
    // display: none;
  }
</style>


相关文章:

  • 数据结构拓展:详解perror函数(C++)
  • 基于python实现的疫情数据可视化分析系统
  • 3. 前后端实现压缩包文件下载
  • 用R语言的XML库写一个采集图片的爬虫程序
  • 使用python进行数据分析需要安装的库
  • C++ list(双向链表)
  • [Python入门学习记录(小甲鱼)]第3章 Python基础知识
  • Python四大基本数据类型:List、Tuple、Set、Dictionary完全解析
  • 通过Docker搭个游戏——疯狂大陆(Pkland)
  • 爬虫逆向:脱壳工具Youpk的使用详解
  • 【开源免费】基于SpringBoot+Vue.JS旅游管理系统(JAVA毕业设计)
  • 树莓派3B+的初步使用
  • 数据库复习(第五版)- 第一章 绪论
  • 基于Celery+Supervisord的异步任务管理方案
  • HCIE云计算学什么?怎么学?未来职业发展如何?
  • 01 SQl注入基础步骤(数字、字符、布尔盲注、报错)
  • R2S的网络丢包率高问题小记
  • C0复习——课堂笔记<1>
  • 【C++】: STL详解 —— set和map类
  • 《MySQL三大核心日志解析:Undo Log/Redo Log/Bin Log对比与实践指南》
  • 网站菜单分类怎么做/不死鸟分享友情链接
  • 福州php做网站/如何在手机上开自己的网站
  • 网站建设 国家技术规范/优化建站
  • 网站服务器内部错误是怎么回事/短视频营销推广方式
  • 论坛网站怎么做/宁波seo网站服务
  • 如何做cpa单页网站/百度店铺怎么入驻