Itext进行PDF的编辑开发

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

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

1:对于分页的处理。

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

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

具体实现(GPT实现)

java 复制代码
 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,然后形成了内容的叠加。遂放弃了用监听器。

手动计算分页

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

java 复制代码
              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)得到的行数,这个函数就是计算我这个详情实际占多少行。

java 复制代码
if(i+calculateLineBreaks(commercialInvoiceDto.getItems().get(i).getDescription()
                    , font
                    , 8
                    , 340)<12)

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

字体缩放

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

java 复制代码
        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) 操作
表格初始化
  • PdfPTable table = new PdfPTable(3);:创建一个包含 3 列的表格。
  • table.setWidthPercentage(100);:设置表格宽度占页面宽度的 100%。
  • table.setTotalWidth(new float[]{30, 20, 50});:自定义列宽比例,这里三列的宽度比例分别为 30、20、50。
表格样式属性
  • table.setSpacingBefore(10f);:设置表格上方的间距为 10 个单位。
  • table.setSpacingAfter(10f);:设置表格下方的间距为 10 个单位。
  • table.setLockedWidth(true);:锁定列宽,防止列宽在后续操作中自动调整。
  • table.getDefaultCell().setBorder(0);:设置表格默认单元格无边框。
二、单元格 (Cell) 操作
基础单元格
  • PdfPCell cell = new PdfPCell(new Paragraph("内容"));:创建一个包含文本内容的单元格。
  • cell.setBackgroundColor(BaseColor.ORANGE);:设置单元格的背景颜色为橙色。
  • cell.setBorderColor(BaseColor.BLUE);:设置单元格的边框颜色为蓝色。
高级属性
  • cell.setColspan(2);:使单元格横向合并 2 列。
  • cell.setRowspan(2);:使单元格纵向合并 2 行。
  • cell.setHorizontalAlignment(Element.ALIGN_CENTER);:设置单元格内容水平居中对齐。
  • cell.setVerticalAlignment(Element.ALIGN_MIDDLE);:设置单元格内容垂直居中对齐。
三、文本与段落
段落样式
  • Paragraph p = new Paragraph("文本", FontFactory.getFont("SIMHEI", 12));:创建一个包含文本的段落,并设置中文字体为 "黑体",字号为 12。
  • p.setAlignment(Element.ALIGN_CENTER);:设置段落内容居中对齐。
  • p.setSpacingBefore(5f);:设置段落前的间距为 5 个单位。
四、图形与图像
插入图片
  • Image img = Image.getInstance("logo.png");:从指定路径(这里是 "logo.png")获取图片。
  • PdfPCell imgCell = new PdfPCell(img, true);:创建一个包含图片的单元格,true表示图片将被缩放以适应单元格大小。
五、文档元数据
  1. document.addTitle("标题");:设置文档的标题。
  2. document.addKeywords("关键词");:添加文档的关键词。
  3. document.addCreator("创建者");:设置文档的创建者信息。
相关推荐
安替-AnTi1 小时前
Google Colab测试部署Qwen大模型,实现PDF转MD场景OCR 识别(支持单机环境)
pdf·ocr·多模态·qwen 2.5·图片转文本
AI偶然3 小时前
AI智能体|扣子(Coze)搭建【一键转换为Word/pdf/Excel】工作流保姆级教学
人工智能·pdf·word
朴拙数科3 小时前
LangChain实现PDF中图表文本多模态数据向量化及RAG应用实战指南
langchain·pdf
摸鱼仙人~18 小时前
开源的 PDF 文件翻译软件
pdf
辣香牛肉面18 小时前
PDF多功能转换编辑及扫描仪 iLovePDF 3.10.0
pdf·多功能转换·pdf编辑扫描
CHQIUU20 小时前
PDF.js 生态中如何处理“添加注释\添加批注”以及 annotations.contents 属性
开发语言·javascript·pdf
寒也20 小时前
识别法院PDF文件特定字段并插入数据库【正则表达式+本地化部署】
java·数据库·正则表达式·eclipse·pdf·达梦·ruoyi
ZJL-阿友1 天前
python pdf转图片再OCR
python·pdf·ocr
开开心心就好2 天前
实用电脑工具,轻松实现定时操作
python·学习·pdf·电脑·word·excel·生活