网上也有PDF去除水印的文章、方法、和代码,Github上也有,但是这些都是去除以图片为主的水印。一般情况下PDF水印均是斜体,印于文档的底部,按照Github或网上的文章根本无法去除,也不是一个正确的去除办法。这里要说的是一个正确去除水印并已经在实际运行的项目中使用的方法。

斜体水印并不是图片,因此不能通过检测PDF中的图片来删除水印。这种水印其实本身是文字,要用清除文字的方式来清除。主要思路是检测PDF中文字的倾斜度来检测水印,然后进行清除。下面给出源代码。

WatermarkScancer.java 水印检测类,用于检测PDF中的水印,并将检测到的文字保存到缓存中。

WatermarkRemover.java 水印清除类,用于清除PDF中的水印。

WatermarkProcessor.java 水印清除器类,用于执行任务。

本文采用并行处理,可处理多页PDF的去水印。

import java.io.OutputStream;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.Vector;
import java.util.concurrent.CompletableFuture;import org.apache.pdfbox.cos.COSName;
import org.apache.pdfbox.pdfwriter.ContentStreamWriter;
import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.pdmodel.common.PDStream;public class WatermarkProcessor extends BaseWatermarkRemover implements IWatermarkProcessor {@Overridepublic void init(PDDocument document) {super.init(document);// 扫描PDF文档,检查是否包含水印CompletableFuture<Void> checkerTask = CompletableFuture.runAsync(() -> {WatermarkChecker checker = new WatermarkChecker(WatermarkProcessor.this);checker.run();});CompletableFuture.allOf(checkerTask).join();// 扫描PDF文档,获取所有水印,如果超过3页,则启动多线程并行扫描int threadCount = getThreadCount();CompletableFuture<?>[] scancerTasks = new CompletableFuture<?>[threadCount];for (int i = 0; i < threadCount; i++) {final int pageStart = i * 3;scancerTasks[i] = CompletableFuture.runAsync(() -> {WatermarkScancer scancer = new WatermarkScancer(WatermarkProcessor.this, pageStart, 3);scancer.run();});}CompletableFuture.allOf(scancerTasks).join();}/*** 清除水印的实现 当超过3页时,本方法采用多线程执行,并行清除页面水印,以提高效率。*/@Overridepublic void removeWatermark() throws Exception {int threadCount = getThreadCount();CompletableFuture<?>[] removerTasks = new CompletableFuture<?>[threadCount];final Vector<RemoveResult> removeResults = new Vector<>();for (int i = 0; i < threadCount; i++) {final int pageStart = i * 3;removerTasks[i] = CompletableFuture.runAsync(() -> {WatermarkRemover remover = new WatermarkRemover(WatermarkProcessor.this, pageStart, 3, null);remover.removeWatermark();removeResults.addAll(remover.getPageTokens());});}CompletableFuture.allOf(removerTasks).join();// 对所有结果进行排序Collections.sort(removeResults, new Comparator<RemoveResult>() {@Overridepublic int compare(RemoveResult o1, RemoveResult o2) {return o1.getPageNo() - o2.getPageNo();}});// 执行完毕后统一进行回写处理for (RemoveResult result : removeResults) {PDStream updatedStream = new PDStream(document);OutputStream out = updatedStream.createOutputStream(COSName.FLATE_DECODE);ContentStreamWriter tokenWriter = new ContentStreamWriter(out);tokenWriter.writeTokens(result.getTokens());out.close();result.getPage().setContents(updatedStream);}}@Overridepublic void removeWatermark(List<String> watermarks) throws Exception {int threadCount = getThreadCount();CompletableFuture<?>[] removerTasks = new CompletableFuture<?>[threadCount];final Vector<RemoveResult> removeResults = new Vector<>();for (int i = 0; i < threadCount; i++) {final int pageStart = i * 3;removerTasks[i] = CompletableFuture.runAsync(() -> {WatermarkRemover remover = new WatermarkRemover(WatermarkProcessor.this, pageStart, 3, watermarks);remover.removeWatermark();removeResults.addAll(remover.getPageTokens());});}CompletableFuture.allOf(removerTasks).join();// 对所有结果进行排序Collections.sort(removeResults, new Comparator<RemoveResult>() {@Overridepublic int compare(RemoveResult o1, RemoveResult o2) {return o1.getPageNo() - o2.getPageNo();}});// 执行完毕后统一进行回写处理for (RemoveResult result : removeResults) {PDStream updatedStream = new PDStream(document);OutputStream out = updatedStream.createOutputStream(COSName.FLATE_DECODE);ContentStreamWriter tokenWriter = new ContentStreamWriter(out);tokenWriter.writeTokens(result.getTokens());out.close();result.getPage().setContents(updatedStream);}}private int getThreadCount() {return new Double(Math.ceil(document.getNumberOfPages() / 3d)).intValue();}
}
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;import org.apache.pdfbox.contentstream.operator.Operator;
import org.apache.pdfbox.cos.COSString;
import org.apache.pdfbox.pdfparser.PDFStreamParser;
import org.apache.pdfbox.pdmodel.PDPage;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;public class WatermarkRemover {Logger logger = LoggerFactory.getLogger(WatermarkRemover.class);IWatermarkProcessor remover;List<RemoveResult> pageTokens = new ArrayList<>();List<String> watermarks = null;int pageStartIndex;int pageLength;public WatermarkRemover(IWatermarkProcessor remover, int pageStartIndex, int pageLength, List<String> watermarks) {this.remover = remover;this.pageStartIndex = pageStartIndex;this.pageLength = pageLength;this.watermarks = watermarks;}public void removeWatermark() {for (int i = pageStartIndex; i < pageStartIndex + pageLength; i++) {if (i >= remover.getDocument().getNumberOfPages()) {break;}try {processPage(i, remover.getDocument().getPage(i));} catch (Exception e) {logger.error("【解析PDF页面失败】", e);}}}public void processPage(int index, PDPage page) throws Exception {Object next;Operator op;PDFStreamParser parser = new PDFStreamParser(page);parser.parse();List<?> tokens = parser.getTokens();if (Objects.nonNull(tokens)) {for (int j = 0; j < tokens.size(); j++) {next = tokens.get(j);if (Objects.isNull(next))continue;if (next instanceof Operator) {op = (Operator) next;if (op.getName().equals("Tj")) {COSString previous = (COSString) tokens.get(j - 1);String string = previous.getString();if (Utils.isISO8859_1Charset(string)) {string = new String(string.getBytes("ISO8859-1"), "GBK");}// 判断是否是水印if (null != watermarks && watermarks.contains(string)) {previous.setValue("".getBytes("GBK"));} else if (remover.isWatermarkWord(string)) {// 判断是否是水印previous.setValue("".getBytes("GBK"));}}}}}RemoveResult pageResult = new RemoveResult(page, index, tokens);pageTokens.add(pageResult);}public List<RemoveResult> getPageTokens() {return pageTokens;}static class RemoveResult {PDPage page;int pageNo;List<?> tokens;public RemoveResult(PDPage page, int pageNo, List<?> tokens) {this.page = page;this.pageNo = pageNo;this.tokens = tokens;}public PDPage getPage() {return page;}public void setPage(PDPage page) {this.page = page;}public int getPageNo() {return pageNo;}public void setPageNo(int pageNo) {this.pageNo = pageNo;}public List<?> getTokens() {return tokens;}public void setTokens(List<?> tokens) {this.tokens = tokens;}}
}
import org.apache.pdfbox.contentstream.PDFStreamEngine;
import org.apache.pdfbox.contentstream.operator.DrawObject;
import org.apache.pdfbox.contentstream.operator.Operator;
import org.apache.pdfbox.contentstream.operator.state.Concatenate;
import org.apache.pdfbox.contentstream.operator.state.Restore;
import org.apache.pdfbox.contentstream.operator.state.Save;
import org.apache.pdfbox.contentstream.operator.state.SetGraphicsStateParameters;
import org.apache.pdfbox.contentstream.operator.state.SetMatrix;
import org.apache.pdfbox.cos.COSBase;
import org.apache.pdfbox.cos.COSString;
import org.apache.pdfbox.util.Matrix;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;public class WatermarkScancer extends PDFStreamEngine {Logger logger = LoggerFactory.getLogger(WatermarkScancer.class);IWatermarkProcessor remover;int pageStartIndex;int pageLength;public WatermarkScancer(IWatermarkProcessor remover, int pageStartIndex, int pageLength) {addOperator(new Concatenate());addOperator(new DrawObject());addOperator(new SetGraphicsStateParameters());addOperator(new Save());addOperator(new Restore());addOperator(new SetMatrix());this.remover = remover;this.pageStartIndex = pageStartIndex;this.pageLength = pageLength;}/*** 开始扫描,检查所有水印*/public void run() {try {for (int i = pageStartIndex; i < pageStartIndex + pageLength; i++) {if (i >= remover.getDocument().getNumberOfPages()) {break;}processPage(remover.getDocument().getPage(i));}} catch (Exception e) {logger.error("【扫描页面水印出错】", e);}}/*** 处理读取的每一个点位*/@Overrideprotected void processOperator(Operator operator, List<COSBase> operands) throws IOException {String operation = operator.getName();if ("Tj".equals(operation)) {COSString textObj = (COSString) operands.get(0);String string = textObj.getString();if (Utils.isISO8859_1Charset(string)) {string = new String(string.getBytes("ISO8859-1"), "GBK");}// 检查是否是倾斜的水印Matrix matrix = getTextLineMatrix();if (matrix != null && matrix.getScaleY() != 0 && matrix.getScaleY() != 1 && matrix.getShearY() != 0) {if (!remover.isWatermarkWord(string)) {remover.addWatermarkWord(string);}}} else {// 此代码必须,必须对else进行处理super.processOperator(operator, operands);}}
}
public static void main (String [] args){String pdfPath = "d:/test.pdf";PDDocument document = PDDocument.load(pdfPath);WatermarkProcessor processor = new WatermarkProcessor();processor.init(document);if (processor.isWatermarkPDF()) {// 去除水印processor.removeWatermark();}
}

去除PDF文件中的斜体文字水印相关推荐

  1. 使用C#在PDF文件中插入或删除文本/图像水印

    PDF文件中经常使用水印.只需几个简单的步骤即可在PDF文件中插入或删除文本和图像水印.同样可以轻松控制用于处理水印的许多属性.例如,不透明度,位置,旋转度,颜色,字体,图像等,等等.同样,可以使用快 ...

  2. 使用itext7在PDF文件中的指定文字位置添加电子签名图片技术记录

    使用itext7在PDF文件中的指定文字位置添加电子签名图片 文章目录 使用itext7在PDF文件中的指定文字位置添加电子签名图片 一.技术使用背景 二.使用步骤 1.引入依赖 2.具体代码 2.控 ...

  3. Java去除PDF文件中的图片

    场景:调用第三方的接口取得的PDF文件流中有个不想要的图片,跟第三方接口沟通无果的情况下,自己想办法去掉吧. 解决思路:打算用PDFBox去掉PDF中的图片,但是在代码中resources.getIm ...

  4. 使用 C# 提取 PDF 文件中的所有文字(支持 .NET Core)

    PDF 是 Portable Document Format 的简称,意为"可携带文档格式",是由 Adobe Systems 用于与应用程序.操作系统.硬件无关的方式进行文件交换 ...

  5. PDF去水印的方法有哪些?怎么去除PDF文件中的水印

    PDF去水印的方法你知道几个?下载的文件中有水印使用起来就比较麻烦,一不小心就会触碰到版权问题,如果大家使用文件的过程中遇到有水印的文件,尽量将水印去除掉哦,下面小编为大家讲解去水印的方法,可以来了解 ...

  6. 如何将PDF文件中不需要的文字进行删除

    我们将PDF文件下载下来的时候有时候需要将PDF文件中的图片进行文件的修改,想要将PDF文件进行文件的编辑是一件不简单的事,但是如果使用PDF编辑器的话就简单的多了,想要给PDF文件中的文字进行文件的 ...

  7. Python提取PDF文件中的表格文本保存为Excel文件

    "Python小屋"编程比赛正式开始 推荐图书: <Python程序设计(第3版)>,(ISBN:978-7-302-55083-9),董付国,清华大学出版社,2020 ...

  8. java pdf添加透明水印_如何在PDF文件中添加透明水印

    原标题:如何在PDF文件中添加透明水印 有些文件添加水印,但是又不想水印影响文件的使用有时候会设置透明水印,那么PDF怎么设置透明水印呢,应该有很多的小伙伴们都很好奇应该怎么做吧,接下来就为大家分享一 ...

  9. 告诉你一个去除PDF文件水印简单快速的方法

    有时候下载一个pdf文件会发里面有水印,正在使用里面内容的时候很不方便,那么如何能去掉这些pdf中的水印呢?下面就告诉你一个去除PDF文件水印简单快速的方法. 方法/步骤 先安装一个迅捷pdf编辑器, ...

最新文章

  1. python 保留两位小数 实现方法
  2. SQL Server连接中的常见错误
  3. 【DP】Bovine Genetics G(P7152)
  4. SpringBoot写后端接口,看这一篇就够了!
  5. 教你在 Centos 7 中使用 DenyHosts防止ssh暴力破解(亲测)
  6. Jupyter Notebooks的安装和使用介绍
  7. 计算机系统的组成一般不包括,建筑设备监控子系统组成一般不包括( )A.中央计算机系统B.布线系统C.DDCD.各类传感器及执 - 作业在线问答...
  8. 汉高澳大利亚sinox2014电影播放flash最好的办法是安装游戏windows文本firefox
  9. 如何注册、发布 CSDN博客
  10. 2020-12-10 PMP 群内练习题 - 光环
  11. 删除一个数据库中所有数据的方法
  12. 鉴频鉴相器(PFD)不同结构讨论
  13. 根据配置好的图层文件(*.lyr),对加载的栅格数据进行渲染!
  14. python RTL自动生成_实例16:用Python自动生成Excel档每日领料单
  15. Joomla 一个实例建站过程
  16. 如何软著办理,软著申请步骤,软著办理流程
  17. 07-----给音视频文件添加字幕流
  18. 机器学习领域权威会议与期刊整理
  19. 华人最多的和比例最高的国家
  20. Type parameter T has incompatible upper bounds: ViewDataBinding and FragmentBeatBoxBindin

热门文章

  1. lol网通区服务器的位置,LOL英雄联盟转区系统地址在哪?
  2. Adobe国际认证设计师证书含金量怎么样?
  3. 甘特图是什么?如何快速搭建?
  4. 自己的邮箱还有在用吗,还安全吗?
  5. python代码使用cython进行加密
  6. 关于监控摄像头小程序直播使用流程及主要应用
  7. 数据可视化Matplotlib使用5-改变坐标轴的默认显示方式
  8. android app wifi密码,无广告查看wifi密码的软件-WiFi密码查看清爽版app下载V999安卓版-西西软件下载...
  9. android群聊红包原理,Android之微信抢红包实现原理分析
  10. Java如何实现手动连接数据库(Mysql或Oracle) | 超级详细,建议收藏