zoukankan      html  css  js  c++  java
  • JSPDF支持中文(思源黑体)采坑之旅,JSPDF中文字体乱码解决方案

    我拍个砖,通常标称自己文章完美解决何种问题的,往往就是解决不了任何问题!

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

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

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

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

    .

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

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

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

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

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

     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
    ode.exe" (
      "%~dp0
    ode.exe"  "%~dp0..umiinumi.js" %*
    ) ELSE (
      @SETLOCAL
      @SET PATHEXT=%PATHEXT:;.JS;=;%
      node --max-old-space-size=4096  "%~dp0..umiinumi.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');
  • 相关阅读:
    让Flask-admin支持markdown编辑器
    单例模式
    【Python】关于如何判断一个list是否为空的思考
    【Python】抽象工厂模式
    【Python篇】工厂模式
    【Python】直接赋值,深拷贝和浅拷贝
    【Python】可变对象和不可变对象
    【Python】__name__ 是什么?
    【Python】any() 或者 or
    [Python] list vs tupple
  • 原文地址:https://www.cnblogs.com/ww01/p/11496213.html
Copyright © 2011-2022 走看看