1. 1 不可撤销
  2. 2 小年兽 程嘉敏
  3. 3 手放开 李圣杰
  4. 4 迷人的危险3(翻自 dance flow) FAFA
  5. 5 山楂树之恋 程佳佳
  6. 6 summertime cinnamons / evening cinema
  7. 7 不谓侠(Cover 萧忆情Alex) CRITTY
  8. 8 神武醉相思(翻自 优我女团) 双笙
  9. 9 空山新雨后 音阙诗听 / 锦零
  10. 10 Wonderful U (Demo Version) AGA
  11. 11 广寒宫 丸子呦
  12. 12 陪我看日出 回音哥
  13. 13 春夏秋冬的你 王宇良
  14. 14 世界が终わるまでは… WANDS
  15. 15 多想在平庸的生活拥抱你 隔壁老樊
  16. 16 千禧 徐秉龙
  17. 17 我的一个道姑朋友 双笙
  18. 18 大鱼  (Cover 周深) 双笙
  19. 19 霜雪千年(Cover 洛天依 / 乐正绫) 双笙 / 封茗囧菌
  20. 20 云烟成雨(翻自 房东的猫) 周玥
  21. 21 情深深雨濛濛 杨胖雨
  22. 22 Five Hundred Miles Justin Timberlake / Carey Mulligan / Stark Sands
  23. 23 斑马斑马 房东的猫
  24. 24 See You Again Wiz Khalifa / Charlie Puth
  25. 25 Faded Alan Walker / Iselin Solheim
  26. 26 Natural J.Fla
  27. 27 New Soul Vox Angeli
  28. 28 ハレハレヤ(朗朗晴天)(翻自 v flower) 猫瑾
  29. 29 像鱼 王贰浪
  30. 30 Bye Bye Bye Lovestoned
  31. 31 Blame You 眠 / Lopu$
  32. 32 Believer J.Fla
  33. 33 书信 戴羽彤
  34. 34 柴 鱼 の c a l l i n g【已售】 幸子小姐拜托了
  35. 35 夜空中最亮的星(翻自 逃跑计划) 戴羽彤
  36. 36 慢慢喜欢你 LIve版(翻自 莫文蔚) 戴羽彤
  37. 37 病变(翻自 cubi) 戴羽彤
  38. 38 那女孩对我说 (完整版) Uu
  39. 39 绿色 陈雪凝
  40. 40 月牙湾 LIve版(翻自 F.I.R.) 戴羽彤
夜空中最亮的星(翻自 逃跑计划) - 戴羽彤
00:00 / 04:10

夜空中最亮的星 能否听清

那仰望的人 心底的孤独和叹息

夜空中最亮的星 能否记起

那曾与我同行 消失在风里的身影

我祈祷拥有一颗透明的心灵

和会流泪的眼睛

给我再去相信的勇气

越过谎言去拥抱你

每当我找不到存在的意义

每当我迷失在黑夜里

噢喔喔 夜空中最亮的星

请指引我靠近你

夜空中最亮的星 是否知道

那曾与我同行的身影 如今在哪里

夜空中最亮的星 是否在意

是等太阳先升起 还是意外先来临

我宁愿所有痛苦都留在心底

也不愿忘记你的眼睛

哦 给我再去相信的勇气

哦 越过谎言去拥抱你

每当我找不到存在的意义

每当我迷失在黑夜里

噢喔喔 夜空中最亮的星

请照亮我向前行 哒~

我祈祷拥有一颗透明的心灵

和会流泪的眼睛 哦

给我再去相信的勇气

哦 越过谎言去拥抱你

每当我找不到存在的意义

每当我迷失在黑夜里

噢喔喔 夜空中最亮的星

请照亮我向前行

在线签名前后端业务实现(pc端及移动端)

在线签名前后端业务实现

前言

最近做了一个需求,是一个用户在线完成个人承诺书生成并手写签字的功能,最开始的时候是需要用户自行下载附件,并打印签名后上传,但是这种方式的不好之处在于,你压根就不知道用户上传的到底是不是你要的附件。后来服务升级,客户让参照国家app的形式,在线生成承诺书,并签名。最近新需求刚上线,我来整理下实现过程。

效果图

pdf模板

pc端

表单数据填写

未签名承诺书预览

签名

签名后预览

