background

In daily work, I have met the need to export data to PDF. Here is a simple summary. The current service involves four entity classes. Data of different entities is assembled in the background and exported as PDF files.

  • The domain model
  1. StdCommittee
  2. StdCommitteeBranch
  3. StdCommitteeSecretariat
  4. StdCommitteeSecretariatStaff
  • Entity relationship

  • Involving technology

SpringBoot, MyBatisPlus, ITextPDF, and ConfigProperties are customized.

  • Export interface
/ * * *@Author Heartsuit
 * @DateThe 2022-01-02 * /
@RestController
@RequestMapping("committee")
public class StdCommitteeController {
    @Autowired
    private IStdCommitteeService stdCommitteeService;

    /** * export declaration */
    @GetMapping("/download/{id}")
    public void downloadPdf(@PathVariable Long id, HttpServletResponse response)
    {
        StdCommittee stdCommittee = stdCommitteeService.getById(id);

        OutputStream outputStream = null;
        try {
            outputStream = new BufferedOutputStream(response.getOutputStream());
            // Generate a PDF file
            stdCommitteeService.generatePdf(stdCommittee, outputStream);
            outputStream.flush();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                if(outputStream ! =null) { outputStream.close(); }}catch(IOException e) { e.printStackTrace(); }}}}Copy the code

The exported data is in PDF format

  • The configuration file
server:
  port: 8080

# spring configuration
spring:
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://localhost:3306/standard-core? useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
    username: root
    password: root

# mybatisplus configuration
mybatis-plus:
  Search for the specified package alias
  typeAliasesPackage: com.heartsuit.domain
  Configure mapper scan to find all mapper. XML mapping files
  mapper-locations: classpath:mapper/**/*.xml
  configuration:
    map-underscore-to-camel-case: true
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
Copy the code
  • Core services
/ * * *@Author Heartsuit
 * @DateThe 2022-01-02 * /
@Service
public class StdCommitteeServiceImpl extends ServiceImpl<StdCommitteeMapper.StdCommittee> implements IStdCommitteeService {

    @Autowired
    private IStdCommitteeSecretariatService stdCommitteeSecretariatService;

    @Autowired
    private IStdCommitteeSecretariatStaffService stdCommitteeSecretariatStaffService;

    @Autowired
    private IStdCommitteeBranchService stdCommitteeBranchService;

    private void buildCommittee(StdCommittee stdCommittee) {
        // one 2 one
        StdCommitteeSecretariat stdCommitteeSecretariat = stdCommitteeSecretariatService.getOne(new QueryWrapper<StdCommitteeSecretariat>()
                .lambda().eq(StdCommitteeSecretariat::getCommitteeId, stdCommittee.getId()));

        // one 2 many
        List<StdCommitteeSecretariatStaff> staffs = stdCommitteeSecretariatStaffService.list(new QueryWrapper<StdCommitteeSecretariatStaff>()
                .lambda().eq(StdCommitteeSecretariatStaff::getCommitteeSecretariatId, stdCommitteeSecretariat.getId()));
        stdCommitteeSecretariat.setStdCommitteeSecretariatStaffs(staffs);
        stdCommittee.setStdCommitteeSecretariat(stdCommitteeSecretariat);

        // one 2 many
        List<StdCommitteeBranch> branches = stdCommitteeBranchService.list(new QueryWrapper<StdCommitteeBranch>()
                .lambda().eq(StdCommitteeBranch::getCommitteeId, stdCommittee.getId()));
        stdCommittee.setStdCommitteeBranches(branches);
    }

