最近有个需求,要支持批量打印用户订单部分信息,考虑个技术方案如下
定义一个freemaker模板,把订单信息放到freemaker模板里,然后通过freemaker把数据合并成一个html字符串,然后通过itext把html信息转成对应的pdf
1引入POM
<dependency>
<groupId>com.itextpdf</groupId>
<artifactId>itextpdf</artifactId>
<version>5.5.13</version>
</dependency>
<dependency>
<groupId>com.itextpdf</groupId>
<artifactId>itext-xtra</artifactId>
<version>5.5.13</version>
</dependency>
<dependency>
<groupId>com.itextpdf.tool</groupId>
<artifactId>xmlworker</artifactId>
<version>5.5.13</version>
</dependency>
<dependency>
<groupId>com.itextpdf</groupId>
<artifactId>itext-asian</artifactId>
<version>5.2.2</version>
</dependency>
<!-- freemarker engine -->
<dependency>
<groupId>org.freemarker</groupId>
<artifactId>freemarker</artifactId>
<version>2.3.27-incubating</version>
</dependency>
2 freemakerUtil
这个里加载模板文件有3种,看https://blog.csdn.net/qq_33442546/article/details/52609758
import freemarker.template.Configuration;
import freemarker.template.Template;
import freemarker.template.TemplateException;
import freemarker.template.TemplateExceptionHandler;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.File;
import java.io.IOException;
import java.io.StringWriter;
import java.math.BigDecimal;
import java.util.*;
public class FreemarkerUtil {
private static final Logger logger = LoggerFactory.getLogger(FreemarkerUtil.class);
/**
* freemarker config
*/
private static Configuration freemarkerConfig = new Configuration(Configuration.DEFAULT_INCOMPATIBLE_IMPROVEMENTS);
static{
String templatePath = Thread.currentThread().getContextClassLoader().getResource("").getPath();
int wei = templatePath.lastIndexOf("WEB-INF/classes/");
if (wei > -1) {
templatePath = templatePath.substring(0, wei);
}
try {
freemarkerConfig.setDirectoryForTemplateLoading(new File(templatePath, "templates/freemaker"));
freemarkerConfig.setNumberFormat("#");
freemarkerConfig.setClassicCompatible(true);
freemarkerConfig.setDefaultEncoding("UTF-8");
freemarkerConfig.setLocale(Locale.CHINA);
freemarkerConfig.setTemplateExceptionHandler(TemplateExceptionHandler.RETHROW_HANDLER);
} catch (IOException e) {
logger.error(e.getMessage(), e);
}
}
/**
* process Template Into String
*
* @param template
* @param model
* @return
* @throws IOException
* @throws TemplateException
*/
public static String processTemplateIntoString(Template template, Object model)
throws IOException, TemplateException {
StringWriter result = new StringWriter();
template.process(model, result);
return result.toString();
}
/**
* process String
*
* @param templateName
* @param params
* @return
* @throws IOException
* @throws TemplateException
*/
public static String processString(String templateName, Map<String, Object> params)
throws IOException, TemplateException {
Template template = freemarkerConfig.getTemplate(templateName);
String htmlText = processTemplateIntoString(template, params);
return htmlText;
}
public static String getTestData() throws Exception{
OrderPrintInfo orderPrintInfo=new OrderPrintInfo();
orderPrintInfo.setOrderId(555L);
orderPrintInfo.setAddress("北京市区");
orderPrintInfo.setCustomerName("小辣椒");
orderPrintInfo.setPhone("22222");
OrderSkuInfo orderSkuInfo1 =new OrderSkuInfo();
orderSkuInfo1.setName("one phone");
orderSkuInfo1.setSize("1");
orderSkuInfo1.setColor("1 color");
orderSkuInfo1.setProductId(22222L);
orderSkuInfo1.setNum(8);
orderSkuInfo1.setSingleShouldPrice(new BigDecimal(22));
orderSkuInfo1.setSubtotalPrice(new BigDecimal(22));
orderSkuInfo1.setImgPath("222");
OrderSkuInfo orderSkuInfo2 =new OrderSkuInfo();
orderSkuInfo2.setName("two phone");
orderSkuInfo2.setSize("2");
orderSkuInfo2.setColor("2 color");
orderSkuInfo2.setNum(9);
orderSkuInfo2.setProductId(123455L);
orderSkuInfo2.setSingleShouldPrice(new BigDecimal(233));
orderSkuInfo2.setSubtotalPrice(new BigDecimal(22));
orderSkuInfo2.setImgPath("333");
List<OrderSkuInfo> orderSkuInfos=new ArrayList<OrderSkuInfo>();
orderSkuInfos.add(orderSkuInfo1);
orderSkuInfos.add(orderSkuInfo2);
orderPrintInfo.setOrderSkuInfos(orderSkuInfos);
// code genarete
Map<String, Object> params = new HashMap<String, Object>();
params.put("orderPrintInfo", orderPrintInfo);
return FreemarkerUtil.processString("/printorderdetail.ftl", params);
}
public static void main(String args[]){
// // parse table
// ClassInfo classInfo=new ClassInfo();
// classInfo.setClassComment("ProductInfoComment");
// classInfo.setClassName("ProductInfoClass");
// classInfo.setTableName("ProductInfoTable");
OrderPrintInfo orderPrintInfo=new OrderPrintInfo();
orderPrintInfo.setOrderId(555L);
orderPrintInfo.setAddress("北京市区");
orderPrintInfo.setCustomerName("小辣椒");
orderPrintInfo.setPhone("22222");
OrderSkuInfo orderSkuInfo1 =new OrderSkuInfo();
orderSkuInfo1.setName("one phone");
orderSkuInfo1.setSize("1");
orderSkuInfo1.setColor("1 color");
orderSkuInfo1.setProductId(22222L);
orderSkuInfo1.setNum(8);
orderSkuInfo1.setSingleShouldPrice(new BigDecimal(22));
orderSkuInfo1.setSubtotalPrice(new BigDecimal(22));
orderSkuInfo1.setImgPath("222");
OrderSkuInfo orderSkuInfo2 =new OrderSkuInfo();
orderSkuInfo2.setName("two phone");
orderSkuInfo2.setSize("2");
orderSkuInfo2.setColor("2 color");
orderSkuInfo2.setNum(9);
orderSkuInfo2.setProductId(123455L);
orderSkuInfo2.setSingleShouldPrice(new BigDecimal(233));
orderSkuInfo2.setSubtotalPrice(new BigDecimal(22));
orderSkuInfo2.setImgPath("333");
List<OrderSkuInfo> orderSkuInfos=new ArrayList<OrderSkuInfo>();
orderSkuInfos.add(orderSkuInfo1);
orderSkuInfos.add(orderSkuInfo2);
orderPrintInfo.setOrderSkuInfos(orderSkuInfos);
// code genarete
Map<String, Object> params = new HashMap<String, Object>();
params.put("orderPrintInfo", orderPrintInfo);
try{
System.out.println(FreemarkerUtil.processString("/printorderdetail.ftl", params));
}catch (Exception e){
e.printStackTrace();
}
}
}
3PDF
public static Font getChineseFont() {
BaseFont bfChinese;
Font fontChinese = null;
try {
bfChinese = BaseFont.createFont("STSongStd-Light", "UniGB-UCS2-H", BaseFont.NOT_EMBEDDED);
// fontChinese = new Font(bfChinese, 12, Font.NORMAL);
fontChinese = new Font(bfChinese, 12, Font.NORMAL, BaseColor.BLUE);
} catch (DocumentException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return fontChinese;
}
public static void main(String[] args) {
Rectangle tRectangle = new Rectangle(PageSize.A4); // 页面大小
// tRectangle = new Rectangle(0, 0, 800, 600);
tRectangle.setBackgroundColor(BaseColor.ORANGE); // 页面背景色
tRectangle.setBorder(1220);// 边框
tRectangle.setBorderColor(BaseColor.BLUE);// 边框颜色
tRectangle.setBorderWidth(244.2f);// 边框宽度
Document document = new Document(tRectangle);
document = new Document(tRectangle.rotate());// 横向打印
try {
PdfWriter writer = PdfWriter.getInstance(document, new FileOutputStream("HelloWorld124.pdf"));
// 2-PDF文档属性
document.addTitle("33");// 标题
document.addAuthor("22");// 作者
document.addSubject("33");// 主题
document.addKeywords("nice");// 关键字
document.addCreator("33");// 谁创建的
// 3-综合属性:
document.setMargins(10, 20, 30, 40);// 页边空白
document.open();
//document.add(new Chunk("今天晚上我要中大奖了:明天我就不用来公司了啊哈哈哈哈 ", getChineseFont()));
// document.add(new Chunk("带着钱我要去新西兰了呀,啦啦啦啦: ", getChineseFont()));
writer.setPageEmpty(true);// fasle-显示空内容的页;true-不会显示
// 1-Chunk块对象: a String, a Font, and some attributes
// document.add(new Chunk("太高兴了: ", getChineseFont()));
//
// Chunk tChunk2 = new Chunk("中大奖", getChineseFont());
// tChunk2.setBackground(BaseColor.CYAN, 1f, 0.5f, 1f, 1.5f); // 设置背景色
// tChunk2.setTextRise(6); // 上浮
// tChunk2.setUnderline(0.2f, -2f); // 下划线
// document.add(tChunk2);
//
// document.add(Chunk.NEWLINE); // 新建一行
// // document.add(new Phrase("Phrase page :")); //会上浮,不知道原因??
//
// // 2-Phrase短语对象: a List of Chunks with leading
// document.add(new Phrase("Phrase page :"));
// Phrase tPhrase = new Phrase();
// Chunk name = new Chunk("China");
// name.setUnderline(0.2f, -2f);
// tPhrase.add(name);
// tPhrase.add(Chunk.NEWLINE);// 放在容器中好用
// tPhrase.add(new Chunk("换行了 :", getChineseFont()));
// tPhrase.add(new Chunk("chinese"));
// tPhrase.setLeading(14f);// 行间距
// document.add(tPhrase);
//
// // 这边就好用,上面是Chunk,就不好用
// // 放在段落或短语中又好用
// document.add(Chunk.NEWLINE);
// Phrase director2 = new Phrase();
// Chunk name2 = new Chunk("换行了---Japan", getChineseFont());
// name2.setUnderline(0.2f, -2f);
// director2.add(name2);
// director2.add(new Chunk(","));
// director2.add(new Chunk(" "));
// director2.add(new Chunk("japanese上浮下", getChineseFont()).setTextRise(3f));
// director2.setLeading(24);
// document.add(director2);
// 3-Paragraph段落对象: a Phrase with extra properties and a newline
// document.add(new Paragraph("Paragraph page"));
// Paragraph info = new Paragraph();
// info.add(new Chunk("China "));
// info.add(new Chunk("chinese"));
// info.add(Chunk.NEWLINE); // 好用的
// info.add(new Phrase("Japan "));
// info.add(new Phrase("japanese"));
// info.setSpacingAfter(10f);// 设置段落下空白
// document.add(info);
XMLWorkerHelper.getInstance().parseXHtml(writer, document,
new ByteArrayInputStream(FreemarkerUtil.getTestData().getBytes("Utf-8")),
Charset.forName("UTF-8"));
document.newPage();
XMLWorkerHelper.getInstance().parseXHtml(writer, document,
new ByteArrayInputStream(FreemarkerUtil.getTestData().getBytes("Utf-8")),
Charset.forName("UTF-8"));
// 段落是比较好用的
// Paragraph tParagraph = new Paragraph(FreemarkerUtil.getTestData(), getChineseFont());
// tParagraph.setAlignment(Element.ALIGN_JUSTIFIED);// 对齐方式
//
// tParagraph.setIndentationLeft(12);// 左缩进
// tParagraph.setIndentationRight(12);// 右缩进
// tParagraph.setFirstLineIndent(24);// 首行缩进
//
// tParagraph.setLeading(20f);// 行间距
// tParagraph.setSpacingBefore(5f);// 设置上空白
// tParagraph.setSpacingAfter(10f);// 设置段落下空白
// document.add(tParagraph);
// // 每个新的段落会另起一行
// tParagraph = new Paragraph("新的段落", getChineseFont());
// tParagraph.setAlignment(Element.ALIGN_CENTER);// 居中
// document.add(tParagraph);
document.close();
writer.close();
} catch (Exception e) {
e.printStackTrace();
}
}
freemaker模板文件
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<meta name="keywords" content="" />
<meta name="description" content="" />
<title>$!printText.title</title>
<link type="text/css" rel="stylesheet" href="../css/print.css" source="combo" />
</head>
<body style = "font-family: SimSun;">
<div id="printDiv">
<div class="header">
<div class="w">
<a href="javascript:;" class="logo"><img src="../img/logo.png" alt="" /></a>
</div>
</div>
<div class="w">
<div class="shop-list">
<ul>
<li>
<div class="cell">
${orderPrintInfo.orderId}
</div> </li>
<li>
<div class="cell">
${orderPrintInfo.customerName}
</div> </li>
<li>
<div class="cell">
${orderPrintInfo.phone}
</div> </li>
<li>
<div class="cell">
${orderPrintInfo.address}
</div> </li>
</ul>
</div>
<div class="shop-detail">
<table cellpadding="0" cellspacing="0" width="100%">
<tbody>
<tr>
<th>ProductID</th>
<th class="align-right">NUM</th>
<th class="align-right">price</th>
<th class="align-right">All Price</th>
</tr>
<#list orderPrintInfo.orderSkuInfos as c>
<tr>
<td class="align-left">
<div class="s-img">
${c.imgPath}
</div>
<div class="s-detail">
<div class="s-title">
${c.name}
</div>
<div class="s-info">
${c.size}
</div>
</div>
</td>
<td>${c.productId}</td>
<td class="align-right">${c.num}</td>
<td class="align-right">${c.singleShouldPrice}</td>
<td class="align-right">${c.subtotalPrice}</td>
</tr>
</#list>
</tbody>
</table>
</div>
</div>
</div>
</body>
</html>
题外话:如果想把配置文件放到一个xml里,然后xml增加别的信息可以先解析xml(使用Jaxb2.0实现XML<->Java Object的Mapper)然后在把对应配置文件元数据拿出来通过freemaker来生成对应的文件.