java 制作签名版电子合同 您所在的位置:网站首页 怎么制作电子照片文件 java 制作签名版电子合同

java 制作签名版电子合同

2024-07-04 20:13| 来源: 网络整理| 查看: 265

Java 制作带签名的电子合同

根据项目需求,需要生成一个带电子签名的合同,即客户在手机进行签名后,将签名图片合成到合同中。

目前方案有两个,使用docx4j插件根据书签生成word版电子合同 或者 使用itext插件根据PDF模版生成PDF版电子合同。

方案一:使用docx4j生成word电子合同

本文参考文章来自:https://www.cnblogs.com/qlqwjy/p/9866484.html

1.引入docx4j

本案例使用maven仓库引入jar包

org.docx4j docx4j 6.1.2 2.docx4j配置

可以不添加配置文件,但debug日志会提示找不到docx4j配置文件

在src下创建docx4j.properties配置文件,maven项目请放在resources文件夹下

# Page size: use a value from org.docx4j.model.structure.PageSizePaper enum # eg A4, LETTER docx4j.PageSize=LETTER # Page size: use a value from org.docx4j.model.structure.MarginsWellKnown enum docx4j.PageMargins=NORMAL docx4j.PageOrientationLandscape=false # Page size: use a value from org.pptx4j.model.SlideSizesWellKnown enum # eg A4, LETTER pptx4j.PageSize=LETTER pptx4j.PageOrientationLandscape=false # These will be injected into docProps/app.xml # if App.Write=true docx4j.App.write=true docx4j.Application=docx4j docx4j.AppVersion=6.1.2 # of the form XX.YYYY where X and Y represent numerical values # These will be injected into docProps/core.xml docx4j.dc.write=true docx4j.dc.creator.value=docx4j docx4j.dc.lastModifiedBy.value=docx4j # #docx4j.McPreprocessor=true # If you haven't configured log4j yourself # docx4j will autoconfigure it. Set this to true to disable that docx4j.Log4j.Configurator.disabled=false 3.构建WordProcessingMlPackage对象

docx格式文档可以理解为一个压缩包,若将其解压可看到一个类似前端的工程项目,其中document.xml用于全文的配置,详细解说请自行查阅资料

WordprocessingMLPackage是操作word文档包对象;

创建新的word文档

import org.docx4j.openpackaging.packages.WordprocessingMLPackage; /** * 构建新word文件 */ public static WordprocessingMLPackage build() throws Exception{ return WordprocessingMLPackage.createPackage(); }

加载存在的word文档

import org.docx4j.openpackaging.packages.WordprocessingMLPackage; /** * 读取存在的word文件 * @param wordFilePath word文件路径 */ public static WordprocessingMLPackage load(String wordFilePath) throws Exception{ return WordprocessingMLPackage.load(new File(wordFilePath)); } 4.获取书签

书签:CTBookmark对象

