实战动态PDF在线预览及带签名的PDF文件转换

开篇语:

最近工作需要做一个借款合同,公司以前的合同都是通过app端下载,然后通过本地打开pdf文件,而喜欢创新的我,心想着为什么不能在线H5预览,正是这个想法,说干就干,实践过程总是艰难的,折腾了3,4天的时间,熬了两个凌晨3,4点,其中的艰辛、以及各中的曲折、压力只有自己能体会,项目上线后心里想着我要写一篇博文,一是总结一下经验,其次就是和大家分享自己这一路走来的的心得体会,欢迎吐槽!,废话不多说,来点干货!

PDF在线预览实现:

8个实现在线浏览PDF文件的实用插件,笔者选择pdf.js,下面简单介绍8个插件:

PDFObject

PDFobject可以帮助你在页面直接嵌入pdf文件,有时候有些项目需要动态地嵌入PDF文件。PDFObject为此而设计的,他能够快速和容易的嵌入PDF文件,PDFObject使用JavaScript来产生相同的符合标准的 标记,然后插入 到您的HTML元素的选择。您可以填满整个浏览器窗口,或将PDF格式转换成一个或其他块级元素。

pdf.js

和 Google Chrome 使用的源自 Foxit 的闭源 PDF 浏览插件不同,PDF.js 是基于开放的 HTML5 及 JavaScript 技术实现的开源产品

pdf.js 是一个主要用于HTML5 平台上在线阅读PDF文档的小插件,基于JavaScript技术编写而成,无需任何本地技术支持。

pdf.js是由Mozilla Labs发布的。他们的目标是创建一个通用的,基于标准的网络平台,能够解析和渲染PDF文件,并最终发布一个PDF阅读器扩展,毫无疑问 pdf.js 将被整合入 Gecko 成为 Firefox 的内嵌 PDF 阅读器,但是具体整合时间表尚未确定

jsPDF

jsPDF 是一个使用Javascript语言生成PDF的开源库。你可以在Firefox插件,服务端脚本或是浏览器脚本中使用它。客户端Safari 和 iPhone Safari 支持得最好,其次是Opera和Windows下的Firefox 3等。IE暂不支持。。

jQuery Media Plugin

jQuery Media Plugin是一款基于jQuery的网页媒体播放器插件,它支持大部分的网络多媒体播放器和多媒体格式,比如:Flash, Windows Media Player, Real Player, Quicktime, MP3,Silverlight, PDF。它根据当前的脚本配置,自动将a标签替换成div,并生成object, embed甚至是iframe代码,至于生成object还是embed,jQuery Media会根据当前平台自动判别,因此兼容性方面非常出色下面这段代码是jQuery Media生成后的。

Google Docs PDF viewer

ZOHO Viewer

Anychart:使用JavaScript导出PDF

下图可以导出为PNG或JPG格式的静态图像或嵌入式静态图像,图表或一个完全互动的功能图

jQuery Document Viewer

Document Viewer是一个jQuery插件,可以让你在网页中直接查看多种文件格式。文档浏览器支持的文件格式:PDF文件,文本文件,代码,图像,音频,视频等。

PDF.js实践篇

  第一步 pdf.js源码下载https://github.com/mozilla/pdf.js

源码页有详细编译步奏,最后编译出来,将generic文件copy至tomcat webapps目录下,浏览器输入http://localhost:8080/generic/web/viewer.html,H5预览效果如下,图片压缩了,实际预览效果好很多

(我这里覆盖了webapps\generic\web\compressed.tracemonkey-pldi-09.pdf文件,它自带的是英文文档):

  第二步 我集成到项目以插件的形式目录结构如下:

第三步:编写H5文件,这里是将pdf.js viever.html 页面通过Ifram嵌入进来,并通过指定file参数动态传参,实现动态pdf文件预览

loanPdfContract.jsp:

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>

