java原装代码完成pdf在线预览和pdf打印及下载

作者:追逐繁星 时间:2022-06-18 03:23:21 

前提准备:

1. 项目中至少需要引入的jar包,注意版本:

    a) core-renderer.jar

    b) freemarker-2.3.16.jar

    c) iText-2.0.8.jar

    d) iTextAsian.jar

上代码:

注释: 此类为自定义的Tag类的基类,在action中怎么放的数据,在ftl中就怎么取数据,简洁明了。

 1. 自定义Tag类的基类


/**
* 通用的生成pdf预览和生成打印的html文件
*
* @author xg君
*
*/
public abstract class PDFTag extends BodyTagSupport {
private static final long serialVersionUID = 1L;
// 标签属性变量
private String json = "";
private String tempDir = "";
// 非标签属性变量
private Map<String, Object> rootMap = new HashMap<String, Object>();
private String templateStr = null;
private String freemarkereConfigurationBeanName = null;
private String fileName = null;
private String basePath = null;
private String fileEncoding = "utf-8";
@Override
public int doStartTag() throws JspException {
setConfigParams();
WebApplicationContext application = WebApplicationContextUtils.getWebApplicationContext(pageContext
 .getServletContext());
doServiceStart();
String ctx = (String) pageContext.getAttribute("ctx");
rootMap.put("ctx", ctx);
Map<String, Object> map = parseJSON2Map(json);
rootMap.putAll(map);
if (freemarkereConfigurationBeanName == null) {
 try {
 throw new CstuException("FreemarkereConfigurationBeanName不能为空!");
 } catch (CstuException e) {
 e.printStackTrace();
 }
}
Configuration cptFreemarkereConfiguration = (Configuration) application
 .getBean(freemarkereConfigurationBeanName);
try {
 if (templateStr == null) {
 throw new CstuException("模板文件不能为空!");
 }
 Template template = cptFreemarkereConfiguration.getTemplate(templateStr);
 if (basePath == null) {
 throw new CstuException("文件的基本路径(父路径)不能为空!");
 }
 File htmlPath = new File(tempDir + File.separator + basePath);
 if (!htmlPath.exists()) {
 htmlPath.mkdirs();
 }
 if (fileName == null) {
 throw new CstuException("生成的html文件名不能为空!");
 }
 File htmlFile = new File(htmlPath, File.separator + fileName);
 if (!htmlFile.exists()) {
 htmlFile.createNewFile();
 }
 BufferedWriter out = new BufferedWriter(
  new OutputStreamWriter(new FileOutputStream(htmlFile), fileEncoding));
 template.process(rootMap, out);
 out.flush();
 doServiceDoing();
 // 显示在页面
 template.process(rootMap, pageContext.getResponse().getWriter());
} catch (Exception e) {
 e.printStackTrace();
}
doServiceEnd();
return SKIP_BODY;
}
/**
* 配置基础参数,如
*/
public abstract void setConfigParams();
/**
* 业务处理方法-开始 填充数据
*
* @return
*/
public abstract void doServiceStart();
/**
* 业务处理方法-执行中 备用,可空实现,若rootMap中存在双份数据则可在此处填充判断条件
*
* @return
*/
public abstract void doServiceDoing();
/**
* 业务处理方法-结束 清空rootMap并调用垃圾回收,也可空实现
*
* @return
*/
public abstract void doServiceEnd();

/**
* 将元素放入rootMap中
*/
public void putKV(String key, Object value) {
rootMap.put(key, value);
}
/**
* 将map放入rootMap中
*
* @param m
*/
public void putMap(Map m) {
rootMap.putAll(m);
}
public void clear() {
rootMap.clear();
rootMap = null;
}
/**
* 移除元素
*
* @param key
* @return
*/
public Object remove(String key) {
return rootMap.remove(key);
}
public static Map<String, Object> parseJSON2Map(String jsonStr) {
Map<String, Object> map = new HashMap<String, Object>();
JSONObject json = JSONObject.fromObject(jsonStr);
for (Object k : json.keySet()) {
 Object v = json.get(k);
 if (v instanceof JSONArray) {
 List<Map<String, Object>> list = new ArrayList<Map<String, Object>>();
 Iterator<JSONObject> it = ((JSONArray) v).iterator();
 while (it.hasNext()) {
  JSONObject json2 = it.next();
  list.add(parseJSON2Map(json2.toString()));
 }
 map.put(k.toString(), list);
 } else {
 map.put(k.toString(), v);
 }
}
return map;
}
public String getJson() {
return json;
}
public void setJson(String json) {
this.json = json;
}
public String getTempDir() {
return tempDir;
}
public void setTempDir(String tempDir) {
this.tempDir = tempDir;
}
public String getTemplateStr() {
return templateStr;
}
public void setTemplateStr(String templateStr) {
this.templateStr = templateStr;
}
public String getFreemarkereConfigurationBeanName() {
return freemarkereConfigurationBeanName;
}
public void setFreemarkereConfigurationBeanName(String freemarkereConfigurationBeanName) {
this.freemarkereConfigurationBeanName = freemarkereConfigurationBeanName;
}
public String getFileName() {
return fileName;
}
public void setFileName(String fileName) {
this.fileName = fileName;
}
public String getBasePath() {
return basePath;
}
public void setBasePath(String basePath) {
this.basePath = basePath;
}
public String getFileEncoding() {
return fileEncoding;
}
public void setFileEncoding(String fileEncoding) {
this.fileEncoding = fileEncoding;
}
}

