pdf去除文本水印

使用工具包itextpdf-5.5.13.4.jar
优势:此方法是直接对pdf的文件流进行操作,通过itextpdf工具包,扫描到倾斜的文本水印,删除对应流数据,然后重新拼接流,生成新的pdf文件; 而不是通过覆盖,隐藏等方式,虽然不显示,但是水印数据还是在文件里。

代码如下:

package aa;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Map;
import java.util.TreeMap;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.zip.DeflaterOutputStream;
import java.util.zip.InflaterInputStream;

import com.itextpdf.text.pdf.MemoryLimitsAwareException;
import com.itextpdf.text.pdf.PRStream;
import com.itextpdf.text.pdf.PdfArray;
import com.itextpdf.text.pdf.PdfDictionary;
import com.itextpdf.text.pdf.PdfName;
import com.itextpdf.text.pdf.PdfObject;
import com.itextpdf.text.pdf.PdfReader;
import com.itextpdf.text.pdf.RandomAccessFileOrArray;

/**
 * 文本水印消除器
 * 
 * @author liubing1
 *
 */
public class TextWaterMarkerRemover {
	// 水印命中正则(Tm pdf转置操作符; Tj pdf文本操作符 )
	private static Pattern pt = Pattern.compile("[0-9\\.\\s]+Tm[\r\n]+\\(.+\\)Tj");
	// pdf页面数据
	private  Map<Long, DataInfo> pageContent = new TreeMap<Long, DataInfo>();
	// 源文件
	private String srcPdf;
	// 生成目标文件
	private String destPdf;

	public TextWaterMarkerRemover(String src, String dest) {
		this.destPdf = dest;
		this.srcPdf = src;
	}

	public void work() {
		InputStream is=null;
		OutputStream os=null;
		PdfReader reader=null;
		try {
			is = new FileInputStream(srcPdf);
			os = new FileOutputStream(destPdf);
            
		    reader = new PdfReader(srcPdf);
			int numPages = reader.getNumberOfPages();
			RandomAccessFileOrArray rf = reader.getSafeFile();
			// 扫描水印内容,并将其替换
			for (int i = 1; i <= numPages; i++) {
				handlePageContent(i, reader, rf);
			}

			long index = 0;
			for (Long beg : pageContent.keySet()) {
				DataInfo info = pageContent.get(beg);
				byte[] dd = new byte[(int) (beg - index)];
				is.read(dd);
				os.write(dd);
				is.skip(info.end - info.beg);
				index = info.end;
				os.write(info.data);
				os.flush();
			}
			byte[] dd = new byte[1024];
			int i = 0;
			while ((i = is.read(dd)) > -1) {
				os.write(dd, 0, i);
			}
			os.flush();
		} catch (Exception e) {
			e.printStackTrace();
		}finally {
			if(is!=null) {
				try {
					is.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
			reader.close();
			try {
				if(os!=null) {
					os.close();
				}
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
	}

	public void handlePageContent(int pageNum, PdfReader reader, RandomAccessFileOrArray file) throws Exception {
		PdfDictionary page = reader.getPageNRelease(pageNum);
		if (page == null) {
			return;
		}
		PdfObject contents = PdfReader.getPdfObjectRelease(page.get(PdfName.CONTENTS));
		if (contents == null) {
			return;
		}
		if (contents.isStream()) {
			getStreamBytes((PRStream) contents, file);
		}
		if (contents.isArray()) {
			PdfArray array = (PdfArray) contents;
			for (int k = 0; k < array.size(); ++k) {
				PdfObject item = PdfReader.getPdfObjectRelease(array.getPdfObject(k));
				if (item == null || !item.isStream())
					continue;
				getStreamBytes((PRStream) item, file);
				if (k == array.size() - 1)
					continue;
			}
		}
	}

	public void getStreamBytes(PRStream stream, RandomAccessFileOrArray file) throws IOException {
		byte[] b;// 原数据
		if (stream.getOffset() < 0L) {
			b = stream.getBytes();
		} else {
			b = new byte[stream.getLength()];
			file.seek(stream.getOffset());
			long beg = file.getFilePointer();
			file.readFully(b);
			long end = file.getFilePointer();
			// 文件内容解压
			byte[] b2 = FlateDecode(b, true);
			if (b2 == null) {
				b2 = FlateDecode(b, false);
			}
			if (b2 != null && b2.length > 0) {
				// 字节数组和字符串转换,必须使用ISO-8859-1,否则会造成数据丢失
				String str = new String(b2, "ISO-8859-1");
				Matcher mm = pt.matcher(str);
				while (mm.find()) {
					String a = mm.group();
					String[] arr = a.split(" ");
					// 获取转置矩阵,倾斜度的属性不为0; 文本倾斜
					if (arr.length > 2 && !arr[1].equals("0") && !arr[2].equals("0")) {
						str = str.replace(a, "");
					}
				}
				byte[] bb = str.getBytes("ISO-8859-1");
				// 文件内容再次压缩
				bb = FlateEncode(bb);
				pageContent.put(beg, new DataInfo(beg, end, bb));
			}
		}
	}

	// 压缩
	private static byte[] FlateEncode(byte[] in) {
		ByteArrayOutputStream out = new ByteArrayOutputStream();
		DeflaterOutputStream os = new DeflaterOutputStream(out);
		try {
			for (byte bb : in) {
				os.write(bb);
			}
			out.close();
			os.close();
			byte[] arrby = out.toByteArray();
			return arrby;
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			try {
				out.close();
			} catch (IOException iOException) {
			}
		}
		return null;
	}

	// 解压
	private static byte[] FlateDecode(byte[] in, boolean strict) {
		ByteArrayOutputStream out = new ByteArrayOutputStream();
		ByteArrayInputStream stream = new ByteArrayInputStream(in);
		InflaterInputStream zip = new InflaterInputStream(stream);
		byte[] b = new byte[strict ? 4092 : 1];
		try {
			int n;
			while ((n = zip.read(b)) >= 0) {
				out.write(b, 0, n);
			}
			zip.close();
			out.close();
			byte[] arrby = out.toByteArray();
			return arrby;
		} catch (MemoryLimitsAwareException e) {
			throw e;
		} catch (Exception e) {
			if (strict) {
				byte[] arrby = null;
				return arrby;
			}
			byte[] arrby = out.toByteArray();
			return arrby;
		} finally {
			try {
				zip.close();
			} catch (IOException iOException) {
			}
			try {
				out.close();
			} catch (IOException iOException) {
			}
		}
	}

	private static class DataInfo {
		private long beg;
		private long end;
		private byte[] data;

		public DataInfo(long beg, long end, byte[] data) {
			this.beg = beg;
			this.end = end;
			this.data = data;
		}
	}

	public static void main(String[] args) throws Exception {
		long beg = System.currentTimeMillis();
		new TextWaterMarkerRemover("1.pdf","11.pdf").work();
		System.out.println(System.currentTimeMillis() - beg);
	}
}

Logo

腾讯云面向开发者汇聚海量精品云计算使用和开发经验,营造开放的云计算技术生态圈。

更多推荐