java为PDF盖(签)电子签章--关键词定位

java PDF盖章-位置定位,请看这篇文章

本文为关键词盖章,需求就是根据关键词在pdf进行签章,如:pdf尾页盖上xxx机构的电子章。

直接上代码:所需要的依赖和位置定位的差不多,请看上文。

Itext5PdfSign.java

package test02itextpdf.keyword;

import com.itextpdf.text.Image;
import com.itextpdf.text.Rectangle;
import com.itextpdf.text.pdf.PdfReader;
import com.itextpdf.text.pdf.PdfSignatureAppearance;
import com.itextpdf.text.pdf.PdfSignatureAppearance.RenderingMode;
import com.itextpdf.text.pdf.PdfStamper;
import com.itextpdf.text.pdf.PdfStream;
import com.itextpdf.text.pdf.parser.PdfReaderContentParser;
import com.itextpdf.text.pdf.parser.TextMarginFinder;
import com.itextpdf.text.pdf.parser.TextRenderInfo;
import com.itextpdf.text.pdf.security.*;
import com.itextpdf.text.pdf.security.MakeSignature.CryptoStandard;
import lombok.Data;
import lombok.Getter;
import lombok.Setter;
import org.apache.commons.lang.StringUtils;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.security.KeyStore;
import java.security.PrivateKey;
import java.security.Security;
import java.security.cert.Certificate;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.stream.IntStream;

/**
 * pdf 签章实现类
 */
public class Itext5PdfSign {
    private static final Logger LOG = LoggerFactory.getLogger(Itext5PdfSign.class);

    private SignConfig signConfig;

    public void sign(InputStream waitSignPdf, OutputStream signedPdf) {
        try {
            PdfReader reader = new PdfReader(waitSignPdf);
            /**
             * false的话,pdf文件只允许被签名一次,多次签名,最后一次有效
             * true的话,pdf可以被追加签名,验签工具可以识别出每次签名之后文档是否被修改
             */
            PdfStamper stamper = PdfStamper.createSignature(reader, signedPdf, '\0', null, true);
            /**
             * 设定签章为高清章,默认模糊
             */
            stamper.getWriter().setCompressionLevel(PdfStream.BEST_COMPRESSION);
            /**
             * 设定签章属性
             */
            PdfSignatureAppearance appearance = stamper.getSignatureAppearance();
            appearance.setReason(signConfig.getReason());
            appearance.setLocation(signConfig.getLocation());
            appearance.setSignatureCreator(signConfig.getSignername());
            PdfLocationResult locationResult = calcSignLocation(reader, KeyWordFinder.KeyWordMatchType.MATCH_LAST);
            appearance.setVisibleSignature(locationResult.getRectangle(), locationResult.getPageNum(), signConfig.getSignFiledName());
            /**
             * 设定签章图片,签章类别
             */
            appearance.setSignatureGraphic(signConfig.getChapterImg());
            //NOT_CERTIFIED 不会导致pdf上其他签章无效
            appearance.setCertificationLevel(PdfSignatureAppearance.NOT_CERTIFIED);
            /**
             * 设置图章的显示方式,如下选择的是只显示图章(还有其他的模式,可以图章和签名描述一同显示)
             */
            appearance.setRenderingMode(RenderingMode.GRAPHIC);
            /**
             * 指定摘要算法
             */
            ExternalDigest digest = signConfig.getDigest();
            /**
             * 指定签名对象
             */
            ExternalSignature signature = signConfig.getSignature();
            /**
             * 构造时间时间戳服务器
             */
            TSAClient tsaClient = getTsaClient();
            /**
             * itext 签章
             */
            MakeSignature.signDetached(appearance, digest, signature, signConfig.getChain(), null, null, tsaClient, 0, CryptoStandard.CMS);
        } catch (Exception e) {
            throw new RuntimeException("签章异常", e);
        }
    }


