网页盖章效果实现

先看效果

网页盖章效果实现_第1张图片

支持pdf、Word、图片盖章。下面讲一下实现要点。

  1. 前端需要实现拖拽图片,并反馈图片的最终位置给后台,后台根据前端返回的位置信息进行电子签章操作。
  2. 由于没有找到合适的前端pdf展示框架(支持印章的拖拽),所以目前方案是将pdf转为图片(一页pdf即为一张图片)后再进行盖章操作。
  3. word和图片需要先转为pdf再进行操作,已经有成熟的API可实现该效果,其他文件类型需要再研究。
  • 前端效果实现

实现图片的拖拽,核心代码如下

其中id="sign"即为印章,id="image"即为pdf转换成的图片

function setSign(){
        var container_top = $('#container').offset().top;
        var container_left = $('#container').offset().left;
        var container_width = $("#container").width();
        var container_height = $("#container").height();
        var sign_width = $("#sign").width();
        var sign_height = $("#sign").height();
        //锚定印章可移动的范围
        var range = {
            minX: container_left,
            minY: container_top,
            maxX: container_width + container_left - sign_width,      //扣去2个padding=8px以及2个边框1px
            maxY: container_height + container_top - sign_height
        };
        console.log(range);
        //绑定移动事件
        var sign = $('#sign');
        //绑定移动事件
        sign.on('mousedown', function (e) {
            sign.data('x', e.clientX);
            sign.data('y', e.clientY);
            var position = sign.position();
            $(document).on('mousemove', function (e1) {
                var x = e1.clientX - sign.data('x') + position.left;
                var y = e1.clientY - sign.data('y') + position.top;
                x = x < range.minX ? range.minX : x;
                x = x > range.maxX ? range.maxX : x;
                y = y < range.minY ? range.minY : y;
                y = y > range.maxY ? range.maxY : y;

                sign.css({ left: x, top: y });
            }).on('mouseup', function () {
                $(this).off('mousemove').off('mouseup');
            });
        });
    }

上述代码是为了实现印章的拖拽效果,同时限定印章的活动范围(不能超过PDF的范围)。

