网页盖章效果实现

先看效果

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

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

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

<div id="container" style="width:600px;height:800px;border:1px solid red;margin:0 auto;">
    <div class='sign' id="sign" style='height:120px;width:120px;position:absolute;'><img src='' id="signImg" draggable='false' style='width: 120px;height: 120px;'/><button class='btn del' onclick="deleteSign();" style="display:block;margin: 0 auto;">删除</button></div>
    <img src="" id="image" style="width:auto;height:auto;">
</div>

其中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>");
                    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>');//将你请求的数据模仿成一个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可能有多页,所以还需要将当前页的页码传递给后台。

  • 后端实现

依赖如下

<dependency>
      <groupId>org.apache.pdfbox</groupId>
      <artifactId>fontbox</artifactId>
      <version>2.0.17</version>
    </dependency>
    <!-- https://mvnrepository.com/artifact/org.apache.pdfbox/pdfbox -->
    <dependency>
      <groupId>org.apache.pdfbox</groupId>
      <artifactId>pdfbox</artifactId>
      <version>2.0.17</version>
    </dependency>
    <dependency>
      <groupId>org.apache.pdfbox</groupId>
      <artifactId>pdfbox-tools</artifactId>
      <version>2.0.17</version>
    </dependency>
    <dependency>
      <groupId>com.jacob</groupId>
      <artifactId>jacob</artifactId>
      <version>1.19</version>
    </dependency>
<dependency>
      <groupId>com.itextpdf</groupId>
      <artifactId>itextpdf</artifactId>
      <version>5.5.9</version>
    </dependency>
    <dependency>
      <groupId>com.itextpdf.tool</groupId>
      <artifactId>xmlworker</artifactId>
      <version>5.5.9</version>
    </dependency>

    <dependency>
      <groupId>com.itextpdf</groupId>
      <artifactId>itext-asian</artifactId>
      <version>5.2.0</version>
    </dependency>

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的内容。后面只要调用盖章的方法就可以了,不再赘述。

  • 4
    点赞
  • 28
    收藏
    觉得还不错? 一键收藏
  • 7
    评论
好的,实现PDF盖章效果主要有两个步骤: 1. 生成印章图片 印章图片可以在PS、AI等软件中设计,也可以使用Java代码生成。这里演示使用Java代码生成一个简单的圆形印章图片: ```java import java.awt.Color; import java.awt.Font; import java.awt.Graphics2D; import java.awt.RenderingHints; import java.awt.image.BufferedImage; import java.io.File; import java.io.IOException; import javax.imageio.ImageIO; public class SealGenerator { private static final int IMAGE_SIZE = 200; private static final int PADDING = 20; private static final int CIRCLE_SIZE = 160; private static final String FONT_NAME = "微软雅黑"; private static final int FONT_SIZE = 30; private static final String TEXT = "印"; public static void main(String[] args) throws IOException { BufferedImage image = new BufferedImage(IMAGE_SIZE, IMAGE_SIZE, BufferedImage.TYPE_INT_RGB); Graphics2D g = image.createGraphics(); g.setColor(Color.WHITE); g.fillRect(0, 0, IMAGE_SIZE, IMAGE_SIZE); g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); g.setColor(Color.RED); g.fillOval(PADDING, PADDING, CIRCLE_SIZE, CIRCLE_SIZE); g.setColor(Color.WHITE); g.setFont(new Font(FONT_NAME, Font.PLAIN, FONT_SIZE)); int textWidth = g.getFontMetrics().stringWidth(TEXT); g.drawString(TEXT, IMAGE_SIZE / 2 - textWidth / 2, IMAGE_SIZE / 2 + FONT_SIZE / 2); g.dispose(); ImageIO.write(image, "png", new File("seal.png")); } } ``` 运行后会生成一个名称为`seal.png`的印章图片。 2. 在PDF上盖章 使用Java代码在PDF上盖章需要使用第三方库,这里演示使用iText库。 ```java import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import com.itextpdf.io.image.ImageDataFactory; import com.itextpdf.kernel.geom.Rectangle; import com.itextpdf.kernel.pdf.PdfDocument; import com.itextpdf.kernel.pdf.PdfReader; import com.itextpdf.kernel.pdf.PdfWriter; import com.itextpdf.kernel.pdf.canvas.PdfCanvas; import com.itextpdf.layout.Document; import com.itextpdf.layout.element.Image; public class PdfSeal { private static final String INPUT_FILE = "input.pdf"; private static final String OUTPUT_FILE = "output.pdf"; private static final String SEAL_FILE = "seal.png"; private static final float SEAL_X = 400; private static final float SEAL_Y = 100; private static final float SEAL_WIDTH = 100; private static final float SEAL_HEIGHT = 100; public static void main(String[] args) throws IOException { PdfReader reader = new PdfReader(INPUT_FILE); PdfWriter writer = new PdfWriter(new FileOutputStream(new File(OUTPUT_FILE))); PdfDocument pdf = new PdfDocument(reader, writer); Document document = new Document(pdf); Image seal = new Image(ImageDataFactory.create(SEAL_FILE)); seal.scaleToFit(SEAL_WIDTH, SEAL_HEIGHT); seal.setFixedPosition(SEAL_X, SEAL_Y); PdfCanvas canvas = new PdfCanvas(pdf.getFirstPage()); Rectangle rect = new Rectangle(SEAL_X, SEAL_Y, SEAL_WIDTH, SEAL_HEIGHT); canvas.rectangle(rect); canvas.stroke(); document.add(seal); document.close(); } } ``` 运行后会在PDF的第一页上添加一个印章。其中`INPUT_FILE`为输入PDF文件路径,`OUTPUT_FILE`为输出PDF文件路径,`SEAL_FILE`为印章图片路径,`SEAL_X`、`SEAL_Y`、`SEAL_WIDTH`、`SEAL_HEIGHT`分别为印章在PDF中的位置和大小。 希望这个简单的示例能对您有所帮助!

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值