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
- StdCommittee
- StdCommitteeBranch
- StdCommitteeSecretariat
- 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!