惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

Attack and Defense Labs
Attack and Defense Labs
T
Threatpost
C
Cybersecurity and Infrastructure Security Agency CISA
H
Hackread – Cybersecurity News, Data Breaches, AI and More
I
Intezer
C
Cyber Attacks, Cyber Crime and Cyber Security
The Register - Security
The Register - Security
量子位
Security Latest
Security Latest
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
大猫的无限游戏
大猫的无限游戏
小众软件
小众软件
Exploit-DB.com RSS Feed
Exploit-DB.com RSS Feed
C
CXSECURITY Database RSS Feed - CXSecurity.com
MyScale Blog
MyScale Blog
J
Java Code Geeks
Apple Machine Learning Research
Apple Machine Learning Research
Google DeepMind News
Google DeepMind News
WordPress大学
WordPress大学
Spread Privacy
Spread Privacy
Jina AI
Jina AI
博客园 - 【当耐特】
P
Palo Alto Networks Blog
Last Week in AI
Last Week in AI
SecWiki News
SecWiki News
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
cs.AI updates on arXiv.org
cs.AI updates on arXiv.org
G
GRAHAM CLULEY
宝玉的分享
宝玉的分享
Hacker News - Newest:
Hacker News - Newest: "LLM"
T
The Blog of Author Tim Ferriss
V
Vulnerabilities – Threatpost
有赞技术团队
有赞技术团队
T
Tor Project blog
H
Hacker News: Front Page
A
Arctic Wolf
NISL@THU
NISL@THU
A
About on SuperTechFans
云风的 BLOG
云风的 BLOG
Engineering at Meta
Engineering at Meta
V
V2EX
N
News and Events Feed by Topic
Webroot Blog
Webroot Blog
Know Your Adversary
Know Your Adversary
P
Privacy International News Feed
I
InfoQ
D
Docker
L
LINUX DO - 最新话题
K
KPMG report finds enterprise disconnect between AI and its ROI | CIO
U
Unit 42

博客园 - 卡车司机

macOS系统下修改hosts文件,安装homebrew & nvm How to see log files in MySQL? git 设置和取消代理 使用本地下载和管理的免费 Windows 10 虚拟机测试 IE11 和旧版 Microsoft Edge 在Microsoft SQL SERVER Management Studio下如何完整输出NVARCHAR(MAX)字段或变量的内容 windows 10 x64系统下在vmware workstation pro 15安装macOS 10.15 Catelina, 并设置分辨率为3840x2160 在Windows 10系统下将Git项目签出到磁盘分区根目录的方法 群晖NAS(Synology NAS)环境下安装GitLab, 并在Windows 10环境下使用Git windows 10 专业版安装VMware虚拟机碰到的坑 Java平台下利用aspose转word为PDF实现文档在线预览 Razor Page Library:开发独立通用RPL(内嵌wwwroot资源文件夹) 杂记 Code First Migrations in Team Environments SQL Server UPDATE JOIN visual studio 使用正则表达式实现代码批量查找和替换 AngularJs - Calling Directive Method from Controller Entity Framework Power Tools 执行数据库反向工程时报错.... SVN-无法查看log,提示Want to go offline,时间显示1970问题 windows server安装dotnet-sdk-2.2.108-win-x64.exe时报dll找不到
PDF.js实现个性化PDF渲染(文本复制)
卡车司机 · 2020-02-08 · via 博客园 - 卡车司机

原文链接: https://segmentfault.com/a/1190000016963084

前不久,产品经理提出要在界面上优雅地展示PDF文档,当即就有了两种实现方式:

实现方式一
使用embed标记来使用浏览器自带的pdf工具。

这种实现方式优缺点都很明显:
优点:自带“打印”,“搜索”,“翻页”等功能,强大且实现方便。
缺点:不同浏览器的pdf工具样式不一,且无法满足个性化需求,比如:禁止打印,下载等。

我们的产品经理是挑剔的😒,于是...

实现方式二
使用Mozilla的PDF.js,自定义展示PDF。

下面我们就细致讲述一下使用PDF.js过程中遇到的问题。主要包括:

  • 基础功能集成
  • 使用Text-Layers渲染

什么是PDF.JS

PDF.js是基于HTML5技术构建的,用于展示可移植文档格式的文件(PDF),它可以在现代浏览器中使用且无需安装任何第三方插件。

基础功能集成

1️⃣引用

首先,引用PDF.js就遇到了问题,官网中提到通过CDN引用或者下载源码至本地。
而我们并不想污染我们的index.html并且希望可以对每一个引用的框架有统一的版本管理。于是,我们搜寻到一个包:pdfjs-dist。