    public Itext5PdfSign(SignConfig signConfig) {
        checkInit(signConfig);
        try {
            BouncyCastleProvider bc = new BouncyCastleProvider();
            Security.addProvider(bc);
            if (StringUtils.isNotEmpty(signConfig.getSignP12Path())) {
                String p12Path = signConfig.getSignP12Path();
                String password = signConfig.getSignP12Password();
                KeyStore ks = RSAUtil.getKeyStore(p12Path, password);
                String alias = ks.aliases().nextElement();
                PrivateKey privateKey = (PrivateKey) ks.getKey(alias, password.toCharArray());
                signConfig.setChain(ks.getCertificateChain(alias));
                signConfig.setPrivateKey(privateKey);
            }
            signConfig.setDigest(new BouncyCastleDigest());
            signConfig.setSignature(new PrivateKeySignature(signConfig.getPrivateKey(), DigestAlgorithms.SHA256, BouncyCastleProvider.PROVIDER_NAME));
            if (Objects.isNull(signConfig.getChapterImg())) {
                signConfig.setChapterImg(Image.getInstance(signConfig.getChapterImgPath()));
            }
            Image chapterImg = signConfig.getChapterImg();
            signConfig.setStampWidth(chapterImg.getWidth());
            signConfig.setStampHeight(chapterImg.getHeight());
            this.signConfig = signConfig;
        } catch (Exception e) {
            throw new RuntimeException("init error", e);
        }
    }

    private void checkInit(SignConfig signConfig) {
        if (StringUtils.isEmpty(signConfig.getSignP12Path())) {
            throw new RuntimeException("缺少签章用的公私钥对信息");
        }
        if (StringUtils.isNotEmpty(signConfig.getSignP12Path()) && StringUtils.isEmpty(signConfig.getSignP12Password())) {
            throw new RuntimeException("签章证书密码配置缺少");
        }
        if (Objects.isNull(signConfig.getChapterImg()) && StringUtils.isEmpty(signConfig.getChapterImgPath())) {
            throw new RuntimeException("缺少签章图片地址信息");
        }
        if (StringUtils.isEmpty(signConfig.getSignername())) {
            throw new RuntimeException("缺少签章者名称信息");
        }
        if (StringUtils.isEmpty(signConfig.getReason())) {
            throw new RuntimeException("缺少签章原因信息");
        }
        if (StringUtils.isEmpty(signConfig.getLocation())) {
            throw new RuntimeException("缺少签章位置信息");
        }
        if (StringUtils.isEmpty(signConfig.getSignFiledName())) {
            throw new RuntimeException("缺少签章域名称信息");
        }
        if (Objects.nonNull(signConfig.getTsaClientFactory())) {
            signConfig.setNoTsa(false);
        }
        if (Objects.nonNull(signConfig.getTsaUrl())) {
            signConfig.setNoTsa(false);
            signConfig.tsaClientFactory = new TsaClientBCFactory(signConfig.getTsaUrl());
        }
    }

    private TSAClient getTsaClient() {
        if (signConfig.isNoTsa()) {
            return null;
        }
        return signConfig.tsaClientFactory.newTSAClient();

    }

    @Data
    public static class SignConfig {
        /**
         * 签章hash 及签名实现
         */
        private ExternalDigest digest;
        private ExternalSignature signature;
        /**
         * 证书链及私钥
         */
        private Certificate[] chain;
        private PrivateKey privateKey;
        /**
         * 签章原因
         */
        private String reason;
        /**
         * 签章位置
         */
        private String location;
        /**
         * 签章者名称
         */
        private String signername;
        /**
         * 签章图片地址
         */
        private Image chapterImg;
        private String chapterImgPath;
        /**
         * 签章域名称可根据域查找章的位置
         */
        private String signFiledName;

        /**
         * 签章公私钥文件
         */
        private String signP12Path;
        private String signP12Password;
        /**
         * 签章关键词及关键词所在的页码
         */
        private String signKeyWord;
        private Integer signKeyWordPageNum;
        /**
         * 签章图片的宽高
         */
        private float stampHeight;
        private float stampWidth;

