• ★itext-为pdf文件添加页眉页脚 | 3步完成 |


      由于上一篇自定义生成pdf的功能需求又增加了,需要加上页码。所以本博客诞生了~

      1. 通过继承PdfPageEventHelper类,实现需要实现的方法

      

    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'?>
    ");
            result.append(metadata);
            result.append("
    <?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()之前。

    
    

      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行业中被大家一直所使用,不仅仅因为它自身功能的强大,也因为它在很多地方提供了可供程序员操作的接口或者类,使得这门技术能够不断适合程序员的需求,而一个技术即便功能再强大,在全面,不开放可供他人操作的入口,依旧会被淘汰,因为其自身不可能适合所有程序员,只有程序员知道自己所需要的,那么对于程序员来说,根据公司需求做出功能,我们则要谨记为功能安全的开放部分入口,这样以便在后期需求更新时,能够尽可能的不改变源代码的基础上进行需求更新,同时,也方便了其他同事进行功能增强等一系列操作。以上纯属本人个人见解,若有不足,还望赐教~

    
    
    

      

  • 相关阅读:
    第1周作业
    第0次作业
    第三周作业
    随笔1
    第一次作业
    第二周作业
    第零次作业
    第四周作业
    第三周作业
    第二次作业
  • 原文地址:https://www.cnblogs.com/king-xg/p/7240467.html
Copyright © 2020-2023  润新知