javascript技巧

关注公众号 jb51net

关闭
首页 > 网络编程 > JavaScript > javascript技巧 > html2canvas jspdf下载pdf

html2canvas+jspdf实现下载pdf文件并添加水印

作者:嘻嘻哈哈猿人

这篇文章主要为大家详细介绍了如何使用html2canvas + jspdf进行下载pdf文件添加水印,以及echarts图片防止截断处理,有需要的小伙伴可以了解下

背景

一开始实现长截图使用 html2canvas 直接搞定, 后来改成需要下载 pdf 文件, ... 查找相关内容通过改造最终全部完成实现

jsPdf文档

html部分

// 使用class 或者 ref 获取都可以
<div class="pdfDom" ref="pdfDom"> 
   <div data-content-group="content-group" > 不能被截断的内容 </div>
    <div data-content-item="content-item"> 不能被截断的内容 </div>
    <div data-content-item="content-item"> 不能被截断的内容 </div>
    <div data-content-item="content-item"> 不能被截断的内容 </div>
    <div data-content-group="content-group">
        <div data-content-item="content-item"> 嵌套的内容不能被截断 </div>
        <div data-content-item="content-item"> 嵌套的内容不能被截断 </div>
        <div data-content-item="content-item"> 嵌套的内容不能被截断 </div>
    </div>
</div>

js部分

import JsPdf from "jspdf";
import html2Canvas from "html2canvas";
import "./simhei-normal.js" // 解决pdf添加水印中文字体乱码问题 ttf转为js后引用

