使用pdf-lib.js实现pdf添加自定义水印功能

html 复制代码
<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <title>PDF Watermark Example</title>
  <script src="https://cdn.bootcdn.net/ajax/libs/pdf-lib/1.17.1/pdf-lib.min.js"></script> 
</head>

<body>
  <input type="file" id="pdfFile" accept="application/pdf">
  <button onclick="addWatermark()">添加水印</button>
  <a id="downloadLink" style="display:none;">下载PDF</a>

  <script>
    async function addWatermark() {
      const fileInput = document.getElementById('pdfFile');
      const file = fileInput.files[0];

      if (!file) {
        alert('请选择一个PDF文件');
        return;
      }

      const arrayBuffer = await file.arrayBuffer();
      const pdfDoc = await PDFLib.PDFDocument.load(arrayBuffer);
      const pages = pdfDoc.getPages();
      // 获取当前时间并格式化
      const now = new Date();
      const year = now.getFullYear();
      const month = String(now.getMonth() + 1).padStart(2, '0');
      const day = String(now.getDate()).padStart(2, '0');
      const hour = String(now.getHours()).padStart(2, '0');
      const minute = String(now.getMinutes()).padStart(2, '0');

      // 创建水印
      const watermarkText = `无敌暴龙兽-${year}-${month}-${day}-${hour}:${minute}-加密`;
      const canvas = document.createElement('canvas');
      const ctx = canvas.getContext('2d');
      const fontSize = 20;
      const textColor = 'rgba(0, 0, 0, 0.2)';

      // 计算文本宽度和高度
      ctx.font = `${fontSize}px Arial`;
      const textMetrics = ctx.measureText(watermarkText);
      const textWidth = textMetrics.width;
      const textHeight = fontSize;

      // 计算旋转后的文本边界
      const angle = -Math.PI / 4; // 旋转45度
      const rotatedWidth = Math.abs(textWidth * Math.cos(angle)) + Math.abs(textHeight * Math.sin(angle));
      const rotatedHeight = Math.abs(textHeight * Math.cos(angle)) + Math.abs(textWidth * Math.sin(angle));

      // 设置Canvas大小
      canvas.width = rotatedWidth + 20; // 增加一些边距
      canvas.height = rotatedHeight + 20;

      // 绘制水印文本
      ctx.clearRect(0, 0, canvas.width, canvas.height);
      ctx.translate(canvas.width / 2, canvas.height / 2);
      ctx.rotate(angle);
      ctx.font = `${fontSize}px Arial`;
      ctx.fillStyle = textColor;
      ctx.textAlign = 'center';
      ctx.textBaseline = 'middle';
      ctx.fillText(watermarkText, 0, 0);

      // 将Canvas转换为Image对象
      const image = new Image();
      image.src = canvas.toDataURL('image/png');

      // 添加水印到每一页PDF
      for (const page of pages) {
        const { width, height } = page.getSize();

        // 计算新的水印尺寸,以便它可以跨越页面的两个相邻边缘
        const cornerMargin = 50; // 角落与页面边缘之间的最小距离
        const cornerSizeMultiplier = 0.5; // 控制水印相对于页面尺寸的比例
        const scale = 1; // 水印缩放比例

        const cornerWidth = Math.min(width, height) * cornerSizeMultiplier * scale;
        const cornerHeight = cornerWidth; // 假设是正方形水印

        // 计算水印的新位置,使其跨越页面的两个相邻边缘
        const positions = [
          { x: cornerMargin, y: cornerMargin }, // 左上角
          { x: width - cornerWidth - cornerMargin, y: cornerMargin }, // 右上角
          { x: cornerMargin, y: height - cornerHeight - cornerMargin }, // 左下角
          { x: width - cornerWidth - cornerMargin, y: height - cornerHeight - cornerMargin } // 右下角
        ];

        // 需要重新创建水印图像以匹配新尺寸
        const newCanvas = document.createElement('canvas');
        const newCtx = newCanvas.getContext('2d');
        newCanvas.width = cornerWidth;
        newCanvas.height = cornerHeight;

        // 复制旧水印到新画布
        newCtx.drawImage(canvas, 0, 0, cornerWidth, cornerHeight);

        // 将新画布转换回Image对象
        const newImage = new Image();
        newImage.src = newCanvas.toDataURL('image/png');

        // 嵌入新水印到PDF文档
        const newImg = await pdfDoc.embedPng(newImage.src);

        for (const pos of positions) {
          page.drawImage(newImg, {
            x: pos.x,
            y: pos.y,
            width: newImg.width,
            height: newImg.height,
          });
        }
      }

      // 保存修改后的PDF
      const pdfDataUri = await pdfDoc.saveAsBase64({ dataUri: true });
      const downloadLink = document.getElementById('downloadLink');
      downloadLink.href = pdfDataUri;
      downloadLink.download = 'watermarked.pdf';
      downloadLink.click();
    }
  </script>
</body>

</html>
相关推荐
水域安全老周7 小时前
水趣钓鱼救生衣专利拆解:两级锁紧如何解决落水人衣分离
java·前端·网络
计算机魔术师8 小时前
Anthropic CEO突然喊踩刹车,OpenAI罕见力挺:AI这辆车不能只踩油门了
前端
wing988 小时前
从codex转战workbuddy使用一周的感受
前端·人工智能·后端
Highcharts.js8 小时前
常见报错排雷指南2:导出失败的官方解法
javascript·react.js·ecmascript·highcharts·可视化图表·导出模块失败·导出服务
EatFan8 小时前
Java接入支付宝 JSAPI 支付保姆教程(二):流程讲解与前后端代码讲解
前端·spring boot·后端·微信小程序·小程序·uni-app
梦想平凡9 小时前
百游棋牌源代码开发搭建教程(五):房间创建、座位分配与请求幂等实现
前端·javascript·数据库·源代码管理
IT_陈寒9 小时前
Redis的Set操作居然能把我的服务整挂了?
前端·人工智能·后端
计算机魔术师10 小时前
Suno 发布 v6 音乐模型,推出 v6、v6-wild、v6-mini 三个版本
前端
whyweplay10 小时前
elpis : DSL动态组件学习
前端
用户9210802628610 小时前
前端 Vue 专栏 06:异步更新机制、任务队列与 nextTick
前端