通过npm install pdfjs-dist,我们引入了PDF.js。

基础功能有两个必须引用的文件:

  • pdf.js
  • pdf.worker.js

如果使用CDN的方式,直接引用如下对应文件即可:

如果使用npm的方式,则在需要使用PDF.js的文件中如下引用:

import PDFJS from 'pdfjs-dist';

PDFJS.GlobalWorkerOptions.workerSrc = 'pdfjs-dist/build/pdf.worker.js';

这两个文件包含了获取、解析和展示PDF文档的方法,但是解析和渲染PDF需要较长的时间,可能会阻塞其它JS代码的运行。

为解决该问题,pdf.js依赖了HTML5引入的Web Workers——通过从主线程中移除大量CPU操作(如解析和渲染)来提升性能。

PDF.js的API都会返回一个Promise,使得我们可以优雅的处理异步操作。

2️⃣使用

首先,我们需要在HTML中添加<canvas>元素以渲染PDF:

<canvas id="pdf-canvas"></canvas>

然后添加渲染PDF的js代码:

var url = 'Helloworld.pdf';

PDFJS.getDocument(url).then((pdf) => {
    return pdf.getPage(1);
}).then((page) => {
    // 设置展示比例
    var scale = 1.5;
    // 获取pdf尺寸
    var viewport = page.getViewport(scale);
    // 获取需要渲染的元素
    var canvas = document.getElementById('pdf-canvas');
    var context = canvas.getContext('2d');
    canvas.height = viewport.height;
    canvas.width = viewport.width;
    
    var renderContext = {
        canvasContext: context,
        viewport: viewport
    };
    
    page.render(renderContext);
});

现在,PDF已经成功渲染在界面上了。我们来分析一下使用到的函数:

getDocument()用于异步获取PDf文档,发送多个Ajax请求以块的形式下载文档。它返回一个Promise,该Promise的成功回调传递一个对象,该对象包含PDF文档的信息,该回调中的代码将在完成PDf文档获取时执行。

getPage()用于获取PDF文档中的各个页面。

getViewport()针对提供的展示比例,返回PDf文档的页面尺寸。

render()渲染PDF。

到这里,基本功能告一段落了。
满心欢喜准备上线的时候,产品经理提出了另一个需求:文本复制。
然鹅。。。翻了好几遍官方文档,也没有找到文本复制的方法,并且stackoverflow上有很多类似的问题。
在不断的尝试下,我们发现了Text-Layer

使用Text-Layers渲染

PDF.js支持在使用Canvas渲染的PDF页面上渲染文本图层。然而,这个功能需要用到额外的两个文件:text_layer_builder.jstext_layer_builder.css。我们可以在GitHub的repo中获取到。

如果是使用npm,则需要做如下引用:

import { TextLayerBuilder } from 'pdfjs-dist/web/pdf_viewer';
import 'pdfjs-dist/web/pdf_viewer.css';

现在,我们开始实现文本复制功能。

首先,创建渲染需要用到DOM节点:

<div id="container"></div>

div#container为最外层节点,在该div中,我们会为PDF的每个页面创建自己的div,在每个页面的div中,都会有Canvas元素。

接着,我们修改JS代码:

var container, pageDiv;

function getPDF(url) {
    PDFJS.getDocument(url).then((pdf) => {
        pdfDoc = pdf;
        container = document.getElementById('container');
        for (var i = 1; i<= pdf.numPages; i++) {
            renderPDF(i);
        }
    })
}

function renderPDF(num) {
    pdf.getPage(num).then((page) => {
        var scale = 1.5;
        var viewport = page.getViewport(scale);
        pageDiv = document.createElement('div');
        pageDiv.setAttribute('id', 'page-' + (page.pageIndex + 1));
        pageDiv.setAttribute('style', 'position: relative');
        container.appendChild(pageDiv);
        var canvas = document.createElement('canvas');
        pageDiv.appendChild(canvas);
        var context = canvas.getContext('2d');
        canvas.height = viewport.height;
        canvas.width = view.width;
        
        var renderContext = {
            canvasContext: context,
            viewport: viewport
        };
        
        page.render(renderContext);
    });
}

以上代码只是实现了多页渲染,接下来,开始渲染文本图层。我们需要将page.render(renderContext)修改为以下代码:

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()用于渲染文本图层。

Bingo😎!通过以上改造,文本复制功能就实现了。官方文档上可没有这个小技巧哦。

PDF.js是一个很棒的工具,但无奈文档写的较为精简,需要开发人员不断探索PDF.js的强大功能。

如果这篇文章有帮助到您,记得点赞咯👍!