首页 > 编程语言 >电子签章Java后端与前端交互签名位置计算

电子签章Java后端与前端交互签名位置计算

时间:2024-01-17 11:26:13浏览次数:42  
标签:sourcePositionProperty Java float private new 签章 import null 交互

电子签章过程中存在着在网页上对签署文件进行预览、指定签署位置、文件签署等操作,由于图片在浏览器上的兼容性和友好性优于PDF文件,所以一般在网页上进行电子签章时,会先将PDF文件转换成图片,展示给用户。用户在页面上确定好签署位置,并进行签署时,后端服务会通过对电子印章/手写签名位置、大小以及PDF文件的大小进行计算,在PDF文件的准确位置上完成文件签署。以下代码是Java后端与前端交互签名位置计算的源代码,希望对大家有帮助。

更多电子签章前后端交互体验,可访问开源网站获取电子签章/电子合同工具源码:

https://gitee.com/kaifangqian

https://github.com/kaifangqian

关联工具包:itext-pdf;

​1、计算签署配置业务类;

​
import com.itextpdf.text.Document;
import com.itextpdf.text.pdf.PdfReader;
import com.resrun.service.pojo.RealPositionProperty;
import com.resrun.service.pojo.SelectKeywords;
import com.resrun.service.pojo.SourcePositionProperty;
import org.springframework.stereotype.Service;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

/**
 * @Description: 计算签署位置业务
 * @Package: com.resrun.service.pdf
 * @ClassName: CalculatePositionService
 * @copyright 北京资源律动科技有限公司
 */
@Service
public class CalculatePositionService {