// 这里使用pdf添加水印
function addWatermark (pdf, watermarkText) {
  // 获取PDF页数,给PDF每一页添加水印
   var totalPages = pdf.internal.getNumberOfPages();
   for (let i = 1; i <= totalPages; i++) {
     pdf.setPage(i);
     addWatermarkFill(pdf, watermarkText);
   }
   return pdf;
}
function addWatermarkFill (pdf, watermarkText) {
 let pdfW = pdf.internal.pageSize.getWidth();
 let pdfH = pdf.internal.pageSize.getHeight();
 // 250是可以根据水印的大小调整的 可以优化为单水印的长和宽
 let xCount = pdfW / 250; 
 let yCount = pdfH / 250;
 // 下面的for循环的作用是在页面上铺满水印
 for (let y = 0; y < yCount; y++) {
   let yLocation = y * 250;
   for (let x = 0; x < xCount; x++) {
     let xLocation = x * 250;
     pdf.saveGraphicsState() // 保存图形状态
     pdf.setFontSize(18);       // 设置水印字体大小
     pdf.setTextColor(200);    // 设置水印颜色
    //  https://artskydj.github.io/jsPDF/docs/jsPDF.html#setTextColor
     pdf.setGState(pdf.GState({ opacity: 0.3 })) // 设置透明度为0.3
     // 这里的SIMHEI是字体包 解决jsPDF 无法使用中文问题
    //  pdf.setFont('Alternate-normal');
     pdf.setFont('SIMHEI');
    //  https://artskydj.github.io/jsPDF/docs/jsPDF.html#setFont
     pdf.text(watermarkText, xLocation, yLocation, { align: 'left', angle: -45});
     pdf.restoreGraphicsState()
   }
 }
 return pdf;
}
// 以上代码为添加水印逻辑处理
// pdfDom 页面dom , intervalHeight 留白间距  fileName 文件名 watermarkText: 水印内容
export function html2Pdf (pdfDom, intervalHeight, fileName, watermarkText) {
  // 获取元素的高度
  function getElementHeight (element) {
    return element.offsetHeight;
  }
  // A4 纸宽高
  const A4_WIDTH = 592.28; const A4_HEIGHT = 841.89;
  // 获取元素去除滚动条的高度
  const domScrollHeight = pdfDom.scrollHeight;
  const domScrollWidth = pdfDom.scrollWidth;
  // 保存当前页的已使用高度
  let currentPageHeight = 0;
  // 获取所有的元素  我这儿是手动给页面添加class 用于计算高度 你也可以动态添加 这个不重要,主要是看逻辑
  // let elements = pdfDom.querySelectorAll('.content-item'); // 没有嵌套逻辑可以直接获取
  // 代表不可被分页的
  const newPage = "content-item"; 
  const wholeNodes = []; // 嵌套元素遍历出来 push
  // traversingNodes 当不可截断元素过大时候 递归获取子元素 
  function traversingNodes (nodes) {
    if (nodes.length === 0) return;
    nodes.forEach(element => {
      if (element.nodeType !== 1) return;
      // contentItem 不能截断标识
      // contentGroup 嵌套的父级标识
      const { contentItem: item, contentGroup: group } = element.dataset;
      if (item) {
        const elementHeight = getElementHeight(element);
        console.log(elementHeight, "我是页面上的elementHeight"); // 检查
        // 检查添加这个元素后的总高度是否超过 A4 纸的高度
        if (currentPageHeight + elementHeight > A4_HEIGHT) {
        // 如果超过了,创建一个新的页面,并将这个元素添加到新的页面上
          currentPageHeight = elementHeight;
          element.classList.add(newPage);
          console.log(element, "我是相加高度大于A4纸的元素");
        }
        currentPageHeight += elementHeight;
        wholeNodes.push(element); // 不可截断元素存在嵌套
      } else if (group) {
        traversingNodes(element.childNodes);
      }
    });
  }
  traversingNodes(pdfDom.childNodes);
  // 遍历所有内容的高度
  // 根据 A4 的宽高等比计算 dom 页面对应的高度

  const pageWidth = pdfDom.offsetWidth;
  const pageHeight = (pageWidth / A4_WIDTH) * A4_HEIGHT;
  // 将所有不允许被截断的子元素进行处理 如果没有嵌套逻辑可以直接获取 有嵌套逻辑 通过 traversingNodes 遍历push
  // const wholeNodes = pdfDom.querySelectorAll(`.${newPage}`);
  console.log(wholeNodes, "将所有不允许被截断的子元素进行处理");
  // 插入空白块的总高度
  let allEmptyNodeHeight = 0;
  for (let i = 0; i < wholeNodes.length; i++) {
    // 判断当前的不可分页元素是否在两页显示
    const topPageNum = Math.ceil(wholeNodes[i].offsetTop / pageHeight);
    const bottomPageNum = Math.ceil((wholeNodes[i].offsetTop + wholeNodes[i].offsetHeight) / pageHeight);

    // 是否被截断
    if (topPageNum !== bottomPageNum) {
      // 创建间距
      const newBlock = document.createElement("div");
      newBlock.className = "empty-node";
      newBlock.style.background = "#FFFFFF";

      // 计算空白块的高度,可以适当留出空间,根据自己需求而定
      const _H = topPageNum * pageHeight - wholeNodes[i].offsetTop;
      newBlock.style.height = _H + intervalHeight + "px";

      // 插入空白块
      wholeNodes[i].parentNode.insertBefore(newBlock, wholeNodes[i]);

      // 更新插入空白块的总高度
      allEmptyNodeHeight = allEmptyNodeHeight + _H + intervalHeight;
    }
  }
  // 这里可以不加 页面高度会变
  //  pdfDom.setAttribute(
  //   'style',
  //   `height: ${domScrollHeight + allEmptyNodeHeight}px; width: ${domScrollWidth}px;`,
  //   )

  return html2Canvas(pdfDom, {
    width: pdfDom.offsetWidth,
    height: pdfDom.offsetHeight,
    useCORS: true,
    allowTaint: true
    // scale: 1
  }).then(canvas => {
    // dom 已经转换为 canvas 对象,可以将插入的空白块删除了
    const emptyNodes = pdfDom.querySelectorAll(".empty-node");
    for (let i = 0; i < emptyNodes.length; i++) {
      emptyNodes[i].style.height = 0;
      emptyNodes[i].parentNode.removeChild(emptyNodes[i]);
    }
    
    const canvasWidth = canvas.width; const canvasHeight = canvas.height;
    // html 页面实际高度
    let htmlHeight = canvasHeight;
    // 页面偏移量
    let position = 0;
   
    // 根据 A4 的宽高等比计算 pdf 页面对应的高度
    const pageHeight = (canvasWidth / A4_WIDTH) * A4_HEIGHT;
   
    // html 页面生成的 canvas 在 pdf 中图片的宽高
    const imgWidth = A4_WIDTH;
    const imgHeight = 592.28 / canvasWidth * canvasHeight;
    // 将图片转为 base64 格式
    const imageData = canvas.toDataURL("image/jpeg", 1.0);
    
    
    // 生成 pdf 实例
    let PDF = new JsPdf("", "pt", "a4", true); 
    // html 页面的实际高度小于生成 pdf 的页面高度时,即内容未超过 pdf 一页显示的范围,无需分页
    if (htmlHeight <= pageHeight) {
      PDF.addImage(imageData, "JPEG", 0, 0, imgWidth, imgHeight);
    } else {
      while (htmlHeight > 0) {
        PDF.addImage(imageData, "JPEG", 0, position, imgWidth, imgHeight);
        // 更新高度与偏移量
        htmlHeight -= pageHeight;
        position -= A4_HEIGHT;
        if (htmlHeight > 0) {
          // 在 PDF 文档中添加新页面
          PDF.addPage();
        }
      }
    }
    PDF = addWatermark(PDF, watermarkText );
    // 保存 pdf 文件
    PDF.save(`${fileName}.pdf`);
  }).catch(err => {
    console.log(err);
  });
}

调用

    import { html2Pdf } from "./outpdf.js";

    const pdfDom = ref();
    html2Pdf(pdfDom.value, 20, "下载PDF文件", "水印文字");

水印添加问题

添加水印的时候遇到了中文乱码问题 查看文档jspdf 是支持自定义字体的

在电脑中找到找到font文件 也可以在网上下载对应的 ttf 文件 我这里用的 黑体常规

ttf文件转换为 js 文件 ttf文件转js地址jsPdf文档首页中有讲到Use of UTF-8 / TTF:

将转换后的js文件,导入到工程内用,在文件内引入,用jsPDF里的 setFont()方法加载一下字体包

到此这篇关于html2canvas+jspdf实现下载pdf文件并添加水印的文章就介绍到这了,更多相关html2canvas jspdf下载pdf内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

您可能感兴趣的文章:
阅读全文