注释: setConfigParams方法是用于调用接口定义的配置参数的方法,如:templateStr,basePath等,doServiceStart,doServiceDoing和doServiceEnd等方法用于处理业务逻辑,比如我的需求是做出合同在一个页面显示,要分页,要加水印,但生成的pdf样式与预览的是不同的,所以我加了一个doServiceDoing中给rootMap添加判断条件,这样就能一个flt文件作出两种效果(预览和打印),当然如果预览和打印要一样的效果,doServiceDoing方法可以空实现。这四个方法总结一下就是:

1. setConfigParams : 配置参数

2. doServiceStart : 填充数据/条件

3. doServiceDoing : 填充数据/条件,到这里是个分界线,此方法之前,rootMap数据先进入html再进入浏览器(预览),此方法之后,rootMap数据会再次进入html文件,结束,所以此处可写判断

4. doServiceEnd : 可有可无,我还是写上了,万一哪天的数据集太大,此处便可以在数据填充完后,清理掉,节省内存空间

2. PDFTag的子类


/**
* 用户自定义PDFTag类
*
* @author xg君
*
*/
public class ViewPDFTag extends PDFTag {
private static final long serialVersionUID = 4528567203497016087L;
private String prjNature = "";
private String bookName = "";
private String prjCode = "";
/**
* 用户自定义的配置参数
*/
public PDFConfigurationInterface pDFConfigurationInterface = new PDFConfigurationInterface() {
@Override
public void configTemplateStr() {
 // 横,纵向
 if (prjNature.equalsIgnoreCase("2") || prjNature.equalsIgnoreCase("1")) {
 setTemplateStr("wj-project-print.ftl");
 }
}
@Override
public void configFreemarkereConfigurationBeanName() {
 setFreemarkereConfigurationBeanName("cptFreemarkereConfiguration");
}
@Override
public void configFileName() {
 // 填入html文件
 setFileName(prjCode + ".html");
}
@Override
public void configFileEncoding() {
 // 默认utf-8
}
@Override
public void configBasePath() {
 setBasePath("html_pdf");
}
};
@Override
public void doServiceStart() {
putKV("prjNature", prjNature);
putKV("bookName", bookName);
putKV("flag", "0");
}
@Override
public void doServiceDoing() {
putKV("flag", "1");
}
@Override
public void doServiceEnd() {
clear();
System.gc();
}
public String getPrjNature() {
return prjNature;
}
public void setPrjNature(String prjNature) {
this.prjNature = prjNature;
}
public String getBookName() {
return bookName;
}
public void setBookName(String bookName) {
this.bookName = bookName;
}
public String getPrjCode() {
return prjCode;
}
public void setPrjCode(String prjCode) {
this.prjCode = prjCode;
}
@Override
public void setConfigParams() {
pDFConfigurationInterface.configTemplateStr(); pDFConfigurationInterface.configFreemarkereConfigurationBeanName();
pDFConfigurationInterface.configFileName();
pDFConfigurationInterface.configBasePath();
pDFConfigurationInterface.configFileEncoding();
}
}