<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8"/>
<meta name="viewport" content="width=device-width,initial-scale=1.0,minimum-scale=1.0,maximum-scale=1.0,user-scalable=no"/>
<meta name="apple-mobile-web-app-capable" content="yes"/>
<meta name="apple-mobile-web-app-status-bar-style" content="black"/>
<meta name="format-detection" content="telephone=no,email=no,address=no"/>
<title>借款合同</title>
</head>
<body>
<iframe src="<c:url value="/plugin/pdfjs/generic/web/viewer.html"/>?file=<c:url value="/app/credit/loanApplication/contracts/${fileId}"/>"
width="100%" height="800">
</iframe>
</body>
</html>
第四步:编写Controller方法,
/**
* 16.借款申请[信息确认]《合同及相关协议》-借款合同 H5接口 提供给app端调用
*
* @param userId
* @return
* @since 3.4
*/
@RequestMapping(value ="/app/credit/loanApplication/contracts/loanContract.security")
public Object loanContract(String userId,String borrowCode,String productTypeCode, Model model) {
if (StringUtils.isEmpty(userId) || StringUtils.isEmpty(borrowCode)) {
return "app/credit/canaLoanApplication/404Error";
}
Map<String,String> returnMap = canaApplyLoanService.investmentContractTemplate(userId,borrowCode,productTypeCode);
if( BizCodeType.IS_NO.getCode().equals(returnMap.get("flag"))){
return "app/credit/canaLoanApplication/404Error";
}
model.addAttribute("fileId",returnMap.get("fileId")+".pdf");
return "app/credit/canaLoanApplication/loanPdfContract";
}
  
第五步:编写Controller方法,注意这个 http head响应头信息设置是HttpStatus.OK,和文件下载HttpStatus.CREATE头信息有差异。

/**

 * 查看PDF文件
* @param fileId
* @return
* @throws IOException
*/
@RequestMapping(value = "/app/credit/loanApplication/contracts/{fileId}.pdf", method = RequestMethod.GET)
public ResponseEntity<byte[]> loadContract(@PathVariable String fileId) throws IOException {
byte[] bytes=canaApplyLoanService.downContract(fileId);
final HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.valueOf("application/pdf"));
headers.setContentLength(bytes.length);
headers.add(HttpHeaders.ACCEPT_RANGES,"bytes");
return new ResponseEntity<byte[]>(bytes, headers, HttpStatus.OK);
}

结束语 看着这里,PDF文件在线动态预览基本完成,赶快体验吧!

进阶篇:Java实现PDF文件转图片、多个图片合成PDF文件.

 前言:

    因为笔者这里的PDF文件带有交互式表单域,导致PDF文件无法预览,因为是合同PDF文件,敲章带有签名信息导致PDF预览失败,当时非常迷茫,没有方向,后来和同城金服一架构师朋友聊天中,他给了我提示,“说是签名的问题,需要把PDF转成图片”,对于笔者来说,有了思路剩下的就是实践的事情。

因为PDF有多页所以会转出多张图片,至于为什么要转成图片,仅仅是为了将代签名的交互式表单域给干掉、因为笔者以前做过多张图片合成一张大图,所以当时就想,这个多张图片合成一张PDF文件应该不是问题,对于以前较少玩PDF文件的我来说,这个想法已经非常大胆了,如果读者的你也对PDF文件了解不深的话,那么本文将非常适合你,带你实战PDF!

我在啰嗦一句,因为这个PDF文件签名导致App端无法查看,因为是第三方合同公司,所以我们需要他们的签名SDK文件才能预览,

  第一篇:带你装B,带你飞!

      说了这么多,不来点干货恐怕你们都看不下去了,主流的Pdf与图片互转

1.PDFRenderer: 确实效率最高,但是缺少字体支持对大多数中文pdf处理不了,这个笔者最开始使用了一下,但是报错后来放弃了。

2.jpedal:这个是商业版本的,它官网的jar下载较慢,最开始本来打算用,好不容易csdn down下一个jar包,一运行提示jar包过期了。

3.pdfbox:笔者最终采用pdfbox,效果还不错。之前网上说它对中文支持不好,但是笔者在pdfbox_2.0.2.jar使用过程中都没遇见乱码.

  第二篇:pdfbox使用

   maven配置:

