近来收到一个需求, 制作 PDF 制作发票.
类似于制作这样的发票
技术选型我选择java
在网上寻找了一些操作PDF的框架决定用iText制作, 因为它比较活跃, 而且后期做签章和插入图片二维码都有着现成的接口.
大体想了下难点都在什么地方, 插入图片,商品太多时候的分页问题,数字签章........
于是我就开始尝试着使用 iText的新建操作pdf,
新建maven工程
加入依赖
<!-- https://mvnrepository.com/artifact/org.projectlombok/lombok -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.16.20</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>com.itextpdf</groupId>
<artifactId>itextpdf</artifactId>
<version>5.3.2</version>
</dependency>
起初准备自己画这个table, 后来花了几个都很丑于是参考网上发可以先制作模板,预设表单值再往里面填入表单数据.
具体可以通过Adobe Acrobat Pro去制作
制作好的模板
JAVA操作PDF
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import com.boss.hmc.bean.Commodity;
import com.boss.hmc.bean.Ticket;
import com.itextpdf.text.DocumentException;
import com.itextpdf.text.Image;
import com.itextpdf.text.Rectangle;
import com.itextpdf.text.pdf.AcroFields;
import com.itextpdf.text.pdf.BaseFont;
import com.itextpdf.text.pdf.PdfContentByte;
import com.itextpdf.text.pdf.PdfReader;
import com.itextpdf.text.pdf.PdfStamper;
public class PDFTemplet2Ticket {
// public static String modelPath = "D:\\work\\pdf\\发票模板123_bak.pdf";
public static String modelPath = "D:\\work\\pdf\\模板1.pdf";
public static String modelPath2 = "D:\\work\\pdf\\模板2.pdf";
private static String imagePath = "D:\\study\\practice\\chapter.png";
public void ticketInPDF(Ticket ticket, File target) throws IOException, DocumentException {
if (target == null)
target.createNewFile();
PdfReader reader = new PdfReader(modelPath);
ByteArrayOutputStream bos = new ByteArrayOutputStream();
// 读pdf
PdfStamper ps = new PdfStamper(reader, bos);
AcroFields s = ps.getAcroFields();
ArrayList<BaseFont> fontList = new ArrayList<BaseFont>();
// BaseFont bf = BaseFont.createFont("STSong-Light","UniGB-UCS2-H", BaseFont.NOT_EMBEDDED);
BaseFont bf = BaseFont.createFont("STSong-Light", "UniGB-UCS2-H", BaseFont.NOT_CACHED);
insertImage(s, "quickMark", ps);
fontList.add(bf);
s.setSubstitutionFonts(fontList);
s.setField("machineIdentificNumber", ticket.getMachineIdentificNumber());
s.setField("invoiceNumber", ticket.getInvoiceNumber());
s.setField("invoiceCode", ticket.getInvoiceCode());
s.setField("consumerPassword", ticket.getConsumerPassword());
String date = ticket.getInvoiceDate();
s.setField("invoiceDate",
new StringBuilder(date.substring(0, 4)).append(" ").append(date.substring(4, 6)).append(" ")
+ date.substring(6));
s.setField("checkCode", ticket.getCheckCode());
s.setField("consumerName", ticket.getConsumerName());
s.setField("consumerIdentityNumber", ticket.getConsumerIdentityNumber());
s.setField("consumerAddTel", ticket.getConsumerAddTel());
s.setField("consumerAccount", ticket.getConsumerAccount());
s.setField("figure", "¥"+ticket.getFigure() + " .");
s.setField("tax", "¥"+ticket.getTax() + " .");
s.setField("sumFigureTax", "¥"+ticket.getSumFigureTax() + " ");
s.setField("chineseSumFigureTax", ticket.getChineseSumFigureTax());
List<Commodity> list = ticket.getCommodities();
if (list != null && list.size() >= 5) {
s.setField("name0", " (详见销货清单)");
addDetailItems(list);
} else if (list != null && list.size() < 5) {
addItems(list, s);
}
s.setField("merchantName", ticket.getMerchantName());
s.setField("merchantIdentityNumber", ticket.getMerchantIdentityNumber());
s.setField("merchantAddTel", ticket.getMerchantAddTel());
s.setField("merchantAccount", ticket.getMerchantAccount());
s.setField("receiver", ticket.getReceiver());
s.setField("proofreader", ticket.getProofreader());
s.setField("drawer", ticket.getDrawer());
ps.setFormFlattening(true);
ps.close();
FileOutputStream fos = new FileOutputStream(target);
fos.write(bos.toByteArray());
fos.close();
}
private void addDetailItems(List<Commodity> list) throws IOException, DocumentException {
int size = list.size();
//每页最多的条数
int pageSize = 20;
//总页数
int total = size/pageSize + (size%pageSize > 0 ? 1 : 0);
for (int i = 0; i < total; i++) {
int nowPageSize = pageSize;
//末页条数
if(i == total-1) nowPageSize = (size%pageSize == 0 ? pageSize : size % pageSize);
printEveryDetails(list,i*pageSize,i,nowPageSize,total);
}
}
private void printEveryDetails(List<Commodity> list, int start, int page, int size, int total) throws IOException, DocumentException {
PdfReader reader = new PdfReader(modelPath2);
ByteArrayOutputStream bos = new ByteArrayOutputStream();
// 读pdf
PdfStamper ps = new PdfStamper(reader, bos);
AcroFields s = ps.getAcroFields();
s.addSubstitutionFont(BaseFont.createFont("STSong-Light", "UniGB-UCS2-H", BaseFont.NOT_CACHED));
for (int i = start,j = 1; i < start+size; i++,j++) {
Commodity c = list.get(i);
s.setField("no", j+"");
s.setField("name" + i, c.getName());
s.setField("modelType" + i, c.getModelType());
s.setField("unit" + i, c.getUnit());
s.setField("count" + i, c.getCount() + "");
s.setField("price" + i, c.getPrice().toString());
s.setField("figure" + i, c.getFigure().toString());
s.setField("taxRate" + i, c.getTaxRate());
s.setField("taxMoney" + i, c.getTaxMoney() + "");
}
ps.setFormFlattening(true);
ps.close();
FileOutputStream fos = new FileOutputStream(new File("detail"+page));
fos.write(bos.toByteArray());
fos.close();
}
private void addItems(List<Commodity> list, AcroFields s) throws IOException, DocumentException {
for (int i = 0; i < list.size(); i++) {
Commodity c = list.get(i);
s.setField("name" + i, c.getName());
s.setField("modelType" + i, c.getModelType());
s.setField("unit" + i, c.getUnit());
s.setField("count" + i, c.getCount() + "");
s.setField("price" + i, c.getPrice().toString());
s.setField("figure" + i, c.getFigure().toString());
s.setField("taxRate" + i, c.getTaxRate());
s.setField("taxMoney" + i, c.getTaxMoney() + "");
}
}
public static void generatePDF() {
PDFMergerUtility PDFmerger = new PDFMergerUtility();
}
public static void insertImage(AcroFields form, String fieldName, PdfStamper stamper)
throws DocumentException, IOException {
// form.addSubstitutionFont(BaseFont.createFont("STSong-Light", "UniGB-UCS2-H", BaseFont.NOT_EMBEDDED));
// 通过域名获取所在页和坐标,左下角为起点
int pageNo = form.getFieldPositions(fieldName).get(0).page;
Rectangle signRect = form.getFieldPositions(fieldName).get(0).position;
float x = signRect.getLeft();
float y = signRect.getBottom();
// 读图片
Image image = Image.getInstance(imagePath);
// 获取操作的页面
PdfContentByte under = stamper.getOverContent(pageNo);
// 根据域的大小缩放图片
image.scaleToFit(signRect.getWidth(), signRect.getHeight());
// 添加图片
image.setAbsolutePosition(x, y);
under.addImage(image);
}
public static void main(String[] args) throws IOException, DocumentException {
// 读模板
Ticket ticket = Xml2Ticket.xml2ticket(new File("example.txt"));
File file = new File("D:\\work\\pdf\\target.pdf");
// 打印pdf
new PDFTemplet2Ticket().ticketInPDF(ticket, file);
}
}
制作好的初期效果,细节慢慢调整