微信端

填写表单数据

生成承诺书

签名确认

签名后预览

需求实现过程

最开始拿到这个需求的时候,我就想到了通过js实现签名,所以在查了一些资料以后,前端确认用jSignature,后端根据用户信息,通过pdf表单的方式生成承诺书,然后返回未签名的承诺书图片,最后用户签名确认后,后端将签名和承诺书进行合成,然后返回给前端,如果用户确认办理业务,则后端保存签名后的个人承诺书。

总结以下整个流程:

因为项目是前后端分离的,所以图片是通过base64传输的。签名部分我是通过图片合成的,当然你也可以做成电子签章的方式,这样也更合理。

这里再补充说明下jSignature,这个js库可以直接生成base64的签名,当然它支持的格式很多,具体的可以看文档:

下面直接放代码了:

java接口

根据pdf模板生成pdf

  /**
     * 根据pdf模板,生成pdf
     *
     * @param pdfReader        PdfReader
     * @param saveFullFileName 保存文件的完整文件名(包含文件路径)
     * @param parameters       参数
     * @throws DocumentException
     */
    public static void exportPdf(PdfReader pdfReader, File saveFullFileName, Map<String, String> parameters) throws Exception {
        ByteArrayOutputStream byteArrayOutputStream = null;
        FileOutputStream fileOutputStream = null;
        try {
            fileOutputStream = new FileOutputStream(saveFullFileName);
            byteArrayOutputStream = new ByteArrayOutputStream();
            PdfStamper pdfStamper = new PdfStamper(pdfReader, byteArrayOutputStream);
            //获取模板所有域参数
            AcroFields acroFields = pdfStamper.getAcroFields();

            //解决中文字体不显示的问题
            BaseFont baseFont = BaseFont.createFont("STSongStd-Light", "UniGB-UCS2-H", BaseFont.NOT_EMBEDDED);
            ArrayList<BaseFont> fontArrayList = new ArrayList<BaseFont>();
            fontArrayList.add(baseFont);
            acroFields.setSubstitutionFonts(fontArrayList);

            for (String key : parameters.keySet()) {
                acroFields.setField(key, parameters.get(key));
            }

            pdfStamper.setFormFlattening(true);//如果为false那么生成的PDF文件还能编辑,一定要设为true
            pdfStamper.flush();
            pdfStamper.close();
            //设置纸张,可以在Excel制作是设定好纸张大小
            Document doc = new Document(PageSize.A4);
            PdfCopy copy = new PdfCopy(doc, fileOutputStream);
            doc.open();
            PdfImportedPage importPage = copy.getImportedPage(new PdfReader(byteArrayOutputStream.toByteArray()), 1);
            copy.addPage(importPage);
            doc.close();
        } catch (Exception e) {
            throw new Exception("生成pdf文件失败", e);
        } finally {
            if (byteArrayOutputStream != null) {
                try {
                    byteArrayOutputStream.close();
                } catch (IOException e) {
                    throw new Exception("生成pdf文件失败,关闭资源失败");
                }
            }
            if (fileOutputStream != null) {
                try {
                    fileOutputStream.close();
                } catch (IOException e) {
                    throw new Exception("生成pdf文件失败,关闭资源失败");
                }
            }
            if (pdfReader != null) {
                pdfReader.close();
            }
        }
    }

pdf转图片

 /**
     * pdf转图片
     *
     * @param pdfFullName pdf完整文件名
     * @param imgSavePath 图片保存路径
     * @throws Exception
     */
    public static void pdf2Pic(String pdfFullName, String imgSavePath) throws Exception {
        org.icepdf.core.pobjects.Document document = new org.icepdf.core.pobjects.Document();
        document.setFile(pdfFullName);
        //缩放比例
        float scale = 2.5f;
        //旋转角度
        float rotation = 0f;
        // 文件名
        String pdfFileName = new File(pdfFullName).getName();
        try {
            for (int i = 0; i < document.getNumberOfPages(); i++) {
                BufferedImage image = (BufferedImage)
                        document.getPageImage(i, GraphicsRenderingHints.SCREEN, org.icepdf.core.pobjects.Page.BOUNDARY_CROPBOX, rotation, scale);

                String imgName = pdfFileName + i + ".png";
                System.out.println(imgName);
                File file = new File(imgSavePath + imgName);
                ImageIO.write(image, "png", file);

                image.flush();
            }
        } catch (Exception e) {
            throw new Exception("pdf文件转图片失败", e);
        }
        document.dispose();
    }