    @Override
    public void generatePdf(StdCommittee stdCommittee, OutputStream outputStream) {
        buildCommittee(stdCommittee);
        Document document = new Document();
        try {
            PdfWriter.getInstance(document, outputStream);
            document.open();

            // Solve the problem that Chinese is not displayed
            BaseFont bfChinese = BaseFont.createFont("STSong-Light"."UniGB-UCS2-H", BaseFont.NOT_EMBEDDED);

            // The title is bold
            Font fontChina18 = new Font(bfChinese, PDFConstant.FONT_SIZE_18, Font.BOLD);
            Font fontChina15 = new Font(bfChinese, PDFConstant.FONT_SIZE_15, Font.BOLD);
            Font fontChina10 = new Font(bfChinese, PDFConstant.FONT_SIZE_10);

            // Get the PDF header information
            Map<String, String> headInfo = new HashMap<>();
            headInfo.put("firstHeadInfo".Registration Form of Standardization Technical Committee);
            / / space
            Paragraph blank1 = new Paragraph("".new Font(bfChinese, PDFConstant.FONT_SIZE_5));

            Paragraph firstTitle = new Paragraph(headInfo.get("firstHeadInfo"), fontChina18);
            firstTitle.setAlignment(Element.ALIGN_CENTER);/ / in the middle
            document.add(firstTitle);

            // Add Spaces
            document.add(blank1);

            // 3 Create the table
            PdfPTable table = new PdfPTable(PDFConstant.TABLE_COLUMN_NUMBER_8);// How many columns are there in the table
            table.setWidthPercentage(PDFConstant.TABLE_WIDTH_PERCENTAGE);// The table width is 100%

            PdfUtil.addTableCell(table, "Name", fontChina10, false.false.0.0);
            PdfUtil.addTableCell(table, stdCommittee.getName(), fontChina10, true.false.3.0);/ / across the three columns

            PdfUtil.addTableCell(table, "Number", fontChina10, false.false.0.0);
            PdfUtil.addTableCell(table, stdCommittee.getCode(), fontChina10, true.false.3.0);/ / across the three columns

            PdfUtil.addTableCell(table, "What is the current session?", fontChina10, false.false.0.0);
            PdfUtil.addTableCell(table, stdCommittee.getNumberSession().toString(), fontChina10, true.false.3.0);/ / across the three columns

            PdfUtil.addTableCell(table, "Current date of establishment", fontChina10, false.false.0.0);
            PdfUtil.addTableCell(table, DateFormatUtils.format(stdCommittee.getEstablishDate(), "yyyy-MM-dd"), fontChina10, true.false.3.0);/ / across the three columns

            PdfUtil.addTableCell(table, "Areas of expertise responsible for revising local standards", fontChina10, false.true.0.3); / / across the three lines
            PdfUtil.addTableCell(table, stdCommittee.getProfessionalField(), fontChina10, true.true.7.3);/ / across the seven columns

            List<StdCommitteeSecretariatStaff> staffs = stdCommittee.getStdCommitteeSecretariat().getStdCommitteeSecretariatStaffs();
            PdfUtil.addTableCell(table, "Staff of the Secretariat of the Technical Committee", fontChina10, false.true.0, staffs.size() + 1);
            PdfUtil.addTableCell(table, "Name", fontChina10, true.false.0.0);
            PdfUtil.addTableCell(table, "Secretary Type", fontChina10, true.false.0.0);
            PdfUtil.addTableCell(table, "Position/title", fontChina10, true.false.0.0);
            PdfUtil.addTableCell(table, Date of Birth, fontChina10, true.false.0.0);
            PdfUtil.addTableCell(table, "Degree", fontChina10, true.false.0.0);
            PdfUtil.addTableCell(table, "Telephone", fontChina10, true.false.2.0);
            staffs.forEach(staff -> {
                PdfUtil.addTableCell(table, staff.getName(), fontChina10, true.false.0.0);
                PdfUtil.addTableCell(table, staff.getType(), fontChina10, true.false.0.0);
                PdfUtil.addTableCell(table, staff.getProfessionalTitle(), fontChina10, true.false.0.0);
                PdfUtil.addTableCell(table, DateFormatUtils.format(staff.getBirthday(), "yyyy-MM-dd"), fontChina10, true.false.0.0);
                PdfUtil.addTableCell(table, staff.getQualification(), fontChina10, true.false.0.0);
                PdfUtil.addTableCell(table, staff.getPhone(), fontChina10, true.false.2.0);
            });

            PdfUtil.addTableCell(table, "The technical committee shall have sub-technical committees or expert groups on Standardization.", fontChina15, true.false.8.0);/ / across eight columns
            PdfUtil.addTableCell(table, "id", fontChina10, true.false.2.0);
            PdfUtil.addTableCell(table, "Number", fontChina10, true.false.2.0);
            PdfUtil.addTableCell(table, "Name", fontChina10, true.false.2.0);
            PdfUtil.addTableCell(table, "Number of members", fontChina10, true.false.2.0);
            stdCommittee.getStdCommitteeBranches().forEach(branch -> {
                PdfUtil.addTableCell(table, branch.getId().toString(), fontChina10, true.false.2.0);
                PdfUtil.addTableCell(table, branch.getCode(), fontChina10, true.false.2.0);
                PdfUtil.addTableCell(table, branch.getName(), fontChina10, true.false.2.0);
                PdfUtil.addTableCell(table, branch.getNumberMember().toString(), fontChina10, true.false.2.0);
            });
            document.add(table);
        } catch (DocumentException | IOException e) {
            e.printStackTrace();
        } finally{ document.close(); }}}Copy the code
  • Export effect

