因爲上一篇自定義生成pdf的功能需求又增長了,須要加上頁碼。因此本博客誕生了~css
1. 經過繼承PdfPageEventHelper類,實現須要實現的方法
html
import com.lowagie.text.Document; import com.lowagie.text.Element; import com.lowagie.text.Phrase; import com.lowagie.text.Rectangle; import com.lowagie.text.pdf.ColumnText; import com.lowagie.text.pdf.PdfPageEventHelper; import com.lowagie.text.pdf.PdfWriter; public class PdfBuilder extends PdfPageEventHelper {
// 噹噹前頁面開始加載時,觸發(頁眉) @Override public void onStartPage(PdfWriter writer, Document document) { // TODO Auto-generated method stub super.onStartPage(writer, document); }
// 噹噹前頁面初始化完成,切入document以前觸發(頁腳) @Override public void onEndPage(PdfWriter writer, Document document) { Rectangle rect = new Rectangle(0, 38, 50, 50); // ColumnText.showTextAligned(writer.getDirectContent(), Element.ALIGN_CENTER, // new Phrase(String.format(" ", writer.getPageNumber())), (rect.getLeft() + rect.getRight()) / 2, // rect.getBottom() - 18, 0); ColumnText.showTextAligned(writer.getDirectContent(), Element.ALIGN_BOTTOM, new Phrase(String.format("- %d -", writer.getPageNumber())), (rect.getLeft() + rect.getRight()) / 2, rect.getBottom() - 18, 0); } }
tip: writer.getPageNumber();可以獲取到當前頁面的頁碼
2. 重寫一下ITextRenderer類
import java.awt.Dimension; import java.awt.Rectangle; import java.awt.Shape; import java.io.BufferedReader; import java.io.File; import java.io.IOException; import java.io.OutputStream; import java.io.StringReader; import java.io.StringWriter; import java.io.UnsupportedEncodingException; import java.io.Writer; import java.util.List; import java.util.regex.Pattern; import javax.xml.transform.OutputKeys; import javax.xml.transform.Transformer; import javax.xml.transform.TransformerConfigurationException; import javax.xml.transform.TransformerException; import javax.xml.transform.TransformerFactory; import javax.xml.transform.dom.DOMSource; import javax.xml.transform.stream.StreamResult; import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.Node; import org.xhtmlrenderer.css.style.CalculatedStyle; import org.xhtmlrenderer.extend.NamespaceHandler; import org.xhtmlrenderer.extend.UserInterface; import org.xhtmlrenderer.layout.BoxBuilder; import org.xhtmlrenderer.layout.Layer; import org.xhtmlrenderer.layout.LayoutContext; import org.xhtmlrenderer.layout.SharedContext; import org.xhtmlrenderer.pdf.ITextFontContext; import org.xhtmlrenderer.pdf.ITextFontResolver; import org.xhtmlrenderer.pdf.ITextOutputDevice; import org.xhtmlrenderer.pdf.ITextRenderer; import org.xhtmlrenderer.pdf.ITextReplacedElementFactory; import org.xhtmlrenderer.pdf.ITextTextRenderer; import org.xhtmlrenderer.pdf.ITextUserAgent; import org.xhtmlrenderer.pdf.PDFCreationListener; import org.xhtmlrenderer.pdf.PDFEncryption; import org.xhtmlrenderer.render.BlockBox; import org.xhtmlrenderer.render.PageBox; import org.xhtmlrenderer.render.RenderingContext; import org.xhtmlrenderer.render.ViewportBox; import org.xhtmlrenderer.resource.XMLResource; import org.xhtmlrenderer.simple.extend.XhtmlNamespaceHandler; import org.xhtmlrenderer.util.Configuration; import org.xml.sax.InputSource; import com.lowagie.text.DocumentException; import com.lowagie.text.pdf.PdfPageEvent; import com.lowagie.text.pdf.PdfWriter; public class ITextRenderer2 { private ITextRenderer iTextRenderer = null; // These two defaults combine to produce an effective resolution of 96 px to the inch private static final float DEFAULT_DOTS_PER_POINT = 20f * 4f / 3f; private static final int DEFAULT_DOTS_PER_PIXEL = 20; private SharedContext _sharedContext; private ITextOutputDevice _outputDevice; private Document _doc; private BlockBox _root; private float _dotsPerPoint; private com.lowagie.text.Document _pdfDoc; private PdfWriter _writer; private PDFEncryption _pdfEncryption; // note: not hard-coding a default version in the _pdfVersion field as this may change between iText releases // check for null before calling writer.setPdfVersion() // use one of the values in PDFWriter.VERSION... private Character _pdfVersion; private char[] validPdfVersions = new char[] { PdfWriter.VERSION_1_2, PdfWriter.VERSION_1_3, PdfWriter.VERSION_1_4, PdfWriter.VERSION_1_5, PdfWriter.VERSION_1_6, PdfWriter.VERSION_1_7 }; private PDFCreationListener _listener; private PdfPageEvent pdfPageEvent; public ITextRenderer2(ITextRenderer iTextRenderer) { this(DEFAULT_DOTS_PER_POINT, DEFAULT_DOTS_PER_PIXEL); this.iTextRenderer = iTextRenderer; } public ITextRenderer2() { this(DEFAULT_DOTS_PER_POINT, DEFAULT_DOTS_PER_PIXEL); } public ITextRenderer2(float dotsPerPoint, int dotsPerPixel) { _dotsPerPoint = dotsPerPoint; _outputDevice = new ITextOutputDevice(_dotsPerPoint); ITextUserAgent userAgent = new ITextUserAgent(_outputDevice); _sharedContext = new SharedContext(userAgent); userAgent.setSharedContext(_sharedContext); _outputDevice.setSharedContext(_sharedContext); ITextFontResolver fontResolver = new ITextFontResolver(_sharedContext); _sharedContext.setFontResolver(fontResolver); ITextReplacedElementFactory replacedElementFactory = new ITextReplacedElementFactory(_outputDevice); _sharedContext.setReplacedElementFactory(replacedElementFactory); _sharedContext.setTextRenderer(new ITextTextRenderer()); _sharedContext.setDPI(72 * _dotsPerPoint); _sharedContext.setDotsPerPixel(dotsPerPixel); _sharedContext.setPrint(true); _sharedContext.setInteractive(false); } public PdfPageEvent getPdfPageEvent() { return pdfPageEvent; } public void setPdfPageEvent(PdfPageEvent pdfPageEvent) { this.pdfPageEvent = pdfPageEvent; } public ITextFontResolver getFontResolver() { return (ITextFontResolver) _sharedContext.getFontResolver(); } private Document loadDocument(final String uri) { return _sharedContext.getUac().getXMLResource(uri).getDocument(); } public void setDocument(String uri) { setDocument(loadDocument(uri), uri); } public void setDocument(Document doc, String url) { setDocument(doc, url, new XhtmlNamespaceHandler()); } public void setDocument(File file) throws IOException { File parent = file.getAbsoluteFile().getParentFile(); setDocument(loadDocument(file.toURI().toURL().toExternalForm()), (parent == null ? "" : parent.toURI().toURL() .toExternalForm())); } public void setDocumentFromString(String content) { InputSource is = new InputSource(new BufferedReader(new StringReader(content))); Document dom = XMLResource.load(is).getDocument(); setDocument(dom, null); } public void setDocument(Document doc, String url, NamespaceHandler nsh) { _doc = doc; getFontResolver().flushFontFaceFonts(); _sharedContext.reset(); if (Configuration.isTrue("xr.cache.stylesheets", true)) { _sharedContext.getCss().flushStyleSheets(); } else { _sharedContext.getCss().flushAllStyleSheets(); } _sharedContext.setBaseURL(url); _sharedContext.setNamespaceHandler(nsh); _sharedContext.getCss().setDocumentContext(_sharedContext, _sharedContext.getNamespaceHandler(), doc, new NullUserInterface()); getFontResolver().importFontFaces(_sharedContext.getCss().getFontFaceRules()); } public PDFEncryption getPDFEncryption() { return _pdfEncryption; } public void setPDFEncryption(PDFEncryption pdfEncryption) { _pdfEncryption = pdfEncryption; } public void setPDFVersion(char _v) { for (int i = 0; i < validPdfVersions.length; i++) { if (_v == validPdfVersions[i]) { _pdfVersion = new Character(_v); return; } } throw new IllegalArgumentException("Invalid PDF version character; use " + "valid constants from PdfWriter (e.g. PdfWriter.VERSION_1_2)"); } public char getPDFVersion() { return _pdfVersion == null ? '0' : _pdfVersion.charValue(); } public void layout() { LayoutContext c = newLayoutContext(); BlockBox root = BoxBuilder.createRootBox(c, _doc); root.setContainingBlock(new ViewportBox(getInitialExtents(c))); root.layout(c); Dimension dim = root.getLayer().getPaintingDimension(c); root.getLayer().trimEmptyPages(c, dim.height); root.getLayer().layoutPages(c); _root = root; } private Rectangle getInitialExtents(LayoutContext c) { PageBox first = Layer.createPageBox(c, "first"); return new Rectangle(0, 0, first.getContentWidth(c), first.getContentHeight(c)); } private RenderingContext newRenderingContext() { RenderingContext result = _sharedContext.newRenderingContextInstance(); result.setFontContext(new ITextFontContext()); result.setOutputDevice(_outputDevice); _sharedContext.getTextRenderer().setup(result.getFontContext()); result.setRootLayer(_root.getLayer()); return result; } private LayoutContext newLayoutContext() { LayoutContext result = _sharedContext.newLayoutContextInstance(); result.setFontContext(new ITextFontContext()); _sharedContext.getTextRenderer().setup(result.getFontContext()); return result; } public void createPDF(OutputStream os) throws DocumentException { createPDF(os, true, 0); } public void writeNextDocument() throws DocumentException { writeNextDocument(0); } public void writeNextDocument(int initialPageNo) throws DocumentException { List pages = _root.getLayer().getPages(); RenderingContext c = newRenderingContext(); c.setInitialPageNo(initialPageNo); PageBox firstPage = (PageBox) pages.get(0); com.lowagie.text.Rectangle firstPageSize = new com.lowagie.text.Rectangle(0, 0, firstPage.getWidth(c) / _dotsPerPoint, firstPage.getHeight(c) / _dotsPerPoint); _outputDevice.setStartPageNo(_writer.getPageNumber()); _pdfDoc.setPageSize(firstPageSize); _pdfDoc.newPage(); writePDF(pages, c, firstPageSize, _pdfDoc, _writer); } public void finishPDF() { if (_pdfDoc != null) { fireOnClose(); _pdfDoc.close(); } } public void createPDF(OutputStream os, boolean finish) throws DocumentException { createPDF(os, finish, 0); } /** * <B>NOTE:</B> Caller is responsible for cleaning up the OutputStream if something goes wrong. */ public void createPDF(OutputStream os, boolean finish, int initialPageNo) throws DocumentException { List pages = _root.getLayer().getPages(); RenderingContext c = newRenderingContext(); c.setInitialPageNo(initialPageNo); PageBox firstPage = (PageBox) pages.get(0); com.lowagie.text.Rectangle firstPageSize = new com.lowagie.text.Rectangle(0, 0, firstPage.getWidth(c) / _dotsPerPoint, firstPage.getHeight(c) / _dotsPerPoint); com.lowagie.text.Document doc = new com.lowagie.text.Document(firstPageSize, 0, 0, 0, 0); PdfWriter writer = PdfWriter.getInstance(doc, os); if (_pdfVersion != null) { writer.setPdfVersion(_pdfVersion.charValue()); } if (_pdfEncryption != null) { writer.setEncryption(_pdfEncryption.getUserPassword(), _pdfEncryption.getOwnerPassword(), _pdfEncryption.getAllowedPrivileges(), _pdfEncryption.getEncryptionType()); } _pdfDoc = doc; _writer = writer; firePreOpen(); if (pdfPageEvent != null) { writer.setPageEvent(pdfPageEvent); } doc.open(); writePDF(pages, c, firstPageSize, doc, writer); if (finish) { fireOnClose(); doc.close(); } } private void firePreOpen() { if (_listener != null) { _listener.preOpen(iTextRenderer); } } private void fireOnClose() { if (_listener != null) { _listener.onClose(iTextRenderer); } } private void writePDF(List pages, RenderingContext c, com.lowagie.text.Rectangle firstPageSize, com.lowagie.text.Document doc, PdfWriter writer) throws DocumentException { _outputDevice.setRoot(_root); _outputDevice.start(_doc); _outputDevice.setWriter(writer); _outputDevice.initializePage(writer.getDirectContent(), firstPageSize.getHeight()); _root.getLayer().assignPagePaintingPositions(c, Layer.PAGED_MODE_PRINT); int pageCount = _root.getLayer().getPages().size(); c.setPageCount(pageCount); for (int i = 0; i < pageCount; i++) { PageBox currentPage = (PageBox) pages.get(i); c.setPage(i, currentPage); paintPage(c, writer, currentPage); _outputDevice.finishPage(); if (i != pageCount - 1) { PageBox nextPage = (PageBox) pages.get(i + 1); com.lowagie.text.Rectangle nextPageSize = new com.lowagie.text.Rectangle(0, 0, nextPage.getWidth(c) / _dotsPerPoint, nextPage.getHeight(c) / _dotsPerPoint); doc.setPageSize(nextPageSize); doc.newPage(); _outputDevice.initializePage(writer.getDirectContent(), nextPageSize.getHeight()); } } _outputDevice.finish(c, _root); } private void paintPage(RenderingContext c, PdfWriter writer, PageBox page) { provideMetadataToPage(writer, page); page.paintBackground(c, 0, Layer.PAGED_MODE_PRINT); page.paintMarginAreas(c, 0, Layer.PAGED_MODE_PRINT); page.paintBorder(c, 0, Layer.PAGED_MODE_PRINT); Shape working = _outputDevice.getClip(); Rectangle content = page.getPrintClippingBounds(c); _outputDevice.clip(content); int top = -page.getPaintingTop() + page.getMarginBorderPadding(c, CalculatedStyle.TOP); int left = page.getMarginBorderPadding(c, CalculatedStyle.LEFT); _outputDevice.translate(left, top); _root.getLayer().paint(c); _outputDevice.translate(-left, -top); _outputDevice.setClip(working); } private void provideMetadataToPage(PdfWriter writer, PageBox page) { byte[] metadata = null; if (page.getMetadata() != null) { try { String metadataBody = stringfyMetadata(page.getMetadata()); if (metadataBody != null) { metadata = createXPacket(stringfyMetadata(page.getMetadata())).getBytes("UTF-8"); } } catch (UnsupportedEncodingException e) { // Can't happen throw new RuntimeException(e); } } writer.setPageXmpMetadata(metadata); } private String stringfyMetadata(Element element) { Element target = getFirstChildElement(element); if (target == null) { return null; } try { TransformerFactory factory = TransformerFactory.newInstance(); Transformer transformer = factory.newTransformer(); transformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "yes"); StringWriter output = new StringWriter(); transformer.transform(new DOMSource(target), new StreamResult(output)); return output.toString(); } catch (TransformerConfigurationException e) { // Things must be in pretty bad shape to get here so // rethrow as runtime exception throw new RuntimeException(e); } catch (TransformerException e) { throw new RuntimeException(e); } } private static Element getFirstChildElement(Element element) { Node n = element.getFirstChild(); while (n != null) { if (n.getNodeType() == Node.ELEMENT_NODE) { return (Element) n; } n = n.getNextSibling(); } return null; } private String createXPacket(String metadata) { StringBuffer result = new StringBuffer(metadata.length() + 50); result.append("<?xpacket begin='\uFEFF' id='W5M0MpCehiHzreSzNTczkc9d'?>\n"); result.append(metadata); result.append("\n<?xpacket end='r'?>"); return result.toString(); } public ITextOutputDevice getOutputDevice() { return _outputDevice; } public SharedContext getSharedContext() { return _sharedContext; } public void exportText(Writer writer) throws IOException { RenderingContext c = newRenderingContext(); c.setPageCount(_root.getLayer().getPages().size()); _root.exportText(c, writer); } public BlockBox getRootBox() { return _root; } public float getDotsPerPoint() { return _dotsPerPoint; } public List findPagePositionsByID(Pattern pattern) { return _outputDevice.findPagePositionsByID(newLayoutContext(), pattern); } private static final class NullUserInterface implements UserInterface { public boolean isHover(Element e) { return false; } public boolean isActive(Element e) { return false; } public boolean isFocus(Element e) { return false; } } public PDFCreationListener getListener() { return _listener; } public void setListener(PDFCreationListener listener) { _listener = listener; } public PdfWriter getWriter() { return _writer; } }
tip: 因爲繼承沒法獲取部分私有變量和方法,動態代理方式在此處不可用,於是選擇重寫該類,在重寫的類ITextRenderer2中維護了一個ITextRenderer對象,使其內部功能正常執行,咱們只須要爲ITextRenderer2額外維護一個PdfPageEvent對象,而後在createPDF 方法中,非空賦值一下便可。注意爲PdfWriter設置PdfPageEvent實現類的操做必須在document.open()以前。java
3. 拿到html文件,進行html轉pdf操做程序員
String url = htmlFile.toURI().toURL().toString(); ITextRenderer2 renderer = new ITextRenderer2(new ITextRenderer()); renderer.setDocument(url); ITextFontResolver fontResolver = renderer.getFontResolver(); fontResolver.addFont(pdfFont + File.separator + "SimSun.ttc", BaseFont.IDENTITY_H, BaseFont.NOT_EMBEDDED); fontResolver .addFont(pdfFont + File.separator + "Arial.ttf", BaseFont.IDENTITY_H, BaseFont.NOT_EMBEDDED); renderer.setPdfPageEvent(new PDFBuilder()); renderer.layout(); renderer.createPDF(fos);
總結: 用了個人上一篇pdf生成技術(java原裝代碼完成pdf在線預覽和pdf打印及下載)的童鞋,若是也碰到了這樣的需求,能夠參考一下本篇博客哦
代碼改動量少,總共增長了2個類,改動了一行代碼,便可完成pdf的頁眉頁腳的添加。安全
經過這個例子,其實對於咱們是有啓示的,一種技術他能長久的在it行業中被你們一直所使用,不單單由於它自身功能的強大,也由於它在不少地方提供了可供程序員操做的接口或者類,使得這門技術可以不斷適合程序員的需求,而一個技術即使功能再強大,在全面,不開放可供他人操做的入口,依舊會被淘汰,由於其自身不可能適合全部程序員,只有程序員知道本身所須要的,那麼對於程序員來講,根據公司需求作出功能,咱們則要謹記爲功能安全的開放部分入口,這樣以便在後期需求更新時,可以儘量的不改變源代碼的基礎上進行需求更新,同時,也方便了其餘同事進行功能加強等一系列操做。以上純屬本人我的看法,如有不足,還望賜教~app