jsPDF 分页分割问题踩坑

jsPDF 是一个基于用于生成 PDF 文档 JS 库

用例网上真的挺多的,一搜一大推,代码少,简洁明了,粘过来就能用,真省事。
就是用例好像都是千篇一律的风格,互抄的吗?

好吧,港正经话。

网上实例导出的是 A4 大小,用到的是 addImage,以及需要 html2canvas 库。

其实就是把需要导出的网页,利用 html2canvas 生成图片,在 addImage 至 PDF 中

至于为什么需要先转成图片呢,直接网页转不是很好吗,还能文字复制。因为不支中文,而且,样式还原度也不高。当然,执意于文本复制,网上也有解决方式(搜索关键字:jsPDF 中文)。

image.png
QQ图片20191210103437.jpg

分页问题

页面过长,就涉及分页问题。

因为 A4 是有固定页面大小的(宽:595.28, 高:841.89),保存的内容高度(缩放至A4宽度)超过该长度,则会出现截断问题。

image.png
QQ图片20191210103354.gif

解决方法

解决方式其实不多,分情况而定。

1. 不分页

其实最简单的方式是不分页。

QQ图片20191210103601.gif

不是吗?不分页,啥问题都没。

这个问题就是,为什么要分页?讲真,没有分页需求,也没有打印需求,没必要分页,不是吗?整个页面导成图片就行:

import jsPDF from 'jspdf'
import html2canvas from 'html2canvas'

function exportPDF (dom, filename) {
    const scale = 2

    // 滚动到顶部,避免打印不全
    document.documentElement.scrollTop = 0

    html2canvas(this.$refs.pdf, {
        allowTaint: true, // Whether to allow cross-origin images to taint the canvas
        scale // The scale to use for rendering. Defaults to the browsers device pixel ratio.
    }).then((canvas) => {
        const contentWidth = canvas.width / scale
        const contentHeight = canvas.height / scale
        const pdf = new jsPDF('', 'pt', [contentWidth, contentHeight])
        const pageData = canvas.toDataURL('image/jpeg', 1.0)

        pdf.addImage(pageData, 'JPEG', 0, 0, contentWidth, contentHeight)
        pdf.save(`${filename}.pdf`)
    })
}

2. 分页

好吧,还是兜到分页这个坑里。

2.1 纯静态页面

这个比较简单,根据 A4 的尺寸比例,预留好间隔,这样,裁剪的时候,便不会出现文本或者其他内容被裁成两半。

image.png
2.2 动态页面

这是最复杂的问题了吧,因为动态不好预估什么内容,什么宽高。

- 我们需要解决的问题是什么呢?
- 文本或者图片等被切成两半

- 文本或者图片等,这个等又是什么呢?
- 就是文字、图片、表格,本应该是一个整体的东西

好吧,有点眉目了。

页面上的内容,其实,就是一块块的块拼接在一起的,每一块,都是一个不可分割的单元,只要保证切开的地方,不是在单元格里面就行。

示意如下图,要且在单元格(内容)间隙处(绿线),而不是单元格里面(红线)。

image.png

那么,我怎么知道哪些是不可分割的啊,这代码没法写啊。

是的,代码还真不知道哪里能不能分割,除非你代码,简洁到没有嵌套,一个标签就是一个整体。

那,人肉标记呗。

人工标记最小单元格,给这个标签加上一个 class ,然后 js 获取这些元素,判断这些元素距离顶部的高度,以及自身的高度,来判断这个元素在那哪一页。

const A4_WIDTH = 595.28
const A4_HEIGHT = 841.89

function splitPage ($dom) {
    const pageOffsetTop = $dom.offsetTop
    const pageOffsetWidth = $dom.offsetWidth
    const pageOffsetHeight = $dom.offsetHeight
    const $unitElements = $dom.querySelectorAll('.minimum-unit')

    const peerPageHeight = pageOffsetWidth / A4_WIDTH * A4_HEIGHT // 获取缩放后的一页页面高度
    const pages = [
        [
            {
                top: 0, // 起点初始化
                offsetTop: 0
            }
        ]
    ]

    // 遍历最小单元格
    // 获取单元格底部距离顶部的高度 top,以及 offsetTop
    // 根据 top 值,算出该单元格的页码,放入数组 pages
    $unitElements.forEach($element => {
        const offsetTop = $element.offsetTop - pageOffsetTop
        const top = offsetTop + $element.offsetHeight
        const pageIndex = parseInt(top / peerPageHeight)

        // 新的一页
        if (typeof pages[pageIndex] === 'undefined') {
          pages[pageIndex] = []
        }

        pages[pageIndex].push({
            top,
            offsetTop
        })
    })

    console.log(pages)

    return pages
}

导出就是(导出代码有问题,缩放比列和 pt 有问题,待解决,仅供思路参考):

function exportPDF ($dom, filename) {
    // 滚动到页面顶部,避免导出不全
    document.documentElement.scrollTop = 0

    html2canvas($dom, {
        allowTaint: true,
        scale: 2
    }).then((canvas) => {
        const pdf = new jsPDF('', 'pt', 'a4')
        const contentWidth = canvas.width
        const contentHeight = canvas.height
        const pageData = canvas.toDataURL('image/jpeg', 1.0)
        const pageHeight = contentWidth / A4_WIDTH * A4_HEIGHT // 内容缩放后的高度
        const pages = splitPage()

        pages.forEach((page, index) => {
            const {offsetTop} = page[0]
            const {top} = page[page.length - 1]

            if (index > 0) {
                pdf.addPage()
            }

            pdf.addImage(pageData, 'JPEG', 0, -1 * offsetTop, A4_WIDTH, top)
        })
    
        pdf.save(`${filename}.pdf`)
    })
}

分割的代码待解决,先小计一下

QQ图片20191210103606.jpg

—— 2019/12/10 By Vinci, Mostly Sunny.

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,294评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,493评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,790评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,595评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,718评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,906评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,053评论 3 410
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,797评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,250评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,570评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,711评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,388评论 4 332
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,018评论 3 316
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,796评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,023评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,461评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,595评论 2 350

推荐阅读更多精彩内容