1
In addition to processing word, excel and other files, the most common one is the export of PDF. In the java technology stack, itext is the most commonly used for PDF creation and operation, but when using itext, you must understand its version history and license issues. In the previous version, the MPL and LGPL dual license agreement was used. In versions above 5.x The AGPLv3 is used (this agreement means that only personal use and open source projects can use the itext library, otherwise there is a charge). This article mainly introduces the realization of PDF export function through SpringBoot integration of itextpdf. @pdai

Knowledge preparation

Need to understand itext, as well as itext historical version changes, and license issues.

what is itext

From Baidu Encyclopedia: iText is a project of the famous open source site sourceforge (written by Bruno Lowagie), which is a library written in Java and .NET languages for creating and modifying PDF files. Through iText, you can not only generate PDF or rtf documents, but also convert XML and Html files into PDF files. The installation of iText is very convenient. After downloading the iText.jar file, you only need to add the path of iText.jar to the CLASSPATH of the system, and the iText class library can be used in the program.

iText provides other advanced PDF features in addition to the basic creation and modification of PDF files, such as PKI-based signatures, 40-bit and 128-bit encryption, color correction, tagged PDF, PDF forms (AcroForms), PDF/X, via ICC profiles and barcodes for color management. These features are used in several products and services, including Eclipse BIRT, Jasper Reports, JBoss Seam, Windward Reports, and pdftk.

In general, iText is used in projects that have one of the following requirements:

  • Content not available in advance: depends on user input or real-time database information.
  • Due to the content, too many pages, the PDF document cannot be generated manually.
  • Documents need to be created automatically in unattended, batch mode.
  • The content is customized or personalized; for example, the end customer's name needs to be marked on a large number of pages.

Historical versions of itext and License issues

Before using itext, you must understand its version history and license issues. In earlier versions, the MPL and LGPL dual-license agreements were used, and in versions 5.x and above, AGPLv3 was used (this agreement means that <mark> is only for personal use. And open source projects can use the itext library, otherwise it will be charged</mark>)
  • iText 0.x-2.x/iTextSharp 3.x-4.x

    • Update time is 2000-2009
    • Using the MPL and LGPL dual license agreement
    • The most recent update is 2009 and the version number is iText 2.1.7 /iTextSharp 4.1.6.0
    • The GAV version of the imported package is as follows:
 <dependency>
  <groupId>com.lowagie</groupId>
  <artifactId>itext</artifactId>
  <version>2.1.7</version>
</dependency>
  • iText 5.x and iTextSharp 5.x

    • Updated 2009-2016, corporatized operations and standardized and improved performance
    • Getting started with the AGPLv3 protocol

      • Only personal use and open source projects can use the itext library, otherwise it will be charged
    • iTextSharp is designed to be the .NET version of the iText library and is synchronized with the iText version number, iText 5.0.0 and iTextSharp5.0.0 are released at the same time
    • New features are not added here, but the official will fix important bugs
    • The GAV version of the imported package is as follows:
 <dependency>
  <groupId>com.itextpdf</groupId>
  <artifactId>itextpdf</artifactId>
  <version>5.5.13.3</version>
</dependency>
  • iText 7.x

    • Updated from 2016 to now
    • AGPLv3 protocol
    • Completely rewritten with a focus on extensibility and modularity
    • The name iTextSharp is not applicable, they are collectively referred to as iText, with Java and .Net versions
    • JDK 1.7+
    • The GAV version of the imported package is as follows:
 <dependency>
  <groupId>com.itextpdf</groupId>
  <artifactId>itext7-core</artifactId>
  <version>7.2.2</version>
  <type>pom</type>
</dependency>

Note: After the iText changes, a team on GitHub forked a branch based on the 4.x version (MPL and LGPL dual license agreement) to become OpenPDF , and continue to maintain the project.

Standard itextpdf export steps

Itextpdf exporting pdf mainly includes the following steps:

 @Override
public Document generateItextPdfDocument(OutputStream os) throws Exception {
    // 1. 创建文档
    Document document = new Document(PageSize.A4);

    // 2. 绑定输出流(通过pdfwriter)
    PdfWriter.getInstance(document, os);

    // 3. 打开文档
    document.open();

    // 4. 往文档中添加内容
    document.add(xxx);

    // 5. 关闭文档
    document.close();
    return document;
}

