Itext进行PDF的编辑开发

这周写了一周的需求,是制作一个PDF生成功能,其中用到了Itext来制作PDF的视觉效果。其中一些功能不是很懂,仅作记录,若要学习请仔细甄别正确与否。

开始之前,我还是想说,这傻福需求怎么想出来的,让人手撸PDF,哥们一两页PDF写了快1k行代码,代码量大并且感觉极难维护。这次遇见的难点主要有:

1:对于分页的处理。

这个问题真的困扰了很久,现在也无法良好解决。比如在遇见我写一个动态表格的时候,发生了分页,但我一旦分页我要先添加一些别的表头信息再继续我的分页实现。这个时候我首先想到了用事件监听之类的,在发生分页时自动插入某些函数。

注册事件监听器:
        pdfDoc.addEventHandler(PdfDocumentEvent.START_PAGE,
                new PageNumberEventHandler(font, boldFont));

具体实现(GPT实现)

 private static class PageNumberEventHandler implements IEventHandler {
        private final PdfFont font;
        private final PdfFont boldFont;

        public PageNumberEventHandler(PdfFont font, PdfFont boldFont) {
            this.font = font;
            this.boldFont = boldFont;
        }

        @Override
        public void handleEvent(Event event) {
            PdfDocumentEvent docEvent = (PdfDocumentEvent) event;
            PdfDocument pdfDoc = docEvent.getDocument();
            PdfPage page = docEvent.getPage();
            int pageNumber = pdfDoc.getPageNumber(page);
            int totalPages = pdfDoc.getNumberOfPages();

            // 更新静态变量
            InvoiceGenerator.currentPage = pageNumber;
            InvoiceGenerator.totalPages = totalPages;

            Rectangle pageSize = page.getPageSize();
            PdfCanvas pdfCanvas = new PdfCanvas(page.newContentStreamBefore(),
                    page.getResources(), pdfDoc);

            // 定义绘制区域(关键修正)
            Rectangle rootArea = new Rectangle(36, 36,
                    pageSize.getWidth() - 72,
                    pageSize.getHeight() - 72);

            // 创建Canvas对象
            Canvas canvas = new Canvas(pdfCanvas, rootArea);

            // 如果是续页(第2页及以后),添加续篇表头
            if (pageNumber > 1) {
                try {
                    addContinuationHeader(canvas, font, boldFont, pageNumber);
                } catch (IOException e) {
                    throw new RuntimeException(e);
                }
            }

            // 添加页码(右上角)
            Paragraph pageInfo = new Paragraph()
                    .add(String.format(PAGE_X_OF_Y_TEXT, pageNumber, totalPages))
                    .setFont(font)
                    .setFontSize(8)
                    .setFixedPosition(
                            pageSize.getRight() - 36,
                            pageSize.getTop() - 20,
                            100)
                    .setTextAlignment(TextAlignment.RIGHT);

            canvas.add(pageInfo);

            // 添加修订信息(左下角)
            Paragraph revInfo = new Paragraph(REV_INFO)
                    .setFont(font)
                    .setFontSize(7)
                    .setFixedPosition(36, 20, 100);

            canvas.add(revInfo);
            canvas.close();
            pdfCanvas.release();
        }

我后面发现,handler实现出来要写在一个canvas里面,而我开始是把所有内容绘制在一个document里面的,这就导致我把canvas放在document里面的时候document里面的内容无法正确识别出我加入的canvas,然后形成了内容的叠加。遂放弃了用监听器。

手动计算分页

比如计算一页最多可以放多少数据,然后放了这么多条我就新建一页

              document.add(new AreaBreak(AreaBreakType.NEXT_PAGE));// 强制分页

然后继续在这一页里面写。这样的缺点就是我要计算出准确的能放多少数据,如果数据的变化太多这个方法就不行了。其实后面就遇见了,因为后面插入的每条数据的宽度不一样。然后我之前想的是。把宽度计算出来,结果我第一页的宽度计算和后面的不一样,缕了半小时思路还是有问题。

实际场景是:

插入items里面的每一行数据,按道理来说每一行setbond是(10),但是里面的详情字段可能会超长,然后导致换行,我这里就把这个item当作了两行,这样确实可以做到准确分页,但是会出现的问题是,如果分页刚好发生在换行这个item,那么我这个item就应该到下面去,我设定的是第一页只存12行,存过了就分页。但这样如果我第4个item的时候前面已经有10行了,那么我第4行就要超过了,所以第4行就应该放到下一个去。然后当时就是设置了一个总行数totalNum和一个真实到了哪一个item的数量currentNum。第一页的逻辑就是。总行数totalNum就是每次calculateLineBreaks(commercialInvoiceDto.getItems().get(i).getDescription()
                    , font
                    , 8
                    , 340)得到的行数,这个函数就是计算我这个详情实际占多少行。

if(i+calculateLineBreaks(commercialInvoiceDto.getItems().get(i).getDescription()
                    , font
                    , 8
                    , 340)<12)

那么在第二页的逻辑的时候,我就传一个currentNum去代表这一页开始遍历的item的index。这一页可以存40行数据,那么我每遍历一个currentNum进去,我的totalNum就加一次calculateLineBreaks,然后如果下一次+calculateLineBreaks会超过40我就提前分页。这样我感觉从逻辑上是能行通的。结果后面告诉我不想要这种换行的效果,要采用字体缩放来解决。

字体缩放

结果这才是最痛苦的,本来打算周五可以摸摸鱼看看八股,在GPT和百度的挣扎下,最开始老板告诉我有一个自适应缩放的,找了半天没找到。我就打算采用百度上通过计算我这个字段,计算出他会占多少行,然后按照比例缩小字体大小。结果他的计算方式是大致计算,不能准确的识别出里面的空格,就导致缩放的有可能会不够小。然后我采用一个处理溢出操作的方法,结果和注释所说一样,我的自适应缩放只能缩放到7f大小就不能更小了。

        Paragraph para = new Paragraph(Des)
                .setFont(font)
                .setFontSize(fontSize)
                .setWidth(maxWidth);
               para.setProperty(Property.OVERFLOW_X, OverflowPropertyValue.FIT);//只能自适应缩放到字体大小大于7f的,看见有什么MIN_FONT_SIZE字段,但我用的7.2.5没有,我看了好久源码也没找到。
2:布局问题
边框重合

在生成过程中,我发现有的线框会变得很粗,这是因为两个外边框的重合会引起变粗的效果。因此可以通过setBorder来操控,比如说和上面的方框重合了,就把上面的setBorderBottom设置为(NO_Border)或者把下面的setBorderTop。

同时注意,外边框和内部文字的边框,如果只想呈现一个框框里面只有文字没有分割线的效果就只在new Table的时候设置边框,如果文字中间需要分割就在Cell里面设置。

对齐问题

一行有多少内容可以存放是由开始设置的分割布局来决定的,因此如果有两行内容可以放在一个table里面,但是注意存放的数量。

3:常见概念

(这是在下周一写的了,哥们明明功能都写完了,结果组长告诉我周五说我现在用的包不能过甲方审核,heartbreak了。然后后面尝试用openPdf做,我感觉太麻烦了,刚好老板说不用做了,但是都写这么多了,再说点我这次 遇见的常用的一些内容吧)

一、表格 (Table) 操作
  1. 表格初始化
    • PdfPTable table = new PdfPTable(3);:创建一个包含 3 列的表格。
    • table.setWidthPercentage(100);:设置表格宽度占页面宽度的 100%。
    • table.setTotalWidth(new float[]{30, 20, 50});:自定义列宽比例,这里三列的宽度比例分别为 30、20、50。
  2. 表格样式属性
    • table.setSpacingBefore(10f);:设置表格上方的间距为 10 个单位。
    • table.setSpacingAfter(10f);:设置表格下方的间距为 10 个单位。
    • table.setLockedWidth(true);:锁定列宽,防止列宽在后续操作中自动调整。
    • table.getDefaultCell().setBorder(0);:设置表格默认单元格无边框。
二、单元格 (Cell) 操作
  1. 基础单元格
    • PdfPCell cell = new PdfPCell(new Paragraph("内容"));:创建一个包含文本内容的单元格。
    • cell.setBackgroundColor(BaseColor.ORANGE);:设置单元格的背景颜色为橙色。
    • cell.setBorderColor(BaseColor.BLUE);:设置单元格的边框颜色为蓝色。
  2. 高级属性
    • cell.setColspan(2);:使单元格横向合并 2 列。
    • cell.setRowspan(2);:使单元格纵向合并 2 行。
    • cell.setHorizontalAlignment(Element.ALIGN_CENTER);:设置单元格内容水平居中对齐。
    • cell.setVerticalAlignment(Element.ALIGN_MIDDLE);:设置单元格内容垂直居中对齐。
三、文本与段落
  1. 段落样式
    • Paragraph p = new Paragraph("文本", FontFactory.getFont("SIMHEI", 12));:创建一个包含文本的段落,并设置中文字体为 “黑体”,字号为 12。
    • p.setAlignment(Element.ALIGN_CENTER);:设置段落内容居中对齐。
    • p.setSpacingBefore(5f);:设置段落前的间距为 5 个单位。
四、图形与图像
  1. 插入图片
    • Image img = Image.getInstance("logo.png");:从指定路径(这里是 “logo.png”)获取图片。
    • PdfPCell imgCell = new PdfPCell(img, true);:创建一个包含图片的单元格,true表示图片将被缩放以适应单元格大小。
五、文档元数据
  1. document.addTitle("标题");:设置文档的标题。
  2. document.addKeywords("关键词");:添加文档的关键词。
  3. document.addCreator("创建者");:设置文档的创建者信息。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值