注释: PDFConfigurationInterface 是自定义的标签参数配置接口,子类必须定义一个该接口的实现类的成员变量或定义一个成员变量内部类,并在setConfigParams方法中调用使其生效。

子类的成员变量接收在tld文件中配置的属性。

3. 自定义的标签参数配置接口


/**
* PdfTag类的配置
*
* @author xg君
*
*/
public interface PDFConfigurationInterface {
/**
* 设置模板名称
*/
void configTemplateStr();
/**
* 设置配置的FreemarkereConfigurationBean的名称
*/
void configFreemarkereConfigurationBeanName();
/**
* 设置生成的html文件名称
*/
void configFileName();
/**
* 设置生成的html文件的基本路径(父目录)
*/
void configBasePath();
/**
* 设置文件编码,默认utf-8
*/
void configFileEncoding();
}

4. 自定义异常类


/**
* 自定义异常类
*
* @author Administrator
*
*/
public class CstuException extends Exception {
private static final long serialVersionUID = 4266461814747405870L;
public CstuException(String msg) {
super(msg);
}
}

5. tld文件配置


<tag>
<name>print</name> <tagclass>com.iris.taglib.web.PreviewPDFTag</tagclass>
<bodycontent>JSP</bodycontent>
<attribute>
 <name>json</name>
 <required>true</required>
 <rtexprvalue>true</rtexprvalue>
</attribute>
<attribute>
 <name>prjNature</name>
 <required>true</required>
 <rtexprvalue>true</rtexprvalue>
</attribute>
<attribute>
 <name>bookName</name>
 <required>true</required>
 <rtexprvalue>true</rtexprvalue>
</attribute>
<attribute>
 <name>tempDir</name>
 <required>true</required>
 <rtexprvalue>true</rtexprvalue>
</attribute>
<attribute>
 <name>prjCode</name>
 <required>true</required>
 <rtexprvalue>true</rtexprvalue>
</attribute>
</tag>

6. action


