SpreadJS 导出PDF是一个强大的功能,解决了很多用户的实际问题,但是在导出PDF时有些问题始终困扰着很多开发者,例如排版、字体乱码等问题。那么本文就着重讲解一下字体乱码的解决办法。

问题分析

有些朋友会有疑问,同样是使用了printInfo实例来调整样式,为什么浏览器打印看着好好的字体,导出到PDF就乱码了呢?

这是由PDF文件的特殊性造成的。PDF是Portable Document Format的简称,意为“便携式文档格式”,它原生仅仅带有英文字体,不包含任何中文字体,因此当导出的内容中含有中文字体编码时,一定会显示乱码,所以通常情况下,我们都需要为PDF进行字体注册操作。

Spread JS 对于导出PDF时的字体乱码的处理方式,分为两个部分,一是表格内容,二是页眉页脚。下面我将利用一个代码示例来说明如何处理乱码。

示例下载与部署

由于字体文件比较大,因此示例项目采用网盘来分享,请访问:

链接:https://pan.baidu.com/s/1Cw33CHC-aE9YmGyhLBlYlw 密码:sq4v

下载完毕后,将得到的zip包解压到您系统的任意目录下,本例是直接解压到D盘根目录下。

这个示例是纯前端工程,可以直接运行,但是JS访问目录下的字体文件时,很大概率会被浏览器的安全策略所限制,这是前端语言无法避免的问题,所以我们先把项目部署到服务器。

本例直接采用了Win10系统的IIS服务器来部署,如果不了解如何部署IIS工程,请移步:

win10 IIS网站部署实录

部署成功后,访问localhost:[port num]/index.html即可访问到示例页面,本例设置端口是8899,即:http://localhost:8899/index.html

导出PDF示例

示例分析

为了把问题考虑得更全面,本例采用了多种字体和多种注册方法来全面展示如何解决导出PDF的乱码问题,字体包括微软雅黑和宋体,注册方法分为前端注册和Ajax访问后端字体文件注册两种方式。

  1. 前端字体注册:

    要实现前端注册字体,需要获取到对应字体文件的base64编码字符串,示例目录下的data/fonts.js中有两种字体的base64编码,分别是MTCORSVA和simkai,代码如下:

    function addFontsToFontManager(fontsObj) {
        var fonts = {
            normal: fontsObj["simkai.ttf"]
        };
        GC.Spread.Sheets.PDF.PDFFontsManager.fallbackFont = function (font) {
            var fontInfoArray = font.split(' ');
            var fontName = fontInfoArray[fontInfoArray.length - 1];
            if (fontName === 'mtcorsva') {
                return fonts.normal;
            }
        }
    }
    

    其中参数fontsObj就是fonts.js中定义的字体编码实例。此处实现了将simkai注册到PDF中,除此之外还将默认字体设置为mtcorsva。

  2. 访问后台字体文件执行注册:

    由于字体文件通常比较大,而前端注册字体的方法要求在页面加载时就要加载进来,这会导致前端加载时间长,影响性能。而且字体文件的base64编码通常也需要事先生成,这给开发同学们带来了不小的麻烦。

    后台字体文件注册可以解决这些问题,字体文件在服务器上存储,只有当需要导出PDF时才执行加载操作,简直完美解决问题!

    先看代码:

    function registerServerFont(name, type, serverPath) {
        var xhr = new XMLHttpRequest();
        xhr.open('GET', serverPath, true);
        xhr.responseType = 'blob';
    
        xhr.onload = function (e) {
            if (this.status == 200) {
                // get binary data as a response
                var blob = this.response;
    
                //将Blob 对象转换成 ArrayBuffer
                var reader = new FileReader();
                reader.onload = function (e) {
                    var fontrrayBuffer = reader.result;
                    var fonts = GC.Spread.Sheets.PDF.PDFFontsManager.getFont(name) || {};
                    fonts[type] = fontrrayBuffer;
                    GC.Spread.Sheets.PDF.PDFFontsManager.registerFont(name, fonts);
                }
                reader.readAsArrayBuffer(blob);
            }
        };
    
        xhr.send();
    }
    

    OK,熟悉Ajax的同学就明白了这里实际上采用了XMLHttpRequest对象的数据流读取了blob格式的字体文件,然后把这个流直接注册到PDFFontsManager中,实现了字体注册操作。

    调用方法很简单,如下:

    registerServerFont('微软雅黑', 'normal', 'font/msyh.ttf');
    

    第一个参数对应了PDF中的字体名称,最后一个参数指定了字体文件的相对路径(这就是为什么要先部署后才能访问)。

    如果设置的字体在后台字体库中没有怎么办?其实解决方法也很简单,我们的客户需要的首先应该是一个能够阅读的PDF文件,当服务器端没有对应的字体文件时,可以指定别的字体库来代替,例如:

    registerServerFont('Times New Roman', 'normal', 'font/simsun.ttf');
    

    这是我们小伙伴遇到的一个实际的问题,客户的Excel中显示的中文字体是Times New Roman,但是Times New Roman本身是英文字体库,并不包含中文,在这种情况下浏览器会自动指定一个字体,比如简宋,那么我们在注册PDF字体时也可以灵活应用。

  3. 页眉页脚乱码怎么办:

    有同学发现,导出PDF时如果设置了页眉页脚,这里出现乱码怎么解决呢?其实学习指南里有代码示例,只是不在PDF导出里,而是在自定制打印的“描述”标签页的最下边,如图:

    自定制打印的“描述”页

    其中的printInfo.headerCenter("&\"Comic Sans MS\"System Information");的参数中是以“&”开头,后面的Comic Sans MS被双引号引起来了,这里就制定了字体名称。 在本例中,我们稍稍修改一下代码,验证一下这个方法: 在432行处添加一行:

    <input type="text" id="headerCenter" value="表头内容" class="input" style="font-size: 16px">
    

    在168行处添加一行:

    printInfo.headerCenter('&"simsun"' + $("#headerCenter").val());
    

    再执行导出,OK,问题解决!

    表头字体注册示例

    注意:导出的PDF中仍会有一处乱码,这是有意留下的,这里的字体示例中没有注册,留给同学们练手。