        /**
         * 签章时间服务
         */
        private boolean noTsa = true;
        private TsaClientFactory tsaClientFactory;
        private String tsaUrl;

    }


    private PdfLocationResult calcSignLocation(PdfReader reader, KeyWordFinder.KeyWordMatchType matchType) {
        /**
         * 1. 如果关键字存在,则签章在关键字上
         * 2. 如果关键字不存在 则签章在尾页的右下角
         */
        KeyWordLocation keyWordLocation = keyWordLocation(reader, signConfig.getSignKeyWord(), signConfig.getSignKeyWordPageNum(), matchType);
        if (Objects.nonNull(keyWordLocation)) {
            /**
             * 计算规则:
             * 1. 由于获取的关键词文件块宽高过于夸张,所以先计算关键词文本块矩形右下角坐标( keyWordLocation.getUrx(),keyWordLocation.getUry()-文本块高度)
             * 2. 然后将 文本块右下角坐标作为签章矩形的中心点,然后计算签章域的左下角右上角坐标
             * 3.签章域超出pdf页宽高,并做溢出处理
             */
            float keyWordTextBlockHeight = keyWordLocation.getKeyWordTextBlockHeight();
            /**
             * 关键字右下角坐标
             */
            float keyWordLrx = keyWordLocation.getUrx();
            float keyWordLry = keyWordLocation.getUry() - keyWordTextBlockHeight;

            //签章域左下角坐标
            float llx = keyWordLrx - signConfig.getStampWidth() / 2;
            float lly = keyWordLry - signConfig.getStampHeight() / 2;
            /**
             * 横纵坐标向溢出处理
             */
            Rectangle pageSize = reader.getPageSize(keyWordLocation.getPageNum());
            if (llx + signConfig.getStampWidth() > pageSize.getWidth()) {
                llx = pageSize.getWidth() - signConfig.getStampWidth();
            }
            if (lly < 0) {
                llx = 0;
            }
            //签章域右上角坐标
            float urx = llx + signConfig.getStampWidth();
            float ury = lly + signConfig.getStampHeight();


            Rectangle rectangle = new Rectangle(llx, lly, urx, ury);

            LOG.debug("查找到关键词[{}]位置,坐标为{}", keyWordLocation.getText(), keyWordLocation);
            return new PdfLocationResult(keyWordLocation.getPageNum(), rectangle);
        } else {
            int numberOfPages = reader.getNumberOfPages();
            Rectangle lastPageSize = reader.getPageSize(numberOfPages);
            float pageWidth = lastPageSize.getWidth();
            //左下角坐标 lly 表示距离底部的距离
            float llx = pageWidth - signConfig.getStampWidth();
            float lly = 50;
            //右上角坐标
            float urx = llx + signConfig.getStampWidth();
            float ury = lly + signConfig.getStampHeight();
            Rectangle rectangle = new Rectangle(llx, lly, urx, ury);
            LOG.debug("未查找到关键词位置,签章位置默认在尾页", signConfig.getSignKeyWord());
            return new PdfLocationResult(numberOfPages, rectangle);
        }

    }

