There is a requirement in the project to generate invoice pdf or contract pdf for existing data. These PDFs have some features, that is, pdf has a fixed format, similar to a table, we just need to fill the table with data.Of course, signatures, QR codes and other requirements will also be involved.
Overall ideas:
1. Get the initial template of the pdf that needs to be generated, with the format but no data
2. Using the tool Adobe Acrobat, edit the pdf template and generate text fields in the corresponding areas, each with its own name
3. Using itextpdf to operate on pdf in java, the corresponding data is filled into pdf
1. Use Adobe Acrobat to manipulate pdf
(1) First assume we have a pdf template for the invoice (partial screenshot)
(2) Download the Adobe Acrobat tool and edit the pdf
I'm using Adobe Acrobat Pro DC here, open pdf and select Prepare Form in the toolbar
We can clear the default generated text field and right-click to generate the text field size according to our needs, here I generate a gfmc text field, in this text field property you can set the size of the font, the type of font; in the options you can set the text left alignment, in the play, right alignment, line wrapping, and so on.
2. Use itextpdf to manipulate pdf
When we have set up the required text fields for pdf, we need to do a data filling for pdf.The main method code shown here is not the logical code for the entire process.
1. Get the PdfReader object
PdfReader reader = new PdfReader(template);//Template is the relative path of the template pdf file
2. Get the PdfStamper object to read the template pdf and write to a new target file
File deskFile = new File(filePath);//filePath is the file that will be generated PdfStamper stamp = new PdfStamper(reader, new FileOutputStream(deskFile));
3. Get the text field from the template file and write the data to the new file
AcroFields form = stamp.getAcroFields(); form.setField("gfmc", "This is the name of the buyer");
4. Custom fonts (on demand)
(1) Loading fonts
Where TARGET_COUR_FONT_PATH is the font file path
BaseFont courBf = BaseFont.createFont(TARGET_COUR_FONT_PATH, BaseFont.IDENTITY_H, BaseFont.EMBEDDED);
(2) Assigning values to text fields and setting fonts
Where chunkStr represents text content, size1 font size, size2 line spacing, and property is the text domain name
public static void setFieldAndFont(BaseFont bf, PdfStamper stamp, AcroFields form, String chunkStr, Integer size1, Integer size2, String property) { try { Font font = new Font(bf, size1,-1,new BaseColor(0,0,0)); List<AcroFields.FieldPosition> list = form.getFieldPositions(property); int page = list.get(0).page; PdfContentByte pdfContentByte = stamp.getOverContent(page); ColumnText columnText = new ColumnText(pdfContentByte); Rectangle rectangle = list.get(0).position; columnText.setSimpleColumn(rectangle); Chunk chunk = null; chunk = new Chunk(chunkStr); Paragraph paragraph = new Paragraph(size2, chunk); columnText.addText(paragraph); paragraph.setFont(font); columnText.addElement(paragraph); columnText.go(); } catch (DocumentException e) { e.printStackTrace(); } }
5. Add QR code
com.google.zxing is used here
<dependency> <groupId>com.google.zxing</groupId> <artifactId>core</artifactId> <version>3.3.3</version> </dependency> <dependency> <groupId>com.google.zxing</groupId> <artifactId>javase</artifactId> <version>3.3.3</version> </dependency>
public static void invoiceEwm(PdfStamper stamp, String content) throws Exception { if(content == null || "".equals(content)){ return; } BufferedImage image = createQrCodeBufferdImage(content, 70, 70); //PdfGState gs = new PdfGState(); PdfContentByte waterMar = stamp.getOverContent(1);// Watermark Content //waterMar = stamp.getUnderContent(1); //Watermark below content // Set Picture Transparency to 0.2f //gs.setFillOpacity(0.2f); // Set the opacity of the stylus font to 0.4f //gs.setStrokeOpacity(0.4f); // Begin Watermark Processing waterMar.beginText(); // Set Transparency //waterMar.setGState(gs); // Setting Watermark Font Parameters and Size //waterMar.setFontAndSize(BaseFont.createFont(BaseFont.HELVETICA, BaseFont.WINANSI, BaseFont.NOT_EMBEDDED), 60); // Set Watermark Alignment Watermark Content X Coordinate Y Coordinate Rotation Angle //waterMar.showTextAligned(Element.ALIGN_CENTER, ""Company internal files, please keep confidential!"", 500, 430, 45"; // Set Watermark Color waterMar.setColorFill(BaseColor.GRAY); // Create Watermark Picture //com.itextpdf.text.Image itextimage = getImage(image, 99); com.itextpdf.text.Image itextimage = com.itextpdf.text.Image.getInstance(image, new Color(128, 128, 128)); // Watermark Picture Location itextimage.setAbsolutePosition(150, 380); // Border Fixed 3 //itextimage.scaleToFit(200, 200); // Set Rotation Radius //image.setRotation(30);//Rotation Radius // Set rotation angle //Image.setRotation Degrees(45);//rotation angle // Set equal scale itextimage.scalePercent(85); // custom size //itextimage.scaleAbsolute(100, 100); // Attachment with Watermark Picture waterMar.addImage(itextimage); // Complete Watermark Addition waterMar.endText(); // stroke waterMar.stroke(); } /** * Generate two-dimensional code * @author swj * @date 2019 June 11, 2001 4:39:16 p.m. * @param contents Contents of the two-dimensional code * @param width 2D Code Picture Width * @param height 2D Code Picture Height */ public static BufferedImage createQrCodeBufferdImage(String contents, int width, int height){ Hashtable hints= new Hashtable(); hints.put(EncodeHintType.ERROR_CORRECTION, ErrorCorrectionLevel.L); hints.put(EncodeHintType.CHARACTER_SET, "utf-8"); hints.put(EncodeHintType.MARGIN, 0); BufferedImage image = null; try { BitMatrix bitMatrix = new MultiFormatWriter().encode( contents, BarcodeFormat.QR_CODE, width, height, hints); image = toBufferedImage(bitMatrix); } catch (WriterException e) { e.printStackTrace(); } return image; }
6. Add a signature (seal)
Add pictures at specified locations
private static void invoiceDefaultSign(PdfStamper stamper, String filePath, int x, int y) throws Exception { Resource resource = new ClassPathResource(filePath); byte[] bytes = null; bytes = FileUtils.is2ByeteArray(resource.getInputStream()); // Read pictures Image image = Image.getInstance(bytes); // Get the page for the action PdfContentByte under = stamper.getOverContent(1); // Scale pictures by field size image.scaleToFit(MAIN_SIGN_WIDTH, MAIN_SIGN_HEIGHT); // Add Picture image.setAbsolutePosition(x, y); under.addImage(image); } /** * Convert to Byte Array via InputStream * * @param is * @return * @throws IOException */ public static byte[] is2ByeteArray(InputStream is) throws IOException { ByteArrayOutputStream baos = new ByteArrayOutputStream(); byte[] buff = new byte[100]; int rc = 0; while((rc=is.read(buff, 0, 100))>0) { baos.write(buff, 0, rc); } return baos.toByteArray(); }
7. Close Flow
When you're done, remember to close the stream at the end. It's easy, but it's important
finally { if (stamp != null) { stamp.close(); } if (reader != null) { reader.close(); } }
Write at the end
If the position of the text field of the pdf is required to be higher, it needs to be fine-tuned step by step or tested with patience.When it comes to data security, I don't upload some of the effects.If more than one pdf is generated, a merge operation of the pdf is required, and then the pdf can be uploaded to the company's fastdfs file server as required.