Browser to access directly: http://localhost:8080/committee/download/1477482360448090113

Or in the PostMan visit: http://localhost:8080/committee/download/1477482360448090113, but needs to be saved as a file and then view the generated PDF file content.

Add text watermark

  • The configuration file

In order to conveniently control whether to enable text watermarking, I added the following custom configuration to the original configuration (it is generally used together with the configuration center in actual production to realize dynamic control switch) :

pdf:
  watermark:
    text:
      enabled: true
      content: 'Heartsuit Text Watermark 666'
Copy the code
  • The configuration class
/ * * *@Author Heartsuit
 * @DateThe 2022-01-02 * /
@Configuration
@ConfigurationProperties(prefix = "pdf.watermark")
@Data
public class PdfConfigProperties
{
    private TextProperties text = new TextProperties();

    @Data
    public static class TextProperties{
        private Boolean enabled;
        privateString content; }}Copy the code
  • Core Service Class
public class TextWaterMark extends PdfPageEventHelper {
    private String waterMarkText;

    public TextWaterMark(String waterMarkText) {
        this.waterMarkText = waterMarkText;
    }

    public void onEndPage(PdfWriter writer, Document document) {
        try {
            float pageWidth = document.right() + document.left();// Get the body page width of the PDF content
            float pageHeight = document.top() + document.bottom();// Get the body page height of the PDF content
            // Set the watermark font format
            BaseFont base = BaseFont.createFont("STSong-Light"."UniGB-UCS2-H", BaseFont.NOT_EMBEDDED);
            Font waterMarkFont = new Font(base, 20, Font.BOLD, BaseColor.LIGHT_GRAY);
            PdfContentByte waterMarkPdfContent = writer.getDirectContentUnder();
            Phrase phrase = new Phrase(waterMarkText, waterMarkFont);
            // Two rows and three columns
            ColumnText.showTextAligned(waterMarkPdfContent, Element.ALIGN_CENTER, phrase,
                    pageWidth * 0.25 f, pageHeight * 0.2 f.45);
            ColumnText.showTextAligned(waterMarkPdfContent, Element.ALIGN_CENTER, phrase,
                    pageWidth * 0.25 f, pageHeight * 0.5 f.45);
            ColumnText.showTextAligned(waterMarkPdfContent, Element.ALIGN_CENTER, phrase,
                    pageWidth * 0.25 f, pageHeight * 0.8 f.45);
            ColumnText.showTextAligned(waterMarkPdfContent, Element.ALIGN_CENTER, phrase,
                    pageWidth * 0.65 f, pageHeight * 0.2 f.45);
            ColumnText.showTextAligned(waterMarkPdfContent, Element.ALIGN_CENTER, phrase,
                    pageWidth * 0.65 f, pageHeight * 0.5 f.45);
            ColumnText.showTextAligned(waterMarkPdfContent, Element.ALIGN_CENTER, phrase,
                    pageWidth * 0.65 f, pageHeight * 0.8 f.45);
        } catch(DocumentException | IOException de) { de.printStackTrace(); }}}Copy the code
  • Export the PDF code that needs to be modified

  • Export effect