function gz() {
        $("#gz_button").attr("disabled", true);
        //获取container
        var container_top = $('#container').offset().top;
        var container_left = $('#container').offset().left;
        var sign_top = $('#sign').offset().top;
        var sign_left = $('#sign').offset().left;
        var sign_width = $('#sign').outerWidth(true);
        var sign_height = $('#sign').outerHeight(true);
        var container_height = $('#container').height();
        var container_width = $('#container').width();
        var margin_left = parseInt(sign_left - container_left + sign_width / 2);
        var margin_bottom = parseInt(container_height - sign_top + container_top - sign_height / 2);
        console.log(margin_left, margin_bottom);
        var left_percent = margin_left / container_width;
        var bottom_percent = margin_bottom / container_height;
        console.log(container_width, left_percent, bottom_percent);
        //传递到后台
        if (currentPage != 1) {
            currentPage = $(".current_page").html();
        }
        console.log(currentPage);
        $.ajax({
            url:'/wechartminiprogram/ydqz/gz',
            contentType: "application/json;charset=UTF-8",
            dataType:'json',
            type:'POST',
            data: JSON.stringify({"fileName":fileName,"pageIndex":currentPage,"leftPercent":left_percent,"bottomPercent":bottom_percent,"signName":sign_name}),
            responseType: 'blob',
            success: function(data){
                console.log(data);
                if (data.code == 0) {
                    var form = $("
"); form.attr('style', 'display:none'); form.attr('target', ''); form.attr('method', 'post'); //请求方式 form.attr('action', '/wechartminiprogram/ydqz/getPdf');//请求地址 form.attr('target','_blank');//新窗口打开 var input1 = $('');//将你请求的数据模仿成一个input表单 input1.attr('type', 'hidden'); input1.attr('name', 'fileName');//该输入框的name input1.attr('value',data.data);//该输入框的值 $('body').append(form); form.append(input1); form.submit(); form.remove(); } }, error:function(response){ console.log(response); } }); }

上述代码即为盖章按钮的点击事件,获取印章距PDF最左侧的百分比及最底部的百分比,传递给后台,同时因为PDF可能有多页,所以还需要将当前页的页码传递给后台。

  • 后端实现

依赖如下


      org.apache.pdfbox
      fontbox
      2.0.17
    
    
    
      org.apache.pdfbox
      pdfbox
      2.0.17
    
    
      org.apache.pdfbox
      pdfbox-tools
      2.0.17
    
    
      com.jacob
      jacob
      1.19
    

      com.itextpdf
      itextpdf
      5.5.9
    
    
      com.itextpdf.tool
      xmlworker
      5.5.9
    

    
      com.itextpdf
      itext-asian
      5.2.0
    

pdf转图片:

private int pdf2png(String fileAddress,String filename,String type) {
		// 将pdf装图片 并且自定义图片得格式大小
		File file = new File(fileAddress+"\\"+filename+".pdf");
		try {
			PDDocument doc = PDDocument.load(file);
			PDFRenderer renderer = new PDFRenderer(doc);
			int pageCount = doc.getNumberOfPages();
			for (int i = 0; i < pageCount; i++) {
				BufferedImage image = renderer.renderImageWithDPI(i, 144); // Windows native DPI
				// BufferedImage srcImage = resize(image, 240, 240);//产生缩略图
				ImageIO.write(image, type, new File(fileAddress+"\\"+filename+"_"+(i+1)+"."+type));
			}
			doc.close();
			return pageCount;
		} catch (IOException e) {
			e.printStackTrace();
		}
		return 0;
	}

word转pdf:

private String word2pdf(String filePath) throws Exception{
		ActiveXComponent app = null;
		String pdfFileName = new Date().getTime() + "_converted";
		String pdfFile = fileTempPath + File.separator + pdfFileName + ".pdf";

		System.out.println("开始转换...");
		// 开始时间
		long start = System.currentTimeMillis();
		try {
			// 打开word
			app = new ActiveXComponent("Word.Application");
			// 设置word不可见,很多博客下面这里都写了这一句话,其实是没有必要的,因为默认就是不可见的,如果设置可见就是会打开一个word文档,对于转化为pdf明显是没有必要的
			//app.setProperty("Visible", false);
			// 获得word中所有打开的文档
			Dispatch documents = app.getProperty("Documents").toDispatch();
			System.out.println("打开文件: " + filePath);
			// 打开文档
			Dispatch document = Dispatch.call(documents, "Open", filePath, false, true).toDispatch();
			// 如果文件存在的话,不会覆盖,会直接报错,所以我们需要判断文件是否存在
			File target = new File(pdfFile);
			if (target.exists()) {
				target.delete();
			}
			System.out.println("另存为: " + pdfFile);
			// 另存为,将文档报错为pdf,其中word保存为pdf的格式宏的值是17
			Dispatch.call(document, "SaveAs", pdfFile, 17);
			// 关闭文档
			Dispatch.call(document, "Close", false);
			// 结束时间
			long end = System.currentTimeMillis();
			System.out.println("转换成功,用时:" + (end - start) + "ms");
			return pdfFileName;
		}catch(Exception e) {
			throw new RuntimeException("word转换成pdf失败" + e.getMessage());
		}finally {
			// 关闭office
			app.invoke("Quit", 0);
		}
	}

图片转pdf:

private void image2pdf(String imagePath, String pdfPath) throws Exception{
		Document document = new Document();
		FileOutputStream fos = null;
		try {
			fos = new FileOutputStream(pdfPath);
			PdfWriter.getInstance(document, fos);
			// 设置文档的大小
			document.setPageSize(PageSize.A4);
			// 打开文档
			document.open();
			// 写入一段文字
			// 读取一个图片
			Image image = Image.getInstance(imagePath);
			float imageHeight=image.getScaledHeight();
			float imageWidth=image.getScaledWidth();
			int i=0;
			while(imageHeight>500||imageWidth>500){
				image.scalePercent(100-i);
				i ++;
				imageHeight=image.getScaledHeight();
				imageWidth=image.getScaledWidth();
			}
			image.setAlignment(Image.ALIGN_CENTER);
			//设置图片的绝对位置
			// image.setAbsolutePosition(0, 0);
			// image.scaleAbsolute(500, 400);
			// 插入一个图片
			document.add(image);
		} catch (DocumentException de) {
			System.out.println(de.getMessage());
		} catch (IOException ioe) {
			System.out.println(ioe.getMessage());
		}
		document.close();
		fos.flush();
		fos.close();
	}

由于我司合作的CA厂商提供的电子签章API只有通过关键字盖章的方法,所以我还需要在指定的盖章位置添加一段关键字:

private ByteArrayOutputStream addText(String filePath,String text, float leftPercent, float bottomPercent, int pageIndex){
		ByteArrayOutputStream byteArrayOutputStream = null;
		PdfReader pdfReader = null;
		PdfStamper pdfStamper = null;
		PdfContentByte pdfContentByte = null;
		try {
			BaseFont baseFont = BaseFont.createFont(NAME, ENCODE, BaseFont.NOT_EMBEDDED);
			InputStream inputStream = new FileInputStream(new File(filePath)); // 读取pdf文件
			pdfReader = new PdfReader(inputStream); // 加载文件到pdf引擎
			Document document = new Document(pdfReader.getPageSize(pageIndex));
			// 获取页面宽度
			float widths = document.getPageSize().getWidth();
			// 获取页面高度
			float heights = document.getPageSize().getHeight();
			int width = Math.round(widths * leftPercent);
			int height = Math.round(heights * bottomPercent);
			byteArrayOutputStream = new ByteArrayOutputStream();
			pdfStamper = new PdfStamper(pdfReader, byteArrayOutputStream); // 加载模板
			pdfContentByte = pdfStamper.getOverContent(pageIndex); // 获取顶部
			pdfContentByte.beginText(); // 插入文字信息
			pdfContentByte.setFontAndSize(baseFont, 1);
			int len = Math.round(baseFont.getWidthPoint(text, 1) / 2);//文字长度
			BaseColor baseColor = new BaseColor(0, 0, 0);//最后一个1为透明
			pdfContentByte.setColorFill(baseColor);
			pdfContentByte.setTextMatrix(width - len, height); // 设置文字在页面中的坐标
			pdfContentByte.showText(text);
			pdfContentByte.endText();
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			try {
				pdfStamper.close();
				pdfReader.close();
				byteArrayOutputStream.close();
			} catch (Exception _e) {
				_e.printStackTrace();
			}
		}
		return byteArrayOutputStream;
	}

字体是透明的,不影响原PDF的内容。后面只要调用盖章的方法就可以了,不再赘述。

你可能感兴趣的:(java)