/**
* 处理PDF导出
*
*/
@Namespace("/export")
@Results({ @Result(name = "exceltemplate", location = "/WEB-INF/content/pdf/export-pdf.jsp"),
@Result(name = "exprotPdf2", location = "/WEB-INF/content/project/project/export/export-pdf2.jsp") })
public class ExportPdfAction extends ActionSupport {
private static final long serialVersionUID = -5454188364706173477L;
@Value("${tempDir}")
private String tempDir;
@Value("${pdfFont}")
private String pdfFont;
@Value("${staticResRootDir}")
private String staticResRootDir;
@Value("${staticResRootDir2}")
private String staticResRootDir2;
@Value("${WaterMarkImgDir}")
private String waterMarkImgDir;
@Autowired
private ProjectService projectService;
@Autowired
private PersonService personService;
@Autowired
private ConstDictionaryService constDictionaryService;
@Autowired
private FdPlanDetailService fdPlanDetailService;
@Autowired
private ServiceFactory serviceFactory;
@Action("exprotPdf2")
public String exprotPdf2() {
String prjCode = Struts2Utils.getParameter("prjCode");
prjCode = Struts2Utils.decodeDesString(prjCode);
Project project = projectService.getProjectById(Long.parseLong(prjCode));
Map<String, String> baseInfo = new HashMap<String, String>();
baseInfo.put("tempDir", tempDir);
// 项目编号
baseInfo.put("prjCode", prjCode);
// 项目类型
String prjNature = project.getPrjNature();
baseInfo.put("prjNature", prjNature);
// 水印名称格式:watermark+"-"+prjNature
baseInfo.put("waterMarkImg", waterMarkImgDir + File.separator + "watermark-" + prjNature + ".png");
// 负责人
Person person = personService.getPerson(project.getPsnCode());
String zhName = person.getZhName();
baseInfo.put("zhName", addStr(9, "<br/>", zhName));
// 项目编号
String prjNo = project.getPrjNo();
baseInfo.put("prjNo", prjNo);
// 项目来源
ConstDictionary cd = constDictionaryService.findCdByCategoryCode("project_from", project.getGrantNo());
String project_from = cd.getzh_cn_caption();
baseInfo.put("project_from", addStr(9, "<br/>", project_from));
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
// 起止年限
String startDate = sdf.format(project.getStartDate());
String endDate = sdf.format(project.getEndDate());
String startEndDate = startDate + "~" + endDate;
baseInfo.put("startEndDate", startEndDate);
// 项目类别--资助类别
String grantName = project.getGrantName();
baseInfo.put("grantName", addStr(9, "<br/>", grantName));
// 合同金额
String totalAmt = project.getTotalAmt().toString();
BigDecimal totalAmt_ = checkNumber(totalAmt);
baseInfo.put("totalAmt", totalAmt_.toString());
// 项目名称
String zhTitle = project.getZhTitle();
baseInfo.put("zhTitle", addStr(38, "<br/>", zhTitle));
List<Map<String, String>> ps = null;
try {
 ps = getMembers(project.getPrjXml());
} catch (Exception e) {
 e.printStackTrace();
}
String bookName = "";
// 判断项目类型
Map<String, Object> itemMap = new HashMap<String, Object>();
if (prjNature.equalsIgnoreCase("1")) {
 bookName = "第一份合同";
 // 获取fdPlanDetail
 List<Map<String, Object>> list = fdPlanDetailService.getFdPlanDetailsByPrjCode(Long.parseLong(prjCode));
 // test
 /*
 * Map<String, Object> test = new HashMap<String, Object>(); test.put("itemValue", "6");
 * test.put("itemName", "差旅费"); test.put("remark", "xxxxx"); list.add(test);
 */
 for (Map<String, Object> m : list) {
 String key = (String) m.get("ITEMNAME");
 BigDecimal itemValue = (BigDecimal) m.get("ITEMVALUE");
 BigDecimal proportion = new BigDecimal(0.00);
 if (itemValue != null && totalAmt_.compareTo(new BigDecimal("0.00")) != 0) {
  proportion = itemValue.divide(totalAmt_, 6, BigDecimal.ROUND_HALF_EVEN);
 }
 if (itemValue == null) {
  itemValue = new BigDecimal("0.00");
 }
 proportion = checkNumber(proportion.toString());
 Map<String, Object> data = new HashMap<String, Object>();
 // 添加比例
 data.put("proportion", proportion.toString());
 // 检测金额规范
 BigDecimal amt = checkNumber(itemValue.toString());
 data.put("itemValue", amt.toString());
 // remark
 String remark = (String) m.get("REAMRK");
 data.put("remark", remark == null ? "" : remark);

itemMap.put(key, data);
 }
} else if (prjNature.equalsIgnoreCase("2")) {
 bookName = "第二份合同";
}
Map<String, Object> map = new HashMap<String, Object>();
map.put("baseInfo", baseInfo);
map.put("projectMember", ps);
map.put("itemMap", itemMap);
map.put("psCount", ps.size());
map.put("psSum", 25 - ps.size());
String json = JSONObject.fromObject(map).toString();
Struts2Utils.getRequest().setAttribute("jsonData", json);
Struts2Utils.getRequest().setAttribute("prjNature", prjNature);
Struts2Utils.getRequest().setAttribute("bookName", bookName);
Struts2Utils.getRequest().setAttribute("tempDir", tempDir);
Struts2Utils.getRequest().setAttribute("prjCode", prjCode);
return "exprotPdf2";
}
public List<Map<String, String>> getMembers(String xmlData) throws Exception {
List<Map<String, String>> list = new ArrayList<Map<String, String>>();
Document doc = DocumentHelper.parseText(xmlData);
Node ps = doc.selectSingleNode("/data/project/persons");
List<Node> psList = ps.selectNodes("person");
String totalAmt = doc.selectSingleNode("/data/project/basic_info/total_amt").getText();
for (Node person : psList) {
 Map<String, String> map = new HashMap<String, String>();
 Node fund_proportion = person.selectSingleNode("fund_proportion");
 String fund_proportion_text = "";
 if (fund_proportion == null) {
 map.put("proportion", "0.00");
 map.put("fpAmt", "0.00");
 } else {
 fund_proportion_text = fund_proportion.getText();
 BigDecimal fp = new BigDecimal(fund_proportion_text);
 fp = fp.multiply(new BigDecimal("0.01"));
 fp = checkNumber(fp.toString());
 map.put("proportion", fp.toString());
 BigDecimal fdAmt_ = fp.multiply(new BigDecimal(totalAmt));
 fdAmt_ = checkNumber(fdAmt_.toString());
 map.put("fpAmt", fdAmt_.toString());
 }
 Node psn_name = person.selectSingleNode("psn_name");
 String psn_name_text = psn_name.getText();
 map.put("zhName_p", addStr(9, "<br/>", psn_name_text));
 Node psn_work = person.selectSingleNode("psn_work");
 String psn_work_text = "";
 if (psn_work != null) {
 psn_work_text = psn_work.getText();
 }
 map.put("work", addStr(9, "<br/>", psn_work_text));
 Node dept_code_name = person.selectSingleNode("dept_code_name");
 String dept_code_name_text = "";
 if (dept_code_name != null) {
 dept_code_name_text = dept_code_name.getText();
 }
 map.put("deptName", addStr(9, "<br/>", dept_code_name_text));
 Node psn_type_name = person.selectSingleNode("psn_type_name");
 String psn_type_name_text = "";
 if (psn_type_name != null) {
 psn_type_name_text = psn_type_name.getText();
 }
 map.put("psnTypeName", psn_type_name_text);

list.add(map);
}
return list;
}
/**
* 为字符串添加指定字符
*
* @param num
* @param splitStr
* @param str
* @return
*/
public String addStr(int num, String splitStr, String str) {
StringBuffer sb = new StringBuffer();
String temp = str;
int len = str.length();
while (len > 0) {
 if (len < num) {
 num = len;
 }
 sb.append(temp.substring(0, num)).append(splitStr);
 temp = temp.substring(num);
 len = temp.length();
}
return sb.toString();
}
/**
* 两个数字/英文
*
* @param str
* @param num
* @return 最终索引
*/
public static int getEndIndex(String str, double num) {
int idx = 0;
int count = 0;
double val = 0.00;
// 判断是否是英文/数字
for (int i = 0; i < str.length(); i++) {
 if ((str.charAt(i) >= 'A' && str.charAt(i) <= 'Z') || (str.charAt(i) >= 'a' && str.charAt(i) <= 'z')
  || Character.isDigit(str.charAt(i))) {
 val += 0.50;
 } else {
 val += 1.00;
 }
 count = i + 1;
 if (val >= num) {
 idx = i;
 break;
 }
}
if (idx == 0) {
 idx = count;
}
return idx;
}
/**
* 下载pdf文件
*
* @return
*/
@Action("downLoad")
public String downLoad() {
String prjCode = Struts2Utils.getParameter("prjCode");
String basePath = "html_pdf";
Project project = projectService.getProjectById(Long.parseLong(prjCode));
String zhTitle = project.getZhTitle();
FileOutputStream fos = null;
// html
File htmlFile = new File(tempDir + File.separator + basePath + File.separator + prjCode + ".html");
String pdfPath = tempDir + File.separator + basePath + File.separator + zhTitle + ".pdf";
try {
 fos = new FileOutputStream(pdfPath);

String url = htmlFile.toURI().toURL().toString();
 ITextRenderer renderer = new ITextRenderer();
 renderer.setDocument(url);
 ITextFontResolver fontResolver = renderer.getFontResolver();
 fontResolver.addFont(pdfFont + File.separator + "SimSun.ttc", BaseFont.IDENTITY_H, BaseFont.NOT_EMBEDDED);
 renderer.layout();
 renderer.createPDF(fos);
} catch (Exception e) {
 e.printStackTrace();
} finally {
 try {
 if (fos != null) {
  fos.close();
 }
 } catch (IOException e) {
 e.printStackTrace();
 }
}
// 添加水印
String wartermark = "";
if (project.getPrjNature().equalsIgnoreCase("1")) {
 // 横向
 wartermark = "CTGU横向科研项目立项通知书";
} else if (project.getPrjNature().equalsIgnoreCase("2")) {
 // 纵向
 wartermark = "CTGU纵向科研项目立项通知书";
}
String wm_pdf = tempDir + File.separator + "wm_" + project.getZhTitle() + ".pdf";
try {
 BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(wm_pdf));
 waterMark(bos, pdfPath, wartermark, staticResRootDir2 + File.separator + waterMarkImgDir + File.separator
  + "watermark-" + project.getPrjNature() + ".png");
} catch (Exception e2) {
 e2.printStackTrace();
}
// 拿到pdf
File pdfFile = new File(wm_pdf);
BufferedOutputStream out = null;
FileInputStream in = null;
try {
 in = new FileInputStream(pdfFile);
 HttpServletResponse response = Struts2Utils.getResponse();
 response.reset();
 String fileName = zhTitle + ".pdf";
 String fileName2 = URLEncoder.encode(fileName, "UTF-8");
 String agent = Struts2Utils.getRequest().getHeader("USER-AGENT");
 // IE
 if (null != agent && -1 != agent.indexOf("MSIE")) {
 fileName2 = new String(fileName.getBytes("GBK"), "ISO8859-1");
 } else if (null != agent && -1 != agent.indexOf("Mozilla")) {
 fileName2 = new String(fileName.getBytes("UTF-8"), "ISO8859-1");
 }
 response.setCharacterEncoding("UTF-8");
 response.setHeader("Content-Disposition", "attachment;filename=\"" + fileName2 + "\"");
 response.setContentType(FileContentTypes.getContentType(zhTitle + ".pdf"));
 out = new BufferedOutputStream(response.getOutputStream());
 byte[] buffer = new byte[16 * 1024];
 int len = 0;
 while ((len = in.read(buffer)) > 0) {
 out.write(buffer, 0, len);
 }
 out.flush();
} catch (Exception e) {
 e.printStackTrace();
} finally {
 if (out != null) {
 try {
  out.close();
 } catch (IOException e1) {
  e1.printStackTrace();
 }
 try {
  in.close();
 } catch (IOException e) {
  e.printStackTrace();
 }
 }
}
// 清理残留文件
// if (htmlFile.exists()) {
// htmlFile.delete();
// }
File temp_file = new File(pdfPath);
if (temp_file.exists()) {
 temp_file.delete();
}
if (pdfFile.exists()) {
 pdfFile.delete();
}
return null;
}
public BigDecimal checkNumber(String number) {
// 初始化为6位小数
DecimalFormat df = new DecimalFormat("0.000000");
String num = df.format(Double.parseDouble(number));
BigDecimal bd = new BigDecimal(num);
String val = bd.toString();
val = val.replaceAll("^(0+)", "");
val = val.replaceAll("(0+)$", "");
int idx = val.indexOf(".");
int len = val.substring(idx + 1).length();
if (len < 2) {
 if (len == 0 && idx == 0) {
 bd = new BigDecimal("0.00");
 } else {
 bd = new BigDecimal(val).setScale(2);
 }
} else {
 bd = new BigDecimal(val).setScale(len);
}
return bd;
}
private String replaceStr(String str, String reVal) {
Pattern pattern = Pattern.compile("^" + reVal + "+|" + reVal + "+$");
Matcher matcher = pattern.matcher(str);
return matcher.replaceAll("");
}
/**
* 添加水印
*/
private void waterMark(BufferedOutputStream bos, String input, String waterMarkName, String imagePath) {
try {
 PdfReader reader = new PdfReader(input);
 PdfStamper stamper = new PdfStamper(reader, bos);
 int total = reader.getNumberOfPages() + 1;
 PdfContentByte content;
 BaseFont base = BaseFont.createFont("STSong-Light", "UniGB-UCS2-H", BaseFont.EMBEDDED);
 PdfGState gs = new PdfGState();
 for (int i = 1; i < total; i++) {
 content = stamper.getOverContent(i);// 在内容上方加水印
 content.setGState(gs);
 content.beginText();
 Image image = Image.getInstance(imagePath);
 image.setAbsolutePosition(-30, 200);
 image.scalePercent(80);
 content.addImage(image);
 content.endText();
 }
 stamper.close();
} catch (Exception e) {
 e.printStackTrace();
}
}
}