    /**
     * 根据关键词 计算签章位置
     *
     * @param pdfReader      解析reader
     * @param keyWord        关键词
     * @param keyWordPageNum 关键词所在页码
     * @return
     */
    private KeyWordLocation keyWordLocation(PdfReader pdfReader, String keyWord, Integer keyWordPageNum, KeyWordFinder.KeyWordMatchType matchType) {
        List<KeyWordLocation> keyWordLocationList = new ArrayList<>();
        try {
            int pageSize = pdfReader.getNumberOfPages();
            PdfReaderContentParser pdfReaderContentParser = new PdfReaderContentParser(pdfReader);
            IntStream pageStream = null;
            if (Objects.nonNull(keyWordPageNum)) {
                pageStream = IntStream.of(keyWordPageNum);
            } else {
                pageStream = IntStream.range(1, pageSize + 1);//含头不含尾
            }
            pageStream.forEach(pageNum -> {
                try {
                    pdfReaderContentParser.processContent(pageNum, new KeyWordFinder(keyWord, pageNum, keyWordLocationList));
                } catch (IOException e) {
                    throw new RuntimeException(e);
                }
            });

        } catch (Exception e) {
            throw new RuntimeException(e);
        }
        KeyWordLocation keyWordLocation = null;
        if (keyWordLocationList.isEmpty()) {
            return null;
        }
        if (keyWordLocationList.size() == 1) {
            keyWordLocation = keyWordLocationList.get(0);
        } else {
            switch (matchType) {
                case MATCH_LAST:
                    keyWordLocation = LittlePdfUtil.getLast(keyWordLocationList);
                    break;
                case MATCH_FIRST:
                    keyWordLocation = keyWordLocationList.get(0);
                    break;
                default:
                    keyWordLocation = LittlePdfUtil.getLast(keyWordLocationList);
                    break;

            }
        }
        if (KeyWordFinder.KEYWORD_MATCH_TYPE_FULL.equals(keyWordLocation.getKeywordMatchType())) {
            return keyWordLocation;
        } else if (KeyWordFinder.KEYWORD_MATCH_TYPE_MIX.equals(keyWordLocation.getKeywordMatchType()) &&
                Objects.nonNull(keyWordLocation.getMixMatchCharList())) {

            /**
             * 如果关键词被分拆,则返回中间字符的坐标
             */
            List<KeyWordCharLocation> mixMatchCharList = keyWordLocation.getMixMatchCharList();
            KeyWordLocation midChar = KeyWordFinder.getMidChar(keyWord, mixMatchCharList);
            keyWordLocation.setLlx(midChar.getLlx());
            keyWordLocation.setLly(midChar.getLly());
            keyWordLocation.setUrx(midChar.getUrx());
            keyWordLocation.setUry(midChar.getUry());
            keyWordLocation.setKeyWordTextBlockHeight(midChar.getKeyWordTextBlockHeight());
            keyWordLocation.setKeyWordTextBlockWidth(midChar.getKeyWordTextBlockWidth());
            return keyWordLocation;
        }
        return keyWordLocation;
    }


    public static class KeyWordFinder extends TextMarginFinder {
        @Getter
        @Setter
        private String keyWord;
        @Getter
        @Setter
        private Integer pageNum;
        @Getter
        @Setter
        private List<KeyWordLocation> keyWordLocationList;
        /**
         * 匹配类型
         * <p>
         * KEYWORD_MATCH_TYPE_FULL 关键词完全匹配
         * KEYWORD_MATCH_TYPE_MIX 关键词混乱匹配
         */
        public static final Integer KEYWORD_MATCH_TYPE_FULL = 1,
                KEYWORD_MATCH_TYPE_MIX = 2;
        /**
         * 当前模式 是否为混乱模式
         */
        private boolean isMixMatch;
        /**
         * 混乱匹配模式,已匹配的字符数(左匹配)
         */
        private Integer keyWordMixMatchCharMatchCount = 0;

        public KeyWordFinder(String keyWord, Integer pageNum, List<KeyWordLocation> keyWordLocationList) {
            this.keyWord = keyWord;
            this.pageNum = pageNum;
            this.keyWordLocationList = keyWordLocationList;
        }

        @Override
        public void renderText(TextRenderInfo renderInfo) {
            super.renderText(renderInfo);
            String text = renderInfo.getText();
            LOG.debug("pdf文本:[{}] 文本宽度{},文本高度", text, this.getWidth(), this.getHeight());
            //查找到关键词,并设置关键词位置
            if (LittlePdfUtil.isNotEmpty(text)) {
                if (text.contains(keyWord)) {
                    keyWordLocationList.add(toKeyWordLocation(text, KEYWORD_MATCH_TYPE_FULL));
                    return;
                } else {
                    handleMatchMix(text);
                    return;
                }
            }


        }