合成签名

 /**
     *
     * @Title: 构造图片
     * @Description: 生成水印并返回java.awt.image.BufferedImage
     * @param buffImg 源文件(BufferedImage)
     * @param waterImg 水印文件(BufferedImage)
     * @param x 距离右下角的X偏移量
     * @param y  距离右下角的Y偏移量
     * @param alpha  透明度, 选择值从0.0~1.0: 完全透明~完全不透明
     * @param scale  缩放比例,整数,如50表示缩放为0.5
     * @return BufferedImage
     * @throws IOException
     */
    public static BufferedImage overlyingImage(BufferedImage buffImg, BufferedImage waterImg,
                                               int x, int y, float alpha, int scale) {

        // 创建Graphics2D对象,用在底图对象上绘图
        Graphics2D g2d = buffImg.createGraphics();
        int waterImgWidth = waterImg.getWidth()*scale/100;// 获取层图的宽度
        int waterImgHeight = waterImg.getHeight()*scale/100;// 获取层图的高度
        logger.debug("图片宽度" + waterImgWidth);
        logger.debug("图片高度" + waterImgHeight);
        // 在图形和图像中实现混合和透明效果
        g2d.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_ATOP, alpha));
        // 绘制
        g2d.drawImage(waterImg, x, y, waterImgWidth, waterImgHeight, null);
        g2d.dispose();// 释放图形上下文使用的系统资源
        return buffImg;
    }

js核心代码

  function signInit() {
        $("#signature").html('');
        // This is the part where jSignature is initialized.
        var $sigdiv = $("#signature").jSignature({'height': '300px', 'width': '100%'})

        $('#sign-make-sure').click(function () {
            notice();
            var data = $sigdiv.jSignature('getData', 'image');

            var datas = sessionStorage.getItem("apply-record-datas");
            var signSourceImg = sessionStorage.getItem("sign-source-img");
            var parseJSON = JSON.parse(datas);
            parseJSON['FILENAME'] = signSourceImg;
            parseJSON['sign'] = data[1];

            var requestParameter = {"datas": JSON.stringify(parseJSON)};
            var url = "/signTest/signPersonalCommitment";
            aj(url, requestParameter, function (result) {
                if (result.success) {
                    swal.close();
                    var modal_body_html = "<img id='sign-source-img' style='width: 100%' src='data:image/png;base64, " + result.obj.datas + "' >"
                    $("#ydjyrecodSignModal .modal-body").html(modal_body_html);
                    var modal_footer_html = "<button class=\"btn btn-primary\" type=\"button\" onclick=\"toSign()\">\n" +
                        " <i class=\"fa fa-fw fa-lg fa-check-circle\"></i>重新签名\n</button>\n" +
                        " <button id=\"save-commit\" class=\"btn btn-danger\" type=\"button\" onclick='saveCommit()'>\n" +
                        " <i class=\"fa fa-fw fa-lg fa-trash\"></i>保存并提交\n</button>\n";
                    $("#ydjyrecodSignModal .modal-footer").html(modal_footer_html);
                    $("#ydjyrecodSignModal").modal({
                        backdrop: "static",//点击空白处不关闭对话框
                        show: true
                    });
                } else {
                    swal("生成个人承诺书失败", result.msg, "error");
                }
            }, function (err) {
                console.log(err);
            });

        });

        $('#rest').click(function () {
            $sigdiv.jSignature('reset')
        });
    }

签名区html

<div id = "sign-tips" style="margin-bottom: 5px; text-align:center">
	<p>请用鼠标在虚线框内绘制签名</p>
</div>
<div id=\"content\">
    <div id=\"signatureparent\">
        <div id=\"signature\"></div>
    </div>
</div>

完整的示例,请看文末链接。

结语

今天的分享,只是提供一种解决方案的思路,如果能帮到你或者能够给你一些启发,那一切都值得。这里是本文的项目地址,感兴趣的小伙伴可以去了解下:

web-sign-demo

posted @ 2021-01-24 13:51  云中志  阅读(986)  评论(0编辑  收藏  举报