import org.docx4j.TraversalUtil; import org.docx4j.dml.wordprocessingDrawing.Inline; import org.docx4j.finders.RangeFinder; import org.docx4j.jaxb.Context; import org.docx4j.openpackaging.packages.WordprocessingMLPackage; import org.docx4j.openpackaging.parts.WordprocessingML.BinaryPartAbstractImage; import org.docx4j.openpackaging.parts.WordprocessingML.MainDocumentPart; import org.docx4j.org.apache.poi.util.IOUtils; import org.docx4j.wml.*; public static void main(String[] args) throws Exception { String wordFilePath = ""; WordprocessingMLPackage wordMLPackage = load(wordFilePath); // 提取正文 MainDocumentPart main = wordMLPackage.getMainDocumentPart(); Document doc = main.getContents(); Body body = doc.getBody(); // 获取段落 List paragraphs = body.getContent(); // 提取书签并获取书签的游标 RangeFinder rt = new RangeFinder("CTBookmark", "CTMarkupRange"); new TraversalUtil(paragraphs, rt); // 遍历书签 for (CTBookmark bm : rt.getStarts()) { System.out.println("name:"+bm.getName()); } } 5.在书签位置加入图片 import org.docx4j.TraversalUtil; import org.docx4j.dml.wordprocessingDrawing.Inline; import org.docx4j.finders.RangeFinder; import org.docx4j.jaxb.Context; import org.docx4j.openpackaging.packages.WordprocessingMLPackage; import org.docx4j.openpackaging.parts.WordprocessingML.BinaryPartAbstractImage; import org.docx4j.openpackaging.parts.WordprocessingML.MainDocumentPart; import org.docx4j.org.apache.poi.util.IOUtils; import org.docx4j.wml.*; public static void addImage(WordprocessingMLPackage wPackage,CTBookmark bm) throws Exception{ P p = (P) (bm.getParent()); ObjectFactory factory = Context.getWmlObjectFactory(); R run = factory.createR(); // 读入图片并转化为字节数组,因为docx4j只能字节数组的方式插入图片 // byte[] bytes = IOUtils.toByteArray(new FileInputStream("图片路径")); byte[] bytes = getFileBytes("图片路径"); // 开始创建一个行内图片 BinaryPartAbstractImage imagePart = BinaryPartAbstractImage.createImagePart(wPackage, bytes); // 最后一个是限制图片的宽度,缩放的依据 Inline inline = imagePart.createImageInline(null, null, 0, 1, false, 0); // 构建绘图 Drawing drawing = factory.createDrawing(); // 加入图片段落 drawing.getAnchorOrInline().add(inline); run.getContent().add(drawing); // 清理书签所在数据 // p.getContent().clear(); // 加入图片信息 p.getContent().add(run); } public static byte[] getFileBytes(String filePath) throws Exception{ File file = new File(filePath); if(!file.exists()){ throw new Exception("文件不存在!"); } byte[] data = null; try(FileInputStream fis = new FileInputStream(file);ByteArrayOutputStream baos = new ByteArrayOutputStream()){ int len; byte[] buffer = new byte[1024]; while ((len = fis.read(buffer)) != -1) { baos.write(buffer, 0, len); } data = baos.toByteArray(); } return data; } 6.保存文件

必须调用保存,否则更改无效

// 保存有两种方式 // 方式一 wordMLPackage.save(new File(wordFilePath)); // 方式二 Docx4J.save(wordMLPackage, new File(wordFilePath)); 7.完整代码 import org.docx4j.TraversalUtil; import org.docx4j.dml.wordprocessingDrawing.Inline; import org.docx4j.finders.RangeFinder; import org.docx4j.jaxb.Context; import org.docx4j.openpackaging.packages.WordprocessingMLPackage; import org.docx4j.openpackaging.parts.WordprocessingML.BinaryPartAbstractImage; import org.docx4j.openpackaging.parts.WordprocessingML.MainDocumentPart; import org.docx4j.org.apache.poi.util.IOUtils; import org.docx4j.wml.*; import javax.xml.bind.JAXBElement; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.FileInputStream; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; public class WordTest { public static void main(String[] args) throws Exception { String wordFilePath = "word文档路径"; WordprocessingMLPackage wordMLPackage = load(wordFilePath); // 提取正文 MainDocumentPart main = wordMLPackage.getMainDocumentPart(); Document doc = main.getContents(); Body body = doc.getBody(); // 获取段落 List paragraphs = body.getContent(); // 提取书签并获取书签的游标 RangeFinder rt = new RangeFinder("CTBookmark", "CTMarkupRange"); new TraversalUtil(paragraphs, rt); // 遍历书签 for (CTBookmark bm : rt.getStarts()) { System.out.println("name:"+bm.getName()); // 替换image if ("sign".equals(bm.getName())){ addImage(wordMLPackage, bm); break; } } save(wordMLPackage,wordFilePath); } public static void addImage(WordprocessingMLPackage wPackage,CTBookmark bm) throws Exception{ P p = (P) (bm.getParent()); ObjectFactory factory = Context.getWmlObjectFactory(); R run = factory.createR(); // 读入图片并转化为字节数组,因为docx4j只能字节数组的方式插入图片 // byte[] bytes = IOUtils.toByteArray(new FileInputStream("图片文件路径")); byte[] bytes = getFileBytes("图片文件路径"); // 开始创建一个行内图片 BinaryPartAbstractImage imagePart = BinaryPartAbstractImage.createImagePart(wPackage, bytes); // 最后一个是限制图片的宽度,缩放的依据 Inline inline = imagePart.createImageInline(null, null, 0, 1, false, 0); // 构建绘图 Drawing drawing = factory.createDrawing(); // 加入图片段落 drawing.getAnchorOrInline().add(inline); run.getContent().add(drawing); // 清理书签所在数据 // p.getContent().clear(); // 加入图片信息 p.getContent().add(run); } /** * 构建文件 */ public static WordprocessingMLPackage build() throws Exception{ return WordprocessingMLPackage.createPackage(); } /** * 读取存在的word文件 * @param wordFilePath word文件路径 */ public static WordprocessingMLPackage load(String wordFilePath) throws Exception{ return WordprocessingMLPackage.load(new File(wordFilePath)); } /** * 保存 * @param wordMLPackage word */ public static void save(WordprocessingMLPackage wordMLPackage,String wordFilePath) throws Exception{ wordMLPackage.save(new File(wordFilePath)); } public static byte[] getFileBytes(String filePath) throws Exception{ File file = new File(filePath); if(!file.exists()){ throw new Exception("文件不存在!"); } byte[] data = null; try(FileInputStream fis = new FileInputStream(file);ByteArrayOutputStream baos = new ByteArrayOutputStream()){ int len; byte[] buffer = new byte[1024]; while ((len = fis.read(buffer)) != -1) { baos.write(buffer, 0, len); } data = baos.toByteArray(); } return data; } } 8.效果图

加入标签

使用docx4j在标签位置加入图片

方案二:使用itext生成PDF电子合同 1.引入itext com.itextpdf itextpdf 5.5.11 com.itextpdf itext-asian 5.2.0 2.设置表单数据&图片 import com.itextpdf.text.Image; import com.itextpdf.text.Rectangle; import com.itextpdf.text.pdf.*; import java.io.ByteArrayOutputStream; import java.io.FileOutputStream; import java.util.HashMap; public class PDFTest { public static void main(String[] args) throws Exception { // 模版文件 String templateFilePath = "模版文件路径"; // 保存PDF文件 String pdfFilePath = "保存PDF文件路径"; // 表单数据 HashMap data = new HashMap(); data.put("amount","100000"); data.put("month","24"); data.put("begin_date","2020年4月7日"); data.put("end_date","2022年4月6日"); // 图片数据 HashMap imageData = new HashMap(); imageData.put("sign","签名图片"); // 根据PDF模版生成PDF文件 createPDF(templateFilePath,data,imageData,true,pdfFilePath); } /** * 根据PDF模版生成PDF文件 * @param templateFilePath PDF模版文件路径 * @param data 表单数据 * @param imageData 图片数据 VALUE为图片文件路径 * @param formFlattening false:生成后的PDF文件表单域仍然可编辑 true:生成后的PDF文件表单域不可编辑 * @param pdfFilePath 生成PDF的文件路径 */ private static void createPDF(String templateFilePath, HashMap data, HashMap imageData, boolean formFlattening, String pdfFilePath) throws Exception{ PdfReader reader = null; ByteArrayOutputStream bos = null; PdfStamper pdfStamper = null; FileOutputStream fos = null; try{ // 读取PDF模版文件 reader = new PdfReader(templateFilePath); // 输出流 bos = new ByteArrayOutputStream(); // 构建PDF对象 pdfStamper = new PdfStamper(reader, bos); // 获取表单数据 AcroFields form = pdfStamper.getAcroFields(); // 使用中文字体 使用 AcroFields填充值的不需要在程序中设置字体,在模板文件中设置字体为中文字体 Adobe 宋体 std L BaseFont bfChinese = BaseFont.createFont("STSongStd-Light", "UniGB-UCS2-H", BaseFont.NOT_EMBEDDED); form.addSubstitutionFont(bfChinese); // 表单赋值 for(String key : data.keySet()){ form.setField(key,data.get(key)); // 也可以指定字体 form.setFieldProperty(key, "textfont", bfChinese, null); } // 添加图片 if(null != imageData && imageData.size() > 0){ for(String key : imageData.keySet()){ int pageNo = form.getFieldPositions(key).get(0).page; Rectangle signRect = form.getFieldPositions(key).get(0).position; float x = signRect.getLeft(); float y = signRect.getBottom(); // 读图片 Image image = Image.getInstance(imageData.get(key)); // 获取操作的页面 PdfContentByte under = pdfStamper.getOverContent(pageNo); // 根据域的大小缩放图片 image.scaleToFit(signRect.getWidth(), signRect.getHeight()); // 添加图片 image.setAbsolutePosition(x, y); under.addImage(image); } } // 如果为false那么生成的PDF文件还能编辑,一定要设为true pdfStamper.setFormFlattening(formFlattening); pdfStamper.close(); // 保存文件 fos = new FileOutputStream(pdfFilePath); fos.write(bos.toByteArray()); fos.flush(); }finally { if(null != fos){ try {fos.close(); }catch (Exception e){e.printStackTrace();} } if(null != bos){ try {bos.close(); }catch (Exception e){e.printStackTrace();} } if(null != reader){ try {reader.close(); }catch (Exception e){e.printStackTrace();} } } } } 3.签名效果

PDF模版文件

生成PDF文件

总结

itext比docx使用方式更友好,通过PDF模版可以设置表单字体以及样式;

docx好处在于不需要依赖第三方PDF编辑器,只需要使用word自带的书签功能,辅助程序定位

两者原理均一样,需要约定KEY名称,便于程序定位填充数据

异常定位

itext报:javax.xml.parsers.DocumentBuilderFactory.setFeature(Ljava/lang/String;Z)V

这个错误由引入了jaxen、xom、xstream引起的xml工厂多个实现导致,删除引入即可

 



【本文地址】

公司简介

联系我们

今日新闻

    推荐新闻

    专题文章
      CopyRight 2018-2019 实验室设备网 版权所有