        private void handleMatchMix(String text) {
            if (!isMixMatch) {
                int matchCount = leftMatch(keyWord, text, 0);
                if (matchCount > 0) {
                    isMixMatch = true;
                    keyWordMixMatchCharMatchCount = matchCount;
                    KeyWordLocation keyWordMixMatchLocation = toKeyWordLocation(text, KEYWORD_MATCH_TYPE_MIX);

                    KeyWordCharLocation keyWordCharLocation = toKeyWordCharLocation(text);
                    keyWordCharLocation.setMixMatchCount(matchCount);
                    keyWordCharLocation.setMixMatchStart(0);

                    List<KeyWordCharLocation> mixMatchCharList = new ArrayList<>();
                    mixMatchCharList.add(keyWordCharLocation);

                    keyWordMixMatchLocation.setMixMatchCharList(mixMatchCharList);
                    keyWordLocationList.add(keyWordMixMatchLocation);
                    return;
                }
            }
            if (isMixMatch) {
                int matchCount = leftMatch(keyWord, text, keyWordMixMatchCharMatchCount);
                if (keyWordMixMatchCharMatchCount.equals(keyWord.length())) {
                    //匹配结束
                    isMixMatch = false;
                    keyWordMixMatchCharMatchCount = 0;
                } else if (matchCount > 0) {
                    //继续匹配中
                    List<KeyWordCharLocation> mixMatchCharList = LittlePdfUtil.getLast(keyWordLocationList).getMixMatchCharList();
                    KeyWordCharLocation keyWordCharLocation = toKeyWordCharLocation(text);
                    keyWordCharLocation.setMixMatchCount(matchCount);
                    keyWordCharLocation.setMixMatchStart(keyWordMixMatchCharMatchCount);
                    mixMatchCharList.add(keyWordCharLocation);
                    keyWordMixMatchCharMatchCount += matchCount;
                } else {
                    isMixMatch = false;
                    keyWordMixMatchCharMatchCount = 0;
                    //删除不完全匹配
                    LittlePdfUtil.removeLastList(keyWordLocationList, 1);
                }

            }
        }

        private static int leftMatch(String keyWord, String text, int pos) {
            int matchCount = 0, keyWordLen = keyWord.length();
            for (int i = 0, len = text.length(); i < len; i++) {
                int matchStart = pos + matchCount;
                if (matchStart >= keyWordLen) {
                    break;
                }
                if (Objects.equals(keyWord.charAt(matchStart), text.charAt(i))) {
                    matchCount++;
                }
            }
            if (matchCount > 0) {
                return matchCount;
            }
            return -1;
        }

        public static enum KeyWordMatchType {
            MATCH_LAST,
            MATCH_FIRST;
        }

        private KeyWordLocation toKeyWordLocation(String text, Integer keywordMatchType) {
            KeyWordLocation keyWordLocation = new KeyWordLocation();
            keyWordLocation.setKeyWordTextBlockHeight(this.getHeight());
            keyWordLocation.setKeyWordTextBlockWidth(this.getWidth());
            keyWordLocation.setLlx(this.getLlx());
            keyWordLocation.setLly(this.getLly());
            keyWordLocation.setUrx(this.getUrx());
            keyWordLocation.setUry(this.getUry());
            keyWordLocation.setText(text);
            keyWordLocation.setPageNum(pageNum);
            keyWordLocation.setKeywordMatchType(keywordMatchType);
            return keyWordLocation;
        }

