前端有时会在页面展示pdf文件,falsh也可以展示pdf文件,但是效果不好,不可控制。pdf.js可以很好的实现该功能,可以接受后台二进制的pdf流文件或者后台提供的有URL地址的PDF文件。下图是我展示的效果:
上图pdf展示有下面几个功能可以控制:
1.pdf.js 可以从后台获取二进制pdf流文件展示,或者常规拥有URL的pdf文件
2.可控用户是否有权限下载,打印。
3.可控用户是否有权限查看pdf全部内容(一般用户只能查看前几页内容)
4.可控在pdf文档附上水印。
5.用户可保存阅读进度,下次可从保存进度出浏览。(也就是默认显示指定页面的功能)
1.上面案例是有pdf官网提供的demo修改而成的,首先要去官网下载demo https://mozilla.github.io/pdf.js/
1.从后台获取流文件进行内容展示,(注意:该功能只能在pdf页面没渲染前发送ajax获取pdf流文件数据,然后进行渲染.目前我还没想到好的办法,在A页面获取二进制数据流文件,去其他页面进行渲染)
首先官网下载后
注释掉build\generic\web\viewer.js 中的
var DEFAULT_URL = 'compressed.tracemonkey-pldi-09.pdf'; 这个变量
然后在引入build\generic\web\viewer.html页面中引入下面代码(注意:要在引入viewer.js前引入)
<script type="text/javascript">
$.ajax({
type:"post",
async:false,
mimeType: 'text/plain; charset=utf-8',
url:后台地址URL,
success:function(data){
PDFData = data;
},
error:function () {
alert('加载失败')
}
});
var rawLength = PDFData.length;
//转换成pdf.js能直接解析的Uint8Array类型,见pdf.js-4068
var array = new Uint8Array(new ArrayBuffer(rawLength));
for(i = 0; i < rawLength; i++) {
array[i] = PDFData.charCodeAt(i) & 0xff;
}
DEFAULT_URL = array
</script>
这样就可以实现二进制数据流展示pdf文件了,如果是常规的URL地址文件,则直接把PDFdata 赋值给 DEFAULT_URL即可。
2.可控制用户下载 打印功能。
该功能我是控制下载 和打印按钮的显示和隐藏,实际逻辑没有改变,只是改变了视图。
js代码
if (ControlBtn) {
$('#print').css('display','none')
$('#download').css('display','none')
} else {
$('#baifen').css('right','216px')
$('#print').css('display','inline-block')
$('#download').css('display','inline-block')
}
html代码:
<div id="baifen">
<span>阅读进度:<i id="baifenpro">0%</i></span>
<span title="保存阅读进度" class="keepbtn"></span>
</div>
<div id="toolbarViewerRight">
<button id="presentationMode" class="toolbarButton presentationMode hiddenLargeView" title="Switch to Presentation Mode" tabindex="31" data-l10n-id="presentation_mode">
<span data-l10n-id="presentation_mode_label">Presentation Mode</span>
</button>
<button id="openFile" class="toolbarButton openFile hiddenLargeView" title="Open File" tabindex="32" data-l10n-id="open_file">
<span data-l10n-id="open_file_label">Open</span>
</button>
<button id="print" class="toolbarButton print hiddenMediumView" title="Print" tabindex="33" data-l10n-id="print">
<span data-l10n-id="print_label">Print</span>
</button>
<button id="download" class="toolbarButton download hiddenMediumView" title="Download" tabindex="34" data-l10n-id="download">
<span data-l10n-id="download_label">Download</span>
</button>
<a href="#" id="viewBookmark" class="toolbarButton bookmark hiddenSmallView" title="Current view (copy or open in new window)" tabindex="35" data-l10n-id="bookmark">
<span data-l10n-id="bookmark_label">Current View</span>
</a>
<div class="verticalToolbarSeparator hiddenSmallView"></div>
<button id="secondaryToolbarToggle" class="toolbarButton" title="Tools" tabindex="36" data-l10n-id="tools">
<span data-l10n-id="tools_label">Tools</span>
</button>
</div>
css代码
@media screen and (max-width:1024px) {
#baifen {
display:none;
}
}
#baifen{
position:relative;
float: right;
right: 150px;
line-height:32px;
text-align: center;
font-size:12px;
color:#ddd;
}
.cover{
position:absolute;
top:0;
overflow:hidden;
}
#baifenpro{
font-style: normal;
}
#baifen .keepbtn{
display:inline-block;
width:16px;
height:14px;
padding:2px 2px 0px 3px;
background:#474747 url('images/keep1.png') no-repeat center;
vertical-align: sub;
}
由上面的代码可以看出下载和打印显示与否 是由ControlBtn 这个变量控制。还增加了阅读进度的显示和保存阅读进度的按钮
3.可控用户是否有权限查看pdf全部内容(一般用户只能查看前几页内容)
该功能 需要去build\generic\web\viewer.js 中修改
var PDFFintPageNumber = 3; // 阅读进度的变量
var LimitPage = 0; // 是否限制页数的变量
var ControlBtn = true; // 是否显示下载的变量
var DEFAULT_URL = 'compressed.tracemonkey-pldi-09.pdf';
var DEFAULT_SCALE_DELTA = 1.1;
var MIN_SCALE = 0.25;
var MAX_SCALE = 10.0;
var VIEW_HISTORY_MEMORY = 20;
var SCALE_SELECT_CONTAINER_PADDING = 8;
var SCALE_SELECT_PADDING = 22;
var PAGE_NUMBER_LOADING_INDICATOR = 'visiblePageIsLoading';
var DISABLE_AUTO_FETCH_LOADING_BAR_TIMEOUT = 5000;
我声明了一个 LimitPage 的变量 进行控制是否限制页数和限制几页
this.pdfDocument.getMetadata().then(function(data) {
var content = {
'fileName': getPDFFileNameFromURL(this.url),
'fileSize': this._parseFileSize(),
'title': data.info.Title,
'author': data.info.Author,
'subject': data.info.Subject,
'keywords': data.info.Keywords,
'creationDate': this._parseDate(data.info.CreationDate),
'modificationDate': this._parseDate(data.info.ModDate),
'creator': data.info.Creator,
'producer': data.info.Producer,
'version': data.info.PDFFormatVersion,
'pageCount': LimitPage ? LimitPage : this.pdfDocument.numPages
};
var pagesCount = LimitPage ? LimitPage : pdfDocument.numPages;
var pagesRefMap = this.pagesRefMap = {};
var self = this;
return pdfDocument.getPage(1).then(function (firstPage) {
var pagesCount = LimitPage ? LimitPage : pdfDocument.numPages;
var viewport = firstPage.getViewport(1.0);
for (var pageNum = 1; pageNum <= pagesCount; ++pageNum) {
var thumbnail = new PDFThumbnailView({
container: this.container,
id: pageNum,
defaultViewport: viewport.clone(),
linkService: this.linkService,
renderingQueue: this.renderingQueue
});
this.thumbnails.push(thumbnail);
}
}.bind(this));
},
var pagesCount = LimitPage ? LimitPage : pdfDocument.numPages;
document.getElementById('numPages').textContent =
mozL10n.get('page_of', {pageCount: pagesCount}, 'of {{pageCount}}');
document.getElementById('pageNumber').max = pagesCount;
var id = this.documentFingerprint = pdfDocument.fingerprint;
var store = this.store = new ViewHistory(id);
我在这里更改了4处内容,用三元运算符和变量改变了是否显示全部页数和限制页数的功能。
我是根据 pdfDocument.numPages 这个变量来进行决定是否要把值替换成三元运算符表单式的
4.可控在pdf文档附上水印
水印功能上面css代码已经写了,我这里只展示js代码(该功能 需要去build\generic\web\viewer.js 中修改)
canvas.width = (Math.floor(viewport.width) * outputScale.sx) | 0;
canvas.height = (Math.floor(viewport.height) * outputScale.sy) | 0;
canvas.style.width = Math.floor(viewport.width) + 'px';
canvas.style.height = Math.floor(viewport.height) + 'px';
// Add the viewport so it's known what it was originally drawn with.
canvas._viewport = viewport;
var textLayerDiv = null;
var textLayer = null;
if (this.textLayerFactory) {
textLayerDiv = document.createElement('div');
textLayerDiv.className = 'textLayer';
textLayerDiv.style.width = canvas.style.width;
textLayerDiv.style.height = canvas.style.height;
// 从这里开始 水印
var cover = document.createElement('div');
cover.className = "cover";
var defaultSettings = {
watermark_txt: "福通",
watermark_x: 0,//水印起始位置x轴坐标
watermark_y: 0,//水印起始位置Y轴坐标
watermark_rows: 20,//水印行数
watermark_cols: 50,//水印列数
watermark_x_space: 20,//水印x轴间隔
watermark_y_space: 20,//水印y轴间隔
watermark_color: 'red',//水印字体颜色
watermark_alpha: 0.3,//水印透明度
watermark_fontsize: '20px',//水印字体大小
watermark_font: '微软雅黑',//水印字体
watermark_width: 120,//水印宽度
watermark_height: 80,//水印长度
watermark_angle: 45//水印倾斜度数
};
var oTemp = document.createDocumentFragment();
//获取页面最大宽度
var page_width = Math.max(document.body.scrollWidth, canvas.style.width);
//获取页面最大长度
var page_height = Math.max(document.body.scrollHeight, canvas.style.height);
//如果将水印列数设置为0,或水印列数设置过大,超过页面最大宽度,则重新计算水印列数和水印x轴间隔
if (defaultSettings.watermark_cols == 0 || (parseInt(defaultSettings.watermark_x + defaultSettings.watermark_width * defaultSettings.watermark_cols + defaultSettings.watermark_x_space * (defaultSettings.watermark_cols - 1)) > page_width)) {
defaultSettings.watermark_cols = parseInt((page_width - defaultSettings.watermark_x + defaultSettings.watermark_x_space) / (defaultSettings.watermark_width + defaultSettings.watermark_x_space));
defaultSettings.watermark_x_space = parseInt((page_width - defaultSettings.watermark_x - defaultSettings.watermark_width * defaultSettings.watermark_cols) / (defaultSettings.watermark_cols - 1));
}
//如果将水印行数设置为0,或水印行数设置过大,超过页面最大长度,则重新计算水印行数和水印y轴间隔
if (defaultSettings.watermark_rows == 0 || (parseInt(defaultSettings.watermark_y + defaultSettings.watermark_height * defaultSettings.watermark_rows + defaultSettings.watermark_y_space * (defaultSettings.watermark_rows - 1)) > page_height)) {
defaultSettings.watermark_rows = parseInt((defaultSettings.watermark_y_space + page_height - defaultSettings.watermark_y) / (defaultSettings.watermark_height + defaultSettings.watermark_y_space));
defaultSettings.watermark_y_space = parseInt(((page_height - defaultSettings.watermark_y) - defaultSettings.watermark_height * defaultSettings.watermark_rows) / (defaultSettings.watermark_rows - 1));
}
var x;
var y;
for (var i = 0; i < defaultSettings.watermark_rows; i++) {
y = defaultSettings.watermark_y + (defaultSettings.watermark_y_space + defaultSettings.watermark_height) * i;
for (var j = 0; j < defaultSettings.watermark_cols; j++) {
x = defaultSettings.watermark_x + (defaultSettings.watermark_width + defaultSettings.watermark_x_space) * j;
var mask_div = document.createElement('div');
mask_div.id = 'mask_div' + i + j;
mask_div.className = 'mask_div';
mask_div.appendChild(document.createTextNode(defaultSettings.watermark_txt));
//设置水印div倾斜显示
mask_div.style.webkitTransform = "rotate(-" + defaultSettings.watermark_angle + "deg)";
mask_div.style.MozTransform = "rotate(-" + defaultSettings.watermark_angle + "deg)";
mask_div.style.msTransform = "rotate(-" + defaultSettings.watermark_angle + "deg)";
mask_div.style.OTransform = "rotate(-" + defaultSettings.watermark_angle + "deg)";
mask_div.style.transform = "rotate(-" + defaultSettings.watermark_angle + "deg)";
mask_div.style.visibility = "";
mask_div.style.position = "absolute";
//奇偶行错开,这样水印就不对齐,显的不呆板
if (i % 2 != 0) {
mask_div.style.left = x + 100 + 'px';
} else {
mask_div.style.left = x + 'px';
}
mask_div.style.top = y + 'px';
mask_div.style.overflow = "hidden";
mask_div.style.opacity = defaultSettings.watermark_alpha;
mask_div.style.fontSize = defaultSettings.watermark_fontsize;
mask_div.style.fontFamily = defaultSettings.watermark_font;
mask_div.style.color = defaultSettings.watermark_color;
mask_div.style.textAlign = "center";
mask_div.style.width = canvasWrapper.style.width + 'px';
mask_div.style.height = canvasWrapper.style.height + 'px';
mask_div.style.display = "block";
oTemp.appendChild(mask_div);
}
}
cover.appendChild(oTemp);
// 大字水印
// var cover = document.createElement('div');
// cover.className = "cover";
// cover.innerText = "北京";
// 结束
if (this.annotationLayer) {
// annotationLayer needs to stay on top
div.insertBefore(textLayerDiv, this.annotationLayer.div);
// 添加水印
div.appendChild(cover);
} else {
div.appendChild(textLayerDiv);
// 添加水印
div.appendChild(cover);
}
textLayer = this.textLayerFactory.createTextLayerBuilder(textLayerDiv,
this.id - 1,
this.viewport);
// 添加水印
var cover = document.getElementsByClassName('cover');
for (var i = 0, len = cover.length; i < len; i++) {
cover[i].style.width = canvasWrapper.style.width;
cover[i].style.height = canvasWrapper.style.height;
}
var cover = document.getElementsByClassName('cover'),size = 0,
nowWidth = +canvas.style.width.split("p")[0],
//714为100%时,每页的宽度。对比当前的宽度可以计算出页面变化后字体的数值
size = 30*nowWidth/714 +"px";
for(var i=0, len=cover.length; i<len; i++) {
cover[i].style.fontSize = size;
cover[i].style.width = canvas.style.width;
cover[i].style.height = canvas.style.height;
}
// 水印结束
}
this.textLayer = textLayer;
上面有两个水印,我注释掉了一个,只要按照我的方法添加水印,即可出现效果,水印我没用变量控制,小伙伴们可以变量进行控制。
5.用户可保存阅读进度,下次可从保存进度出浏览
其实这个功能我是想用户关闭浏览器/刷新浏览器/退后/ 时发送进度给后台,但是遇到各种各样的问题,就增加了一个保存按钮,确保用户可以把进度保存到后台中。
该功能只要只要记录到用户当前看到第几页就可以(这个功能更简单)
由于人家已经记录了现在的页数,我们只要用常规的方法获取input的value即可。如果大家还是不明白,我把我这个demo文件已经上传到我资源中,大家可以下载