jsPDF 文字、图片生成PDF(解决中文乱码)

JSPDF官网在线演示地址(不支持中文)

思源黑体字体库下载地址:https://gitee.com/ABCpril/SourceHansTtf    https://github.com/adobe-fonts/source-han-sans/blob/release/README.md (后面一个是完整的包、比较大,一般用前面一种)

JSPDF支持中文(思源黑体)采坑之旅,JSPDF中文字体乱码解决方案

众所周知,JSPDF是一个开源的,易用的,但是对中文支持非常差的PDF库。

下面,我教大家,如何在pdf中使用思源黑体。思源黑体是开源字体。思源黑体具有很广泛使用性,实用性,也是规避字体版权风险的重要选择!请严格按照我说的做!

1、准备思源黑体的ttf文件,不要用otf文件,如下

https://github.com/be5invis/source-han-sans-ttf/releases

我们挑其中的SourceHanSans-Bold.ttfSourceHanSans-Normal.ttf来使用,代表一粗一细。

2、把下载的字体命名统统改为小写,如下

 为什么改为小写,见 issues2465 ,命名为大写的统统失效~

 

解决中文乱码问题
1、去这里下载一个ttf字体文件,或者自己从别处下载(字体文件越小越好)
2、去这里将项目clone下来,打开 fontconverter/fontconverter.html,操作如下图

或者在这个网站进行转换https://rawgit.com/MrRio/jsPDF/master/fontconverter/fontconverter.html

注意,这个网站就算挂了,我们也可以在jspdf的源码里找到转换器 https://github.com/MrRio/jsPDF/blob/master/fontconverter/fontconverter.html

 转换成后把字体文件生成对应的js文件

 3、于是,我们得到这2个文件

 PS:字体是bold字体,网站的fontStyle你就选bold,normal也是这样!

用记事本(win)打开这2个文件,不要用编辑器,会异常卡,除非你内存高,mac爱什么打开什么打开,双击选中那串长的,ctrl+c。

 

你的项目新建font.js,内容如下

export function addfont(pdf) {

       var font = 'AADSSDDT12......'     // ←就是很长那串 
       pdf.addFileToVFS('bolds', font)  return true;
}

使用方法:(我的项目是ant-design pro 4.0)

import { addfont } from '@/font/font'
//前面只是添加了字体,还要注册字体,addfont第3个参数一定是normal,即使你add的字体是bold的,也要设置为normal
addfont(doc)
doc.addFont('bolds', 'b', 'normal')

//使用字体时,使用这句即可
doc.setFont('b');

坑:

1、autoTable需要使用默认字体,是一种叫做NotoSansCJKtc-Regular.ttf的字体,否则乱码,或者你改源码,使之兼容

 

 doc.addFont('NotoSansCJKtc-Regular.ttf', 'NotoSansCJKtc', 'normal');
 doc.setFont('NotoSansCJKtc');

2、因为字体文件太大,导致JS运行时内存溢出 JavaScript heap out of memory

FATAL ERROR: CALL_AND_RETRY_LAST Allocation failed - JavaScript heap out of memory

 

资料:https://stackoverflow.com/questions/38558989/node-js-heap-out-of-memory

解决办法:https://lwjandmy.github.io/myblog/articles/+.+category+.+%E7%BC%96%E7%A8%8BJavaScript+.+title+.+node%20%E5%87%BA%E7%8E%B0heap%20out%20of%20memory%E9%97%AE%E9%A2%98+.+createtime+.+20181228%E4%B8%80190612+.+lastmodifiedtime+.+20181228%E4%B8%80190612+.+.html

 

 由于我用了umi,因此改umi.cmd,如下

@IF EXIST "%~dp0\node.exe" (
  "%~dp0\node.exe"  "%~dp0\..\umi\bin\umi.js" %*
) ELSE (
  @SETLOCAL
  @SET PATHEXT=%PATHEXT:;.JS;=;%
  node --max-old-space-size=4096  "%~dp0\..\umi\bin\umi.js" %*
)

若你是用webpack,改webpack.cmd

3、最后不得不说句,我把字体放在前端项目,是因为我的项目要打包为electron的,若你的项目是发布到线上,最好做cdn或者考虑使用默认一种字体~!

由于字体文件实在太大,执行打包时必定触发V8的内存限制,我使用umi打包时,8G内存直接爆了,--max-old-space-size=7000也不起作用,换成mac的16G内存一样爆了,问题在哪?思维错了!我们不能苛求webpack/umi能够具有打包系统级文件的能力:如大型音视频,字体包,压缩文件,msi等,此时只有使用系统的文件管理能力来加持,因此,ele的原生能力就要发挥作用。