<dependency>
<groupId>org.apache.pdfbox</groupId>
<artifactId>pdfbox</artifactId>
<version>2.0.2</version>
</dependency>

java代码:

  

import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.pdmodel.PDPage;
import org.apache.pdfbox.pdmodel.PDPageContentStream;
import org.apache.pdfbox.pdmodel.common.PDRectangle;
import org.apache.pdfbox.pdmodel.graphics.image.JPEGFactory;
import org.apache.pdfbox.pdmodel.graphics.image.PDImageXObject;
import org.apache.pdfbox.rendering.PDFRenderer; import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.geom.AffineTransform;
import java.awt.image.BufferedImage;
import java.awt.image.ColorModel;
import java.awt.image.WritableRaster;
import java.io.*;
import java.util.ArrayList;
import java.util.List; public class PdfBoxUtil { public static void main(String[] args) {
//pdfToImg("D:\\test\\22222.pdf","D:\\test\\22222.PNG");
pdfToImgToPdf("D:\\test\\22222.pdf","D:\\test\\3333.pdf");
} /**
* pdf转图片
* @param pdfPath
* @param pngPath
*/
public static void pdfToImg(String pdfPath,String pngPath){
//将pdf装图片 并且自定义图片得格式大小
File file = new File(pdfPath);
try {
PDDocument doc = PDDocument.load(file);
PDFRenderer renderer = new PDFRenderer(doc);
int pageCount = doc.getNumberOfPages();
for (int i = 0; i < pageCount; i++) {
BufferedImage image = renderer.renderImageWithDPI(i, 240);
BufferedImage srcImage = resize(image, image.getWidth(), image.getHeight());
ImageIO.write(srcImage, "PNG", new File(pngPath.replace(".",i+".")));
}
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* pdf转图片然后合成pdf
* @param pdfPath
* @param pdfOutPath
*/
public static void pdfToImgToPdf(String pdfPath,String pdfOutPath){
//将pdf装图片 并且自定义图片得格式大小
File file = new File(pdfPath);
try {
PDDocument doc = PDDocument.load(file);
PDFRenderer renderer = new PDFRenderer(doc);
int pageCount = doc.getNumberOfPages();
List<BufferedImage> images=new ArrayList<BufferedImage>();
for (int i = 0; i < pageCount; i++) {
BufferedImage image = renderer.renderImageWithDPI(i, 240);
BufferedImage srcImage = resize(image, image.getWidth(), image.getHeight());
images.add(srcImage);
}
//合成图片转pdf
createPDFFromImage(pdfOutPath,images);
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* pdf转图片然后合成pdf
* @param input
*/
public static byte[] pdfToImgToPdf(byte[] input){
//将pdf装图片 并且自定义图片得格式大小
byte[] bytes=null;
PDDocument doc=null;
try {
doc = PDDocument.load(input);
List<BufferedImage> images=new ArrayList<BufferedImage>();
PDFRenderer renderer = new PDFRenderer(doc);
int pageCount = doc.getNumberOfPages();
for (int i = 0; i < pageCount; i++) {
BufferedImage image = renderer.renderImageWithDPI(i, 240);
BufferedImage srcImage = resize(image, image.getWidth(), image.getHeight());
images.add(srcImage);
}
bytes=createPDFFromImage(images);
} catch (IOException e) {
e.printStackTrace();
}
if(doc!=null){
try {
doc.close();
} catch (IOException e) {
e.printStackTrace();
}
}
return bytes;
}
/**
*图片合成pdf
* @param images
* @throws Exception
*/
public static void createPDFFromImage(String pdfOutPath,List<BufferedImage> images){
PDDocument doc = new PDDocument();
try {
PDPageContentStream contentStream;
PDPage page;
for (BufferedImage image : images) {
page = new PDPage(new PDRectangle(image.getWidth(),image.getHeight()));
doc.addPage(page);
contentStream = new PDPageContentStream(doc,page,PDPageContentStream.AppendMode.APPEND, true);
PDImageXObject pdImageXObject = JPEGFactory.createFromImage(doc,image);
contentStream.drawXObject(pdImageXObject, 0, 0, image.getWidth(),image.getHeight());
contentStream.close();
}
doc.save(pdfOutPath);
}catch (Exception ex){
ex.printStackTrace();
}finally {
if (doc != null) {
try {
doc.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
/**
*图片合成pdf
* @param images
* @throws Exception
*/
public static byte[] createPDFFromImage(List<BufferedImage> images){
byte[] bytes=null;
ByteArrayOutputStream baos=null;
PDDocument doc = new PDDocument();
try {
PDPageContentStream contentStream;
PDPage page;
for (BufferedImage image : images) {
page = new PDPage(new PDRectangle(image.getWidth(),image.getHeight()));
doc.addPage(page);
contentStream = new PDPageContentStream(doc,page,PDPageContentStream.AppendMode.APPEND, true);
PDImageXObject pdImageXObject = JPEGFactory.createFromImage(doc,image);
contentStream.drawXObject(pdImageXObject, 0, 0, image.getWidth(),image.getHeight());
contentStream.close();
}
baos = new ByteArrayOutputStream();
doc.save(baos);
bytes=baos.toByteArray();
}catch (Exception ex){
ex.printStackTrace();
}finally {
if (baos != null) {
try {
baos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (doc != null) {
try {
doc.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
return bytes;
}
/**
* 生成图片
* @param source
* @param targetW
* @param targetH
* @return
*/
private static BufferedImage resize(BufferedImage source, int targetW, int targetH) {
int type = source.getType();
BufferedImage target = null;
double sx = (double) targetW / source.getWidth();
double sy = (double) targetH / source.getHeight();
if (sx > sy) {
sx = sy;
targetW = (int) (sx * source.getWidth());
} else {
sy = sx;
targetH = (int) (sy * source.getHeight());
}
if (type == BufferedImage.TYPE_CUSTOM) {
ColorModel cm = source.getColorModel();
WritableRaster raster = cm.createCompatibleWritableRaster(targetW, targetH);
boolean alphaPremultiplied = cm.isAlphaPremultiplied();
target = new BufferedImage(cm, raster, alphaPremultiplied, null);
} else {
target = new BufferedImage(targetW, targetH, type);
}
Graphics2D g = target.createGraphics();
g.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
g.drawRenderedImage(source, AffineTransform.getScaleInstance(sx, sy));
g.dispose();
return target;
} }

总结语:

  到这里笔者得和你说拜拜了,基本上将我三天研究的全部成果贡献,其中一路艰辛,扛过多少坑,你未必知道,我只能告诉你,大多时候我内心是奔溃的!

备注 pdf.js 我项目中的插件源码可至我百度云提取  链接: http://pan.baidu.com/s/1c2JVe1M 密码: k9sc

     要是你在使用过程中遇到问题,可以给我邮件,515173248@qq.com

动态PDF在线预览的更多相关文章

  1. 实战动态PDF在线预览及带签名的PDF文件转换

    开篇语: 最近工作需要做一个借款合同,公司以前的合同都是通过app端下载,然后通过本地打开pdf文件,而喜欢创新的我,心想着为什么不能在线H5预览,正是这个想法,说干就干,实践过程总是艰难的,折腾了3 ...

  2. Office在线预览及PDF在线预览的实现方式史上最全大集合

    Office在线预览及PDF在线预览的实现方式大集合 一.服务器先转换为PDF,再转换为SWF,最后通过网页加载Flash预览 微软方:利用Office2007以上版本的一个PDF插件SaveAsPD ...

  3. Aspose office (Excel,Word,PPT),PDF 在线预览

    前文: 做个备份,拿的是试用版的 Aspose,功能见标题 代码: /// <summary> /// Aspose office (Excel,Word,PPT),PDF 在线预览 // ...

  4. java实现word转pdf在线预览(前端使用PDF.js;后端使用openoffice、aspose)

    背景 之前一直是用户点击下载word文件到本地,然后使用office或者wps打开.需求优化,要实现可以直接在线预览,无需下载到本地然后再打开. 随后开始上网找资料,网上资料一大堆,方案也各有不同,大 ...

  5. [Asp.net]常见word,excel,ppt,pdf在线预览方案,有图有真相,总有一款适合你!

    引言 之前项目需要,查找了office文档在线预览的解决方案,顺便记录一下,方便以后查询. 方案一 直接在浏览器中打开Office文档在页面上的链接.会弹出如下窗口: 优点:主流浏览器都支持. 缺点: ...

  6. [Asp.net]常见word,excel,ppt,pdf在线预览方案(转)

    引言 之前项目需要,查找了office文档在线预览的解决方案,顺便记录一下,方便以后查询. 方案一 直接在浏览器中打开Office文档在页面上的链接.会弹出如下窗口: 优点:主流浏览器都支持. 缺点: ...

  7. Office在线预览及PDF在线预览的实现方式大集合

    一.服务器先转换为PDF,再转换为SWF,最后通过网页加载Flash预览 微软方:利用Office2007以上版本的一个PDF插件SaveAsPDFandXPS.exe可以导出PDF文件,然后再利用免 ...

  8. Office在线预览及PDF在线预览的实现方式

    原文链接:http://www.officeweb365.com/officetoview.html 一.服务器先转换为PDF,再转换为SWF,最后通过网页加载Flash预览 微软方:利用Office ...

  9. 记一次关于pdf 下载需求变更到 pdf 在线预览

    背景: 之前的需求是根据接口中提供的Blob数据实现PDF下载,已实现代码如下: 1 const url = window.URL.createObjectURL(newBlob([response. ...

随机推荐

  1. Java学习笔记--JDBC数据库的使用

    参考  hu_shengyang的专栏 : http://blog.csdn.net/hu_shengyang/article/details/6290029 一. JDBC API中提供的常用数据库 ...

  2. ng-repeat 与ng-switch的简单应用

    效果如下图所示: 使用表格显示用户信息,当点击某条用户信息时,在其下方展开一行进行展示. index.html <!DOCTYPE html> <html ng-app=" ...

  3. XJOI网上同步训练DAY2 T2

    [问题描述] 火车司机出秦川跳蚤国王下江南共价大爷游长沙.每个周末勤劳的共价大爷都会开车游历长沙市. 长沙市的交通线路可以抽象成为一个

  4. 解决kibana 4 关于响应时间的问题

    "message" => " 10.252.142.174 [12/Sep/2016:16:43:47 +0800] \"GET /resources/j ...

  5. 页和区 sql server

    原文地址:http://msdn.microsoft.com/zh-cn/library/ms190969.aspx SQL Server 中数据存储的基本单位是页.为数据库中的数据文件(.mdf 或 ...

  6. Uva11183-Teen Girl Squad(有向图最小生成树朱刘算法)

    解析: 裸的有向图最小生成树 代码 #include<cstdio> #include<cstring> #include<string> #include< ...

  7. python练习linux下创建路径

    #coding=utf-8 import os class MakeDirectory(): def mkdir(self,path): # 去除首位空格 path=path.strip() # 去除 ...

  8. hdu 4336 Card Collector(期望 dp 状态压缩)

    Problem Description In your childhood, people in the famous novel Water Margin, you will win an amaz ...

  9. NavMeshAgent 动态加载障碍物

    如果你想让游戏人物绕开一些物体, 这些物体动态生成出来的.只需要给物体添加NavMeshObstacle组件即可 1. 绿色方块添加NavMeshObstacle组件 2. 红色方块没有添加NavMe ...

  10. 如何在已经存在python2的linux环境上安装python3

    最近看到好多人都在问在已经存在python2.7的环境下如何安装python3,于是我决定写下这篇文档,供大家学习参考,希望能够给大家带来帮助 有的人在安装的时候可能会先将python2卸载掉,这个地 ...