What are the elements added to the document?

The difference between the following concepts needs to be explained:

  • Chunk : The smallest chunk unit of the document's text
  • Phrase : a series of blocks that take a specific spacing (the distance between two lines) as a parameter
  • Paragraph : A paragraph is a series of blocks and/or short sentences. Like short sentences, paragraphs have definite spacing. The user can also specify indentation; with some margin on the sides and/or right, paragraphs can be left-aligned, right-aligned, and center-aligned. Each paragraph added to the document will automatically start on a new line.

(Others can be seen literally, so I will not explain them here)

Implementation case

Here is an example of SpringBoot integrating itext5 to export PDF.

Pom dependencies

Import the dependency package of poi

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

Export PDF

Methods exported in UserController

 package tech.pdai.springboot.file.word.poi.controller;


import java.io.OutputStream;

import javax.servlet.http.HttpServletResponse;

import io.swagger.annotations.ApiOperation;
import org.apache.poi.xwpf.usermodel.XWPFDocument;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import tech.pdai.springboot.file.word.poi.service.IUserService;

/**
 * @author pdai
 */
@RestController
@RequestMapping("/user")
public class UserController {

    @Autowired
    private IUserService userService;

    @ApiOperation("Download Word")
    @GetMapping("/word/download")
    public void download(HttpServletResponse response) {
        try {
            XWPFDocument document = userService.generateWordXWPFDocument();
            response.reset();
            response.setContentType("application/vnd.ms-excel");
            response.setHeader("Content-disposition",
                    "attachment;filename=user_world_" + System.currentTimeMillis() + ".docx");
            OutputStream os = response.getOutputStream();
            document.write(os);
            os.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

}

Export PDF method in UserServiceImple

 @Override
public Document generateItextPdfDocument(OutputStream os) throws Exception {
    // document
    Document document = new Document(PageSize.A4);
    PdfWriter.getInstance(document, os);

    // open
    document.open();

    // add content - pdf meta information
    document.addAuthor("pdai");
    document.addCreationDate();
    document.addTitle("pdai-pdf-itextpdf");
    document.addKeywords("pdf-pdai-keyword");
    document.addCreator("pdai");

    // add content -  page content

    // Title
    document.add(createTitle("Java 全栈知识体系"));

    // Chapter 1
    document.add(createChapterH1("1. 知识准备"));
    document.add(createChapterH2("1.1 什么是POI"));
    document.add(createParagraph("Apache POI 是创建和维护操作各种符合Office Open XML(OOXML)标准和微软的OLE 2复合文档格式(OLE2)的Java API。用它可以使用Java读取和创建,修改MS Excel文件.而且,还可以使用Java读取和创建MS Word和MSPowerPoint文件。更多请参考[官方文档](https://poi.apache.org/index.html)"));
    document.add(createChapterH2("1.2 POI中基础概念"));
    document.add(createParagraph("生成xls和xlsx有什么区别?POI对Excel中的对象的封装对应关系?"));

    // Chapter 2
    document.add(createChapterH1("2. 实现案例"));
    document.add(createChapterH2("2.1 用户列表示例"));
    document.add(createParagraph("以导出用户列表为例"));

    // 表格
    List<User> userList = getUserList();
    PdfPTable table = new PdfPTable(new float[]{20, 40, 50, 40, 40});
    table.setTotalWidth(500);
    table.setLockedWidth(true);
    table.setHorizontalAlignment(Element.ALIGN_CENTER);
    table.getDefaultCell().setBorder(1);

    for (int i = 0; i < userList.size(); i++) {
        table.addCell(createCell(userList.get(i).getId() + ""));
        table.addCell(createCell(userList.get(i).getUserName()));
        table.addCell(createCell(userList.get(i).getEmail()));
        table.addCell(createCell(userList.get(i).getPhoneNumber() + ""));
        table.addCell(createCell(userList.get(i).getDescription()));
    }
    document.add(table);

    document.add(createChapterH2("2.2 图片导出示例"));
    document.add(createParagraph("以导出图片为例"));
    // 图片
    Resource resource = new ClassPathResource("pdai-guli.png");
    Image image = Image.getInstance(resource.getURL());
    // Image image = Image.getInstance("/Users/pdai/pdai/www/tech-pdai-spring-demos/481-springboot-demo-file-pdf-itextpdf/src/main/resources/pdai-guli.png");
    image.setAlignment(Element.ALIGN_CENTER);
    image.scalePercent(60); // 缩放
    document.add(image);

    // close
    document.close();
    return document;
}

private List<User> getUserList() {
    List<User> userList = new ArrayList<>();
    for (int i = 0; i < 5; i++) {
        userList.add(User.builder()
                .id(Long.parseLong(i + "")).userName("pdai" + i).email("pdai@pdai.tech" + i).phoneNumber(121231231231L)
                .description("hello world" + i)
                .build());
    }
    return userList;
}

In the implementation, the following methods of creating document content can be encapsulated into the Util tool class

 private Paragraph createTitle(String content) throws IOException, DocumentException {
    Font font = new Font(getBaseFont(), 24, Font.BOLD);
    Paragraph paragraph = new Paragraph(content, font);
    paragraph.setAlignment(Element.ALIGN_CENTER);
    return paragraph;
}


private Paragraph createChapterH1(String content) throws IOException, DocumentException {
    Font font = new Font(getBaseFont(), 22, Font.BOLD);
    Paragraph paragraph = new Paragraph(content, font);
    paragraph.setAlignment(Element.ALIGN_LEFT);
    return paragraph;
}

private Paragraph createChapterH2(String content) throws IOException, DocumentException {
    Font font = new Font(getBaseFont(), 18, Font.BOLD);
    Paragraph paragraph = new Paragraph(content, font);
    paragraph.setAlignment(Element.ALIGN_LEFT);
    return paragraph;
}

private Paragraph createParagraph(String content) throws IOException, DocumentException {
    Font font = new Font(getBaseFont(), 12, Font.NORMAL);
    Paragraph paragraph = new Paragraph(content, font);
    paragraph.setAlignment(Element.ALIGN_LEFT);
    paragraph.setIndentationLeft(12); //设置左缩进
    paragraph.setIndentationRight(12); //设置右缩进
    paragraph.setFirstLineIndent(24); //设置首行缩进
    paragraph.setLeading(20f); //行间距
    paragraph.setSpacingBefore(5f); //设置段落上空白
    paragraph.setSpacingAfter(10f); //设置段落下空白
    return paragraph;
}

public PdfPCell createCell(String content) throws IOException, DocumentException {
    PdfPCell cell = new PdfPCell();
    cell.setVerticalAlignment(Element.ALIGN_MIDDLE);
    cell.setHorizontalAlignment(Element.ALIGN_CENTER);
    Font font = new Font(getBaseFont(), 12, Font.NORMAL);
    cell.setPhrase(new Phrase(content, font));
    return cell;
}

private BaseFont getBaseFont() throws IOException, DocumentException {
    return BaseFont.createFont("STSong-Light", "UniGB-UCS2-H", BaseFont.NOT_EMBEDDED);
}

Exported PDF

Add headers, footers and watermarks

In itextpdf 5.x, PdfPageEvent can be used to complete headers, footers and watermarks.
 package tech.pdai.springboot.file.pdf.itextpdf.pdf;

import com.itextpdf.text.BaseColor;
import com.itextpdf.text.Document;
import com.itextpdf.text.Element;
import com.itextpdf.text.Phrase;
import com.itextpdf.text.pdf.BaseFont;
import com.itextpdf.text.pdf.ColumnText;
import com.itextpdf.text.pdf.PdfContentByte;
import com.itextpdf.text.pdf.PdfGState;
import com.itextpdf.text.pdf.PdfPageEventHelper;
import com.itextpdf.text.pdf.PdfTemplate;
import com.itextpdf.text.pdf.PdfWriter;

/**
 * @author pdai
 */
public class MyHeaderFooterPageEventHelper extends PdfPageEventHelper {

    private String headLeftTitle;

    private String headRightTitle;

    private String footerLeft;

    private String waterMark;

    private PdfTemplate total;

    public MyHeaderFooterPageEventHelper(String headLeftTitle, String headRightTitle, String footerLeft, String waterMark) {
        this.headLeftTitle = headLeftTitle;
        this.headRightTitle = headRightTitle;
        this.footerLeft = footerLeft;
        this.waterMark = waterMark;
    }

    @Override
    public void onOpenDocument(PdfWriter writer, Document document) {
        total = writer.getDirectContent().createTemplate(30, 16);
    }

    @Override
    public void onEndPage(PdfWriter writer, Document document) {
        BaseFont bf = null;
        try {
            bf = BaseFont.createFont("STSong-Light", "UniGB-UCS2-H", BaseFont.NOT_EMBEDDED);
        } catch (Exception e) {
            e.printStackTrace();
        }

        // page header and footer
        addPageHeaderAndFooter(writer, document, bf);

        // watermark
        if (waterMark!=null) {
            addWaterMark(writer, document, bf);
        }
    }

    private void addPageHeaderAndFooter(PdfWriter writer, Document document, BaseFont bf) {
        PdfContentByte cb = writer.getDirectContent();
        cb.saveState();

        cb.beginText();

        cb.setColorFill(BaseColor.GRAY);
        cb.setFontAndSize(bf, 10);


        // header
        float x = document.top(-10);
        cb.showTextAligned(PdfContentByte.ALIGN_LEFT,
                headLeftTitle,
                document.left(), x, 0);
        cb.showTextAligned(PdfContentByte.ALIGN_RIGHT,
                headRightTitle,
                document.right(), x, 0);

        // footer
        float y = document.bottom(-10);
        cb.showTextAligned(PdfContentByte.ALIGN_LEFT,
                footerLeft,
                document.left(), y, 0);
        cb.showTextAligned(PdfContentByte.ALIGN_CENTER,
                String.format("- %d -", writer.getPageNumber()),
                (document.right() + document.left()) / 2,
                y, 0);

        cb.endText();

        cb.restoreState();
    }

    private void addWaterMark(PdfWriter writer, Document document, BaseFont bf) {
        for (int i = 1; i < 7; i++) {
            for (int j = 1; j < 10; j++) {
                PdfContentByte cb = writer.getDirectContent();
                cb.saveState();
                cb.beginText();
                cb.setColorFill(BaseColor.GRAY);
                PdfGState gs = new PdfGState();
                gs.setFillOpacity(0.1f);
                cb.setGState(gs);
                cb.setFontAndSize(bf, 12);
                cb.showTextAligned(Element.ALIGN_MIDDLE, waterMark, 75 * i,
                        80 * j, 30);
                cb.endText();
                cb.restoreState();
            }
        }
    }

    @Override
    public void onCloseDocument(PdfWriter writer, Document document) {
        ColumnText.showTextAligned(total, Element.ALIGN_LEFT, new Phrase(String.valueOf(writer.getPageNumber() - 1)), 2,
                2, 0);
    }
}

Exported PDF after adding watermark

further understanding

Further understand itextpdf through the following questions.

What to do if you encounter a license problem

As mentioned above, when using itext, you must understand its version history and license issues. In earlier versions, the MPL and LGPL dual-license agreements were used, and in versions 5.x and above, AGPLv3 was used. There are two options:

  1. Use version 2.1.7
 <dependency>
  <groupId>com.lowagie</groupId>
  <artifactId>itext</artifactId>
  <version>2.1.7</version>
</dependency>
  1. Using OpenPDF

There is a team on GitHub who forked a branch called OpenPDF based on itext 4.x version (MPL and LGPL dual license agreement), and continues to maintain the project.

Why adding headers, footers and watermarks is done through PdfPageEvent

Why is adding headers, footers and watermarks done through PdfPageEvent?

For example, what if we need to display "Page 1 of 3" in the footer in the above example, that is, the total number of pages? The itext is the content written in the stream mode. Only when it is written to the end can you know how many pages there are. Then the total number of pages displayed must be determined after the content is written (or before it is closed); this is why each page is written in the onEndPage method. The header and footer of the page.

iText only writes the PdfTemplate to the OutputStream after calling the release template method, otherwise the object will remain in memory until the document is closed. So we can use PdfTemplate to write the total page number before closing the document at the end. It can be understood as writing a placeholder first and then replacing it uniformly.

Sample source code

https://github.com/realpdai/tech-pdai-spring-demos

Reference article

https://itextpdf.com

https://blog.csdn.net/u012397189/article/details/80196974

more content

Say goodbye to fragmented learning, one-stop systematic learning without routines Back-end development: Java full-stack knowledge system (https://pdai.tech)


pdai
70 声望158 粉丝