(六)动态文档模板服务

思路

需求:要实现一个前端上传文档模板。利用占位符将模板中想要填写的内容放在模板中。
然后直接生成。当模板想要修改时。直接更新模板即可。不需要修改代码。

呕心沥血一个月完成。希望多多指正问题。谢谢。

用什么模板呢

1.上传word文档模板。
(1)思路:使用word模板,使用占位符匹配需要替换的内容。
(2)问题:但是需要线上转pdf.这样在很多个模板同时生成时很浪费时间。
(3)改进:使用html模板。

2.上传html文档模板
(1)思路:使用html模板,使用占位符匹配需要替换的内容。
(2)问题:但是在遇到表格或者复杂的样式的时候,很难操作。并且展示成想要的样式。
(3)改进:使用pdf模板。

3.上传pdf模板

(1)问题:pdf可以读取到所有的文本内容。但是在不保存文件,使用流保存的时候。替换之后的文本无法再转成,将生成的文档保存。
(2)改进:使用pdf表单域。(这样就不需要占位符了。)

模板存放在哪里呢

1.模板内容放在数据库中。

(1)问题:当数据多的时候读取会非常慢。
(2)改进:将文件转成流存放到mongo中。数据库中直接存放文件的fileId即可。

代码实现

一.pdf中找到所有的表单域

实现代码:
 /**
     * 获取所有的表单域
     * @param inputStream
     * @return
     */
    private static Set<String> getPdfAllFormFieldsByStream(InputStream inputStream){
        try {
            /* 打开已经定义好字段以后的pdf模板 */
            PdfReader reader = new PdfReader(inputStream);

            ByteArrayOutputStream baos = new ByteArrayOutputStream();
            PdfStamper stamp = new PdfStamper(reader,baos);


            /* 取出报表模板中的所有字段 */
           AcroFields form = stamp.getAcroFields();
           Set<String> fields = form.getFields().keySet();

            stamp.close();
            reader.close();
            baos.close();

            return fields;
        }catch(Exception e){
            e.printStackTrace();
        }
        return null;
    }

二.pdf中插入图片

/**
     * 指定位置给pdf插入图片
     * @param pdfB
     * @param imgB
     * @param page
     * @param x
     * @param y
     * @return
     * @throws Exception
     */
    public static byte[] insertImgToPdf(byte[] pdfB,byte[] imgB,int page,float x,float y) throws Exception {

        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        // 读取模板文件
        InputStream input = new ByteArrayInputStream(pdfB);

        PdfReader reader = new PdfReader(input);
        PdfStamper stamper = new PdfStamper(reader,baos);

        // 读图片
        Image image = Image.getInstance(imgB);
        // 获取操作的页面
        PdfContentByte under = stamper.getOverContent(page);
        // 添加图片
        image.setAbsolutePosition(x, y);
        under.addImage(image);


        stamper.close();
        reader.close();

        return baos.toByteArray();
    }

三..pdf中插入二维码图片

   /**
     * 指定位置给pdf插入二维码图片
     * @param pdfB
     * @param page
     * @param x
     * @param y
     * @return
     * @throws Exception
     */
    public static byte[] insertQRCodeImageToPdf(byte[] pdfB,String content,int page,float x,float y,int width,int height) throws Exception {

        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        // 读取模板文件
        InputStream input = new ByteArrayInputStream(pdfB);

        PdfReader reader = new PdfReader(input);
        PdfStamper stamper = new PdfStamper(reader,baos);

        // 解决中文解析乱码
        Map<EncodeHintType, Object> hints = new HashMap<>();
        hints.put(CHARACTER_SET, "UTF-8");
        // 读图片
        BarcodeQRCode barcodeQRCode = new BarcodeQRCode(content, width, height, hints);
        Image image = barcodeQRCode.getImage();

        // 获取操作的页面
        PdfContentByte under = stamper.getOverContent(page);
        // 添加图片
        image.setAbsolutePosition(x, y);
        under.addImage(image);


        stamper.close();
        reader.close();

        return baos.toByteArray();
    }

四..pdf中定位某个表单域的位置。

这个可以用来定位图片。二维码,签章这些的位置。可以非常迅速的实现。

需要注意的是:对于itext.x,y是指左下角是原点。y是距离底边距。如果要顶边距需要进行换算


  /**
     * 获取某个域的坐标
     **/
    public static Map<String,PdfInfo> getPdfTextPosition(InputStream inputStream, List<String> fields) throws Exception {
        Map<String,PdfInfo> positions = new HashMap<>();

        //* 打开已经定义好字段以后的pdf模板 */
        PdfReader reader = new PdfReader(inputStream);
        Rectangle pageSize = reader.getPageSize(1);

        AcroFields acroFields = reader.getAcroFields();

        for(String field:fields){

            List<AcroFields.FieldPosition> pos = acroFields.getFieldPositions(field);

            AcroFields.FieldPosition pitem = pos.get(0);

            int pageNo = pitem.page;
            Rectangle pRectangle = pitem.position;

            positions.put(field,new  PdfInfo(pageSize.getWidth(),pageSize.getHeight(),
                    new Position(pageNo,pRectangle.getLeft(),pRectangle.getBottom(),transform(pageSize,pRectangle.getTop()),
                            pRectangle.getWidth(),pRectangle.getHeight()
                    )));

        }

        reader.close();

        return positions;

    }

	 /**
     * 坐标top需要计算
     * @param pagesize
     * @param y
     * @return
     */
    public static float transform(Rectangle pagesize, float y) {
        return pagesize.getTop() - y;
    }


public  class PdfInfo {

    /**
     * pdf宽
     */
    public float width;
    /**
     * pdf高
     */
    public float height;
    /**
     * 位置信息
     */
    public Position position;
}


public class Position {
    /**
     * 页数
     */
    public int pageNo;
    /**
     * x坐标
     */
    public float x;
    /**
     * y坐标
     */
    public float y;
    /**
     * 距离顶边距(需要进行换算)
     */
    public float top;
    /**
     * 宽(域)
     */
    public float width;
    /**
     * 高(域)
     */
    public float height;
}

五。困扰我两天的终极问题——如果要实现生成的文件可编辑。怎么办。而且是填写好的表单域不可编辑。没有填写内容的实现可编辑。

1.使用itext的removefield.填写一个删除一个表单域
bug:表单域填好的内容也给删除了。
2.我使用了一天的时间开始研究一个新的框架:
spire.pdf(一系列我这里不再赘述)
formWidgetCollection.get(f).setReadOnly(true);

最后出现的问题是:可以让表单域不可见。但是他会出现乱码。水印等等问题。

3.最后突然发现itext就可以实现(把我高兴坏了)
form.setFieldProperty(k, “fflags”, PdfFormField.FF_READ_ONLY, null);

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值