Java 制作带签名的电子合同

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

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

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

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

1.引入docx4j

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

<dependency><groupId>org.docx4j</groupId><artifactId>docx4j</artifactId><version>6.1.2</version>
</dependency>

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<Object> 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<Object> paragraphs  = body.getContent();// 提取书签并获取书签的游标RangeFinder rt = new RangeFinder("CTBookmark", "CTMarkupRange");new TraversalUtil(paragraphs, rt);// 遍历书签for (CTBookmark bm : rt.getStarts()) {System.out.println("name:"+bm.getName());// 替换imageif ("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

<!-- https://mvnrepository.com/artifact/com.itextpdf/itextpdf -->
<dependency><groupId>com.itextpdf</groupId><artifactId>itextpdf</artifactId><version>5.5.11</version>
</dependency>
<!-- https://mvnrepository.com/artifact/com.itextpdf/itext-asian -->
<dependency><groupId>com.itextpdf</groupId><artifactId>itext-asian</artifactId><version>5.2.0</version>
</dependency>

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<String,String> 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<String,String> 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<String,String> data, HashMap<String,String> 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 LBaseFont 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文件还能编辑,一定要设为truepdfStamper.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工厂多个实现导致,删除引入即可

java 制作签名版电子合同相关推荐

  1. java 制作签名版电子合同pdf版本

    一.需求描述 实现合同模板自动生成合同,再添加电子签名.如图: 二.实现步骤 使用java开源itext库对pdf进行操作,主要分为: 1 html模板+model数据,通过freemarker进行渲 ...

  2. easyexcel 填充模板 格式变了_Qamp;A | 如何制作规范的电子合同模板?

    表单中使用电子合同时,不仅需要进行功能设置,还需要制作一份PDF格式的模板,上传到"合同模板"中,才能生成有效的电子合同,接下来,我们一起了解模板的制作方法. 制作方法 1. 准备 ...

  3. 制作网页版电子时钟特效

    可以加我QQ1975728171代做 一.语言和环境 A.实现语言 JavaScript B.环境要求 Dreamweaver.WebStorm.Sublime任选其一 二.功能要求 利用JavaSc ...

  4. textarea实现datalist效果_同步纷享销客CRM合同数据至E签宝,实现全程无感完成电子合同签署!...

    最新消息:纷享销客与E签宝官方已经合作,目前正在进行技术推进,纷享会以系统集成的方式使用E签宝.1纷享销客简介 纷享销客是连接型CRM优质服务商.纷享销客连接型CRM以开放的企业级通讯为基础架构,以连 ...

  5. 闪耀“互联网之光”,法大大重磅发布HR行业电子合同新产品

    2019年10月20日,由国家互联网信息办公室.浙江省人民政府主办的第六届世界互联网大会"世界互联网领先科技成果发布活动"在乌镇互联网国际会展中心成功举行. 大会正式开始之前,其重 ...

  6. 【全面解析】电子合同有效性背后的法律逻辑解读

    作为电子商务的主要表现形式,电子合同与传统合同存在诸多不同,由此产生很多新的法律问题,证明电子合同的法律效力问题随之产生. 结合传统纸质合同向电子合同"进化"的过程,契约锁在本文中 ...

  7. 科技云报道:在线教育下半场,电子合同如何为教育赋能?

    科技云报道原创. 2019年,在线教育依靠"上半场"的跑用户.拼流量.拉投资等模式已不再奏效.行业洗牌之下,在线教育步入更为艰巨的"下半场",企业亟需转变增长模 ...

  8. PDF文档签名证书帮助您签署可信的电子合同

    相信很多单位或者个人都收到过类似这样的文档:对方为了显得正式.严谨,直接在 Word 或者 PDF 这种电子文档上粘贴上一个抠出来的印章图案,最不能让人忍的是有的同学竟然用 Word 自带的画图工具在 ...

  9. 使用itextpdf编辑PDF制作电子合同

    PDF电子合同 使用pdfescape生成一份PDF表单模板 1.打开网站 https://www.pdfescape.com/ 导入一份PDF模板文件 2.为PDF文件插入表单元素,并设置元素名称和 ...

  10. 大型医院云HIS系统:采用前后端分离架构,前端由Angular语言、JavaScript开发;后端使用Java语言开发 融合B/S版电子病历系统

    一套医院云his系统源码 采用前后端分离架构,前端由Angular语言.JavaScript开发:后端使用Java语言开发.融合B/S版电子病历系统,支持电子病历四级,HIS与电子病历系统均拥有自主知 ...

最新文章

  1. 什么是迁移学习?迁移学习的超参数有哪些?
  2. boost::mp11::mp_erase相关用法的测试程序
  3. 二进制安全与MySQL的关系_《高可用MySQL》节选 -- 安全和二进制日志
  4. HALCON示例程序distance_transform.hdev通过distance_transform检测线的缺陷
  5. imread函数_MATLAB图像处理:27:使用imtranslate函数平移图像
  6. java matcher replace_java.util.regex.Matcher.replaceFirst(String replacement)方法
  7. c++ - 在终端中,cout不显示任何内容
  8. 批量生成6位数字_太准了,你是Excel高手or小白看这6个操作就知道了
  9. codeforce 780C Andryusha and Colored Balloons
  10. java通过各种类型驱动连接数据库
  11. C#中的委托和Java中的“委托”(Java8 lambda表达式)
  12. 重装系统(用U盘+wepe)
  13. 5种好看实用的字体分享
  14. 如何快速下载720云高清全景图片
  15. MySQL 权限操作
  16. PyTorch(二次元头像--GAN实战)
  17. [附源码]Node.js计算机毕业设计二手书交易软件设计与实现Express
  18. c#如何wmf图片转换成png图片_【C#】使用fo-dicom完成BMP,JPG,PNG图片转换为DICOM文件-阿里云开发者社区...
  19. HTML Responsive Web Page
  20. 什么是构造方法,为什么要使用构造方法

热门文章

  1. 全民战“疫”,支付宝智能语音机器人到岗就位
  2. Linux查看硬盘挂载
  3. 傻瓜进销存怎么样?收费吗?
  4. mac book pro 音频设备启动失败
  5. Macromedia Flash 8 Video Encoder安装
  6. linux伊甸园论坛,tcsh问题 - Mandrake/Mandriva - Linux伊甸园论坛
  7. Linux 网站推荐
  8. Win32反汇编(四)栈的工作原理与堆栈平衡,函数方法参数的调用约定
  9. Zemax操作20--柱透镜
  10. 红帽子linux9百度云,linux安装--红帽子Linux REDHAT 9.0 ISO(3CD)