用到线程通信和node的文件读取能力即可

https://electronjs.org/docs/api/ipc-main

// 在主进程中.
const { ipcMain } = require('electron')
ipcMain.on('asynchronous-message', (event, arg) => {
  console.log(arg) // prints "ping"
  event.reply('asynchronous-reply', 'pong')
})

ipcMain.on('synchronous-message', (event, arg) => {
  console.log(arg) // prints "ping"
  event.returnValue = 'pong'
})
//在渲染器进程 (网页) 中。
const { ipcRenderer } = require('electron')
console.log(ipcRenderer.sendSync('synchronous-message', 'ping')) // prints "pong"

ipcRenderer.on('asynchronous-reply', (event, arg) => {
  console.log(arg) // prints "pong"
})
ipcRenderer.send('asynchronous-message', 'ping')

所谓渲染器进程就是你的前端项目,下面说一下的处理方式

1 准备字体文件

2.由于不是项目主要部分,而是支持性部分,因此我就用了一个回调地狱   /滑稽

var fs = require("fs");
ipcMain.on('asynchronous-message', (event, arg) => {
  console.log('main', arg) // 请求的消息

  // 使用通信方式输送字体给前端
  let filePath = path.join(__dirname, ".", "font/font.js");
  let filePath2 = path.join(__dirname, ".", "font/font2.js");
  let filePath3 = path.join(__dirname, ".", "font/font3.js");
  console.log(filePath, "filePath")

  fs.readFile(filePath, { encoding: "utf-8" }, function (err, fr) {
    //readFile回调函数
    // if (err) {
    //   console.log(err);
    // } 

    fs.readFile(filePath2, { encoding: "utf-8" }, function (err, fr2) {
      //readFile回调函数

      fs.readFile(filePath3, { encoding: "utf-8" }, function (err, fr3) {
        //readFile回调函数

        event.reply('asynchronous-reply', {
          addFont: fr,
          addFont2: fr2,
          addFont3: fr3,
        })
      })
    })
  })
})

前端调用

const { ipcRenderer } = require('electron') 

 ipcRenderer.send('asynchronous-message', 'ping')
 ipcRenderer.on('asynchronous-reply', (event, arg) => {
      // console.log('web', arg) // prints "pong"

      const {
        addFont,
        addFont2,
        addFont3
      } = arg

      this.setState({
        addFont,
        addFont2,
        addFont3
      })
})

使用字体

doc.addFileToVFS('bolds', this.state.addFont)
doc.addFileToVFS('normals', this.state.addFont2)

doc.addFont('bolds', 'b', 'normal')
doc.addFont('normals', 'n', 'normal')

...
doc.setFont('n');


...
doc.setFont('b');

上面解决方案参考来源:https://www.cnblogs.com/ww01/p/11496213.html

其他参考来源:https://www.jianshu.com/p/756141b50ad8   

https://blog.csdn.net/qq_43436008/article/details/134425543

https://www.jianshu.com/p/7e69e9f6191b          https://juejin.cn/post/6844903886994538510

http://easypeak.me/jsPDF-CustomFonts-support/#

 

完整的方法如下(注意字体的base64字符串是简写,并不完整):

引用字体的第一种方式

function demoUsingTTFFontImage() {
    // Don't forget, that there are CORS-Restrictions. So if you want to run it without a Server in your Browser you need to transform the image to a dataURL
    // Use http://dataurl.net/#dataurlmaker
    var doc = new jsPDF();
    //var font = 'data:application/x-font-ttf;charset=utf-8;base64,...' //字体的base64编码
    //jsPDF.API.addFileToVFS("SourceHanSansCN-Normal.ttf", font);
    //doc.addFont("ttc/SourceHanSansCN-Normal.ttf", "SourceHanSans", "normal");

    //doc.addFont('SourceHanSansCN-Normal.otf', 'custom', 'normal');

    //doc.setFont('SourceHanSans')


       var font = 'AAEAAAASAQAABAAg............';
    doc.addFileToVFS('sourcehansanscnnormal-normal.ttf', font);
    doc.addFont('sourcehansanscnnormal-normal.ttf', 'sourcehansanscnnormal', 'normal');

    doc.setFontSize(40);
    doc.text("Octonyan loves jsPDF范德萨", 30, 13);
    doc.setFontSize(20);
    doc.text("Octonyan loves jsPDF6666", 30, 29);
    doc.addImage("images/Octocat.jpg", "JPEG", 15, 40, 150, 70);
    doc.addImage("images/Octocat.jpg", "JPEG", 15, 70, 150, 70);

    doc.setFont("sourcehansanscnnormal", "normal"); // set font
    doc.setTextColor(255, 0, 0);
    doc.setFontSize(20);
    //doc.setFont('SourceHanSansCN-Normal');
    doc.text("556wenzd的d中国", 50, 123);
    doc.addImage("images/Octonyan.jpg", "JPEG", 15, 130, 190, 80);
     doc.save("test.pdf");
}

 引用字体的第二种方式:

function demoUsingTTFFontImage() {
    // Don't forget, that there are CORS-Restrictions. So if you want to run it without a Server in your Browser you need to transform the image to a dataURL
    // Use http://dataurl.net/#dataurlmaker
    var doc = new jsPDF();

    doc.addFont('ttc/sourcehansanscnnormal.ttf', 'sourcehansanscnnormal', 'normal');
    doc.setFont("sourcehansanscnnormal", "normal"); // set font

    doc.setFontSize(30);
    doc.text("Octonyan loves js", 30, 13);
    doc.text("PDF范德萨中国人", 20, 28);
    doc.setFontSize(20);
    doc.text("Octonyan loves jsPDF6666", 30, 36);
    doc.addImage("images/Octocat.jpg", "JPEG", 15, 40, 150, 70);
    doc.addImage("images/Octocat.jpg", "JPEG", 15, 70, 150, 70);
    
    doc.setTextColor(255, 0, 0);
    doc.setFontSize(20);
    //doc.setFont('SourceHanSansCN-Normal');
    doc.text("556wenzd的d中国", 50, 123);
    doc.addImage("images/Octonyan.jpg", "JPEG", 15, 130, 190, 80);
     doc.save("test.pdf");
}

 jspdf线上地址:<script src="https://cdnjs.cloudflare.com/ajax/libs/jspdf/1.3.4/jspdf.debug.js"></script>

报错:

jspdf.debug.js:10687 Font does not exist in FileInVFS, import fonts or remove declaration doc.addFont('ttc/sourcehansanscnnormal.ttf').
(anonymous) @ jspdf.debug.js:10687
PubSub.publish @ jspdf.debug.js:157
addFont @ jspdf.debug.js:647
API.addFont @ jspdf.debug.js:2374
(anonymous) @ echarts_to_pdf.html:83
dispatch @ jquery-1.7.1.min.js:2176
i @ jquery-1.7.1.min.js:1944
jspdf.debug.js:1573 Uncaught TypeError: Cannot read property 'widths' of undefined
    at getCharWidthsArray (jspdf.debug.js:1573)
    at getStringUnitWidth (jspdf.debug.js:1556)
    at Object.API.text (jspdf.debug.js:1700)
    at HTMLButtonElement.<anonymous> (echarts_to_pdf.html:86)
    at HTMLButtonElement.dispatch (jquery-1.7.1.min.js:2176)
    at HTMLButtonElement.i (jquery-1.7.1.min.js:1944)

上面表示需要中文支持,必须要把字体转换成base64:

jspdf主文件在线路径: <script src="https://cdnjs.cloudflare.com/ajax/libs/jspdf/1.3.4/jspdf.debug.js"></script>
必须要引入下面的js文件(配合解决简体中文乱码):
<script src="https://cdn.bootcss.com/jspdf/1.4.0/jspdf.debug.js"></script>
var font = 'AAAf....';

     pdf.addFileToVFS('sourcehansanscnnormal-normal.ttf', font);
     pdf.addFont('sourcehansanscnnormal-normal.ttf', 'sourcehansanscnnormal', 'normal');
     pdf.setFont("sourcehansanscnnormal", "normal");

1、去这里下载一个ttf字体文件,或者自己从别处下载(字体文件越小越好)
2、去这里将项目clone下来,打开 fontconverter/fontconverter.html,操作如下图

或者在这个网站进行转换https://rawgit.com/MrRio/jsPDF/master/fontconverter/fontconverter.html

注意,这个网站就算挂了,我们也可以在jspdf的源码里找到转换器 https://github.com/MrRio/jsPDF/blob/master/fontconverter/fontconverter.html

 转换成后把字体文件生成对应的js文件

 3、于是,我们得到这2个文件

posted on 2024-03-08 11:47  andydaopeng  阅读(2616)  评论(0编辑  收藏  举报

导航