最近要实现一个文件上传,并且在线预览上传文件的功能,设计思路是:把上传的文件通过openoffice转成pdf文件,并将pdf文件以流的形式返回到浏览器,由于上传的部分文件过大,转成pdf后传回前端浏览器需要的时间太长会找出接口超时问题,故需要对转化后的pdf文件进行压缩,分割再分页传回到前台。
在网上看了很多对pdf文件进行操作文档后,收集整理了4个好用代码片段,希望对需要的人有用处吧。
1.java实现对pdf文件压缩:
引用的maven坐标
<!--文件压缩-->
<dependency>
<groupId>com.aspose</groupId>
<artifactId>aspose-pdf</artifactId>
<version>18.2</version>
</dependency>
整理的代码片段
package tvap.pdfutil;
import com.aspose.pdf.Document;
import tvap.pdfutil.util.DateUtil;
/**pdf文件压缩
* @author liyuliang
* @date 2021/7/14
*/
public class PdfZipUtil {
public static void main(String[] args) {
String inputFile = "D:\\opt\\jenkins\\gccc.pdfutil";
String outputFile = "D:\\opt\\jenkins\\gccc-opt2.pdfutil";
System.out.println(DateUtil.getCurrentTime());
optimize(inputFile, outputFile);
System.out.println(DateUtil.getCurrentTime());
}
public static void optimize(String source, String target) {
Document doc = new Document(source);
//设置压缩属性
Document.OptimizationOptions opt = new Document.OptimizationOptions();
//删除PDF不必要的对象
opt.setRemoveUnusedObjects(true);
//链接重复流
opt.setLinkDuplcateStreams(false);
//删除未使用的流
opt.setRemoveUnusedStreams(false);
//删除不必要的字体
// opt.setUnembedFonts(true);
//压缩PDF中的图片
opt.setCompressImages(true);
//图片压缩比, 0 到100可选,越低压缩比越大
opt.setImageQuality(10);
doc.optimizeResources(opt);
//优化web的PDF文档
doc.optimize();
doc.save(target);
doc.close();
}
}
进行pdf文件压缩后,pdf文件顶部会有水印,"Evaluation Only. Created with Aspose.PDF. Copyright 2002-2018 Aspose Pty Ltd." 可以使用对pdf进行修改的方法把水印去掉。
2.java实现对pdf文件的修改
引用的maven坐标
<!--修改pdf水印/分割pdf文件-->
<dependencies>
<dependency>
<groupId>com.itextpdf</groupId>
<artifactId>itextpdf</artifactId>
<version>5.5.13</version>
</dependency>
<dependency>
<groupId>com.itextpdf</groupId>
<artifactId>itext-asian</artifactId>
<version>5.2.0</version>
</dependency>
整理的代码片段
package tvap.pdfutil;
import com.itextpdf.text.BaseColor;
import com.itextpdf.text.Font;
import com.itextpdf.text.Rectangle;
import com.itextpdf.text.pdf.BaseFont;
import com.itextpdf.text.pdf.PdfContentByte;
import com.itextpdf.text.pdf.PdfReader;
import com.itextpdf.text.pdf.PdfStamper;
import com.itextpdf.text.pdf.parser.PdfReaderContentParser;
import tvap.pdfutil.listen_pdf.KeyWordPositionListener;
import tvap.pdfutil.listen_pdf.MatchItem;
import tvap.pdfutil.util.DateUtil;
import java.io.FileOutputStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* @author liyuliang
* @date 2021/7/14
* pdf替换文字工具类
* <p>
* 思路:
* 1.逐页搜索关键字,逐页匹配
* 2.先读取一页的所有字符信息,存放到allItems中
* 3.把一页的字符拼接成字符串,然后匹配关键字,匹配上,记录匹配的第一个字符的MatchItem信息;匹配不是,继续下一页匹配
* 4.根据匹配字符串的长度和字符的宽高信息画遮罩层,然后替换文字生成新的pdf文件
* <p>
* 不足之处:
* 1.目前只支持单字符串匹配
* 2.替换之后的文字无法和原pdf中替换掉的文字信息一致(主要有:字体大小、样式等)
* 3.某些情况下(主要是替换字体的大小)替换之后显示不是太整齐
* 4.字体大小、样式无法把控
* 5.无法匹配目标文字在两页中显示的情况(例如:目标文字:替换工具,第一页页尾有替换两字,第二页页首有工具二字)
*/
*/
public class PdfDelwaterUtil {
public static void main(String[] args) throws Exception {
String src = "D:\\opt\\jenkins\\ilovepdf-opt2.pdf";
String dest = "D:\\opt\\jenkins\\ilovepdf-no-water.pdf";
String keyWord = "Evaluation Only. Created with Aspose.PDF. Copyright 2002-2018 Aspose Pty Ltd.";
String keyWordNew = "";
System.out.println( DateUtil.getCurrentTime());
pdfReplace(src, dest, keyWord, keyWordNew);
System.out.println( DateUtil.getCurrentTime());
}
/**
* 根据关键字和pdf路径,全文搜索关键字
*
* @param filePath pdf目标路径
* @param keyword 关键字
* @return
* @throws Exception
*/
public static List<MatchItem> matchAll(String filePath, String keyword) throws Exception {
List<MatchItem> items = new ArrayList<MatchItem>();
PdfReader reader = new PdfReader(filePath);
//获取pdf页数
int pageSize = reader.getNumberOfPages();
//逐页匹配关键字
for (int page = 1; page <= pageSize; page++) {
items.addAll(matchPage(reader, page, keyword));
}
return items;
}
/**
* 根据关键字、文档路径、pdf页数寻找特定的文件内容
*
* @param reader
* @param pageNumber 页数
* @param keyword 关键字
* @return
* @throws Exception
*/
public static List<MatchItem> matchPage(PdfReader reader, Integer pageNumber, String keyword) throws Exception {
PdfReaderContentParser parse = new PdfReaderContentParser(reader);
Rectangle rectangle = reader.getPageSize(pageNumber);
//匹配监听
KeyWordPositionListener renderListener = new KeyWordPositionListener();
renderListener.setKeyword(keyword);
renderListener.setPageNumber(pageNumber);
renderListener.setCurPageSize(rectangle);
parse.processContent(pageNumber, renderListener);
return findKeywordItems(renderListener, keyword);
}
/**
* 找到匹配的关键词块
*
* @param renderListener
* @param keyword
* @return
*/
public static List<MatchItem> findKeywordItems(KeyWordPositionListener renderListener, String keyword) {
//先判断本页中是否存在关键词
List<MatchItem> allItems = renderListener.getAllItems();//所有块LIST
StringBuffer sbtemp = new StringBuffer("");
for (MatchItem item : allItems) {//将一页中所有的块内容连接起来组成一个字符串。
sbtemp.append(item.getContent());
}
List<MatchItem> matches = renderListener.getMatches();
//一页组成的字符串没有关键词,直接return
//第一种情况:关键词与块内容完全匹配的项,直接返回
if (sbtemp.toString().indexOf(keyword) == -1 || matches.size() > 0) {
return matches;
}
//第二种情况:多个块内容拼成一个关键词,则一个一个来匹配,组装成一个关键词
sbtemp = new StringBuffer("");
List<MatchItem> tempItems = new ArrayList();
for (MatchItem item : allItems) {
if (keyword.indexOf(item.getContent()) != -1) {
tempItems.add(item);
sbtemp.append(item.getContent());
if (keyword.indexOf(sbtemp.toString()) == -1) {//如果暂存的字符串和关键词 不再匹配时
sbtemp = new StringBuffer(item.getContent());
tempItems.clear();
tempItems.add(item);
}
if (sbtemp.toString().equalsIgnoreCase(keyword)) {//暂存的字符串正好匹配到关键词时
matches.add(tempItems.get(0));//得到匹配的项
sbtemp = new StringBuffer("");//清空暂存的字符串
tempItems.clear();//清空暂存的LIST
continue;//继续查找
}
} else {//如果找不到则清空
sbtemp = new StringBuffer("");
tempItems.clear();
}
}
return matches;
}
/**
* 替换目标文字,生成新的pdf文件
*
* @param src 目标pdf路径
* @param dest 新pdf的路径
* @throws Exception
*/
public static void manipulatePdf(String src, String dest, List<MatchItem> matchItems, String keyWord, String keyWordNew) throws Exception {
PdfReader reader = new PdfReader(src);
PdfStamper stamper = new PdfStamper(reader, new FileOutputStream(dest));
PdfContentByte canvas = null;
Map<Integer, List<MatchItem>> mapItem = new HashMap<Integer, List<MatchItem>>();
List<MatchItem> itemList = null;
for (MatchItem item : matchItems) {
Integer pageNum = item.getPageNum();
if (mapItem.containsKey(pageNum)) {
itemList = mapItem.get(pageNum);
itemList.add(item);
mapItem.put(pageNum, itemList);
} else {
itemList = new ArrayList<MatchItem>();
itemList.add(item);
mapItem.put(pageNum, itemList);
}
}
//遍历每一页去修改
for (Integer page : mapItem.keySet()) {
List<MatchItem> items = mapItem.get(page);
//遍历每一页中的匹配项
for (MatchItem item : items) {
canvas = stamper.getOverContent(page);
float x = item.getX();
float y = item.getY();
float fontWidth = item.getFontWidth();
float fontHeight = item.getFontHeight();
canvas.saveState();
canvas.setColorFill(BaseColor.WHITE);
canvas.rectangle(x, y, fontWidth * keyWord.length(), fontWidth + 2);
canvas.fill();
canvas.restoreState();
//开始写入文本
canvas.beginText();
BaseFont bf = BaseFont.createFont("STSong-Light", "UniGB-UCS2-H", BaseFont.EMBEDDED);
Font font = new Font(bf, fontWidth, Font.BOLD);
//设置字体和大小
canvas.setFontAndSize(font.getBaseFont(), fontWidth);
//设置字体的输出位置
canvas.setTextMatrix(x, y + fontWidth / 10 + 0.5f);
//要输出的text
canvas.showText(keyWordNew);
canvas.endText();
}
}
stamper.close();
reader.close();
System.out.println("complete");
}
/**
* 替换pdf中指定文字
*
* @param src 目标pdf路径
* @param dest 新pdf的路径
* @param keyWord 替换的文字
* @param keyWordNew 替换后的文字
* @throws Exception
*/
public static void pdfReplace(String src, String dest, String keyWord, String keyWordNew) throws Exception {
manipulatePdf(src, dest, matchAll(src, keyWord), keyWord, keyWordNew);
}
}
这个水印修改代码片段中还有,两个附属类如下:
实体类MatchItem
package com.Stream.pdfcut;
/**
* @author liyuliang
* @date 2021/7/14
* 用来保存关键字信息
*/
public class MatchItem {
//页数
private Integer pageNum;
//x坐标
private Float x;
//y坐标
private Float y;
//页宽
private Float pageWidth;
//页高
private Float pageHeight;
//匹配字符
private String content;
//字体宽
private float fontWidth;
//字体高
private float fontHeight = 12;
public Integer getPageNum() {
return pageNum;
}
public void setPageNum(Integer pageNum) {
this.pageNum = pageNum;
}
public Float getX() {
return x;
}
public void setX(Float x) {
this.x = x;
}
public Float getY() {
return y;
}
public void setY(Float y) {
this.y = y;
}
public Float getPageWidth() {
return pageWidth;
}
public void setPageWidth(Float pageWidth) {
this.pageWidth = pageWidth;
}
public Float getPageHeight() {
return pageHeight;
}
public void setPageHeight(Float pageHeight) {
this.pageHeight = pageHeight;
}
public String getContent() {
return content;
}
public void setContent(String content) {
this.content = content;
}
public float getFontWidth() {
return fontWidth;
}
public void setFontWidth(float fontWidth) {
this.fontWidth = fontWidth;
}
public float getFontHeight() {
return fontHeight;
}
public void setFontHeight(float fontHeight) {
this.fontHeight = fontHeight;
}
@Override
public String toString() {
return "MatchItem{" +
"pageNum=" + pageNum +
", x=" + x +
", y=" + y +
", pageWidth=" + pageWidth +
", pageHeight=" + pageHeight +
", content='" + content + '\'' +
'}';
}
}
和这个监听类KeyWordPositionListener
package tvap.pdfutil.listen_pdf;
import com.itextpdf.awt.geom.Rectangle2D;
import com.itextpdf.text.Rectangle;
import com.itextpdf.text.pdf.parser.ImageRenderInfo;
import com.itextpdf.text.pdf.parser.RenderListener;
import com.itextpdf.text.pdf.parser.TextRenderInfo;
import java.util.ArrayList;
import java.util.List;
/**
* @author liyuliang
* @date 2021/7/14
*/
/**
* 用来匹配pdf的关键词 监听类
*/
public class KeyWordPositionListener implements RenderListener {
//存放匹配上的字符信息
private List<MatchItem> matches = new ArrayList<MatchItem>();
//存放所有的字符信息
private List<MatchItem> allItems = new ArrayList<MatchItem>();
private Rectangle curPageSize;
/**
* 匹配的关键字
*/
private String keyword;
/**
* 匹配的当前页
*/
private Integer pageNumber;
@Override
public void beginTextBlock() {
//do nothing
}
@Override
public void renderText(TextRenderInfo renderInfo) {
//获取字符
String content = renderInfo.getText();
Rectangle2D.Float textRectangle = renderInfo.getDescentLine().getBoundingRectange();
MatchItem item = new MatchItem();
item.setContent(content);
item.setPageNum(pageNumber);
item.setFontHeight(textRectangle.height == 0 ? 12 : textRectangle.height);//默认12
item.setFontWidth(textRectangle.width);
item.setPageHeight(curPageSize.getHeight());
item.setPageWidth(curPageSize.getWidth());
item.setX((float) textRectangle.getX());
item.setY((float) textRectangle.getY());
//若keyword是单个字符,匹配上的情况
if (content.equalsIgnoreCase(keyword)) {
matches.add(item);
}
//保存所有的项
allItems.add(item);
}
@Override
public void endTextBlock() {
//do nothing
}
@Override
public void renderImage(ImageRenderInfo renderInfo) {
//do nothing
}
/**
* 设置需要匹配的当前页
*
* @param pageNumber
*/
public void setPageNumber(Integer pageNumber) {
this.pageNumber = pageNumber;
}
/**
* 设置需要匹配的关键字,忽略大小写
*
* @param keyword
*/
public void setKeyword(String keyword) {
this.keyword = keyword;
}
/**
* 返回匹配的结果列表
*
* @return
*/
public List<MatchItem> getMatches() {
return matches;
}
public void setCurPageSize(Rectangle rect) {
this.curPageSize = rect;
}
public List<MatchItem> getAllItems() {
return allItems;
}
public void setAllItems(List<MatchItem> allItems) {
this.allItems = allItems;
}
}
3.java实现对pdf文件的拆分
引入的maven坐标
<dependency>
<groupId>com.itextpdf</groupId>
<artifactId>itextpdf</artifactId>
<version>5.5.13</version>
</dependency>
代码片段
package tvap.pdfutil;
import com.itextpdf.text.Document;
import com.itextpdf.text.DocumentException;
import com.itextpdf.text.pdf.PdfCopy;
import com.itextpdf.text.pdf.PdfImportedPage;
import com.itextpdf.text.pdf.PdfReader;
import tvap.pdfutil.util.DateUtil;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
/**
* 分割pdf文件工具类
* @author liyuliang
* @date 2021/7/14
*/
public class PdfSpiltUtil {
public static void main(String[] args) {
String inputFile = "D:\\opt\\jenkins\\ilovepdf.pdfutil";
System.out.println(DateUtil.getCurrentTime());
try {
splitFileBySiz(inputFile,10);
} catch (Exception e) {
e.printStackTrace();
}
System.out.println(DateUtil.getCurrentTime());
}
/**
* 分割pdf文件,并设置尺寸
* @param pdfFilePath
* @param size
* @throws Exception
*/
public static void splitFileBySiz (String pdfFilePath,int size)throws Exception{
PdfReader reader = new PdfReader(pdfFilePath);
int pagecount = reader.getNumberOfPages();
for (int i = 0; i < pagecount / size + 1; i++) {
if (i != pagecount / size) {
splitFile(pdfFilePath, i * size + 1, (i + 1) * size,i);
} else {
splitFile(pdfFilePath, i * size + 1, pagecount,i);
}
}
}
/**
* 分割pdf文件
* @param pdfFile
* @param from
* @param end
* @return
*/
public static String splitFile(String pdfFile, Integer from, Integer end,Integer i) {
Document document = null;
PdfCopy copy = null;
try {
PdfReader reader = new PdfReader(pdfFile);
int n = reader.getNumberOfPages();
if (end == 0) {
end = n;
}
List<String> savepaths = new ArrayList<String>();
int a = pdfFile.lastIndexOf(".pdfutil");
String staticpath = pdfFile.substring(0, a);
// String savepath = staticpath + "_from_" + from + "_to_" + end + "_.pdfutil";
String savepath = staticpath + "_"+i+".pdfutil";
savepaths.add(savepath);
document = new Document(reader.getPageSize(1));
copy = new PdfCopy(document, new FileOutputStream(savepaths.get(0)));
document.open();
for (int j = from; j <= end; j++) {
document.newPage();
PdfImportedPage page = copy.getImportedPage(reader, j);
copy.addPage(page);
}
document.close();
return new File(savepath).getName();
} catch (IOException e) {
return null;
} catch (DocumentException e) {
return null;
}
}
}
4.java实现对pdf文件的水印添加
引入的maven坐标
<!--添加水印-->
<dependency>
<groupId>org.eclipse.birt.runtime.3_7_1</groupId>
<artifactId>com.lowagie.text</artifactId>
<version>2.1.7</version>
</dependency>
代码片段
package tvap.pdfutil;
import java.awt.*;
import java.io.FileOutputStream;
import com.lowagie.text.Element;
import com.lowagie.text.pdf.BaseFont;
import com.lowagie.text.pdf.PdfContentByte;
import com.lowagie.text.pdf.PdfGState;
import com.lowagie.text.pdf.PdfReader;
import com.lowagie.text.pdf.PdfStamper;
import javax.swing.*;
/**
* 添加水印
* @author liyuliang
* @date 2021/7/14
*/
public class PdfAddwaterUtil {
public static void main(String[] args) {
String inputFile = "D:\\opt\\jenkins\\ilovepdf-no-water.pdf";
String outputFile = "D:\\opt\\jenkins\\ilovepdf_shuiyin.pdf";
waterMark(inputFile,outputFile,"北京XXXX科技有限公司 专用");
}
public static void waterMark(String inputFile, String outputFile, String waterMarkName) {
try {
PdfReader reader = new PdfReader(inputFile);
PdfStamper stamper = new PdfStamper(reader, new FileOutputStream(
outputFile));
BaseFont base = BaseFont.createFont("STSong-Light", "UniGB-UCS2-H", BaseFont.EMBEDDED);
Rectangle pageRect = null;
PdfGState gs = new PdfGState();
gs.setFillOpacity(0.3f);
gs.setStrokeOpacity(0.4f);
int total = reader.getNumberOfPages() + 1;
JLabel label = new JLabel();
FontMetrics metrics;
int textH = 0;
int textW = 0;
label.setText(waterMarkName);
metrics = label.getFontMetrics(label.getFont());
textH = metrics.getHeight();
textW = metrics.stringWidth(label.getText());
PdfContentByte under;
for (int i = 1; i < total; i++) {
under = stamper.getOverContent(i);// 在内容上方加水印
//content = stamper.getUnderContent(i);//在内容下方加水印
gs.setFillOpacity(0.2f);
// content.setGState(gs);
under.beginText();
under.setFontAndSize(base, 50);
under.setTextMatrix(70, 200);
under.showTextAligned(Element.ALIGN_CENTER, waterMarkName, 300, 350, 55);
under.endText();
}
//一定不要忘记关闭流
stamper.close();
reader.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
以上是本人整理的4个对pdf文件操作的代码,看了很多文档感觉不容易,所以分享出来希望对需要的人有用,另外还有一个插件也是操作pdf的叫做 spire.pdf ,它的官网有使用文档有兴趣可以去看看,以上资源整理自网络。