        private KeyWordCharLocation toKeyWordCharLocation(String text) {
            KeyWordCharLocation keyWordCharLocation = new KeyWordCharLocation();
            keyWordCharLocation.setKeyWordTextBlockHeight(this.getHeight());
            keyWordCharLocation.setKeyWordTextBlockWidth(this.getWidth());
            keyWordCharLocation.setLlx(this.getLlx());
            keyWordCharLocation.setLly(this.getLly());
            keyWordCharLocation.setUrx(this.getUrx());
            keyWordCharLocation.setUry(this.getUry());
            keyWordCharLocation.setText(text);
            keyWordCharLocation.setPageNum(pageNum);
            keyWordCharLocation.setKeywordMatchType(KEYWORD_MATCH_TYPE_MIX);
            return keyWordCharLocation;
        }

        public static KeyWordCharLocation getMidChar(String keyWord, List<KeyWordCharLocation> mixMatchCharList) {
            int midIndex = keyWord.length() / 2, count = 0;
            for (int i = 0, len = mixMatchCharList.size(); i < len; i++) {
                KeyWordCharLocation keyWordCharLocation = mixMatchCharList.get(i);
                count += keyWordCharLocation.getMixMatchCount();
                if (count >= midIndex) {
                    return keyWordCharLocation;
                }
            }
            return mixMatchCharList.get(0);
        }
    }

    /**
     * 签章关键词文本位置
     */
    @Data
    public static class KeyWordLocation {
        /**
         * 文本
         */
        private String text;
        /**
         * 关键词所在pdf页码
         */
        private Integer pageNum;
        /**
         * 文本块所在矩形左下角坐标
         */
        private float llx;
        private float lly;
        /**
         * 文本块所在矩形右上角角坐标
         */
        private float urx;
        private float ury;
        /**
         * 文本块宽高
         */
        private float keyWordTextBlockWidth;
        private float keyWordTextBlockHeight;


        /**
         * 关键词匹配类型 参见keyFinder
         */
        private Integer keywordMatchType;

        /**
         * 关键词被解析成多个TextRenderInfo
         */
        private List<KeyWordCharLocation> mixMatchCharList;

    }

    /**
     * 混乱匹配模式下的 打散的关键词字符位置
     */
    @Data
    public static class KeyWordCharLocation extends KeyWordLocation {
        /**
         * 混乱匹配时的起始位置
         */
        private Integer mixMatchStart;

        /**
         * 匹配的字符数
         */
        private Integer mixMatchCount;


    }


    @Data
    public static class PdfLocationResult {
        /**
         * 签章所在pdf页码
         */
        private int pageNum;
        /**
         * 签章矩形
         */
        private Rectangle rectangle;

        public PdfLocationResult(int pageNum, Rectangle rectangle) {
            this.pageNum = pageNum;
            this.rectangle = rectangle;
        }
    }
}

LittlePdfUtil.java

package test02itextpdf.keyword;

import org.apache.commons.io.IOUtils;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.List;

public class LittlePdfUtil {
    public static String streamToString(InputStream inputStream, String encoding) throws IOException {
        return IOUtils.toString(inputStream, encoding);
    }

    public static boolean isEmpty(String str) {
        return str == null || str.length() == 0;
    }

    public static boolean isNotEmpty(String str) {
        return str != null && str.length() >= 0;
    }


    public static byte[] streamToBytes(InputStream inputStream) throws IOException {
        ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
        byte[] buf = new byte[4 << 1024];
        int len = -1;
        while ((len = inputStream.read(buf)) != -1) {
            outputStream.write(buf, 0, len);
        }
        inputStream.close();
        return outputStream.toByteArray();
    }

    public static void removeLastList(List list, Integer num) {
        for (int i = 0; i < num; i++) {
            if (!list.isEmpty()) {
                list.remove(list.size() - 1);
            }
        }
    }

    public static <T> T getLast(List<T> list) {
        return list.get(list.size() - 1);

    }

    public static void main(String[] args) {
        System.out.println("123".substring(0, 3));
    }


}

RSAUtil.java

package test02itextpdf.keyword;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
import java.security.KeyStore;

public final class RSAUtil {
    private static final Logger LOG = LoggerFactory.getLogger(RSAUtil.class);


    public static final String KEYSTORE_TYPE_P12 = "PKCS12";
    public static final String KEYSTORE_TYPE_JKS = "JKS";

