动态绘制PDF

324 阅读2分钟

我正在参加「掘金·启航计划」

项目需求

动态渲染PDF文件:

  • 封面背景图不变,封面标题、二维码动态赋值;
  • 内页内容:单双页样式不一样;表格行数的样式要做特别处理(难住我的点),元素未超过10项,则当前页平均分布,table每行高度自适应;如果超过10项,则所有内容项平均分布到N页,table每行高度自适应。

实现

前端做,主要是调取浏览器的打印功能window.print()API。

window.print函数默认打印整个页面,如果想要设置打印的内容和样式,需要用到@media  print{},不需要打印的元素,class的样式为 display:none,然后再设置需要打印出来的class的样式。

引入打印样式的三种方式

在 CSS 中使用 @media print
@media print {
    body {
        background-color: #fff;
    }
    img {
        visibility: hidden;
    }
    a::after {
        content: "(" attr(href) ")"; /* 所有链接后显示链接地址 */
    }
}
在 CSS 中使用 @import
@import url("my-print-style.css") print;
在 HTML 中使用 <link> 标签
<link rel="stylesheet" media="print" href="my-print-style.css">

在 @media print 或 my-print-style.css 中,可以自由的修改大部分样式。

注意,在预览的时候记得勾选这个选项。

image.png

处理数据源

  • 获取到数据源之后,将每一列的内容项高度进行累加,如累加高度超过打印文档页的高度则进行切割,剩余的内容继续以上逻辑
  • 通过高度计算,得知内容高度累加后的总的页数,然后把内容项 / 页数将内容进行切割,可以得到每一页中内容的行数,高度自适应的css关键是给table行增加样式:height:auto
// 处理数据源
resolveData() {
  let meals = this.pdfList.setMeals
  let newMeals = []
  meals.forEach((meal) => {
    // 给表单每项内容添加序号
    meal.sections.forEach((item, index) => {
      item.orderNumber = index + 1
    })
    // 分别传入每个套餐的列表项、获取高度高的那个元素高度、返回的数组为一页
    let obj = this.resolveHeight([], meal.sections) // 第一次分页处理
    let number = obj.length // 单页的话、只返回一个originArr、多页返回 originArr, lastMeals
    let results = this.spliceArr(meal.sections, number)
    // 数组分成3等分
    results.forEach((item, index) => {
      let newMeal = JSON.parse(JSON.stringify(meal))
      newMeal.sections = item
      newMeals.push(newMeal)
    })
  })
  this.setMeals = newMeals
}
/**
  * 传入套餐列表项、计算高度、给列表做分割
  * originArr
*/
resolveHeight(originArr, sections) {
  let height = 0
  let index = -1
  // 获取需要分割的下标  用for
  for (let i = 0; i < sections.length; i++) {
    // 计算每一个项目的高度
    height += this.getHeight(sections[i])
    // 第一次大于 表格高度的时候  获取上个index    index - 1   并且跳出循环
    if (height > 610) {
      // section 截取 index-1  之前的
      index = i - 1
      break
    }
  }
  // index 不变  则返回当前  源数据 加 sectoins
  if (index == -1) {
    originArr.push(sections)
    return originArr
  } else {
    originArr.push(sections.slice(0, index + 1))  // 第一页
    let lastMeals = sections.slice(index + 1, sections.length)  //除第一页外剩余所有
    return this.resolveHeight(originArr, lastMeals)
  }
}
/**
  * 获取每一项元素内容、保留最高项的高度
 */
getHeight(data) {
  let sectionNameHeight = Math.ceil(data.sectionName.length / 6) // A列行数
  let sectionPurposeHeight = Math.ceil(data.sectionPurpose.length / 46) // B列行数
  let max = Math.max(sectionNameHeight, sectionPurposeHeight)
  return 10 + 14.4 * max
},
/**
  * 分割一页 数据为几等份
*/
spliceArr(item, number) {
  if (item.length == 0) {
    return []
  }
  let index = Math.ceil(item.length / number)
  let results = []
  for (let i = 0; i < number; i++) {
    let result = item.slice(0 + index * i, index + index * i)
    results.push(result)
  }
  return results
}