7. ftl文件(模板文件)


<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>打印预览</title>
<meta http-equiv="Content-Type" content="text/html;charset=UTF-8" />
......
</head>
<body>
</body>
</html>

预览效果:

java原装代码完成pdf在线预览和pdf打印及下载

打印效果:

java原装代码完成pdf在线预览和pdf打印及下载

来源:http://www.cnblogs.com/king-xg/p/6369291.html

标签:java,pdf
0
投稿

猜你喜欢

  • java中为何重写equals时必须重写hashCode方法详解

    2022-10-24 01:44:39
  • Spring Cloud Alibaba使用Nacos作为注册中心和配置中心

    2021-07-15 18:18:42
  • Java中通过Class类获取Class对象的方法详解

    2021-11-09 17:12:57
  • Springboot公共字段填充及ThreadLocal模块改进方案

    2023-11-17 22:58:39
  • IDEA安装阿里巴巴编码规范插件的两种方式详解(在线安装和离线安装)

    2022-07-23 19:18:54
  • FeignClient中name和url属性的作用说明

    2023-06-04 13:21:55
  • Java SpringBoot在RequestBody中高效的使用枚举参数原理案例详解

    2022-03-28 01:47:18
  • SpringBoot配置拦 截器实现过程详解

    2023-11-24 17:14:58
  • java实现支付宝退款功能

    2021-09-25 14:00:00
  • 如何使用MybatisPlus快速进行增删改查详解

    2023-11-03 06:58:13
  • jsp如何获取Session中的值

    2023-07-01 05:59:28
  • 永久解决idea git log乱码的问题

    2022-01-10 06:13:13
  • Java IO流 文件传输基础

    2023-08-24 13:27:11
  • JAVA 获取系统当前时间实例代码

    2022-09-01 05:53:01
  • Java线程池submit阻塞获取结果的实现原理详解

    2021-08-29 03:55:45
  • java 创建线程的几种方式

    2023-10-29 19:40:16
  • 详解Spring的StringUtils踩坑记录

    2021-08-23 16:14:39
  • C#使用linq对数组进行筛选排序的方法

    2023-12-06 06:21:21
  • 分布式系统下调用链追踪技术面试题

    2023-11-25 05:55:00
  • java实现简单的俄罗斯方块

    2021-08-02 18:13:11
  • asp之家 软件编程 m.aspxhome.com