前段時間,為瞭解析PDF,花了不少時間去學習PDFbox和itext,這兩個都是處理PDF的開源庫,有java和C#的。作為一個剛開始學習這兩個開源庫的,感覺百度上的資源還是太少了。我做的是一個關於PDF的處理,在百度上找了半天都沒找到答案,最後去itext的官網和Stack Overflow上找到了答案。最後比較了一下,pdfbox和itext相對而言,itext的功能要強不少,本人對比過itext和pdfbox處理pdf 檔案時候的速度itext要快一些,而且itext官網給出的例子和一些問題(這些問題都是從Stack Overflow上面的問題),所以我最後選擇了itext,itext5例子這個連結是itext5官方的例子,非常需要注意的一個地方就是,不同版本的itext對於解決同一個問題的代碼可以不同,個人覺得itext7相對於itext5的改變挺大的。下面我的一些例子都是itext5.5.11版本的,這個連結可以下載itext5.5.11的jar包itext5.5.11 jar下載,itext dll這個連結可以下載itext5.5.11的dll包。其實java和c#在使用itext的時候都是一樣的。下面我會,給出幾個關於PDF操作的一些例子。都是一些處理PDF的例子,對於如何去製作一個PDF檔案可以去itext官網找資料。在itext中處理PDF檔案都是以字典對象進行封裝的,這個和PDF的結構是一樣的,可以去參考PDF reference。
一、匯出PDF中的子圖片
public static void extractImage(String filename){PdfReader reader = null;try {//讀取pdf檔案reader = new PdfReader(filename);//獲得pdf檔案的頁數int sumPage = reader.getNumberOfPages();//讀取pdf檔案中的每一頁for(int i = 1;i <= sumPage;i++){//得到pdf每一頁的字典對象PdfDictionary dictionary = reader.getPageN(i);//通過RESOURCES得到對應的字典對象PdfDictionary res = (PdfDictionary) PdfReader.getPdfObject(dictionary.get(PdfName.RESOURCES));//得到XOBJECT圖片對象PdfDictionary xobj = (PdfDictionary) PdfReader.getPdfObject(res.get(PdfName.XOBJECT));if(xobj != null){for(Iterator it = xobj.getKeys().iterator();it.hasNext();){PdfObject obj = xobj.get((PdfName)it.next());if(obj.isIndirect()){PdfDictionary tg = (PdfDictionary) PdfReader.getPdfObject(obj);PdfName type = (PdfName) PdfReader.getPdfObject(tg.get(PdfName.SUBTYPE));if(PdfName.IMAGE.equals(type)){PdfObject object = reader.getPdfObject(obj);if(object.isStream()){PRStream prstream = (PRStream)object;byte[] b;try{b = reader.getStreamBytes(prstream);}catch(UnsupportedPdfException e){b = reader.getStreamBytesRaw(prstream);}FileOutputStream output = new FileOutputStream(String.format("d:/pdf/output%d.jpg",i));output.write(b);output.flush();output.close();}}}}}}} catch (IOException e) {// TODO Auto-generated catch blocke.printStackTrace();}
}
這個例子是我在百度上搜到的,但是並不完整,這個是匯出PDF中的圖片,我將他補充完整了。這個程式有問題,可以對於某些PDF檔案匯出的圖片無法開啟。
二、去除PDF檔案的浮水印字型
/** * <a href="http://stackoverflow.com/questions/35526822/removing-watermark-from-pdf-itextsharp"> * Removing Watermark from PDF iTextSharp * </a> * <p> * This class presents a simple content stream editing framework. As is it creates an equivalent * copy of the original page content stream. To actually edit, simply overwrite the method * {@link #write(PdfContentStreamProcessor, PdfLiteral, List)} to not (as in this class) write * the given operations as they are but change them in some fancy way. * </p> * * @author mkl */public class PdfContentStreamEditor extends PdfContentStreamProcessor{public static void main(String[] args) {try {PdfReader reader = new PdfReader("input.pdf");OutputStream result = new FileOutputStream(new File("out.pdf"));PdfStamper pdfStamper = new PdfStamper(reader, result);PdfContentStreamEditor identityEditor = new PdfContentStreamEditor();for(int i = 1;i <= reader.getNumberOfPages();i++){identityEditor.editPage(pdfStamper, i);}pdfStamper.close();} catch (IOException e) {// TODO Auto-generated catch blocke.printStackTrace();} catch (DocumentException e) {// TODO Auto-generated catch blocke.printStackTrace();}} /** * This method edits the immediate contents of a page, i.e. its content stream. * It explicitly does not descent into form xobjects, patterns, or annotations. */ public void editPage(PdfStamper pdfStamper, int pageNum) throws IOException { PdfReader pdfReader = pdfStamper.getReader(); PdfDictionary page = pdfReader.getPageN(pageNum); byte[] pageContentInput = ContentByteUtils.getContentBytesForPage(pdfReader, pageNum); page.remove(PdfName.CONTENTS); editContent(pageContentInput, page.getAsDict(PdfName.RESOURCES), pdfStamper.getUnderContent(pageNum)); } /** * This method processes the content bytes and outputs to the given canvas. * It explicitly does not descent into form xobjects, patterns, or annotations. */ public void editContent(byte[] contentBytes, PdfDictionary resources, PdfContentByte canvas) { this.canvas = canvas; processContent(contentBytes, resources); this.canvas = null; } /** * <p> * This method writes content stream operations to the target canvas. The default * implementation writes them as they come, so it essentially generates identical * copies of the original instructions the {@link ContentOperatorWrapper} instances * forward to it. * </p> * <p> * Override this method to achieve some fancy editing effect. * </p> */ protected void write(PdfContentStreamProcessor processor, PdfLiteral operator, List<PdfObject> operands) throws IOException { int index = 0; for (PdfObject object : operands) { object.toPdf(canvas.getPdfWriter(), canvas.getInternalBuffer()); canvas.getInternalBuffer().append(operands.size() > ++index ? (byte) ' ' : (byte) '\n'); } } // // constructor giving the parent a dummy listener to talk to // public PdfContentStreamEditor() { super(new DummyRenderListener()); } // // Overrides of PdfContentStreamProcessor methods // @Override public ContentOperator registerContentOperator(String operatorString, ContentOperator operator) { ContentOperatorWrapper wrapper = new ContentOperatorWrapper(); wrapper.setOriginalOperator(operator); ContentOperator formerOperator = super.registerContentOperator(operatorString, wrapper); return formerOperator instanceof ContentOperatorWrapper ? ((ContentOperatorWrapper)formerOperator).getOriginalOperator() : formerOperator; } @Override public void processContent(byte[] contentBytes, PdfDictionary resources) { this.resources = resources; super.processContent(contentBytes, resources); this.resources = null; } // // members holding the output canvas and the resources // protected PdfContentByte canvas = null; protected PdfDictionary resources = null; // // A content operator class to wrap all content operators to forward the invocation to the editor // class ContentOperatorWrapper implements ContentOperator { public ContentOperator getOriginalOperator() { return originalOperator; } public void setOriginalOperator(ContentOperator originalOperator) { this.originalOperator = originalOperator; } @Override public void invoke(PdfContentStreamProcessor processor, PdfLiteral operator, ArrayList<PdfObject> operands) throws Exception { if (originalOperator != null && !"Do".equals(operator.toString())) { originalOperator.invoke(processor, operator, operands); } write(processor, operator, operands); } private ContentOperator originalOperator = null; } // // A dummy render listener to give to the underlying content stream processor to feed events to // static class DummyRenderListener implements RenderListener { @Override public void beginTextBlock() { } @Override public void renderText(TextRenderInfo renderInfo) { } @Override public void endTextBlock() { } @Override public void renderImage(ImageRenderInfo renderInfo) { } }}上面這個類是官方給出的一個工具類
public static void main(String[] args) {try {PdfReader pdfReader = new PdfReader("d:/1.pdf");FileOutputStream os = new FileOutputStream("d:/reader.pdf");PdfStamper stamper = new PdfStamper(pdfReader,os);PdfContentStreamEditor editor = new PdfContentStreamEditor(){@Overrideprotected void write(PdfContentStreamProcessor processor, PdfLiteral operator, List<PdfObject> operands)throws IOException {String operatorString = operator.toString();//Tj 操作通過當前的字型和其他文字相關的圖形狀態參數來取走一串操作和繪製相應的字形//Tr操作設定的文本渲染模式//一個文字物件開始於BT,結束於ETfinal List<String> TEXT_SHOWING_OPERATORS = Arrays.asList("Tj","'","\\","TJ");System.out.println(operatorString);if(TEXT_SHOWING_OPERATORS.contains(operatorString)){PdfDictionary dic = gs().getFont().getFontDictionary();if(gs().getFont().getPostscriptFontName().endsWith("BoldMT")){//BoldMT字型的名稱return;}}super.write(processor, operator, operands);}};for(int i = 1;i <= pdfReader.getNumberOfPages();i++){editor.editPage(stamper, i);}stamper.close();} catch (IOException e) {// TODO Auto-generated catch blocke.printStackTrace();} catch (DocumentException e) {// TODO Auto-generated catch blocke.printStackTrace();}}
用itext處理PDF檔案的時候RenderListener非常有用,他是itext裡面的一個介面,你可以建立一個類去實現它,然後重新它的方法,可以在裡面寫處理PDF檔案具體功能。這個介面,提供了處理文字和圖片的方法需要你重寫。我是在Stack Overflow上面找到的,真的就像那個說的一樣,就是萬能的。