    public static KeyStore getKeyStore(String filePath, String keyPassword) throws Exception {

        KeyStore keyStore = KeyStore.getInstance(guessKeyStoreType(filePath));
        FileInputStream file = new FileInputStream(new File(filePath));
        keyStore.load(file, keyPassword.toCharArray());
        return keyStore;
    }

    public static KeyStore getKeyStore(InputStream inputStream, String keyPassword) throws Exception {
        KeyStore keyStore = KeyStore.getInstance(KEYSTORE_TYPE_P12);
        keyStore.load(inputStream, keyPassword.toCharArray());
        return keyStore;
    }

    public static String guessKeyStoreType(String filePath) {
        String ext = filePath.substring(filePath.lastIndexOf(".")+1);
        if (ext.equals("p12") || ext.equals("pfx")) {
            return KEYSTORE_TYPE_P12;
        }
        if (ext.equals("jks")) {
            return KEYSTORE_TYPE_JKS;
        }
        return null;
    }

}

TsaClientBCFactory.java

package test02itextpdf.keyword;

import com.itextpdf.text.pdf.security.TSAClient;
import com.itextpdf.text.pdf.security.TSAClientBouncyCastle;

public class TsaClientBCFactory implements TsaClientFactory {
    private String tsaUrl;

    public TsaClientBCFactory(String tsaUrl) {
        this.tsaUrl = tsaUrl;
    }

    @Override
    public TSAClient newTSAClient() {
        return new TSAClientBouncyCastle(tsaUrl);
    }
}

TsaClientFactory.java

package test02itextpdf.keyword;

import com.itextpdf.text.pdf.security.TSAClient;

public interface TsaClientFactory {
     TSAClient newTSAClient();
}

PdfTest.java(测试)

package test02itextpdf.keyword;
import org.junit.Before;
import org.junit.Test;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import org.springframework.util.StopWatch;

public class PdfTest {

    private Itext5PdfSign itext5PdfSign;

    @Test
    public void signTest() throws Exception {
        StopWatch stopWatch = new StopWatch();
        stopWatch.start("signBig");
        itext5PdfSign.sign(new FileInputStream("E:\\iotest\\itextpdf\\keyword\\anxin2.pdf"), new FileOutputStream("E:\\iotest\\itextpdf\\keyword\\anxin2_sign.pdf"));
        stopWatch.stop();
        System.out.println(stopWatch.prettyPrint());
    }
    @Before
    public void before() {
        String signername = "安心集团有限公司";
        String reason = "官方承认,不可篡改";
        String location = "安心集团有限公司";
        String password = "123456";
        String p12Path = "E:\\iotest\\itextpdf\\keyword\\test2.p12";
        String chapterPath = "E:\\iotest\\itextpdf\\keyword\\anxinseal.png";
        String field_name = "sign_Field";
        Itext5PdfSign.SignConfig signConfig = new Itext5PdfSign.SignConfig();
        signConfig.setSignP12Path(p12Path);
        signConfig.setSignP12Password(password);
        signConfig.setChapterImgPath(chapterPath);
        signConfig.setSignername(signername);
        signConfig.setReason(reason);

        signConfig.setLocation(location);
        signConfig.setSignFiledName(field_name);
        signConfig.setSignKeyWord("安心集团有限公司");
        itext5PdfSign = new Itext5PdfSign(signConfig);
    }
}

全部代码结构:

 效果展示:(目前不清楚为啥不盖在关键词上面,等有时间再看看代码)

 注意点:

pdf文本查找时,一个关键词可能被拆分成多个区块,所以要进行特殊处理

itext pdf坐标系

page页的左下角为坐标原点,定位一个矩形坐标时,根据左下角和右上角两个点坐标即可确定一个矩形的位置。

 原文链接:https://blog.csdn.net/do_bset_yourself/article/details/109150218?spm=1001.2014.3001.5501

  • 1
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值