    /**
     * @Description #批量计算真实签署位置
     * @Param [sourcePositionProperties]
     * @return java.util.List<com.resrun.modules.sign.service.tool.pojo.RealPositionProperty>
     **/
    public List<RealPositionProperty> calculatePositions(List<SourcePositionProperty> sourcePositionProperties, byte[] pdfFileByte){
        List<RealPositionProperty> realPositionProperties = new ArrayList<>();


        PdfReader reader = null ;
        try {
            //将pdf文件读入PdfReader工具类
            reader = new PdfReader(pdfFileByte);
            for(SourcePositionProperty sourcePositionProperty : sourcePositionProperties){
                RealPositionProperty realPositionProperty = calculatePosition(sourcePositionProperty,pdfFileByte);
                Document document = new Document(reader.getPageSize(sourcePositionProperty.getPage()));
                //获取真实pdf文件指定页的真实文档宽高
                float realPdfHeight = document.getPageSize().getHeight();
                float realPdfWidth = document.getPageSize().getWidth();
                //获取页面上文档的宽高
                float sourcePageWidth = sourcePositionProperty.getPageWidth();
                float sourcePageHeight = sourcePositionProperty.getPageHeight();
                //计算真实文档的宽高和页面文档的宽高的比率
                float rateHeight = realPdfHeight / sourcePageHeight;
                float rateWidth = realPdfWidth / sourcePageWidth;
                //计算页面上的横纵坐标,由于页面上给出的是左上角的坐标,所以需要再转换计算一下
                //左下角
                float pageStartX = sourcePositionProperty.getOffsetX();
                float pageStartY = sourcePositionProperty.getOffsetY() + sourcePositionProperty.getHeight() ;
                //右上角
                float pageEndX = sourcePositionProperty.getOffsetX() + sourcePositionProperty.getWidth();
                float pageEndY = sourcePositionProperty.getOffsetY();
                //根据比率去计算真实文档上的坐标位置
                float startX = pageStartX * rateWidth ;
                float startY = pageStartY * rateHeight;
                float endX = pageEndX * rateWidth ;
                float endY = pageEndY * rateHeight ;
                //由于页面的纵坐标和pdf的纵坐标是相反的,所以真实的pdf的纵坐标在计算的时候需要再反转一下
                startY = realPdfHeight - startY ;
                endY = realPdfHeight - endY ;
                //封装返回数据
                realPositionProperty.setStartx(startX);
                realPositionProperty.setStarty(startY);
                realPositionProperty.setEndx(endX);
                realPositionProperty.setEndy(endY);
                realPositionProperty.setPageNum(sourcePositionProperty.getPage());
                document.close();
                realPositionProperties.add(realPositionProperty);
            }

            reader.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return realPositionProperties ;
    }



    /**
     * @Description #单独计算真实签署位置
     * @Param [sourcePositionProperty]
     * @return com.resrun.modules.sign.service.tool.pojo.RealPositionProperty
     **/
    public RealPositionProperty calculatePosition(SourcePositionProperty sourcePositionProperty, byte[] pdfFileByte){
        RealPositionProperty realPositionProperty = new RealPositionProperty();
        PdfReader reader = null ;
        Document document = null ;
        try {
            //将pdf文件读入PdfReader工具类
            reader = new PdfReader(pdfFileByte);
            document = new Document(reader.getPageSize(sourcePositionProperty.getPage()));
            //获取真实pdf文件指定页的真实文档宽高
            float realPdfHeight = document.getPageSize().getHeight();
            float realPdfWidth = document.getPageSize().getWidth();
            //获取页面上文档的宽高
            float sourcePageWidth = sourcePositionProperty.getPageWidth();
            float sourcePageHeight = sourcePositionProperty.getPageHeight();
            //计算真实文档的宽高和页面文档的宽高的比率
            float rateHeight = realPdfHeight / sourcePageHeight;
            float rateWidth = realPdfWidth / sourcePageWidth;
            //计算页面上的横纵坐标,由于页面上给出的是左上角的坐标,所以需要再转换计算一下
            //左下角
            float pageStartX = sourcePositionProperty.getOffsetX();
            float pageStartY = sourcePositionProperty.getOffsetY() + sourcePositionProperty.getHeight() ;
            //右上角
            float pageEndX = sourcePositionProperty.getOffsetX() + sourcePositionProperty.getWidth();
            float pageEndY = sourcePositionProperty.getOffsetY();
            //根据比率去计算真实文档上的坐标位置
            float startX = pageStartX * rateWidth ;
            float startY = pageStartY * rateHeight;
            float endX = pageEndX * rateWidth ;
            float endY = pageEndY * rateHeight ;
            //由于页面的纵坐标和pdf的纵坐标是相反的,所以真实的pdf的纵坐标在计算的时候需要再反转一下
            startY = realPdfHeight - startY ;
            endY = realPdfHeight - endY ;
            //封装返回数据
            realPositionProperty.setStartx(startX);
            realPositionProperty.setStarty(startY);
            realPositionProperty.setEndx(endX);
            realPositionProperty.setEndy(endY);
            realPositionProperty.setPageNum(sourcePositionProperty.getPage());

            document.close();
            reader.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return realPositionProperty ;
    }


    public RealPositionProperty calculatePosition(SourcePositionProperty sourcePositionProperty){
        RealPositionProperty realPositionProperty = new RealPositionProperty();
        //获取真实pdf文件指定页的真实文档宽高
        float realPdfHeight = sourcePositionProperty.getRealHeight();
        float realPdfWidth = sourcePositionProperty.getRealWidth();
        //获取页面上文档的宽高
        float sourcePageWidth = sourcePositionProperty.getPageWidth();
        float sourcePageHeight = sourcePositionProperty.getPageHeight();
        //计算真实文档的宽高和页面文档的宽高的比率
        float rateHeight = realPdfHeight / sourcePageHeight;
        float rateWidth = realPdfWidth / sourcePageWidth;
        //计算页面上的横纵坐标,由于页面上给出的是左上角的坐标,所以需要再转换计算一下
        //左下角
        float pageStartX = sourcePositionProperty.getOffsetX();
        float pageStartY = sourcePositionProperty.getOffsetY() + sourcePositionProperty.getHeight() ;
        //右上角
        float pageEndX = sourcePositionProperty.getOffsetX() + sourcePositionProperty.getWidth();
        float pageEndY = sourcePositionProperty.getOffsetY();
        //根据比率去计算真实文档上的坐标位置
        float startX = pageStartX * rateWidth ;
        float startY = pageStartY * rateHeight;
        float endX = pageEndX * rateWidth ;
        float endY = pageEndY * rateHeight ;
        //由于页面的纵坐标和pdf的纵坐标是相反的,所以真实的pdf的纵坐标在计算的时候需要再反转一下
        startY = realPdfHeight - startY ;
        endY = realPdfHeight - endY ;
        //封装返回数据
        realPositionProperty.setStartx(startX);
        realPositionProperty.setStarty(startY);
        realPositionProperty.setEndx(endX);
        realPositionProperty.setEndy(endY);
        realPositionProperty.setPageNum(sourcePositionProperty.getPage());
        return realPositionProperty ;
    }




    /**
     * 通过查询关键字来获得签名位置信息
     * @param pdfFile 签署源文件
     * @param keyWords 关键字
     * @param width 签章宽度
     * @param height 签章高度
     * @return 签署位置信息
     * @throws IOException
     */
    public RealPositionProperty getPositionByKeyWords(byte[] pdfFile, String keyWords, int width, int height) {
        RealPositionProperty positionProperty = new RealPositionProperty();
        //调用通过关键字查询位置的方法
        float[] result = new float[0];
        try {
            result = new SelectKeywords().selectKeyword(pdfFile,keyWords);
        } catch (Exception e) {
            e.printStackTrace();
        }
        if(result !=null){

            positionProperty.setStartx(result[0]);
            positionProperty.setStarty(result[1]+height/4);
            positionProperty.setPageNum((int)result[2]);
            positionProperty.setEndx(result[0]+width/2);
            positionProperty.setEndy(result[1]-height/4);

        }
        return positionProperty;
    }

    /**
     * 通过查询关键字来获得签名位置信息<br/>
     *
     * 同一个关键字出现在多处会一次性全部找出
     *
     * @param pdfFile 签署源文件
     * @param keyWords 关键字
     * @param width 签章宽度
     * @param height 签章高度
     * @return 签署位置信息
     * @throws IOException
     */
    public List<RealPositionProperty> getAllPositionByKeyWords(byte[] pdfFile,String keyWords,int width,int height) {
        List<RealPositionProperty> positions = new ArrayList<RealPositionProperty>();
        //调用通过关键字查询位置的方法
        List<float[]> results = null;
        try {
            results = new SelectKeywords().selectAllKeyword(pdfFile, keyWords);
        } catch (Exception e) {
            e.printStackTrace();
        }
        if(results !=null && results.size()>0){
            for (float[] result : results) {
                RealPositionProperty positionProperty = new RealPositionProperty();

                positionProperty.setStartx(result[0]);
                positionProperty.setStarty(result[1]+height/4);
                positionProperty.setPageNum((int)result[2]);
                positionProperty.setEndx(result[0]+width/2);
                positionProperty.setEndy(result[1]-height/4);


                positions.add(positionProperty);
            }
        }
        return positions;
    }


}

​

2、计算后的签名位置信息类;

​
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

import java.io.Serializable;

/**
 * @Description: 经过计算后的文件签署位置属性类
 * @Package: com.resrun.service.pojo
 * @ClassName: PositionProperty
 * @copyright 北京资源律动科技有限公司
 */
@AllArgsConstructor
@NoArgsConstructor
@Data
public class RealPositionProperty implements Serializable {


        private static final long serialVersionUID = 8586984409612483553L;

        /** 签章左下角x坐标 */
        private  float startx;

        /** 签章左下角y坐标*/
        private  float starty;

        /** 签章右上角x坐标*/
        private  float endx;

        /** 签章右上角x坐标*/
        private  float endy;

        private  int pageNum;

        // 填写值,填写专用
        private String value ;
        //对齐方式
        private String align ;
        //字体
        private String fontFamily ;
        //文字大小
        private Integer fontSize ;
}

​

3、关键字位置计算类;

​
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.text.PDFTextStripper;
import org.apache.pdfbox.text.TextPosition;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.text.DecimalFormat;
import java.util.ArrayList;
import java.util.List;

/**
 * @Description: 关键字计算位置
 * @Package: com.resrun.service.pojo
 * @ClassName: SelectKeywords
 * @copyright 北京资源律动科技有限公司
 */
public class SelectKeywords extends PDFTextStripper {

    private static ThreadLocal<KeyWorkPair> keyWorkPair = new ThreadLocal<KeyWorkPair>();

    private Log logger = LogFactory.getLog(SelectKeywords.class);

    public SelectKeywords() throws IOException {
        super.setSortByPosition(true);
    }

//    public static void main(String[] args) throws Exception {
//        //selectKeyword
//        File file = new File("e:/test/948ad938bab14f4e8a2d843f6dd81d57.pdf");
//        float [] resus = new SelectKeywords().selectKeyword(IOUtils.toByteArray(file), "948ad938bab14f4e8a2d843f6dd81d57");//66   571
//        System.out.println(resus[0]+"--"+resus[1]+"---"+resus[2]);
//    }
    /**
     * 查出PDF里所有的关键字
     * @param pdfFile
     * @param KEY_WORD
     * @return
     */
    public List<float[]> selectAllKeyword(byte [] pdfFile, String KEY_WORD) {
        keyWorkPair.set(new KeyWorkPair(KEY_WORD.split(",")));
        ByteArrayInputStream in = null;
        PDDocument document = null;
        try {
            in = new ByteArrayInputStream(pdfFile);
            document = PDDocument.load(in);//加载pdf文件
            this.getText(document);
            List<float[]> allResu = getAllResult();
            return allResu;
        } catch (Exception e) {
            e.printStackTrace();
        }finally{
            try {
                if(in!=null) in.close();
                if(document!=null) document.close();
            } catch (IOException e) {
            }
        }
        return null;
    }
    private List<float[]> getAllResult(){
        KeyWorkPair pair = keyWorkPair.get();
        if(pair!=null && pair.getResu()!=null){
            keyWorkPair.set(null);
            return pair.getAllResu();
        }else{
            keyWorkPair.set(null);
            return null;
        }
    }
    /**
     * 查出PDF里最后一个关键字
     * @param pdfFile
     * @param KEY_WORD
     * @return
     */
    public float [] selectKeyword(byte [] pdfFile,String KEY_WORD) {
        keyWorkPair.set(new KeyWorkPair(KEY_WORD.split(",")));
        ByteArrayInputStream in = null;
        PDDocument document = null;
        try {
            in = new ByteArrayInputStream(pdfFile);
            document = PDDocument.load(in);//加载pdf文件
            this.getText(document);
            float[] resu = getResult();
            return resu;
        } catch (Exception e) {
            e.printStackTrace();
        }finally{
            try {
                if(in!=null) in.close();
                if(document!=null) document.close();
            } catch (IOException e) {
            }
        }
        return null;
    }

    private float[] getResult(){
        KeyWorkPair pair = keyWorkPair.get();
        if(pair!=null && pair.getResu()!=null){
            keyWorkPair.set(null);
            return pair.getResu();
        }else{
            keyWorkPair.set(null);
            return null;
        }
    }

    @Override
    protected void writeString(String string, List<TextPosition> textPositions) throws IOException {
        for (TextPosition text : textPositions) {
            String tChar = text.toString();
            char c = tChar.charAt(0);
            String REGEX = "[,.\\[\\](:;!?)/]";
            lineMatch = matchCharLine(text);
            if ((!tChar.matches(REGEX)) && (!Character.isWhitespace(c))) {
                if ((!is1stChar) && (lineMatch == true)) {
                    appendChar(tChar);
                } else if (is1stChar == true) {
                    setWordCoord(text, tChar);
                }
            } else {
                endWord();
            }
        }
        endWord();
    }
    protected void appendChar(String tChar) {
        tWord.append(tChar);
        is1stChar = false;
    }

    /**
     *
     * %拼接字符串%。
     */
    protected void setWordCoord(TextPosition text, String tChar) {
        itext = text;
        tWord.append("(").append(pageNo).append(")[").append(roundVal(Float.valueOf(text.getXDirAdj()))).append(" : ")
                .append(roundVal(Float.valueOf(text.getYDirAdj()))).append("] ").append(tChar);
        is1stChar = false;
    }

    protected boolean matchCharLine(TextPosition text) {

        Double yVal = roundVal(Float.valueOf(text.getYDirAdj()));
        if (yVal.doubleValue() == lastYVal) {
            return true;
        }
        lastYVal = yVal.doubleValue();
        endWord();
        return false;
    }

    protected Double roundVal(Float yVal) {
        DecimalFormat rounded = new DecimalFormat("0.0'0'");
        Double yValDub = new Double(rounded.format(yVal));
        return yValDub;
    }

    protected void endWord() {
        // String newWord = tWord.toString().replaceAll("[^\\x00-\\x7F]",
        // "");//为了检索速度 使用正则去掉中文
        String newWord = tWord.toString();// 去掉正则 可以检索中文
        KeyWorkPair pair = keyWorkPair.get();

        try {
            String[] seekA = pair.getSeekA();
            float[] resu = new float[3];
            String sWord = newWord.substring(newWord.lastIndexOf(' ') + 1);
            if (!"".equals(sWord)) {
                if (sWord.contains(seekA[0])) {
                    resu[2] = getCurrentPageNo();// (595,842)
                    resu[0] = (float) (roundVal(Float.valueOf(itext.getXDirAdj())) + 0.0F);
                    resu[1] = 842.0F - (float) (roundVal(Float.valueOf(itext.getYDirAdj())) + 0.0F);
                    logger.info("PDF关键字信息:[页数:" + resu[2] + "][X:" + resu[0] + "][Y:" + resu[1] + "]");
                    pair.setResu(resu);
                    pair.addResuList(resu);//把每一次找出的关键字放在一个集合里
                    keyWorkPair.set(pair);
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
            keyWorkPair.set(null);
            throw new RuntimeException();
        }
        tWord.delete(0, tWord.length());
        is1stChar = true;
    }

    private StringBuilder tWord = new StringBuilder();
    private boolean is1stChar = true;
    private boolean lineMatch;
    private int pageNo = 0;
    private double lastYVal;

    private TextPosition itext;

    /**
     * 关键字和返回的位置信息类
     */
    class KeyWorkPair {

        public KeyWorkPair(String[] seekA) {
            super();
            this.seekA = seekA;
        }
        public KeyWorkPair(String[] seekA, float[] resu) {
            super();
            this.seekA = seekA;
            this.resu = resu;
        }
        public KeyWorkPair() {
            super();
        }
        public String[] getSeekA() {
            return seekA;
        }
        public void setSeekA(String[] seekA) {
            this.seekA = seekA;
        }
        public float[] getResu() {
            return resu;
        }
        public void setResu(float[] resu) {
            this.resu = resu;
        }

        public void addResuList(float[] resu) {
            resuAll.add(resu);
        }
        public List<float[]> getAllResu() {
            return resuAll;
        }

        private String[] seekA;
        private float[] resu;
        //所有的位置
        private List<float[]> resuAll = new ArrayList<>();
    }
}

​

4、原始文件签署位置信息类;

​
import io.swagger.annotations.ApiModelProperty;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

import java.io.Serializable;

/**
 * @Description: 原始文件签署位置属性
 * @Package: com.resrun.service.pojo
 * @ClassName: SourcePositionProperty
 * @copyright 北京资源律动科技有限公司
 */
@Data
@AllArgsConstructor
@NoArgsConstructor
public class SourcePositionProperty implements Serializable {

    private static final long serialVersionUID = 725976764583634367L;

    @ApiModelProperty("控件X坐标(左上角)")
    private Float offsetX ;
    @ApiModelProperty("控件Y坐标(左上角)")
    private Float offsetY ;
    @ApiModelProperty("控件宽度")
    private Float width ;
    @ApiModelProperty("控件高度")
    private Float height ;
    @ApiModelProperty("当前文件页面宽度")
    private Float pageWidth ;
    @ApiModelProperty("当前文件页面高度")
    private Float pageHeight ;
    @ApiModelProperty("控件所属页码")
    private Integer page ;

    @ApiModelProperty("当前文件页面宽度")
    private Float realWidth ;
    @ApiModelProperty("当前文件页面高度")
    private Float realHeight ;


}

​

标签:sourcePositionProperty,Java,float,private,new,签章,import,null,交互
From: https://www.cnblogs.com/kaifangqian/p/17969539

相关文章

  • PDF转图片-itextpdf-java源码
    提供PDF文件转图片的工具类。电子签章过程中存在着在网页上对签署文件进行预览、指定签署位置、文件签署等操作,由于图片在浏览器上的兼容性和友好性优于PDF文件,所以一般在网页上进行电子签章时,会先将PDF文件转换成图片,展示给用户。用户在页面上确定好签署位置,并进行签署时,后......
  • 基于javaweb的吃了吗管理系统
    随着信息技术在管理上越来越深入而广泛的应用,管理信息系统的实施在技术上已逐步成熟。本文介绍了吃了吗管理系统的开发全过程。通过分析吃了吗管理系统管理的不足,创建了一个计算机管理吃了吗管理系统的方案。文章介绍了吃了吗管理系统的系统分析部分,包括可行性分析等,系统设计部分主......
  • [Java]关于基本数据类型与引用类型赋值时的底层分析的小结(简述)
    【版权声明】未经博主同意,谢绝转载!(请尊重原创,博主保留追究权)https://www.cnblogs.com/cnb-yuchen/p/17969159出自【进步*于辰的博客】目录1、关于赋值1.1基本数据类型赋值1.2String类型赋值2、关于String赋值2.1情形一2.2情形二3、关于String与char[]的比较4、不同类型引......
  • 【深入挖掘Java技术】「源码原理体系」盲点问题解析之HashMap工作原理全揭秘(上)
    知识盲点概念介绍HashMap是基于Map接口构建的数据结构,它以键值对的形式存储元素,允许键和值都为null。由于键的唯一性,HashMap中只能有一个键为null。HashMap的特点是元素的无序性和不重复性。注意,HashMap并不是线程安全的。在多线程环境下,如果不进行适当的同步处理,可能会导致数据不......
  • 开发日记3(java面向对象)
    (一)java面向对象1、面向过程和面向对象很久很久以前,系统学习过C语言,但也只是学过,没有产生过除了考试以外的其他价值。后来工作中很多伙伴都在使用java,虽然当时自己不写java,但很久之前就有个概念,这两种语言还是不同的,一个面向过程、一个面向对象。面向过程比较容易理解,基实过程可以理......
  • Java面向对象
    Java面向对象面向对象编程(Object-OrientedProgramming,OOP)面向对象编程本质:以类的方式组织代码,以对象的组织(封装)数据三大特性:1、封装所谓的封装就是把类的属性和方法使用private修饰,不允许类的调用者直接访问,如果想要操作这些属性使用public的get、set方法。封装的作用:不......
  • Java就业学习 Day2 每日一问:我真能找到工作吗/(ㄒoㄒ)/~~
    Java开发能力:今天只看了面向对象这一节的东西,确实有了好多新的理解。①和equals的区别:说实话我现在才弄懂。。比较的其实是地址,所以如果一个变量可以用==,但对象就不行。②重载与重写的区别:重载是构造器重载,构造器的参数不同。而重写是重写方法,形象来说就是父类的方法我不满意,我......
  • Java数组
    Java数组数组一、数组的声明和创建1、声明dataType[]arrayRefVar;//声明数组2、创建dataType[]arrayRefVar=newdataType[dataSize];//创建一个数组二、初始化及内存分析1、初始化//静态初始化:创建加赋值int[]a={1,2,3};Peo......
  • 不可不看的Java基础知识整理,注释、关键字、运算符
    写在开头万丈高楼平地起,要想学好汉语首先学拼音,想学好英语首先学26个字母,对于编程语言来说,一样的道理,要想学好必须先掌握其基础语法和知识,今天我们就来唠一唠Java语言中那些出现频率极高,又很基础的知识点吧!Java中的注释注释的作用:拨云见日!在日常的工作中,总会遇到很多大段的代......
  • Java动态代理、AOP和装饰器模式
    面向切面编程AOP-AspectOrientedPrograming,主要用于处理核心业务逻辑外的一些东西,比如日志和缓存。这个“切面”可以理解为在代码的某个地方切一刀,在其中加一些东西。装饰器以日志为例,如果没有使用AOP,那么可以使用装饰来实现类似的代码。我们使用装饰器模式来实现一下在执行......