When there is only one page, the exported PDF file looks like:

When there are multiple pages, the exported PDF file will look like:

Add image Watermark

  • The configuration file

In order to facilitate the control of whether to enable picture watermarking, I added the following custom configuration to the original configuration (it is generally used together with the configuration center in actual production to realize the dynamic control switch) : In addition, I put the image file directly into the Resources root directory, and then read the path from the Resource class in the code and pass it to the production core service layer method with the image watermark.

pdf:
  watermark:
    text:
      enabled: false
      content: 'Heartsuit Text Watermark 666'
    image:
      enabled: true
      file: 'avatar.jpg'
Copy the code
  • Core Service Class
public class ImageWaterMark extends PdfPageEventHelper {

    private String waterMarkFullFilePath;
    private Image waterMarkImage;

    public ImageWaterMark(String waterMarkFullFilePath) {
        this.waterMarkFullFilePath = waterMarkFullFilePath;
    }

    public void onEndPage(PdfWriter writer, Document document) {
        try {
            float pageWidth = document.right() + document.left();// Get the body page width of the PDF content
            float pageHeight = document.top() + document.bottom();// Get the body page height of the PDF content
            PdfContentByte waterMarkPdfContent = writer.getDirectContentUnder();
            // Set only one image instance object, apply only one image object to the entire PDF document, greatly reduce the size of the PDF document due to the increase of image watermarking
            if (waterMarkImage == null) {
                waterMarkImage = Image.getInstance(waterMarkFullFilePath);
            }
            // Add watermark image, three rows and two columns
            waterMarkPdfContent.addImage(getSingletonWaterMarkImage(waterMarkImage, pageWidth * 0.2 f, pageHeight * 0.1 f));
            waterMarkPdfContent.addImage(getSingletonWaterMarkImage(waterMarkImage, pageWidth * 0.2 f, pageHeight * 0.4 f));
            waterMarkPdfContent.addImage(getSingletonWaterMarkImage(waterMarkImage, pageWidth * 0.2 f, pageHeight * 0.7 f));
            waterMarkPdfContent.addImage(getSingletonWaterMarkImage(waterMarkImage, pageWidth * 0.6 f, pageHeight * 0.2 f));
            waterMarkPdfContent.addImage(getSingletonWaterMarkImage(waterMarkImage, pageWidth * 0.6 f, pageHeight * 0.5 f));
            waterMarkPdfContent.addImage(getSingletonWaterMarkImage(waterMarkImage, pageWidth * 0.6 f, pageHeight * 0.8 f));
            PdfGState gs = new PdfGState();
            gs.setFillOpacity(0.5 f);// Set transparency
            waterMarkPdfContent.setGState(gs);
        } catch(DocumentException | IOException de) { de.printStackTrace(); }}/** * Sets the display position of an image object, which is reused to reduce the size of the PDF file **@param waterMarkImage
     * @param xPosition
     * @param yPosition
     * @return* /
    private Image getSingletonWaterMarkImage(Image waterMarkImage, float xPosition, float yPosition) {
        waterMarkImage.setAbsolutePosition(xPosition, yPosition);/ / coordinates
        waterMarkImage.setRotation(-20);// Rotate radians
        waterMarkImage.setRotationDegrees(-45);// Rotate the Angle
        waterMarkImage.scalePercent(100);// Scale to scale
        returnwaterMarkImage; }}Copy the code
  • Export the PDF code that needs to be modified

  • Export effect

Source Code

The full source can be found at GitHub: github.com/heartsuit/d… , attached with database table model and data, image watermark files and exported PDF samples.

Reference

Blog.csdn.net/xue2xue/art…


If you have any questions or any bugs are found, please feel free to contact me.

Your comments and suggestions are welcome!