美文网首页
itext 导出pdf,页码、目录

itext 导出pdf,页码、目录

作者: o尐白 | 来源:发表于2019-03-15 11:18 被阅读0次

    唉,搜个解决方式真难,网上各种版本,各种复制,关键用着还不爽,真有意思。
    只好自己研究了,希望有个只需要关注业务代码的就好了。
    底层类我放在了最下面。
    不想说话,直接上代码吧、

    maven版本

            <dependency>
                <groupId>com.itextpdf</groupId>
                <artifactId>itextpdf</artifactId>
                <version>5.5.13</version>
            </dependency>
            <dependency>
                <groupId>com.itextpdf</groupId>
                <artifactId>itext-asian</artifactId>
                <version>5.2.0</version>
            </dependency>
    

    仅导出页码code

    这种只需要关注自己的业务代码就可以了,
    底层类复制后,直接在writeBody中写自己的逻辑代码就好了

    import com.alibaba.fastjson.JSONObject;
    import com.itextpdf.text.Chapter;
    import com.itextpdf.text.Rectangle;
    import com.itextpdf.text.Section;
    import com.itextpdf.text.pdf.PdfPTable;
    import lombok.extern.slf4j.Slf4j;
    
    import java.util.List;
    
    /**
     * {
     *     "title":"班级表",
     *     "students":[
     *         {
     *             "id":1,
     *             "name":"小明",
     *             "age":12,
     *             "remark":"小明小明一把手雷弹"
     *         },
     *         {
     *             "id":2,
     *             "name":"小红",
     *             "age":12,
     *             "remark":"小红小红快来我加玩"
     *         }
     *     ],
     *     "teachers":[
     *         {
     *             "id":1,
     *             "name":"小铭",
     *             "age":22,
     *             "remark":"这老师是教...你猜"
     *         },
     *         {
     *             "id":2,
     *             "name":"小鸣",
     *             "age":23,
     *             "remark":"对不起,我只负责打孩纸"
     *         }
     *     ]
     * }
     */
    @Slf4j
    public class TestPdfView extends AbstractPdfView {
    
        private JSONObject json;
    
        private int chapterSeq = 1;             //书签序号
    
        public TestPdfView(JSONObject json) {
            this.json = json;
        }
    
        @Override
        protected void writeBody() throws Exception {
            buildTitle();
            //正文开始
            document.newPage();
            buildTeacher();
            buildStudent();
        }
    
        private void buildTitle() throws Exception {
            String title = json.getString("title");
            document.addTitle(title);
            document.newPage();
            PdfUtil.setLogo(document, title);
        }
    
        private void buildTeacher() throws Exception {
            Chapter chapter = getChapter("教师列表");
    
            List<JSONObject> teachers = json.getJSONArray("teachers").toJavaList(JSONObject.class);
            for (JSONObject j : teachers) {
                Section section = addSection(chapter, getKey(j,"name"));
                PdfPTable table = PdfUtil.getBorderTable(4, Rectangle.BOX);
                table.addCell(addFont10(getKey(j, "id")));
                table.addCell(addFont10(getKey(j, "name")));
                table.addCell(addFont10(getKey(j, "age")));
                table.addCell(addFont10(getKey(j, "remark")));
                document.add(table);
            }
        }
    
        private void buildStudent() throws Exception {
            Chapter chapter = getChapter("学生列表");
    
            List<JSONObject> students = json.getJSONArray("students").toJavaList(JSONObject.class);
            for (JSONObject j : students) {
                Section section = addSection(chapter, getKey(j,"name"));
                PdfPTable table = PdfUtil.getBorderTable(4, Rectangle.BOX);
                table.addCell(addFont10(getKey(j, "id")));
                table.addCell(addFont10(getKey(j, "name")));
                table.addCell(addFont10(getKey(j, "age")));
                table.addCell(addFont10(getKey(j, "remark")));
                document.add(table);
            }
        }
    
        private Chapter getChapter(String title) throws Exception {
            Chapter chapter = new Chapter(addFont12(title), chapterSeq++);
            chapter.setTriggerNewPage(false);
            chapter.setNumberStyle(Section.NUMBERSTYLE_DOTTED);
            document.add(chapter);
    
            return chapter;
        }
    
        private Section addSection(Chapter chapter, String title) throws Exception {
            Section section = chapter.addSection(addFont12(title));
            section.setIndentationLeft(8f);
            section.setNumberStyle(Section.NUMBERSTYLE_DOTTED);
            document.add(section);
    
            return section;
        }
    
        private String getKey(JSONObject j, String key) {
            return String.valueOf(j.get(key));
        }
    }
    

    效果图


    image.png
    image.png

    导出目录

    这个只关注业务代码其实也可以搞,麻烦些也可以实现,但我这人有个优点:懒,蟹蟹。

    导出目录比较麻烦:试过几种方法,最终思路:
    1.先导出正文,得到目录结构与其对应页码的缓存
    2.导出目录到新的document中,得到目录占用的页数
    3.合并俩个document,此时目录缓存的页码=原页码+目录总页码
    注:该方法不支持点击目录跳转页码,所以可以使用标签来进行定位。
    所以,标签中的值也需要重新计算。
    不想说话,不想上代码,啊哈哈哈
    效果图


    image.png
    image.png
    image.png

    好吧,代码不复杂,须重写抽象类的finish方法:
    1.获取目录+标题应占用的页数
    2.创建目录页(注意目录最后的页数应该是多少)
    3.调用父类finish方法(注意reader的顺序)
    因目录不支持点击跳转,所以替代方案使用标签
    效果图

    image.png

    所以,如果要搞一个只需要关注业务逻辑的话,思路应该是:
    1.有个缓存用于存放目录缓存
    2.有个抽象标题方法,用户自定义标题,仅支持一页
    3.添加一个根据目录缓存构造目录的方法
    4.改造finish方法:合并标题、目录与正文,此处应重写目录对应的页码
    5.欢迎大家试着写一下,最好留言给个链接,嘿嘿嘿嘿

    工具类PdfUtil:

    我喜欢所有排版都使用table,主要是简单方便还好用

    
    import com.itextpdf.text.*;
    import com.itextpdf.text.pdf.BaseFont;
    import com.itextpdf.text.pdf.PdfPTable;
    import org.springframework.core.io.ClassPathResource;
    
    public class PdfUtil {
    
        private static final int A4_WIDTH = 595;
        private static final int A4_HEIGHT = 842; 
        private static final int MARGIN_TOP = 36; 
        private static final int MARGIN_BOTTOM = 36;
        private static final int MARGIN_LEFT = 57; 
        private static final int MARGIN_RIGHT = 57; 
    
        public static BaseFont getBaseFont() {
            try {
                return BaseFont.createFont("/TTF/NotoSansCJKsc-Regular.otf", BaseFont.IDENTITY_H, BaseFont.NOT_EMBEDDED);
            } catch (Exception e) {
                throw new MsgException("");
            }
        }
    
        public static Font getFont(float fontSize) {
            return new Font(getBaseFont(), fontSize, Font.NORMAL);
        }
    
        public static PdfPTable getNoBorderTable() {
            return getBorderTable(1, Rectangle.NO_BORDER);
        }
    
        public static PdfPTable getBorderTable(int numColumns, int border) {
            PdfPTable table = new PdfPTable(numColumns);
            table.setSpacingBefore(4f);
            table.setSpacingAfter(4f);
            table.setTotalWidth(getTableWidth());
            table.setLockedWidth(true);
            table.getDefaultCell().setBorder(border);
            table.getDefaultCell().setMinimumHeight(24f);
            table.getDefaultCell().setHorizontalAlignment(Element.ALIGN_LEFT);
            table.getDefaultCell().setVerticalAlignment(Element.ALIGN_MIDDLE);
            return table;
        }
    
        /**
         * 上面是图标,下面是pdf标题名称
         */
        public static void setLogo(Document document, String name) throws Exception {
            Image logo = Image.getInstance(new ClassPathResource("/pdf/logo.png").getURL());
    
            PdfPTable t = getNoBorderTable();
            t.getDefaultCell().setVerticalAlignment(Element.ALIGN_BOTTOM);
            t.getDefaultCell().setHorizontalAlignment(Element.ALIGN_CENTER);
            t.getDefaultCell().setFixedHeight(150f);//表格固定行高
            t.addCell(logo);
            t.addCell(new Paragraph(name, getFont(16f)));
            document.add(t);
        }
    
        public static float getTableWidth() {
            return A4_WIDTH - MARGIN_LEFT - MARGIN_RIGHT;
        }
    
    }
    
    

    公用抽象类

    import com.itextpdf.text.*;
    import com.itextpdf.text.pdf.*;
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    import org.springframework.http.MediaType;
    
    import javax.servlet.ServletOutputStream;
    import javax.servlet.http.HttpServletResponse;
    import java.io.ByteArrayOutputStream;
    import java.nio.charset.Charset;
    import java.util.ArrayList;
    import java.util.List;
    
    public abstract class AbstractPdfView {
        private final Logger log = LoggerFactory.getLogger(AbstractPdfView.class);
    
        protected Document document;
        protected PdfWriter writer;
    
        protected final int size = 2048;
        protected Font font8 = PdfUtil.getFont(8f);     //页码用
        protected Font font10 = PdfUtil.getFont(10f);   //正文用
        protected Font font12 = PdfUtil.getFont(12f);   //标题用
    
        public void start(HttpServletResponse response) {
            try {
                ByteArrayOutputStream baos = new ByteArrayOutputStream(size);
                document = new Document(PageSize.A4);
                writer = PdfWriter.getInstance(document, baos);
                prepare();
                document.open();
                writeBody();
                document.close();
    
                List<PdfReader> readers = new ArrayList<>();
                readers.add(new PdfReader(baos.toByteArray()));
                baos = finish(readers);
    
                // flush to http response.
                flushStream(baos, response);
    
            } catch (Exception e) {
                log.error("导出pdf文档失败", e);
                throw new MsgException("导出pdf文档失败");
            }
        }
    
        /**
         * 添加监听事件请重写该方法
         * 如:writer.setPageEvent(PageHelper...),这种无须finish方法
         */
        protected void prepare() throws Exception {
            writer.setViewerPreferences(PdfWriter.ALLOW_PRINTING | PdfWriter.PageLayoutSinglePage);
        }
    
        protected abstract void writeBody() throws Exception;
    
        /**
         * 完成pdf,写入页码
         */
        protected ByteArrayOutputStream finish(List<PdfReader> readers) throws Exception {
            ByteArrayOutputStream out = new ByteArrayOutputStream(size);
            Document doc = new Document(PageSize.A4);
            PdfCopy copy = new PdfCopy(doc, out);
            doc.open();
    
            int currentPage = 1, totalPage = readers.stream().mapToInt(PdfReader::getNumberOfPages).sum();
            for (PdfReader reader : readers) {
                PdfImportedPage page;
                PdfCopy.PageStamp stamp;
                int pages = reader.getNumberOfPages();
                for (int index = 1; index <= pages; index++) {
                    doc.newPage();
                    page = copy.getImportedPage(reader, index);
                    stamp = copy.createPageStamp(page);
                    ColumnText.showTextAligned(stamp.getUnderContent(), Element.ALIGN_CENTER,
                            new Phrase(addFont8(String.format("第%d页,共%d页", currentPage++, totalPage))), 300f, 16f, 0f);
                    stamp.alterContents();
    
                    copy.addPage(page);
                }
    
                reader.close();
            }
    
            doc.close();
            copy.close();
    
            return out;
        }
    
        private void flushStream(ByteArrayOutputStream baos, HttpServletResponse response) throws Exception {
            response.reset();
            response.setHeader("Cache-Control", "no-cache");
            response.setHeader("Pragma", "no-cache");
            response.setDateHeader("Expires", 0);
            response.setHeader("Content-disposition", "inline; filename=" + RandomUtil.uuid() + ".pdf");
            response.setCharacterEncoding(Charset.forName("UTF-8").name());
            response.setContentType(MediaType.APPLICATION_PDF_VALUE);
            response.setContentLength(baos.size());
            ServletOutputStream out = response.getOutputStream();
            baos.writeTo(out);
            out.flush();
        }
    
        protected Paragraph addFont8(String content) {
            return addText(content, font8);
        }
    
        protected Paragraph addFont10(String content) {
            return addText(content, font10);
        }
    
        protected Paragraph addFont12(String content) {
            return addText(content, font12);
        }
    
        private Paragraph addText(String content, Font font) {
            Paragraph paragraph = new Paragraph(content, font);
            paragraph.setAlignment(Element.ALIGN_LEFT);
            return paragraph;
        }
    }
    

    对了,logo.png是放在resources里面的, 我直接截的百度图
    字体呢,我用的是思源宋体,大家自己搜索,随便下好了。
    有些地方没有给,一些大家直接就能猜到,一些就是我故意的,就是留那么一手防复制党,就是辣么jian,呵。
    拷过去只需要关注pdf的正文代码和样式代码就好了

    相关文章

      网友评论

          本文标题:itext 导出pdf,页码、目录

          本文链接:https://www.haomeiwen.com/subject/fxhlmqtx.html